Skip to content

Commit 7b73dfb

Browse files
fix(autofill-env-vars): simplify/remove logic to not cause useEffect overwrites (#726)
* fix(autofill-env-vars): simplify logic to not cause useEffect overwrites * remove useless comments
1 parent d7a2c07 commit 7b73dfb

File tree

12 files changed

+35
-533
lines changed

12 files changed

+35
-533
lines changed

apps/sim/app/api/users/me/settings/route.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const logger = createLogger('UserSettingsAPI')
1212
const SettingsSchema = z.object({
1313
theme: z.enum(['system', 'light', 'dark']).optional(),
1414
autoConnect: z.boolean().optional(),
15-
autoFillEnvVars: z.boolean().optional(),
15+
autoFillEnvVars: z.boolean().optional(), // DEPRECATED: kept for backwards compatibility
1616
autoPan: z.boolean().optional(),
1717
consoleExpandedByDefault: z.boolean().optional(),
1818
telemetryEnabled: z.boolean().optional(),
@@ -31,7 +31,7 @@ const SettingsSchema = z.object({
3131
const defaultSettings = {
3232
theme: 'system',
3333
autoConnect: true,
34-
autoFillEnvVars: true,
34+
autoFillEnvVars: true, // DEPRECATED: kept for backwards compatibility, always true
3535
autoPan: true,
3636
consoleExpandedByDefault: true,
3737
telemetryEnabled: true,
@@ -65,7 +65,7 @@ export async function GET() {
6565
data: {
6666
theme: userSettings.theme,
6767
autoConnect: userSettings.autoConnect,
68-
autoFillEnvVars: userSettings.autoFillEnvVars,
68+
autoFillEnvVars: userSettings.autoFillEnvVars, // DEPRECATED: kept for backwards compatibility
6969
autoPan: userSettings.autoPan,
7070
consoleExpandedByDefault: userSettings.consoleExpandedByDefault,
7171
telemetryEnabled: userSettings.telemetryEnabled,

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import { cn } from '@/lib/utils'
1717
import { getAllBlocks } from '@/blocks'
1818
import { getProviderFromModel, supportsToolUsageControl } from '@/providers/utils'
1919
import { useCustomToolsStore } from '@/stores/custom-tools/store'
20-
import { useGeneralStore } from '@/stores/settings/general/store'
2120
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
2221
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
2322
import {
@@ -400,7 +399,6 @@ export function ToolInput({
400399
const isWide = useWorkflowStore((state) => state.blocks[blockId]?.isWide)
401400
const customTools = useCustomToolsStore((state) => state.getAllTools())
402401
const subBlockStore = useSubBlockStore()
403-
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
404402

405403
// Get the current model from the 'model' subblock
406404
const modelValue = useSubBlockStore.getState().getValue(blockId, 'model')
@@ -507,26 +505,13 @@ export function ToolInput({
507505
return block.tools.access[0]
508506
}
509507

510-
// Initialize tool parameters with auto-fill if enabled
508+
// Initialize tool parameters - no autofill, just return empty params
511509
const initializeToolParams = (
512510
toolId: string,
513511
params: ToolParameterConfig[],
514512
instanceId?: string
515513
): Record<string, string> => {
516-
const initialParams: Record<string, string> = {}
517-
518-
// Only auto-fill parameters if the setting is enabled
519-
if (isAutoFillEnvVarsEnabled) {
520-
// For each parameter, check if we have a stored/resolved value
521-
params.forEach((param) => {
522-
const resolvedValue = subBlockStore.resolveToolParamValue(toolId, param.id, instanceId)
523-
if (resolvedValue) {
524-
initialParams[param.id] = resolvedValue
525-
}
526-
})
527-
}
528-
529-
return initialParams
514+
return {}
530515
}
531516

532517
const handleSelectTool = (toolBlock: (typeof toolBlocks)[0]) => {
@@ -682,11 +667,6 @@ export function ToolInput({
682667

683668
const tool = selectedTools[toolIndex]
684669

685-
// Store the value in the tool params store for future use
686-
if (paramValue.trim()) {
687-
subBlockStore.setToolParam(tool.toolId, paramId, paramValue)
688-
}
689-
690670
// Update the value in the workflow
691671
setStoreValue(
692672
selectedTools.map((tool, index) =>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/hooks/use-sub-block-value.ts

Lines changed: 23 additions & 215 deletions
Original file line numberDiff line numberDiff line change
@@ -3,161 +3,12 @@ import { isEqual } from 'lodash'
33
import { createLogger } from '@/lib/logs/console-logger'
44
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
55
import { getProviderFromModel } from '@/providers/utils'
6-
import { useGeneralStore } from '@/stores/settings/general/store'
76
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
87
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
98
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
109

1110
const logger = createLogger('SubBlockValue')
1211

13-
// Helper function to dispatch collaborative subblock updates
14-
const dispatchSubblockUpdate = (blockId: string, subBlockId: string, value: any) => {
15-
const event = new CustomEvent('update-subblock-value', {
16-
detail: {
17-
blockId,
18-
subBlockId,
19-
value,
20-
},
21-
})
22-
window.dispatchEvent(event)
23-
}
24-
25-
/**
26-
* Helper to handle API key auto-fill for provider-based blocks
27-
* Used for agent, router, evaluator, and any other blocks that use LLM providers
28-
*/
29-
function handleProviderBasedApiKey(
30-
blockId: string,
31-
subBlockId: string,
32-
modelValue: string | null | undefined,
33-
storeValue: any,
34-
isModelChange = false
35-
) {
36-
// Only proceed if we have a model selected
37-
if (!modelValue) return
38-
39-
// Get the provider for this model
40-
const provider = getProviderFromModel(modelValue)
41-
42-
// Skip if we couldn't determine a provider
43-
if (!provider || provider === 'ollama') return
44-
45-
const subBlockStore = useSubBlockStore.getState()
46-
const isAutoFillEnabled = useGeneralStore.getState().isAutoFillEnvVarsEnabled
47-
48-
// Try to get a saved API key for this provider (only if auto-fill is enabled)
49-
const savedValue = isAutoFillEnabled
50-
? subBlockStore.resolveToolParamValue(provider, 'apiKey', blockId)
51-
: null
52-
53-
// If we have a valid saved API key and auto-fill is enabled, use it
54-
if (savedValue && savedValue !== '' && isAutoFillEnabled) {
55-
// Only update if the current value is different to avoid unnecessary updates
56-
if (storeValue !== savedValue) {
57-
dispatchSubblockUpdate(blockId, subBlockId, savedValue)
58-
}
59-
} else if (isModelChange && (!storeValue || storeValue === '')) {
60-
// Only clear the field when switching models AND the field is already empty
61-
// Don't clear existing user-entered values on initial load
62-
dispatchSubblockUpdate(blockId, subBlockId, '')
63-
}
64-
// If no saved value and this is initial load, preserve existing value
65-
}
66-
67-
/**
68-
* Helper to handle API key auto-fill for non-agent blocks
69-
*/
70-
function handleStandardBlockApiKey(
71-
blockId: string,
72-
subBlockId: string,
73-
blockType: string | undefined,
74-
storeValue: any
75-
) {
76-
if (!blockType) return
77-
78-
const subBlockStore = useSubBlockStore.getState()
79-
80-
// Only auto-fill if the field is empty
81-
if (!storeValue || storeValue === '') {
82-
// Pass the blockId as instanceId to check if this specific instance has been cleared
83-
const savedValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
84-
85-
if (savedValue && savedValue !== '' && savedValue !== storeValue) {
86-
// Auto-fill the API key from the param store
87-
dispatchSubblockUpdate(blockId, subBlockId, savedValue)
88-
}
89-
}
90-
// Handle environment variable references
91-
else if (
92-
storeValue &&
93-
typeof storeValue === 'string' &&
94-
storeValue.startsWith('{{') &&
95-
storeValue.endsWith('}}')
96-
) {
97-
// Pass the blockId as instanceId
98-
const currentValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
99-
100-
if (currentValue !== storeValue) {
101-
// If we got a replacement or null, update the field
102-
if (currentValue) {
103-
// Replacement found - update to new reference
104-
dispatchSubblockUpdate(blockId, subBlockId, currentValue)
105-
}
106-
}
107-
}
108-
}
109-
110-
/**
111-
* Helper to store API key values
112-
*/
113-
function storeApiKeyValue(
114-
blockId: string,
115-
blockType: string | undefined,
116-
modelValue: string | null | undefined,
117-
newValue: any,
118-
storeValue: any
119-
) {
120-
if (!blockType) return
121-
122-
const subBlockStore = useSubBlockStore.getState()
123-
124-
// Check if this is user explicitly clearing a field that had a value
125-
// We only want to mark it as cleared if it's a user action, not an automatic
126-
// clearing from model switching
127-
if (
128-
storeValue &&
129-
storeValue !== '' &&
130-
(newValue === null || newValue === '' || String(newValue).trim() === '')
131-
) {
132-
// Mark this specific instance as cleared so we don't auto-fill it
133-
subBlockStore.markParamAsCleared(blockId, 'apiKey')
134-
return
135-
}
136-
137-
// Only store non-empty values
138-
if (!newValue || String(newValue).trim() === '') return
139-
140-
// If user enters a value, we should clear any "cleared" flag
141-
// to ensure auto-fill will work in the future
142-
if (subBlockStore.isParamCleared(blockId, 'apiKey')) {
143-
subBlockStore.unmarkParamAsCleared(blockId, 'apiKey')
144-
}
145-
146-
// For provider-based blocks, store the API key under the provider name
147-
if (
148-
(blockType === 'agent' || blockType === 'router' || blockType === 'evaluator') &&
149-
modelValue
150-
) {
151-
const provider = getProviderFromModel(modelValue)
152-
if (provider && provider !== 'ollama') {
153-
subBlockStore.setToolParam(provider, 'apiKey', String(newValue))
154-
}
155-
} else {
156-
// For other blocks, store under the block type
157-
subBlockStore.setToolParam(blockType, 'apiKey', String(newValue))
158-
}
159-
}
160-
16112
interface UseSubBlockValueOptions {
16213
debounceMs?: number
16314
isStreaming?: boolean // Explicit streaming state
@@ -199,9 +50,6 @@ export function useSubBlockValue<T = any>(
19950
// Keep a ref to the latest value to prevent unnecessary re-renders
20051
const valueRef = useRef<T | null>(null)
20152

202-
// Previous model reference for detecting model changes
203-
const prevModelRef = useRef<string | null>(null)
204-
20553
// Streaming refs
20654
const lastEmittedValueRef = useRef<T | null>(null)
20755
const streamingValueRef = useRef<T | null>(null)
@@ -216,9 +64,6 @@ export function useSubBlockValue<T = any>(
21664
const isApiKey =
21765
subBlockId === 'apiKey' || (subBlockId?.toLowerCase().includes('apikey') ?? false)
21866

219-
// Check if auto-fill environment variables is enabled - always call this hook unconditionally
220-
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
221-
22267
// Always call this hook unconditionally - don't wrap it in a condition
22368
const modelSubBlockValue = useSubBlockStore((state) =>
22469
blockId ? state.getValue(blockId, 'model') : null
@@ -276,6 +121,29 @@ export function useSubBlockValue<T = any>(
276121
},
277122
}))
278123

124+
// Handle model changes for provider-based blocks - clear API key when provider changes
125+
if (
126+
subBlockId === 'model' &&
127+
isProviderBasedBlock &&
128+
newValue &&
129+
typeof newValue === 'string'
130+
) {
131+
const currentApiKeyValue = useSubBlockStore.getState().getValue(blockId, 'apiKey')
132+
133+
// Only clear if there's currently an API key value
134+
if (currentApiKeyValue && currentApiKeyValue !== '') {
135+
const oldModelValue = storeValue as string
136+
const oldProvider = oldModelValue ? getProviderFromModel(oldModelValue) : null
137+
const newProvider = getProviderFromModel(newValue)
138+
139+
// Clear API key if provider changed
140+
if (oldProvider !== newProvider) {
141+
// Use collaborative function to clear the API key
142+
collaborativeSetSubblockValue(blockId, 'apiKey', '')
143+
}
144+
}
145+
}
146+
279147
// Ensure we're passing the actual value, not a reference that might change
280148
const valueCopy =
281149
newValue === null
@@ -284,11 +152,6 @@ export function useSubBlockValue<T = any>(
284152
? JSON.parse(JSON.stringify(newValue))
285153
: newValue
286154

287-
// Handle API key storage for reuse across blocks
288-
if (isApiKey && blockType) {
289-
storeApiKeyValue(blockId, blockType, modelValue, newValue, storeValue)
290-
}
291-
292155
// If streaming, just store the value without emitting
293156
if (isStreaming) {
294157
streamingValueRef.current = valueCopy
@@ -320,61 +183,6 @@ export function useSubBlockValue<T = any>(
320183
valueRef.current = storeValue !== undefined ? storeValue : initialValue
321184
}, [])
322185

323-
// When component mounts, check for existing API key in toolParamsStore
324-
useEffect(() => {
325-
// Skip autofill if the feature is disabled in settings
326-
if (!isAutoFillEnvVarsEnabled) return
327-
328-
// Only process API key fields
329-
if (!isApiKey) return
330-
331-
// Handle different block types
332-
if (isProviderBasedBlock) {
333-
handleProviderBasedApiKey(blockId, subBlockId, modelValue, storeValue, false)
334-
} else {
335-
// Normal handling for non-provider blocks
336-
handleStandardBlockApiKey(blockId, subBlockId, blockType, storeValue)
337-
}
338-
}, [
339-
blockId,
340-
subBlockId,
341-
blockType,
342-
storeValue,
343-
isApiKey,
344-
isAutoFillEnvVarsEnabled,
345-
modelValue,
346-
isProviderBasedBlock,
347-
])
348-
349-
// Monitor for model changes in provider-based blocks
350-
useEffect(() => {
351-
// Only process API key fields in model-based blocks
352-
if (!isApiKey || !isProviderBasedBlock) return
353-
354-
// Check if the model has changed
355-
if (modelValue !== prevModelRef.current) {
356-
// Update the previous model reference
357-
prevModelRef.current = modelValue
358-
359-
// Handle API key auto-fill for model changes
360-
if (modelValue) {
361-
handleProviderBasedApiKey(blockId, subBlockId, modelValue, storeValue, true)
362-
} else {
363-
// If no model is selected, clear the API key field
364-
dispatchSubblockUpdate(blockId, subBlockId, '')
365-
}
366-
}
367-
}, [
368-
blockId,
369-
subBlockId,
370-
blockType,
371-
isApiKey,
372-
modelValue,
373-
isAutoFillEnvVarsEnabled,
374-
storeValue,
375-
isProviderBasedBlock,
376-
])
377-
378186
// Update the ref if the store value changes
379187
// This ensures we're always working with the latest value
380188
useEffect(() => {

0 commit comments

Comments
 (0)