Browse Source

fix: add Gemini prompt fallback from dynamicInputs, improve parallel batch error logging

- Resolve prompt from dynamicInputs.prompt in Gemini code path as fallback
- Return clear 400 error when prompt is provided but is not a string (corrupted data)
- Improve parallel batch error logging to include nodeId and nodeType for diagnosis
- Replace find() with for-loop to preserve index mapping between results and batch nodes
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
ccd6519512
  1. 19
      src/app/api/generate/route.ts
  2. 22
      src/store/workflowStore.ts

19
src/app/api/generate/route.ts

@ -432,10 +432,27 @@ export async function POST(request: NextRequest) {
// Use selectedModel.modelId if available (new format), fallback to legacy model field // Use selectedModel.modelId if available (new format), fallback to legacy model field
const geminiModel = (selectedModel?.modelId as ModelType) || model; const geminiModel = (selectedModel?.modelId as ModelType) || model;
// Resolve prompt: use top-level prompt, fall back to dynamicInputs.prompt
// This handles cases where the prompt arrives via dynamicInputs instead of top-level
let resolvedPrompt = prompt;
if (!resolvedPrompt && dynamicInputs?.prompt) {
resolvedPrompt = Array.isArray(dynamicInputs.prompt)
? dynamicInputs.prompt[0]
: dynamicInputs.prompt;
}
// Validate: if a prompt was provided but isn't a string (corrupted data), return clear error
// If no prompt provided but images exist, that's valid (image-to-image)
if (resolvedPrompt !== undefined && resolvedPrompt !== null && typeof resolvedPrompt !== 'string') {
return NextResponse.json<GenerateResponse>(
{ success: false, error: "prompt must be a string" },
{ status: 400 }
);
}
return await generateWithGemini( return await generateWithGemini(
requestId, requestId,
geminiApiKey, geminiApiKey,
prompt, resolvedPrompt,
images || [], images || [],
geminiModel, geminiModel,
aspectRatio, aspectRatio,

22
src/store/workflowStore.ts

@ -962,21 +962,21 @@ export const useWorkflowStore = create<WorkflowStore>((set, get) => ({
batch.map((node) => executeSingleNode(node, abortController.signal)) batch.map((node) => executeSingleNode(node, abortController.signal))
); );
// Check for failures (fail-fast behavior) // Check for failures with node context (fail-fast behavior)
const failed = results.find( for (let i = 0; i < results.length; i++) {
(r): r is PromiseRejectedResult => const r = results[i];
r.status === 'rejected' && if (r.status === 'rejected' &&
!(r.reason instanceof DOMException && r.reason.name === 'AbortError') !(r.reason instanceof DOMException && r.reason.name === 'AbortError')) {
); const failedNode = batch[i];
if (failed) {
// Log the failure and abort remaining executions
logger.error('workflow.error', 'Node execution failed in parallel batch', { logger.error('workflow.error', 'Node execution failed in parallel batch', {
level: levelIdx, level: levelIdx,
error: failed.reason instanceof Error ? failed.reason.message : String(failed.reason), nodeId: failedNode.id,
nodeType: failedNode.type,
error: r.reason instanceof Error ? r.reason.message : String(r.reason),
}); });
abortController.abort(); abortController.abort();
throw failed.reason; throw r.reason;
}
} }
} }
} }

Loading…
Cancel
Save