Browse Source

feat(ENHANCE-001): store input schema in node data

- Add ModelInputDef type to node data interfaces
- Update ModelParameters to pass inputs via onInputsLoaded callback
- GenerateImageNode and GenerateVideoNode store input schema

This sets up the foundation for dynamic handle rendering.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 6 months ago
parent
commit
0285d6125c
  1. 11
      src/components/nodes/GenerateImageNode.tsx
  2. 11
      src/components/nodes/GenerateVideoNode.tsx
  3. 12
      src/components/nodes/ModelParameters.tsx
  4. 11
      src/types/index.ts

11
src/components/nodes/GenerateImageNode.tsx

@ -5,7 +5,7 @@ import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
import { BaseNode } from "./BaseNode";
import { ModelParameters } from "./ModelParameters";
import { useWorkflowStore, saveNanoBananaDefaults } from "@/store/workflowStore";
import { NanoBananaNodeData, AspectRatio, Resolution, ModelType, ProviderType, SelectedModel } from "@/types";
import { NanoBananaNodeData, AspectRatio, Resolution, ModelType, ProviderType, SelectedModel, ModelInputDef } from "@/types";
import { ProviderModel, ModelCapability } from "@/lib/providers/types";
// All 10 aspect ratios supported by both models
@ -210,6 +210,14 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
[id, updateNodeData]
);
// Handle inputs loaded from schema
const handleInputsLoaded = useCallback(
(inputs: ModelInputDef[]) => {
updateNodeData(id, { inputSchema: inputs });
},
[id, updateNodeData]
);
// Handle parameters expand/collapse - resize node height
const { setNodes } = useReactFlow();
const handleParametersExpandChange = useCallback(
@ -559,6 +567,7 @@ export function GenerateImageNode({ id, data, selected }: NodeProps<NanoBananaNo
parameters={nodeData.parameters || {}}
onParametersChange={handleParametersChange}
onExpandChange={handleParametersExpandChange}
onInputsLoaded={handleInputsLoaded}
/>
)}
</>

11
src/components/nodes/GenerateVideoNode.tsx

@ -5,7 +5,7 @@ import { Handle, Position, NodeProps, Node, useReactFlow } from "@xyflow/react";
import { BaseNode } from "./BaseNode";
import { ModelParameters } from "./ModelParameters";
import { useWorkflowStore } from "@/store/workflowStore";
import { GenerateVideoNodeData, ProviderType, SelectedModel } from "@/types";
import { GenerateVideoNodeData, ProviderType, SelectedModel, ModelInputDef } from "@/types";
import { ProviderModel, ModelCapability } from "@/lib/providers/types";
// Video generation capabilities
@ -122,6 +122,14 @@ export function GenerateVideoNode({ id, data, selected }: NodeProps<GenerateVide
[id, updateNodeData]
);
// Handle inputs loaded from schema
const handleInputsLoaded = useCallback(
(inputs: ModelInputDef[]) => {
updateNodeData(id, { inputSchema: inputs });
},
[id, updateNodeData]
);
// Handle parameters expand/collapse - resize node height
const { setNodes } = useReactFlow();
const handleParametersExpandChange = useCallback(
@ -325,6 +333,7 @@ export function GenerateVideoNode({ id, data, selected }: NodeProps<GenerateVide
parameters={nodeData.parameters || {}}
onParametersChange={handleParametersChange}
onExpandChange={handleParametersExpandChange}
onInputsLoaded={handleInputsLoaded}
/>
)}
</div>

12
src/components/nodes/ModelParameters.tsx

@ -1,7 +1,7 @@
"use client";
import { useState, useEffect, useCallback } from "react";
import { ProviderType } from "@/types";
import { ProviderType, ModelInputDef } from "@/types";
import { ModelParameter } from "@/lib/providers/types";
import { useWorkflowStore } from "@/store/workflowStore";
@ -11,6 +11,7 @@ interface ModelParametersProps {
parameters: Record<string, unknown>;
onParametersChange: (parameters: Record<string, unknown>) => void;
onExpandChange?: (expanded: boolean, parameterCount: number) => void;
onInputsLoaded?: (inputs: ModelInputDef[]) => void;
}
/**
@ -24,6 +25,7 @@ export function ModelParameters({
parameters,
onParametersChange,
onExpandChange,
onInputsLoaded,
}: ModelParametersProps) {
const [schema, setSchema] = useState<ModelParameter[]>([]);
const [isLoading, setIsLoading] = useState(false);
@ -35,6 +37,7 @@ export function ModelParameters({
useEffect(() => {
if (!modelId || provider === "gemini") {
setSchema([]);
onInputsLoaded?.([]);
return;
}
@ -64,6 +67,11 @@ export function ModelParameters({
const data = await response.json();
setSchema(data.parameters || []);
// Pass inputs to parent for dynamic handle rendering
if (data.inputs && onInputsLoaded) {
onInputsLoaded(data.inputs);
}
} catch (err) {
console.error("Failed to fetch model schema:", err);
setError(err instanceof Error ? err.message : "Failed to fetch schema");
@ -74,7 +82,7 @@ export function ModelParameters({
};
fetchSchema();
}, [modelId, provider, providerSettings]);
}, [modelId, provider, providerSettings, onInputsLoaded]);
const handleParameterChange = useCallback(
(name: string, value: unknown) => {

11
src/types/index.ts

@ -133,6 +133,15 @@ export interface CarouselImageItem {
model: ModelType;
}
// Model input definition for dynamic handles
export interface ModelInputDef {
name: string;
type: "image" | "text";
required: boolean;
label: string;
description?: string;
}
// Nano Banana Node Data (Image Generation)
export interface NanoBananaNodeData extends BaseNodeData {
inputImages: string[]; // Now supports multiple images
@ -146,6 +155,7 @@ export interface NanoBananaNodeData extends BaseNodeData {
selectedModel?: SelectedModel; // Multi-provider model selection (optional for backward compat)
useGoogleSearch: boolean; // Only available for Nano Banana Pro
parameters?: Record<string, unknown>; // Model-specific parameters for external providers
inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles
status: NodeStatus;
error: string | null;
imageHistory: CarouselImageItem[]; // Carousel history (IDs only)
@ -161,6 +171,7 @@ export interface GenerateVideoNodeData extends BaseNodeData {
outputVideoRef?: string; // External video reference for storage optimization
selectedModel?: SelectedModel; // Required for video generation (no legacy fallback)
parameters?: Record<string, unknown>; // Model-specific parameters
inputSchema?: ModelInputDef[]; // Model's input schema for dynamic handles
status: NodeStatus;
error: string | null;
}

Loading…
Cancel
Save