From 0da1f84e4e7bebfcde950b988db679f6f8a81e27 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Sat, 11 Apr 2026 21:45:06 +1200 Subject: [PATCH] feat: add fallback settings tab to LLM generate node Adds SettingsTabBar to LLMGenerateNode with temperature and max tokens controls for the fallback model. Updates llmGenerateExecutor to use fallbackParameters override when running the fallback model. Co-Authored-By: Claude Sonnet 4.5 --- src/components/nodes/LLMGenerateNode.tsx | 181 ++++++++++++++------- src/store/execution/llmGenerateExecutor.ts | 11 +- src/types/nodes.ts | 1 + 3 files changed, 132 insertions(+), 61 deletions(-) diff --git a/src/components/nodes/LLMGenerateNode.tsx b/src/components/nodes/LLMGenerateNode.tsx index 05697b19..e22e2979 100644 --- a/src/components/nodes/LLMGenerateNode.tsx +++ b/src/components/nodes/LLMGenerateNode.tsx @@ -7,6 +7,7 @@ import { useWorkflowStore } from "@/store/workflowStore"; import { LLMGenerateNodeData, LLMProvider, LLMModelType } from "@/types"; import { useInlineParameters } from "@/hooks/useInlineParameters"; import { InlineParameterPanel } from "./InlineParameterPanel"; +import { SettingsTabBar } from "./SettingsTabBar"; // LLM providers and models const LLM_PROVIDERS: { value: LLMProvider; label: string }[] = [ @@ -54,6 +55,7 @@ export function LLMGenerateNode({ id, data, selected }: NodeProps("primary"); const handleCopyOutput = useCallback(async () => { if (nodeData.outputText) { @@ -129,68 +131,131 @@ export function LLMGenerateNode({ id, data, selected }: NodeProps - {/* LLM-specific controls */} -
- {/* Provider */} -
- - -
+ {/* Tab bar for primary/fallback settings */} + {nodeData.fallbackModel && ( + + )} - {/* Model */} -
- - -
+ {/* Primary tab: LLM-specific controls */} + {settingsTab === "primary" && ( +
+ {/* Provider */} +
+ + +
- {/* Temperature */} -
- - -
+ {/* Model */} +
+ + +
- {/* Max Tokens */} -
- - + {/* Temperature */} +
+ + +
+ + {/* Max Tokens */} +
+ + +
-
+ )} + + {/* Fallback tab: temperature + maxTokens for fallback model */} + {settingsTab === "fallback" && nodeData.fallbackModel && (() => { + const fbParams = nodeData.fallbackParameters || {}; + const fbTemp = (fbParams.temperature as number | undefined) ?? 0.7; + const fbMaxTokens = (fbParams.maxTokens as number | undefined) ?? 2048; + const fbProvider = nodeData.fallbackModel!.provider; + const isAnthropicFb = fbProvider === "anthropic"; + + return ( +
+ {/* Read-only model display */} +
+ + {nodeData.fallbackModel!.displayName} +
+ + {/* Temperature */} +
+ + updateNodeData(id, { fallbackParameters: { ...fbParams, temperature: parseFloat(e.target.value) } })} + className="nodrag nopan w-full h-1 bg-neutral-700 rounded-lg appearance-none cursor-pointer accent-blue-500" + /> +
+ + {/* Max Tokens */} +
+ + updateNodeData(id, { fallbackParameters: { ...fbParams, maxTokens: parseInt(e.target.value, 10) } })} + className="nodrag nopan w-full h-1 bg-neutral-700 rounded-lg appearance-none cursor-pointer accent-blue-500" + /> +
+
+ ); + })()} ) : undefined} > diff --git a/src/store/execution/llmGenerateExecutor.ts b/src/store/execution/llmGenerateExecutor.ts index f52b2a71..3d8e7fa7 100644 --- a/src/store/execution/llmGenerateExecutor.ts +++ b/src/store/execution/llmGenerateExecutor.ts @@ -77,11 +77,14 @@ export async function executeLlmGenerate( error: null, }); - const runOnce = async (modelToUse: SelectedModel): Promise => { + const runOnce = async (modelToUse: SelectedModel, parametersOverride?: Record): Promise => { const llmProvider = providerTypeToLlmProvider(modelToUse.provider); const llmModel = modelToUse.modelId as LLMModelType; const headers = buildLlmHeaders(llmProvider, providerSettings); + const temperature = (parametersOverride?.temperature as number | undefined) ?? nodeData.temperature; + const maxTokens = (parametersOverride?.maxTokens as number | undefined) ?? nodeData.maxTokens; + try { const response = await fetch("/api/llm", { method: "POST", @@ -91,8 +94,8 @@ export async function executeLlmGenerate( ...(images.length > 0 && { images }), provider: llmProvider, model: llmModel, - temperature: nodeData.temperature, - maxTokens: nodeData.maxTokens, + temperature, + maxTokens, }), ...(signal ? { signal } : {}), }); @@ -165,7 +168,9 @@ export async function executeLlmGenerate( nodeId: node.id, primary: primaryModel, fallback: nodeData.fallbackModel, + fallbackParameters: nodeData.fallbackParameters, updateNodeData, runOnce, + clearOutput: { outputText: null }, }); } diff --git a/src/types/nodes.ts b/src/types/nodes.ts index 49cc4b73..901dafae 100644 --- a/src/types/nodes.ts +++ b/src/types/nodes.ts @@ -305,6 +305,7 @@ export interface LLMGenerateNodeData extends BaseNodeData { model: LLMModelType; temperature: number; maxTokens: number; + fallbackParameters?: Record; // Parameters for fallback model (temperature, maxTokens) parametersExpanded?: boolean; // Collapse state for inline parameter display _settingsPanelHeight?: number; // Measured settings panel height for reload correction status: NodeStatus;