"use client"; import { useCallback, useState } from "react"; import { Handle, Position, NodeProps, Node } from "@xyflow/react"; import { BaseNode } from "./BaseNode"; import { useWorkflowStore } from "@/store/workflowStore"; import { LLMGenerateNodeData, LLMProvider, LLMModelType } from "@/types"; import { useInlineParameters } from "@/hooks/useInlineParameters"; import { InlineParameterPanel } from "./InlineParameterPanel"; // LLM providers and models const LLM_PROVIDERS: { value: LLMProvider; label: string }[] = [ { value: "google", label: "Google" }, { value: "openai", label: "OpenAI" }, { value: "anthropic", label: "Anthropic" }, ]; const LLM_MODELS: Record = { google: [ { value: "gemini-3-flash-preview", label: "Gemini 3 Flash" }, { value: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }, { value: "gemini-3-pro-preview", label: "Gemini 3.0 Pro" }, { value: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro" }, ], openai: [ { value: "gpt-4.1-mini", label: "GPT-4.1 Mini" }, { value: "gpt-4.1-nano", label: "GPT-4.1 Nano" }, ], anthropic: [ { value: "claude-sonnet-4.5", label: "Claude Sonnet 4.5" }, { value: "claude-haiku-4.5", label: "Claude Haiku 4.5" }, { value: "claude-opus-4.6", label: "Claude Opus 4.6" }, ], }; type LLMGenerateNodeType = Node; export function LLMGenerateNode({ id, data, selected }: NodeProps) { const nodeData = data; const updateNodeData = useWorkflowStore((state) => state.updateNodeData); const regenerateNode = useWorkflowStore((state) => state.regenerateNode); const isRunning = useWorkflowStore((state) => state.isRunning); // Inline parameters infrastructure const { inlineParametersEnabled } = useInlineParameters(); const handleRegenerate = useCallback(() => { regenerateNode(id); }, [id, regenerateNode]); const handleClearOutput = useCallback(() => { updateNodeData(id, { outputText: null, status: "idle", error: null }); }, [id, updateNodeData]); const [copied, setCopied] = useState(false); const handleCopyOutput = useCallback(async () => { if (nodeData.outputText) { try { await navigator.clipboard.writeText(nodeData.outputText); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch (err) { console.error("Failed to copy text:", err); } } }, [nodeData.outputText]); // Inline parameters: compute collapse state and toggle handler const isParamsExpanded = nodeData.parametersExpanded ?? true; // default expanded const handleToggleParams = useCallback(() => { const nodes = useWorkflowStore.getState().nodes; const node = nodes.find(n => n.id === id); const currentExpanded = (node?.data as LLMGenerateNodeData)?.parametersExpanded ?? true; updateNodeData(id, { parametersExpanded: !currentExpanded }); }, [id, updateNodeData]); // LLM parameter handlers const handleProviderChange = useCallback( (e: React.ChangeEvent) => { const newProvider = e.target.value as LLMProvider; const firstModelForProvider = LLM_MODELS[newProvider][0].value; const updates: Partial = { provider: newProvider, model: firstModelForProvider, }; if (newProvider === "anthropic" && (nodeData.temperature || 0.7) > 1) { updates.temperature = 1; } updateNodeData(id, updates); }, [id, nodeData.temperature, updateNodeData] ); const handleModelChange = useCallback( (e: React.ChangeEvent) => { updateNodeData(id, { model: e.target.value as LLMModelType }); }, [id, updateNodeData] ); const handleTemperatureChange = useCallback( (e: React.ChangeEvent) => { updateNodeData(id, { temperature: parseFloat(e.target.value) }); }, [id, updateNodeData] ); const handleMaxTokensChange = useCallback( (e: React.ChangeEvent) => { updateNodeData(id, { maxTokens: parseInt(e.target.value, 10) }); }, [id, updateNodeData] ); const provider = nodeData.provider || "google"; const availableModels = LLM_MODELS[provider] || LLM_MODELS.google; return ( {/* Image input - optional */} {/* Text input */} {/* Text output */}
{nodeData.status === "loading" ? (
) : nodeData.status === "error" ? (
Generation failed
) : nodeData.outputText ? (

{nodeData.outputText}

) : (
Run to generate
)}
{/* Inline parameter panel */} {inlineParametersEnabled && ( {/* LLM-specific controls */}
{/* Provider */}
{/* Model */}
{/* Temperature */}
{/* Max Tokens */}
)}
); }