diff --git a/src/app/api/generate/route.ts b/src/app/api/generate/route.ts index 9e2337c2..fbc11af3 100644 --- a/src/app/api/generate/route.ts +++ b/src/app/api/generate/route.ts @@ -33,7 +33,7 @@ interface MultiProviderGenerateRequest extends GenerateRequest { selectedModel?: SelectedModel; parameters?: Record; /** Dynamic inputs from schema-based connections (e.g., image_url, tail_image_url, prompt) */ - dynamicInputs?: Record; + dynamicInputs?: Record; } /** @@ -1273,7 +1273,11 @@ export async function POST(request: NextRequest) { // - Provided via dynamicInputs // - Images are provided (image-to-video/image-to-image models) // - Dynamic inputs contain image frames (first_frame, last_frame, etc.) - const hasPrompt = prompt || (dynamicInputs && dynamicInputs.prompt); + const hasPrompt = prompt || (dynamicInputs && ( + typeof dynamicInputs.prompt === 'string' + ? dynamicInputs.prompt + : Array.isArray(dynamicInputs.prompt) && dynamicInputs.prompt.length > 0 + )); const hasImages = (images && images.length > 0); const hasImageInputs = dynamicInputs && Object.keys(dynamicInputs).some(key => key.includes('frame') || key.includes('image') @@ -1311,14 +1315,14 @@ export async function POST(request: NextRequest) { const processedImages: string[] = images ? [...images] : []; // Process dynamicInputs: filter empty values, keep Data URIs - let processedDynamicInputs: Record | undefined = undefined; + let processedDynamicInputs: Record | undefined = undefined; if (dynamicInputs) { processedDynamicInputs = {}; for (const key of Object.keys(dynamicInputs)) { const value = dynamicInputs[key]; - // Skip empty/null/undefined values + // Skip empty/null/undefined values (arrays pass through) if (value === null || value === undefined || value === '') { continue; } @@ -1395,14 +1399,14 @@ export async function POST(request: NextRequest) { const processedImages: string[] = images ? [...images] : []; // Process dynamicInputs: filter empty values - let processedDynamicInputs: Record | undefined = undefined; + let processedDynamicInputs: Record | undefined = undefined; if (dynamicInputs) { processedDynamicInputs = {}; for (const key of Object.keys(dynamicInputs)) { const value = dynamicInputs[key]; - // Skip empty/null/undefined values + // Skip empty/null/undefined values (arrays pass through) if (value === null || value === undefined || value === '') { continue; } diff --git a/src/lib/providers/types.ts b/src/lib/providers/types.ts index ff0f67a6..2280da3d 100644 --- a/src/lib/providers/types.ts +++ b/src/lib/providers/types.ts @@ -87,7 +87,7 @@ export interface GenerationInput { /** Model-specific parameters (varies by provider/model) */ parameters?: Record; /** Dynamic inputs mapped from schema (e.g., { "image_url": "data:...", "tail_image_url": "data:..." }) */ - dynamicInputs?: Record; + dynamicInputs?: Record; } /** diff --git a/src/store/workflowStore.ts b/src/store/workflowStore.ts index 04cc6d33..ba460d5d 100644 --- a/src/store/workflowStore.ts +++ b/src/store/workflowStore.ts @@ -132,7 +132,7 @@ interface WorkflowStore { // Helpers getNodeById: (id: string) => WorkflowNode | undefined; - getConnectedInputs: (nodeId: string) => { images: string[]; videos: string[]; text: string | null; dynamicInputs: Record }; + getConnectedInputs: (nodeId: string) => { images: string[]; videos: string[]; text: string | null; dynamicInputs: Record }; validateWorkflow: () => { valid: boolean; errors: string[] }; // Global Image History @@ -745,7 +745,7 @@ export const useWorkflowStore = create((set, get) => ({ const images: string[] = []; const videos: string[] = []; let text: string | null = null; - const dynamicInputs: Record = {}; + const dynamicInputs: Record = {}; // Get the target node to check for inputSchema const targetNode = nodes.find((n) => n.id === nodeId); @@ -825,7 +825,15 @@ export const useWorkflowStore = create((set, get) => ({ // Map normalized handle ID to schema name for dynamicInputs // This allows API to receive schema-specific parameter names if (handleId && handleToSchemaName[handleId]) { - dynamicInputs[handleToSchemaName[handleId]] = value; + const schemaName = handleToSchemaName[handleId]; + const existing = dynamicInputs[schemaName]; + if (existing !== undefined) { + dynamicInputs[schemaName] = Array.isArray(existing) + ? [...existing, value] + : [existing, value]; + } else { + dynamicInputs[schemaName] = value; + } } // Route to typed arrays based on source output type @@ -1051,7 +1059,10 @@ export const useWorkflowStore = create((set, get) => ({ const { images, text, dynamicInputs } = getConnectedInputs(node.id); // For dynamic inputs, check if we have at least a prompt - const promptText = text || dynamicInputs.prompt || null; + const promptFromDynamic = Array.isArray(dynamicInputs.prompt) + ? dynamicInputs.prompt[0] + : dynamicInputs.prompt; + const promptText = text || promptFromDynamic || null; if (!promptText) { logger.error('node.error', 'nanoBanana node missing text input', { nodeId: node.id,