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
This commit is contained in:
Vikhyath Mondreti
2025-07-19 19:00:34 -07:00
committed by GitHub
parent d7a2c0747c
commit 7b73dfb462
12 changed files with 35 additions and 533 deletions

View File

@@ -12,7 +12,7 @@ const logger = createLogger('UserSettingsAPI')
const SettingsSchema = z.object({
theme: z.enum(['system', 'light', 'dark']).optional(),
autoConnect: z.boolean().optional(),
autoFillEnvVars: z.boolean().optional(),
autoFillEnvVars: z.boolean().optional(), // DEPRECATED: kept for backwards compatibility
autoPan: z.boolean().optional(),
consoleExpandedByDefault: z.boolean().optional(),
telemetryEnabled: z.boolean().optional(),
@@ -31,7 +31,7 @@ const SettingsSchema = z.object({
const defaultSettings = {
theme: 'system',
autoConnect: true,
autoFillEnvVars: true,
autoFillEnvVars: true, // DEPRECATED: kept for backwards compatibility, always true
autoPan: true,
consoleExpandedByDefault: true,
telemetryEnabled: true,
@@ -65,7 +65,7 @@ export async function GET() {
data: {
theme: userSettings.theme,
autoConnect: userSettings.autoConnect,
autoFillEnvVars: userSettings.autoFillEnvVars,
autoFillEnvVars: userSettings.autoFillEnvVars, // DEPRECATED: kept for backwards compatibility
autoPan: userSettings.autoPan,
consoleExpandedByDefault: userSettings.consoleExpandedByDefault,
telemetryEnabled: userSettings.telemetryEnabled,

View File

@@ -17,7 +17,6 @@ import { cn } from '@/lib/utils'
import { getAllBlocks } from '@/blocks'
import { getProviderFromModel, supportsToolUsageControl } from '@/providers/utils'
import { useCustomToolsStore } from '@/stores/custom-tools/store'
import { useGeneralStore } from '@/stores/settings/general/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import {
@@ -400,7 +399,6 @@ export function ToolInput({
const isWide = useWorkflowStore((state) => state.blocks[blockId]?.isWide)
const customTools = useCustomToolsStore((state) => state.getAllTools())
const subBlockStore = useSubBlockStore()
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
// Get the current model from the 'model' subblock
const modelValue = useSubBlockStore.getState().getValue(blockId, 'model')
@@ -507,26 +505,13 @@ export function ToolInput({
return block.tools.access[0]
}
// Initialize tool parameters with auto-fill if enabled
// Initialize tool parameters - no autofill, just return empty params
const initializeToolParams = (
toolId: string,
params: ToolParameterConfig[],
instanceId?: string
): Record<string, string> => {
const initialParams: Record<string, string> = {}
// Only auto-fill parameters if the setting is enabled
if (isAutoFillEnvVarsEnabled) {
// For each parameter, check if we have a stored/resolved value
params.forEach((param) => {
const resolvedValue = subBlockStore.resolveToolParamValue(toolId, param.id, instanceId)
if (resolvedValue) {
initialParams[param.id] = resolvedValue
}
})
}
return initialParams
return {}
}
const handleSelectTool = (toolBlock: (typeof toolBlocks)[0]) => {
@@ -682,11 +667,6 @@ export function ToolInput({
const tool = selectedTools[toolIndex]
// Store the value in the tool params store for future use
if (paramValue.trim()) {
subBlockStore.setToolParam(tool.toolId, paramId, paramValue)
}
// Update the value in the workflow
setStoreValue(
selectedTools.map((tool, index) =>

View File

@@ -3,161 +3,12 @@ import { isEqual } from 'lodash'
import { createLogger } from '@/lib/logs/console-logger'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { getProviderFromModel } from '@/providers/utils'
import { useGeneralStore } from '@/stores/settings/general/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
const logger = createLogger('SubBlockValue')
// Helper function to dispatch collaborative subblock updates
const dispatchSubblockUpdate = (blockId: string, subBlockId: string, value: any) => {
const event = new CustomEvent('update-subblock-value', {
detail: {
blockId,
subBlockId,
value,
},
})
window.dispatchEvent(event)
}
/**
* Helper to handle API key auto-fill for provider-based blocks
* Used for agent, router, evaluator, and any other blocks that use LLM providers
*/
function handleProviderBasedApiKey(
blockId: string,
subBlockId: string,
modelValue: string | null | undefined,
storeValue: any,
isModelChange = false
) {
// Only proceed if we have a model selected
if (!modelValue) return
// Get the provider for this model
const provider = getProviderFromModel(modelValue)
// Skip if we couldn't determine a provider
if (!provider || provider === 'ollama') return
const subBlockStore = useSubBlockStore.getState()
const isAutoFillEnabled = useGeneralStore.getState().isAutoFillEnvVarsEnabled
// Try to get a saved API key for this provider (only if auto-fill is enabled)
const savedValue = isAutoFillEnabled
? subBlockStore.resolveToolParamValue(provider, 'apiKey', blockId)
: null
// If we have a valid saved API key and auto-fill is enabled, use it
if (savedValue && savedValue !== '' && isAutoFillEnabled) {
// Only update if the current value is different to avoid unnecessary updates
if (storeValue !== savedValue) {
dispatchSubblockUpdate(blockId, subBlockId, savedValue)
}
} else if (isModelChange && (!storeValue || storeValue === '')) {
// Only clear the field when switching models AND the field is already empty
// Don't clear existing user-entered values on initial load
dispatchSubblockUpdate(blockId, subBlockId, '')
}
// If no saved value and this is initial load, preserve existing value
}
/**
* Helper to handle API key auto-fill for non-agent blocks
*/
function handleStandardBlockApiKey(
blockId: string,
subBlockId: string,
blockType: string | undefined,
storeValue: any
) {
if (!blockType) return
const subBlockStore = useSubBlockStore.getState()
// Only auto-fill if the field is empty
if (!storeValue || storeValue === '') {
// Pass the blockId as instanceId to check if this specific instance has been cleared
const savedValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
if (savedValue && savedValue !== '' && savedValue !== storeValue) {
// Auto-fill the API key from the param store
dispatchSubblockUpdate(blockId, subBlockId, savedValue)
}
}
// Handle environment variable references
else if (
storeValue &&
typeof storeValue === 'string' &&
storeValue.startsWith('{{') &&
storeValue.endsWith('}}')
) {
// Pass the blockId as instanceId
const currentValue = subBlockStore.resolveToolParamValue(blockType, 'apiKey', blockId)
if (currentValue !== storeValue) {
// If we got a replacement or null, update the field
if (currentValue) {
// Replacement found - update to new reference
dispatchSubblockUpdate(blockId, subBlockId, currentValue)
}
}
}
}
/**
* Helper to store API key values
*/
function storeApiKeyValue(
blockId: string,
blockType: string | undefined,
modelValue: string | null | undefined,
newValue: any,
storeValue: any
) {
if (!blockType) return
const subBlockStore = useSubBlockStore.getState()
// Check if this is user explicitly clearing a field that had a value
// We only want to mark it as cleared if it's a user action, not an automatic
// clearing from model switching
if (
storeValue &&
storeValue !== '' &&
(newValue === null || newValue === '' || String(newValue).trim() === '')
) {
// Mark this specific instance as cleared so we don't auto-fill it
subBlockStore.markParamAsCleared(blockId, 'apiKey')
return
}
// Only store non-empty values
if (!newValue || String(newValue).trim() === '') return
// If user enters a value, we should clear any "cleared" flag
// to ensure auto-fill will work in the future
if (subBlockStore.isParamCleared(blockId, 'apiKey')) {
subBlockStore.unmarkParamAsCleared(blockId, 'apiKey')
}
// For provider-based blocks, store the API key under the provider name
if (
(blockType === 'agent' || blockType === 'router' || blockType === 'evaluator') &&
modelValue
) {
const provider = getProviderFromModel(modelValue)
if (provider && provider !== 'ollama') {
subBlockStore.setToolParam(provider, 'apiKey', String(newValue))
}
} else {
// For other blocks, store under the block type
subBlockStore.setToolParam(blockType, 'apiKey', String(newValue))
}
}
interface UseSubBlockValueOptions {
debounceMs?: number
isStreaming?: boolean // Explicit streaming state
@@ -199,9 +50,6 @@ export function useSubBlockValue<T = any>(
// Keep a ref to the latest value to prevent unnecessary re-renders
const valueRef = useRef<T | null>(null)
// Previous model reference for detecting model changes
const prevModelRef = useRef<string | null>(null)
// Streaming refs
const lastEmittedValueRef = useRef<T | null>(null)
const streamingValueRef = useRef<T | null>(null)
@@ -216,9 +64,6 @@ export function useSubBlockValue<T = any>(
const isApiKey =
subBlockId === 'apiKey' || (subBlockId?.toLowerCase().includes('apikey') ?? false)
// Check if auto-fill environment variables is enabled - always call this hook unconditionally
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
// Always call this hook unconditionally - don't wrap it in a condition
const modelSubBlockValue = useSubBlockStore((state) =>
blockId ? state.getValue(blockId, 'model') : null
@@ -276,6 +121,29 @@ export function useSubBlockValue<T = any>(
},
}))
// Handle model changes for provider-based blocks - clear API key when provider changes
if (
subBlockId === 'model' &&
isProviderBasedBlock &&
newValue &&
typeof newValue === 'string'
) {
const currentApiKeyValue = useSubBlockStore.getState().getValue(blockId, 'apiKey')
// Only clear if there's currently an API key value
if (currentApiKeyValue && currentApiKeyValue !== '') {
const oldModelValue = storeValue as string
const oldProvider = oldModelValue ? getProviderFromModel(oldModelValue) : null
const newProvider = getProviderFromModel(newValue)
// Clear API key if provider changed
if (oldProvider !== newProvider) {
// Use collaborative function to clear the API key
collaborativeSetSubblockValue(blockId, 'apiKey', '')
}
}
}
// Ensure we're passing the actual value, not a reference that might change
const valueCopy =
newValue === null
@@ -284,11 +152,6 @@ export function useSubBlockValue<T = any>(
? JSON.parse(JSON.stringify(newValue))
: newValue
// Handle API key storage for reuse across blocks
if (isApiKey && blockType) {
storeApiKeyValue(blockId, blockType, modelValue, newValue, storeValue)
}
// If streaming, just store the value without emitting
if (isStreaming) {
streamingValueRef.current = valueCopy
@@ -320,61 +183,6 @@ export function useSubBlockValue<T = any>(
valueRef.current = storeValue !== undefined ? storeValue : initialValue
}, [])
// When component mounts, check for existing API key in toolParamsStore
useEffect(() => {
// Skip autofill if the feature is disabled in settings
if (!isAutoFillEnvVarsEnabled) return
// Only process API key fields
if (!isApiKey) return
// Handle different block types
if (isProviderBasedBlock) {
handleProviderBasedApiKey(blockId, subBlockId, modelValue, storeValue, false)
} else {
// Normal handling for non-provider blocks
handleStandardBlockApiKey(blockId, subBlockId, blockType, storeValue)
}
}, [
blockId,
subBlockId,
blockType,
storeValue,
isApiKey,
isAutoFillEnvVarsEnabled,
modelValue,
isProviderBasedBlock,
])
// Monitor for model changes in provider-based blocks
useEffect(() => {
// Only process API key fields in model-based blocks
if (!isApiKey || !isProviderBasedBlock) return
// Check if the model has changed
if (modelValue !== prevModelRef.current) {
// Update the previous model reference
prevModelRef.current = modelValue
// Handle API key auto-fill for model changes
if (modelValue) {
handleProviderBasedApiKey(blockId, subBlockId, modelValue, storeValue, true)
} else {
// If no model is selected, clear the API key field
dispatchSubblockUpdate(blockId, subBlockId, '')
}
}
}, [
blockId,
subBlockId,
blockType,
isApiKey,
modelValue,
isAutoFillEnvVarsEnabled,
storeValue,
isProviderBasedBlock,
])
// Update the ref if the store value changes
// This ensures we're always working with the latest value
useEffect(() => {

View File

@@ -17,7 +17,6 @@ import { useGeneralStore } from '@/stores/settings/general/store'
const TOOLTIPS = {
autoConnect: 'Automatically connect nodes.',
autoFillEnvVars: 'Automatically fill API keys.',
autoPan: 'Automatically pan to active blocks during workflow execution.',
consoleExpandedByDefault:
'Show console entries expanded by default. When disabled, entries will be collapsed by default.',
@@ -30,13 +29,13 @@ export function General() {
const error = useGeneralStore((state) => state.error)
const theme = useGeneralStore((state) => state.theme)
const isAutoConnectEnabled = useGeneralStore((state) => state.isAutoConnectEnabled)
const isAutoFillEnvVarsEnabled = useGeneralStore((state) => state.isAutoFillEnvVarsEnabled)
const isAutoPanEnabled = useGeneralStore((state) => state.isAutoPanEnabled)
const isConsoleExpandedByDefault = useGeneralStore((state) => state.isConsoleExpandedByDefault)
// Loading states
const isAutoConnectLoading = useGeneralStore((state) => state.isAutoConnectLoading)
const isAutoFillEnvVarsLoading = useGeneralStore((state) => state.isAutoFillEnvVarsLoading)
const isAutoPanLoading = useGeneralStore((state) => state.isAutoPanLoading)
const isConsoleExpandedByDefaultLoading = useGeneralStore(
(state) => state.isConsoleExpandedByDefaultLoading
@@ -45,7 +44,7 @@ export function General() {
const setTheme = useGeneralStore((state) => state.setTheme)
const toggleAutoConnect = useGeneralStore((state) => state.toggleAutoConnect)
const toggleAutoFillEnvVars = useGeneralStore((state) => state.toggleAutoFillEnvVars)
const toggleAutoPan = useGeneralStore((state) => state.toggleAutoPan)
const toggleConsoleExpandedByDefault = useGeneralStore(
(state) => state.toggleConsoleExpandedByDefault
@@ -69,12 +68,6 @@ export function General() {
}
}
const handleAutoFillEnvVarsChange = async (checked: boolean) => {
if (checked !== isAutoFillEnvVarsEnabled && !isAutoFillEnvVarsLoading) {
await toggleAutoFillEnvVars()
}
}
const handleAutoPanChange = async (checked: boolean) => {
if (checked !== isAutoPanEnabled && !isAutoPanLoading) {
await toggleAutoPan()
@@ -167,35 +160,7 @@ export function General() {
disabled={isLoading || isAutoConnectLoading}
/>
</div>
<div className='flex items-center justify-between py-1'>
<div className='flex items-center gap-2'>
<Label htmlFor='auto-fill-env-vars' className='font-medium'>
Auto-fill environment variables
</Label>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant='ghost'
size='sm'
className='h-7 p-1 text-gray-500'
aria-label='Learn more about auto-fill environment variables'
disabled={isLoading || isAutoFillEnvVarsLoading}
>
<Info className='h-5 w-5' />
</Button>
</TooltipTrigger>
<TooltipContent side='top' className='max-w-[300px] p-3'>
<p className='text-sm'>{TOOLTIPS.autoFillEnvVars}</p>
</TooltipContent>
</Tooltip>
</div>
<Switch
id='auto-fill-env-vars'
checked={isAutoFillEnvVarsEnabled}
onCheckedChange={handleAutoFillEnvVarsChange}
disabled={isLoading || isAutoFillEnvVarsLoading}
/>
</div>
<div className='flex items-center justify-between py-1'>
<div className='flex items-center gap-2'>
<Label htmlFor='console-expanded-by-default' className='font-medium'>

View File

@@ -393,7 +393,7 @@ export const settings = pgTable('settings', {
// General settings
theme: text('theme').notNull().default('system'),
autoConnect: boolean('auto_connect').notNull().default(true),
autoFillEnvVars: boolean('auto_fill_env_vars').notNull().default(true),
autoFillEnvVars: boolean('auto_fill_env_vars').notNull().default(true), // DEPRECATED: autofill feature removed
autoPan: boolean('auto_pan').notNull().default(true),
consoleExpandedByDefault: boolean('console_expanded_by_default').notNull().default(true),

View File

@@ -219,7 +219,6 @@ export const resetAllStores = () => {
})
useWorkflowStore.getState().clear()
useSubBlockStore.getState().clear()
useSubBlockStore.getState().clearToolParams()
useEnvironmentStore.setState({
variables: {},
isLoading: false,

View File

@@ -17,7 +17,6 @@ export const useGeneralStore = create<GeneralStore>()(
const store: General = {
isAutoConnectEnabled: true,
isAutoFillEnvVarsEnabled: true,
isAutoPanEnabled: true,
isConsoleExpandedByDefault: true,
isDebugModeEnabled: false,
@@ -28,7 +27,6 @@ export const useGeneralStore = create<GeneralStore>()(
error: null,
// Individual loading states
isAutoConnectLoading: false,
isAutoFillEnvVarsLoading: false,
isAutoPanLoading: false,
isConsoleExpandedByDefaultLoading: false,
isThemeLoading: false,
@@ -74,17 +72,6 @@ export const useGeneralStore = create<GeneralStore>()(
)
},
toggleAutoFillEnvVars: async () => {
if (get().isAutoFillEnvVarsLoading) return
const newValue = !get().isAutoFillEnvVarsEnabled
await updateSettingOptimistic(
'autoFillEnvVars',
newValue,
'isAutoFillEnvVarsLoading',
'isAutoFillEnvVarsEnabled'
)
},
toggleAutoPan: async () => {
if (get().isAutoPanLoading) return
const newValue = !get().isAutoPanEnabled
@@ -166,7 +153,6 @@ export const useGeneralStore = create<GeneralStore>()(
set({
isAutoConnectEnabled: data.autoConnect,
isAutoFillEnvVarsEnabled: data.autoFillEnvVars,
isAutoPanEnabled: data.autoPan ?? true, // Default to true if undefined
isConsoleExpandedByDefault: data.consoleExpandedByDefault ?? true, // Default to true if undefined
theme: data.theme,

View File

@@ -1,6 +1,5 @@
export interface General {
isAutoConnectEnabled: boolean
isAutoFillEnvVarsEnabled: boolean
isAutoPanEnabled: boolean
isConsoleExpandedByDefault: boolean
isDebugModeEnabled: boolean
@@ -11,7 +10,6 @@ export interface General {
error: string | null
// Individual loading states for optimistic updates
isAutoConnectLoading: boolean
isAutoFillEnvVarsLoading: boolean
isAutoPanLoading: boolean
isConsoleExpandedByDefaultLoading: boolean
isThemeLoading: boolean
@@ -20,7 +18,7 @@ export interface General {
export interface GeneralActions {
toggleAutoConnect: () => Promise<void>
toggleAutoFillEnvVars: () => Promise<void>
toggleAutoPan: () => Promise<void>
toggleConsoleExpandedByDefault: () => Promise<void>
toggleDebugMode: () => void
@@ -36,7 +34,6 @@ export type GeneralStore = General & GeneralActions
export type UserSettings = {
theme: 'system' | 'light' | 'dark'
autoConnect: boolean
autoFillEnvVars: boolean
autoPan: boolean
consoleExpandedByDefault: boolean
telemetryEnabled: boolean

View File

@@ -212,7 +212,6 @@ function resetWorkflowStores() {
// Reset the subblock store
useSubBlockStore.setState({
workflowValues: {},
toolParams: {},
})
}

View File

@@ -1,12 +1,9 @@
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import type { SubBlockConfig } from '@/blocks/types'
import { useEnvironmentStore } from '../../settings/environment/store'
import { useGeneralStore } from '../../settings/general/store'
import { useWorkflowRegistry } from '../registry/store'
// Removed workflowSync import - Socket.IO handles real-time sync
import type { SubBlockStore } from './types'
import { extractEnvVarName, findMatchingEnvVar, isEnvVarReference } from './utils'
// Removed debounce sync - Socket.IO handles real-time sync immediately
@@ -25,9 +22,6 @@ import { extractEnvVarName, findMatchingEnvVar, isEnvVarReference } from './util
export const useSubBlockStore = create<SubBlockStore>()(
devtools((set, get) => ({
workflowValues: {},
// Initialize tool params-related state
toolParams: {},
clearedParams: {},
setValue: (blockId: string, subBlockId: string, value: any) => {
const activeWorkflowId = useWorkflowRegistry.getState().activeWorkflowId
@@ -93,179 +87,5 @@ export const useSubBlockStore = create<SubBlockStore>()(
syncWithDB: () => {
// No-op: Socket.IO handles real-time sync
},
// Tool params related functionality
setToolParam: (toolId: string, paramId: string, value: string) => {
// If setting a non-empty value, we should remove it from clearedParams if it exists
if (value.trim() !== '') {
set((state) => {
const newClearedParams = { ...state.clearedParams }
if (newClearedParams[toolId]?.[paramId]) {
delete newClearedParams[toolId][paramId]
// Clean up empty objects
if (Object.keys(newClearedParams[toolId]).length === 0) {
delete newClearedParams[toolId]
}
}
return { clearedParams: newClearedParams }
})
}
// Set the parameter value
set((state) => ({
toolParams: {
...state.toolParams,
[toolId]: {
...(state.toolParams[toolId] || {}),
[paramId]: value,
},
},
}))
// For API keys, also store under a normalized tool name for cross-referencing
// This allows both blocks and tools to share the same parameters
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
// Extract the tool name part (e.g., "exa" from "exa-search")
const baseTool = toolId.split('-')[0].toLowerCase()
if (baseTool !== toolId) {
// Set the same value for the base tool to enable cross-referencing
set((state) => ({
toolParams: {
...state.toolParams,
[baseTool]: {
...(state.toolParams[baseTool] || {}),
[paramId]: value,
},
},
}))
}
}
},
markParamAsCleared: (instanceId: string, paramId: string) => {
// Mark this specific instance as cleared
set((state) => ({
clearedParams: {
...state.clearedParams,
[instanceId]: {
...(state.clearedParams[instanceId] || {}),
[paramId]: true,
},
},
}))
},
unmarkParamAsCleared: (instanceId: string, paramId: string) => {
// Remove the cleared flag for this parameter
set((state) => {
const newClearedParams = { ...state.clearedParams }
if (newClearedParams[instanceId]?.[paramId]) {
delete newClearedParams[instanceId][paramId]
// Clean up empty objects
if (Object.keys(newClearedParams[instanceId]).length === 0) {
delete newClearedParams[instanceId]
}
}
return { clearedParams: newClearedParams }
})
},
isParamCleared: (instanceId: string, paramId: string) => {
// Only check this specific instance
return !!get().clearedParams[instanceId]?.[paramId]
},
getToolParam: (toolId: string, paramId: string) => {
// Check for direct match first
const directValue = get().toolParams[toolId]?.[paramId]
if (directValue) return directValue
// Try base tool name if it's a compound tool ID
if (toolId.includes('-')) {
const baseTool = toolId.split('-')[0].toLowerCase()
return get().toolParams[baseTool]?.[paramId]
}
// Try matching against any stored tool that starts with this ID
// This helps match "exa" with "exa-search" etc.
const matchingToolIds = Object.keys(get().toolParams).filter(
(id) => id.startsWith(toolId) || id.split('-')[0] === toolId
)
for (const id of matchingToolIds) {
const value = get().toolParams[id]?.[paramId]
if (value) return value
}
return undefined
},
getToolParams: (toolId: string) => {
return get().toolParams[toolId] || {}
},
isEnvVarReference,
resolveToolParamValue: (toolId: string, paramId: string, instanceId?: string) => {
// If this is a specific instance that has been deliberately cleared, don't auto-fill it
if (instanceId && get().isParamCleared(instanceId, paramId)) {
return undefined
}
// Check if auto-fill environment variables is enabled
const isAutoFillEnvVarsEnabled = useGeneralStore.getState().isAutoFillEnvVarsEnabled
if (!isAutoFillEnvVarsEnabled) {
// When auto-fill is disabled, we still return existing stored values, but don't
// attempt to resolve environment variables or set new values
return get().toolParams[toolId]?.[paramId]
}
const envStore = useEnvironmentStore.getState()
// First check params store for previously entered value
const storedValue = get().getToolParam(toolId, paramId)
if (storedValue) {
// If the stored value is an environment variable reference like {{EXA_API_KEY}}
if (isEnvVarReference(storedValue)) {
// Extract variable name from {{VAR_NAME}}
const envVarName = extractEnvVarName(storedValue)
if (!envVarName) return undefined
// Check if this environment variable still exists
const envValue = envStore.getVariable(envVarName)
if (envValue) {
// Environment variable exists, return the reference
return storedValue
}
// Environment variable no longer exists
return undefined
}
// Return the stored value directly if it's not an env var reference
return storedValue
}
// If no stored value, try to guess based on parameter name
// This handles cases where the user hasn't entered a value yet
if (paramId.toLowerCase() === 'apikey' || paramId.toLowerCase() === 'api_key') {
const matchingVar = findMatchingEnvVar(toolId)
if (matchingVar) {
const envReference = `{{${matchingVar}}}`
get().setToolParam(toolId, paramId, envReference)
return envReference
}
}
// No value found
return undefined
},
clearToolParams: () => {
set({ toolParams: {}, clearedParams: {} })
},
}))
)

View File

@@ -1,7 +1,5 @@
export interface SubBlockState {
workflowValues: Record<string, Record<string, Record<string, any>>> // Store values per workflow ID
toolParams: Record<string, Record<string, string>>
clearedParams: Record<string, Record<string, boolean>>
}
export interface SubBlockStore extends SubBlockState {
@@ -11,19 +9,4 @@ export interface SubBlockStore extends SubBlockState {
initializeFromWorkflow: (workflowId: string, blocks: Record<string, any>) => void
// Add debounced sync function
syncWithDB: () => void
// Tool params related functions
setToolParam: (toolId: string, paramId: string, value: string) => void
markParamAsCleared: (instanceId: string, paramId: string) => void
unmarkParamAsCleared: (instanceId: string, paramId: string) => void
isParamCleared: (instanceId: string, paramId: string) => boolean
getToolParam: (toolId: string, paramId: string) => string | undefined
getToolParams: (toolId: string) => Record<string, string>
isEnvVarReference: (value: string) => boolean
resolveToolParamValue: (
toolId: string,
paramId: string,
instanceId?: string
) => string | undefined
clearToolParams: () => void
}

View File

@@ -1,4 +1,4 @@
import { useEnvironmentStore } from '../../settings/environment/store'
// DEPRECATED: useEnvironmentStore import removed as autofill functions were removed
/**
* Checks if a value is an environment variable reference in the format {{ENV_VAR}}
@@ -15,38 +15,3 @@ export const extractEnvVarName = (value: string): string | null => {
if (!isEnvVarReference(value)) return null
return value.slice(2, -2)
}
/**
* Generates possible environment variable names for a tool ID
* For example, "exa-search" could map to "EXA_API_KEY", "EXA_KEY", etc.
*/
export const generatePossibleEnvVarNames = (toolId: string): string[] => {
// Extract base tool name if it's a compound ID
const baseTool = toolId.includes('-') ? toolId.split('-')[0] : toolId
const toolPrefix = baseTool.toUpperCase()
return [
`${toolPrefix}_API_KEY`,
`${toolPrefix.replace(/-/g, '_')}_API_KEY`,
`${toolPrefix}_KEY`,
`${toolPrefix}_TOKEN`,
`${toolPrefix}`,
]
}
/**
* Finds a matching environment variable for a tool ID
*/
export const findMatchingEnvVar = (toolId: string): string | null => {
const envStore = useEnvironmentStore.getState()
const possibleVars = generatePossibleEnvVarNames(toolId)
for (const varName of possibleVars) {
const envValue = envStore.getVariable(varName)
if (envValue) {
return varName
}
}
return null
}