improvement(blocks): depends on misalignments audit (#4409)

* fix(blocks): depends on misalignment

* fix overclearing finds
This commit is contained in:
Vikhyath Mondreti
2026-05-02 11:29:50 -07:00
committed by GitHub
parent 66bab935db
commit 20ee07c6da
37 changed files with 314 additions and 54 deletions
+1 -1
View File
@@ -120,7 +120,7 @@ Transcribe audio and video files to text using leading AI providers. Supports mu
| --------- | ---- | -------- | ----------- |
| `provider` | string | Yes | STT provider \(elevenlabs\) |
| `apiKey` | string | Yes | ElevenLabs API key |
| `model` | string | No | ElevenLabs model to use \(scribe_v1, scribe_v1_experimental\) |
| `model` | string | No | ElevenLabs model to use \(scribe_v2\) |
| `audioFile` | file | No | Audio or video file to transcribe \(e.g., MP3, WAV, M4A, WEBM\) |
| `audioFileReference` | file | No | Reference to audio/video file from previous blocks |
| `audioUrl` | string | No | URL to audio or video file |
+4 -10
View File
@@ -20,6 +20,7 @@ import {
import type { TranscriptSegment } from '@/tools/stt/types'
const logger = createLogger('SttProxyAPI')
const ELEVENLABS_STT_MODEL = 'scribe_v2'
export const dynamic = 'force-dynamic'
export const maxDuration = 300 // 5 minutes for large files
@@ -222,13 +223,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
duration = result.duration
confidence = result.confidence
} else if (provider === 'elevenlabs') {
const result = await transcribeWithElevenLabs(
audioBuffer,
apiKey,
language,
timestamps,
model
)
const result = await transcribeWithElevenLabs(audioBuffer, apiKey, language, timestamps)
transcript = result.transcript
segments = result.segments
detectedLanguage = result.language
@@ -470,8 +465,7 @@ async function transcribeWithElevenLabs(
audioBuffer: Buffer,
apiKey: string,
language?: string,
timestamps?: 'none' | 'sentence' | 'word',
model?: string
timestamps?: 'none' | 'sentence' | 'word'
): Promise<{
transcript: string
segments?: TranscriptSegment[]
@@ -481,7 +475,7 @@ async function transcribeWithElevenLabs(
const formData = new FormData()
const blob = new Blob([new Uint8Array(audioBuffer)], { type: 'audio/mpeg' })
formData.append('file', blob, 'audio.mp3')
formData.append('model_id', model || 'scribe_v1')
formData.append('model_id', ELEVENLABS_STT_MODEL)
if (language && language !== 'auto') {
formData.append('language_code', language)
+3
View File
@@ -344,6 +344,7 @@ Return ONLY the JSON array.`,
value: ['conversation', 'sliding_window', 'sliding_window_tokens'],
and: { field: 'model', value: MODELS_WITHOUT_MEMORY, not: true },
},
dependsOn: ['memoryType'],
},
{
id: 'slidingWindowSize',
@@ -355,6 +356,7 @@ Return ONLY the JSON array.`,
value: ['sliding_window'],
and: { field: 'model', value: MODELS_WITHOUT_MEMORY, not: true },
},
dependsOn: ['memoryType'],
},
{
id: 'slidingWindowTokens',
@@ -366,6 +368,7 @@ Return ONLY the JSON array.`,
value: ['sliding_window_tokens'],
and: { field: 'model', value: MODELS_WITHOUT_MEMORY, not: true },
},
dependsOn: ['memoryType'],
},
{
id: 'temperature',
+1
View File
@@ -74,6 +74,7 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
type: 'short-input',
canonicalParamId: 'baseId',
placeholder: 'Enter your base ID (e.g., appXXXXXXXXXXXXXX)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'listBases', not: true },
required: { field: 'operation', value: 'listBases', not: true },
+2
View File
@@ -74,6 +74,7 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
canonicalParamId: 'workspace',
required: true,
placeholder: 'Enter Asana workspace GID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
@@ -123,6 +124,7 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
type: 'short-input',
canonicalParamId: 'getTasks_workspace',
placeholder: 'Enter workspace GID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
+5
View File
@@ -63,6 +63,7 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: 'https://localhost:9200',
required: true,
condition: { field: 'deploymentType', value: 'self_hosted' },
dependsOn: ['deploymentType'],
},
// Cloud ID
@@ -73,6 +74,7 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: 'deployment-name:base64-encoded-data',
required: true,
condition: { field: 'deploymentType', value: 'cloud' },
dependsOn: ['deploymentType'],
},
// Authentication method
@@ -96,6 +98,7 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
password: true,
required: true,
condition: { field: 'authMethod', value: 'api_key' },
dependsOn: ['authMethod'],
},
// Username
@@ -106,6 +109,7 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
placeholder: 'Enter username',
required: true,
condition: { field: 'authMethod', value: 'basic_auth' },
dependsOn: ['authMethod'],
},
// Password
@@ -117,6 +121,7 @@ export const ElasticsearchBlock: BlockConfig<ElasticsearchResponse> = {
password: true,
required: true,
condition: { field: 'authMethod', value: 'basic_auth' },
dependsOn: ['authMethod'],
},
// Index name - for most operations
+4
View File
@@ -251,6 +251,7 @@ Return ONLY the email body - no explanations, no extra text.`,
type: 'short-input',
canonicalParamId: 'folder',
placeholder: 'Enter Gmail label name (e.g., INBOX, SENT, or custom label)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'read_gmail' },
},
@@ -333,6 +334,7 @@ Return ONLY the search query - no explanations, no extra text.`,
type: 'short-input',
canonicalParamId: 'addLabelIds',
placeholder: 'Enter label ID (e.g., INBOX, Label_123)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'move_gmail' },
required: true,
@@ -359,6 +361,7 @@ Return ONLY the search query - no explanations, no extra text.`,
type: 'short-input',
canonicalParamId: 'removeLabelIds',
placeholder: 'Enter label ID to remove (e.g., INBOX)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'move_gmail' },
required: false,
@@ -412,6 +415,7 @@ Return ONLY the search query - no explanations, no extra text.`,
type: 'short-input',
canonicalParamId: 'manageLabelId',
placeholder: 'Enter label ID (e.g., INBOX, Label_123)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: ['add_label_gmail', 'remove_label_gmail'] },
required: true,
@@ -81,6 +81,7 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
type: 'short-input',
canonicalParamId: 'calendarId',
placeholder: 'Enter calendar ID (e.g., primary or calendar@gmail.com)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'list_calendars', not: true },
},
@@ -347,6 +348,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
type: 'short-input',
canonicalParamId: 'destinationCalendarId',
placeholder: 'destination@group.calendar.google.com',
dependsOn: ['credential'],
condition: { field: 'operation', value: 'move' },
required: true,
mode: 'advanced',
+2 -2
View File
@@ -411,7 +411,7 @@ export const GoogleMapsBlock: BlockConfig = {
config: {
tool: (params) => `google_maps_${params.operation}`,
params: (params) => {
const { operation, locationBias, ...rest } = params
const { operation, locationBias, addressToValidate, ...rest } = params
let location: { lat: number; lng: number } | undefined
if (locationBias && typeof locationBias === 'string' && locationBias.includes(',')) {
@@ -486,7 +486,7 @@ export const GoogleMapsBlock: BlockConfig = {
}
}
const address = params.addressToValidate || params.address
const address = operation === 'validate_address' ? addressToValidate : params.address
// Parse boolean switches (can come as string or boolean from form)
let interpolate: boolean | undefined
+1 -1
View File
@@ -401,7 +401,7 @@ export const GoogleSheetsV2Block: BlockConfig<GoogleSheetsV2Response> = {
canonicalParamId: 'sheetName',
placeholder: 'Name of the sheet/tab (e.g., Sheet1)',
required: true,
dependsOn: ['credential'],
dependsOn: { all: ['credential'], any: ['spreadsheetId', 'manualSpreadsheetId'] },
mode: 'advanced',
condition: { field: 'operation', value: ['read', 'write', 'update', 'append', 'clear'] },
},
+9
View File
@@ -70,6 +70,7 @@ export const GuardrailsBlock: BlockConfig<GuardrailsResponse> = {
field: 'validationType',
value: ['regex'],
},
dependsOn: ['validationType'],
wandConfig: {
enabled: true,
prompt: `Generate a regular expression pattern based on the user's description.
@@ -106,6 +107,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['hallucination'],
},
dependsOn: ['validationType'],
},
{
id: 'model',
@@ -118,6 +120,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['hallucination'],
},
dependsOn: ['validationType'],
},
{
id: 'threshold',
@@ -131,6 +134,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['hallucination'],
},
dependsOn: ['validationType'],
},
{
id: 'topK',
@@ -145,6 +149,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['hallucination'],
},
dependsOn: ['validationType'],
},
// Provider credential subblocks - only shown for hallucination validation
...getProviderCredentialSubBlocks().map((subBlock) => ({
@@ -158,6 +163,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
typeof subBlock.condition === 'function' ? subBlock.condition() : subBlock.condition,
}
: { field: 'validationType' as const, value: ['hallucination'] },
dependsOn: ['validationType'],
})),
{
id: 'piiEntityTypes',
@@ -227,6 +233,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['pii'],
},
dependsOn: ['validationType'],
},
{
id: 'piiMode',
@@ -242,6 +249,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['pii'],
},
dependsOn: ['validationType'],
},
{
id: 'piiLanguage',
@@ -259,6 +267,7 @@ Return ONLY the regex pattern - no explanations, no quotes, no forward slashes,
field: 'validationType',
value: ['pii'],
},
dependsOn: ['validationType'],
},
],
tools: {
+1
View File
@@ -61,6 +61,7 @@ export const HuggingFaceBlock: BlockConfig<HuggingFaceChatResponse> = {
placeholder:
'e.g., deepseek/deepseek-v3-0324, llama3.1-8b, meta-llama/Llama-3.2-3B-Instruct-Turbo',
description: 'The model must be available for the selected provider.',
dependsOn: ['provider'],
},
{
id: 'temperature',
+11 -5
View File
@@ -44,6 +44,7 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
],
value: () => '1024x1024',
condition: { field: 'model', value: 'dall-e-3' },
dependsOn: ['model'],
},
{
id: 'size',
@@ -57,6 +58,7 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
],
value: () => 'auto',
condition: { field: 'model', value: 'gpt-image-1' },
dependsOn: ['model'],
},
{
id: 'quality',
@@ -68,6 +70,7 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
],
value: () => 'standard',
condition: { field: 'model', value: 'dall-e-3' },
dependsOn: ['model'],
},
{
id: 'style',
@@ -79,6 +82,7 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
],
value: () => 'vivid',
condition: { field: 'model', value: 'dall-e-3' },
dependsOn: ['model'],
},
{
id: 'background',
@@ -91,6 +95,7 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
],
value: () => 'auto',
condition: { field: 'model', value: 'gpt-image-1' },
dependsOn: ['model'],
},
{
id: 'apiKey',
@@ -114,22 +119,23 @@ export const ImageGeneratorBlock: BlockConfig<DalleResponse> = {
throw new Error('Prompt is required')
}
// Base parameters for all models
const model = params.model || 'dall-e-3'
const size = params.size || (model === 'gpt-image-1' ? 'auto' : '1024x1024')
const baseParams = {
prompt: params.prompt,
model: params.model || 'dall-e-3',
size: params.size || '1024x1024',
model,
size,
apiKey: params.apiKey,
}
if (params.model === 'dall-e-3') {
if (model === 'dall-e-3') {
return {
...baseParams,
quality: params.quality || 'standard',
style: params.style || 'vivid',
}
}
if (params.model === 'gpt-image-1') {
if (model === 'gpt-image-1') {
return {
...baseParams,
...(params.background && { background: params.background }),
+1 -1
View File
@@ -168,7 +168,7 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
type: 'short-input',
canonicalParamId: 'issueKey',
placeholder: 'Enter Jira issue key',
dependsOn: ['credential', 'domain'],
dependsOn: ['credential', 'domain', 'projectId'],
condition: {
field: 'operation',
value: [
@@ -132,6 +132,7 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
type: 'short-input',
canonicalParamId: 'serviceDeskId',
placeholder: 'Enter service desk ID',
dependsOn: ['credential', 'domain'],
mode: 'advanced',
required: {
field: 'operation',
@@ -181,6 +182,7 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
canonicalParamId: 'requestTypeId',
required: true,
placeholder: 'Enter request type ID',
dependsOn: ['credential', 'domain', 'serviceDeskId'],
mode: 'advanced',
condition: { field: 'operation', value: ['create_request', 'get_request_type_fields'] },
},
+1
View File
@@ -171,6 +171,7 @@ export const KnowledgeBlock: BlockConfig = {
type: 'short-input',
canonicalParamId: 'documentId',
placeholder: 'Enter document ID',
dependsOn: ['knowledgeBaseId'],
required: true,
mode: 'advanced',
condition: {
+12 -3
View File
@@ -197,6 +197,7 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
canonicalParamId: 'teamId',
placeholder: 'Enter Linear team ID',
mode: 'advanced',
dependsOn: ['credential'],
required: {
field: 'operation',
value: [
@@ -271,6 +272,7 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
canonicalParamId: 'projectId',
placeholder: 'Enter Linear project ID',
mode: 'advanced',
dependsOn: ['credential', 'teamId'],
required: {
field: 'operation',
value: [
@@ -1090,7 +1092,7 @@ Return ONLY the description text - no explanations.`,
},
// Customer request priority/urgency
{
id: 'priority',
id: 'customerRequestPriority',
title: 'Urgency',
type: 'dropdown',
options: [
@@ -2001,7 +2003,10 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
...baseParams,
customerId: params.customerId.trim(),
body: params.requestBody?.trim(),
priority: params.priority !== undefined ? Number(params.priority) : 0,
priority:
params.customerRequestPriority !== undefined
? Number(params.customerRequestPriority)
: 0,
issueId: params.linkedIssueId?.trim(),
projectId: effectiveProjectId || undefined,
}
@@ -2015,7 +2020,10 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
customerNeedId: params.customerNeedId.trim(),
customerId: params.customerId?.trim(),
body: params.requestBody?.trim(),
priority: params.priority !== undefined ? Number(params.priority) : undefined,
priority:
params.customerRequestPriority !== undefined
? Number(params.customerRequestPriority)
: undefined,
issueId: params.linkedIssueId?.trim(),
projectId: effectiveProjectId || undefined,
}
@@ -2354,6 +2362,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
stateId: { type: 'string', description: 'Workflow state identifier' },
assigneeId: { type: 'string', description: 'Assignee user identifier' },
priority: { type: 'string', description: 'Priority level' },
customerRequestPriority: { type: 'string', description: 'Customer request urgency level' },
estimate: { type: 'string', description: 'Estimate points' },
query: { type: 'string', description: 'Search query' },
includeArchived: { type: 'boolean', description: 'Include archived items' },
+1
View File
@@ -53,6 +53,7 @@ export const McpBlock: BlockConfig<McpResponse> = {
value: '',
not: true, // Show when tool is not empty
},
dependsOn: ['tool'],
},
],
tools: {
@@ -94,6 +94,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
type: 'short-input',
canonicalParamId: 'teamId',
placeholder: 'Enter team ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
@@ -132,6 +133,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
type: 'short-input',
canonicalParamId: 'chatId',
placeholder: 'Enter chat ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
@@ -169,6 +171,7 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
type: 'short-input',
canonicalParamId: 'channelId',
placeholder: 'Enter channel ID',
dependsOn: ['credential', 'teamId'],
mode: 'advanced',
condition: {
field: 'operation',
+2
View File
@@ -128,6 +128,7 @@ export const MondayBlock: BlockConfig<MondayResponse> = {
type: 'short-input',
canonicalParamId: 'boardId',
placeholder: 'Enter board ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: BOARD_OPS },
required: { field: 'operation', value: BOARD_OPS },
@@ -180,6 +181,7 @@ export const MondayBlock: BlockConfig<MondayResponse> = {
type: 'short-input',
canonicalParamId: 'groupId',
placeholder: 'Enter group ID',
dependsOn: ['credential', 'boardId'],
mode: 'advanced',
condition: {
field: 'operation',
+3
View File
@@ -79,6 +79,7 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
type: 'short-input',
canonicalParamId: 'pageId',
placeholder: 'Enter Notion page ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
@@ -108,6 +109,7 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
type: 'short-input',
canonicalParamId: 'databaseId',
placeholder: 'Enter Notion database ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
@@ -134,6 +136,7 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
type: 'short-input',
canonicalParamId: 'parentId',
placeholder: 'ID of parent page',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: ['notion_create_page', 'notion_create_database'] },
required: true,
+3
View File
@@ -186,6 +186,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
type: 'short-input',
canonicalParamId: 'folder',
placeholder: 'Enter Outlook folder name (e.g., INBOX, SENT, or custom folder)',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'read_outlook' },
},
@@ -233,6 +234,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
type: 'short-input',
canonicalParamId: 'destinationId',
placeholder: 'Enter folder ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'move_outlook' },
required: true,
@@ -280,6 +282,7 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
type: 'short-input',
canonicalParamId: 'copyDestinationId',
placeholder: 'Enter folder ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: { field: 'operation', value: 'copy_outlook' },
required: true,
+3
View File
@@ -74,6 +74,7 @@ export const SftpBlock: BlockConfig<SftpUploadResult> = {
password: true,
placeholder: 'Your SFTP password',
condition: { field: 'authMethod', value: 'password' },
dependsOn: ['authMethod'],
},
{
@@ -82,6 +83,7 @@ export const SftpBlock: BlockConfig<SftpUploadResult> = {
type: 'code',
placeholder: '-----BEGIN OPENSSH PRIVATE KEY-----\n...',
condition: { field: 'authMethod', value: 'privateKey' },
dependsOn: ['authMethod'],
},
{
id: 'passphrase',
@@ -90,6 +92,7 @@ export const SftpBlock: BlockConfig<SftpUploadResult> = {
password: true,
placeholder: 'Passphrase for encrypted key (optional)',
condition: { field: 'authMethod', value: 'privateKey' },
dependsOn: ['authMethod'],
},
{
+6
View File
@@ -179,6 +179,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
type: 'short-input',
canonicalParamId: 'channel',
placeholder: 'Enter Slack channel ID (e.g., C1234567890)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: (values?: Record<string, unknown>) => {
const op = values?.operation as string
@@ -238,6 +239,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
type: 'short-input',
canonicalParamId: 'dmUserId',
placeholder: 'Enter Slack user ID (e.g., U1234567890)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: {
field: 'destinationType',
@@ -267,6 +269,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
type: 'short-input',
canonicalParamId: 'ephemeralUser',
placeholder: 'Enter Slack user ID (e.g., U1234567890)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: {
field: 'operation',
@@ -529,6 +532,7 @@ Do not include any explanations, markdown formatting, or other text outside the
type: 'short-input',
canonicalParamId: 'userId',
placeholder: 'Enter Slack user ID (e.g., U1234567890)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: {
field: 'operation',
@@ -748,6 +752,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
type: 'short-input',
canonicalParamId: 'presenceUserId',
placeholder: 'Enter Slack user ID (e.g., U1234567890)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: {
field: 'operation',
@@ -1108,6 +1113,7 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
type: 'short-input',
canonicalParamId: 'publishUserId',
placeholder: 'Enter Slack user ID (e.g., U0BPQUNTA)',
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken'] },
mode: 'advanced',
condition: {
field: 'operation',
+3
View File
@@ -83,6 +83,7 @@ export const SSHBlock: BlockConfig<SSHResponse> = {
password: true,
placeholder: 'Your SSH password',
condition: { field: 'authMethod', value: 'password' },
dependsOn: ['authMethod'],
},
// Private key authentication
@@ -92,6 +93,7 @@ export const SSHBlock: BlockConfig<SSHResponse> = {
type: 'code',
placeholder: '-----BEGIN OPENSSH PRIVATE KEY-----\n...',
condition: { field: 'authMethod', value: 'privateKey' },
dependsOn: ['authMethod'],
},
{
id: 'passphrase',
@@ -100,6 +102,7 @@ export const SSHBlock: BlockConfig<SSHResponse> = {
password: true,
placeholder: 'Passphrase for encrypted key (optional)',
condition: { field: 'authMethod', value: 'privateKey' },
dependsOn: ['authMethod'],
},
// EXECUTE COMMAND
+27 -9
View File
@@ -351,6 +351,7 @@ Example 3 (Data Collection):
type: 'short-input',
placeholder: 'Enter your API key for the selected provider',
password: true,
dependsOn: ['provider'],
required: true,
},
],
@@ -361,17 +362,34 @@ Example 3 (Data Collection):
return params.operation === 'agent' ? 'stagehand_agent' : 'stagehand_extract'
},
params: (params) => {
const next: Record<string, any> = { ...params }
if (typeof next.maxSteps === 'string') {
const trimmed = next.maxSteps.trim()
if (trimmed === '') {
next.maxSteps = undefined
} else {
const n = Number(trimmed)
next.maxSteps = Number.isFinite(n) ? n : undefined
const baseParams = {
operation: params.operation,
provider: params.provider,
apiKey: params.apiKey,
}
if (params.operation !== 'agent') {
return {
...baseParams,
url: params.url,
instruction: params.instruction,
schema: params.schema,
}
}
return next
const maxStepsInput =
typeof params.maxSteps === 'string' ? params.maxSteps.trim() : params.maxSteps
const maxSteps = maxStepsInput === '' ? Number.NaN : Number(maxStepsInput)
return {
...baseParams,
startUrl: params.startUrl,
task: params.task,
variables: params.variables,
outputSchema: params.outputSchema,
mode: params.mode,
maxSteps: Number.isFinite(maxSteps) ? maxSteps : undefined,
}
},
},
},
+8 -6
View File
@@ -43,6 +43,7 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
condition: { field: 'provider', value: 'whisper' },
options: [{ label: 'Whisper-1', id: 'whisper-1' }],
value: () => 'whisper-1',
dependsOn: ['provider'],
required: true,
},
@@ -52,11 +53,9 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
title: 'Model',
type: 'dropdown',
condition: { field: 'provider', value: 'elevenlabs' },
options: [
{ label: 'Scribe v1', id: 'scribe_v1' },
{ label: 'Scribe v1 Experimental', id: 'scribe_v1_experimental' },
],
value: () => 'scribe_v1',
options: [{ label: 'Scribe v2', id: 'scribe_v2' }],
value: () => 'scribe_v2',
dependsOn: ['provider'],
required: true,
},
@@ -75,6 +74,7 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
{ label: 'Base', id: 'base' },
],
value: () => 'nova-3',
dependsOn: ['provider'],
required: true,
},
@@ -86,6 +86,7 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
condition: { field: 'provider', value: 'assemblyai' },
options: [{ label: 'Best', id: 'best' }],
value: () => 'best',
dependsOn: ['provider'],
required: true,
},
@@ -104,6 +105,7 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
{ label: 'Gemini 2.0 Flash', id: 'gemini-2.0-flash-exp' },
],
value: () => 'gemini-2.5-flash',
dependsOn: ['provider'],
required: true,
},
@@ -295,7 +297,7 @@ export const SttBlock: BlockConfig<SttBlockResponse> = {
model: {
type: 'string',
description:
'Provider-specific model (e.g., scribe_v1 for ElevenLabs, nova-3 for Deepgram, best for AssemblyAI, gemini-2.0-flash-exp for Gemini)',
'Provider-specific model (e.g., scribe_v2 for ElevenLabs, nova-3 for Deepgram, best for AssemblyAI, gemini-2.0-flash-exp for Gemini)',
},
audioFile: { type: 'json', description: 'Audio/video file (UserFile)' },
audioUrl: { type: 'string', description: 'Audio/video URL' },
+1
View File
@@ -230,6 +230,7 @@ export const TableBlock: BlockConfig<TableQueryResponse> = {
title: 'Row ID',
type: 'short-input',
placeholder: 'row_xxxxx',
dependsOn: ['tableId'],
condition: { field: 'operation', value: ['get_row', 'update_row', 'delete_row'] },
required: true,
},
+1
View File
@@ -125,6 +125,7 @@ export const TrelloBlock: BlockConfig<TrelloResponse> = {
type: 'short-input',
canonicalParamId: 'boardId',
placeholder: 'Enter Trello board ID',
dependsOn: ['credential'],
mode: 'advanced',
condition: {
field: 'operation',
+27
View File
@@ -56,6 +56,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'GPT-4o Mini TTS', id: 'gpt-4o-mini-tts' },
],
value: () => 'tts-1',
dependsOn: ['provider'],
required: false,
},
@@ -78,6 +79,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Verse', id: 'verse' },
],
value: () => 'alloy',
dependsOn: ['provider'],
required: false,
},
@@ -95,6 +97,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'WAV', id: 'wav' },
],
value: () => 'mp3',
dependsOn: ['provider'],
required: false,
},
@@ -108,6 +111,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 4.0,
step: 0.25,
value: () => '1.0',
dependsOn: ['provider'],
required: false,
},
@@ -132,6 +136,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Zeus', id: 'aura-zeus-en' },
],
value: () => 'aura-asteria-en',
dependsOn: ['provider'],
required: true,
},
@@ -149,6 +154,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Linear16', id: 'linear16' },
],
value: () => 'mp3',
dependsOn: ['provider'],
required: false,
},
@@ -169,6 +175,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: '48000 Hz', id: '48000' },
],
value: () => '24000',
dependsOn: ['encoding'],
required: false,
},
@@ -179,6 +186,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'elevenlabs' },
placeholder: 'Enter ElevenLabs voice ID',
dependsOn: ['provider'],
required: true,
},
@@ -197,6 +205,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Multilingual v1', id: 'eleven_multilingual_v1' },
],
value: () => 'eleven_turbo_v2_5',
dependsOn: ['provider'],
required: false,
},
@@ -210,6 +219,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 1.0,
step: 0.05,
value: () => '0.5',
dependsOn: ['provider'],
required: false,
},
@@ -223,6 +233,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 1.0,
step: 0.05,
value: () => '0.8',
dependsOn: ['provider'],
required: false,
},
@@ -236,6 +247,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 1.0,
step: 0.05,
value: () => '0.0',
dependsOn: ['provider'],
required: false,
},
@@ -253,6 +265,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Sonic Multilingual', id: 'sonic-multilingual' },
],
value: () => 'sonic-3',
dependsOn: ['provider'],
required: false,
},
@@ -263,6 +276,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'cartesia' },
placeholder: 'Enter Cartesia voice ID',
dependsOn: ['provider'],
required: true,
},
@@ -276,6 +290,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 2.0,
step: 0.1,
value: () => '1.0',
dependsOn: ['provider'],
required: false,
},
@@ -286,6 +301,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'google' },
placeholder: 'e.g., en-US-Neural2-A',
dependsOn: ['provider'],
required: false,
},
@@ -297,6 +313,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
condition: { field: 'provider', value: 'google' },
placeholder: 'e.g., en-US, es-ES',
value: () => 'en-US',
dependsOn: ['provider'],
required: true,
},
@@ -310,6 +327,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 2.0,
step: 0.25,
value: () => '1.0',
dependsOn: ['provider'],
required: false,
},
@@ -323,6 +341,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 20.0,
step: 1.0,
value: () => '0.0',
dependsOn: ['provider'],
required: false,
},
@@ -333,6 +352,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'azure' },
placeholder: 'e.g., en-US-JennyNeural',
dependsOn: ['provider'],
required: false,
},
@@ -343,6 +363,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'azure' },
placeholder: 'e.g., eastus, westus',
dependsOn: ['provider'],
required: false,
},
@@ -358,6 +379,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'MP3 48kHz 96kbps', id: 'audio-48khz-96kbitrate-mono-mp3' },
],
value: () => 'audio-24khz-96kbitrate-mono-mp3',
dependsOn: ['provider'],
required: false,
},
@@ -368,6 +390,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'azure' },
placeholder: 'e.g., cheerful, sad, angry',
dependsOn: ['provider'],
required: false,
},
@@ -379,6 +402,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
condition: { field: 'provider', value: 'playht' },
placeholder: 'Enter your PlayHT user ID',
password: true,
dependsOn: ['provider'],
required: true,
},
@@ -389,6 +413,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
type: 'short-input',
condition: { field: 'provider', value: 'playht' },
placeholder: 'Voice ID or manifest URL',
dependsOn: ['provider'],
required: false,
},
@@ -404,6 +429,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
{ label: 'Premium', id: 'premium' },
],
value: () => 'standard',
dependsOn: ['provider'],
required: false,
},
@@ -417,6 +443,7 @@ export const TtsBlock: BlockConfig<TtsBlockResponse> = {
max: 2.0,
step: 0.1,
value: () => '1.0',
dependsOn: ['provider'],
required: false,
},
+39
View File
@@ -49,6 +49,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: 'Veo 3.1', id: 'veo-3.1' },
],
value: () => 'veo-3',
dependsOn: ['provider'],
required: false,
},
@@ -60,6 +61,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
condition: { field: 'provider', value: 'luma' },
options: [{ label: 'Ray 2', id: 'ray-2' }],
value: () => 'ray-2',
dependsOn: ['provider'],
required: false,
},
@@ -71,6 +73,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
condition: { field: 'provider', value: 'minimax' },
options: [{ label: 'Hailuo 2.3', id: 'hailuo-02' }],
value: () => 'hailuo-02',
dependsOn: ['provider'],
required: false,
},
@@ -84,6 +87,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: 'Standard', id: 'standard' },
],
value: () => 'standard',
dependsOn: ['provider'],
required: false,
},
@@ -104,6 +108,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: 'LTXV 0.9.8', id: 'ltxv-0.9.8' },
],
value: () => 'veo-3.1',
dependsOn: ['provider'],
required: true,
},
@@ -127,6 +132,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '5',
dependsOn: ['provider'],
required: false,
},
@@ -142,6 +148,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '8', id: '8' },
],
value: () => '8',
dependsOn: ['provider'],
required: false,
},
@@ -156,6 +163,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '9', id: '9' },
],
value: () => '5',
dependsOn: ['provider'],
required: false,
},
@@ -170,6 +178,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '6',
dependsOn: ['provider'],
required: false,
},
@@ -193,6 +202,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '5',
dependsOn: ['model'],
required: false,
},
@@ -207,6 +217,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '9:16', id: '9:16' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
@@ -222,6 +233,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '1:1', id: '1:1' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
@@ -237,6 +249,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '1:1', id: '1:1' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
@@ -259,6 +272,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '9:16', id: '9:16' },
],
value: () => '16:9',
dependsOn: ['model'],
required: false,
},
@@ -277,6 +291,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '1080p', id: '1080p' },
],
value: () => '1080p',
dependsOn: ['provider'],
required: false,
},
@@ -292,6 +307,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
{ label: '1080p', id: '1080p' },
],
value: () => '1080p',
dependsOn: ['provider'],
required: false,
},
@@ -306,6 +322,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
placeholder: 'Upload reference image',
mode: 'basic',
multiple: false,
dependsOn: ['provider'],
required: true,
acceptedTypes: '.jpg,.jpeg,.png,.webp',
},
@@ -317,6 +334,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
type: 'long-input',
condition: { field: 'provider', value: 'luma' },
placeholder: 'JSON: [{ "key": "pan_right" }, { "key": "zoom_in" }]',
dependsOn: ['provider'],
required: false,
},
@@ -326,6 +344,7 @@ export const VideoGeneratorBlock: BlockConfig<VideoBlockResponse> = {
title: 'Prompt Optimizer',
type: 'switch',
condition: { field: 'provider', value: 'minimax' },
dependsOn: ['provider'],
},
// API Key
@@ -463,6 +482,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: 'Veo 3.1', id: 'veo-3.1' },
],
value: () => 'veo-3',
dependsOn: ['provider'],
required: false,
},
{
@@ -472,6 +492,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
condition: { field: 'provider', value: 'luma' },
options: [{ label: 'Ray 2', id: 'ray-2' }],
value: () => 'ray-2',
dependsOn: ['provider'],
required: false,
},
{
@@ -481,6 +502,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
condition: { field: 'provider', value: 'minimax' },
options: [{ label: 'Hailuo 2.3', id: 'hailuo-02' }],
value: () => 'hailuo-02',
dependsOn: ['provider'],
required: false,
},
{
@@ -493,6 +515,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: 'Standard', id: 'standard' },
],
value: () => 'standard',
dependsOn: ['provider'],
required: false,
},
{
@@ -511,6 +534,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: 'LTXV 0.9.8', id: 'ltxv-0.9.8' },
],
value: () => 'veo-3.1',
dependsOn: ['provider'],
required: true,
},
{
@@ -530,6 +554,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '5',
dependsOn: ['provider'],
required: false,
},
{
@@ -543,6 +568,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '8', id: '8' },
],
value: () => '8',
dependsOn: ['provider'],
required: false,
},
{
@@ -555,6 +581,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '9', id: '9' },
],
value: () => '5',
dependsOn: ['provider'],
required: false,
},
{
@@ -567,6 +594,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '6',
dependsOn: ['provider'],
required: false,
},
{
@@ -588,6 +616,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '10', id: '10' },
],
value: () => '5',
dependsOn: ['model'],
required: false,
},
{
@@ -600,6 +629,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '9:16', id: '9:16' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
{
@@ -613,6 +643,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '1:1', id: '1:1' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
{
@@ -626,6 +657,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '1:1', id: '1:1' },
],
value: () => '16:9',
dependsOn: ['provider'],
required: false,
},
{
@@ -646,6 +678,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '9:16', id: '9:16' },
],
value: () => '16:9',
dependsOn: ['model'],
required: false,
},
{
@@ -658,6 +691,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '1080p', id: '1080p' },
],
value: () => '1080p',
dependsOn: ['provider'],
required: false,
},
{
@@ -671,6 +705,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
{ label: '1080p', id: '1080p' },
],
value: () => '1080p',
dependsOn: ['provider'],
required: false,
},
{
@@ -682,6 +717,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
placeholder: 'Upload reference image',
mode: 'basic',
multiple: false,
dependsOn: ['provider'],
required: true,
acceptedTypes: '.jpg,.jpeg,.png,.webp',
},
@@ -693,6 +729,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
condition: { field: 'provider', value: 'runway' },
placeholder: 'Reference image from previous blocks',
mode: 'advanced',
dependsOn: ['provider'],
required: true,
},
{
@@ -701,6 +738,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
type: 'long-input',
condition: { field: 'provider', value: 'luma' },
placeholder: 'JSON: [{ "key": "pan_right" }, { "key": "zoom_in" }]',
dependsOn: ['provider'],
required: false,
},
{
@@ -708,6 +746,7 @@ export const VideoGeneratorV2Block: BlockConfig<VideoBlockResponse> = {
title: 'Prompt Optimizer',
type: 'switch',
condition: { field: 'provider', value: 'minimax' },
dependsOn: ['provider'],
},
{
id: 'apiKey',
+2
View File
@@ -69,6 +69,7 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
placeholder: 'Enter Contact ID',
mode: 'basic',
canonicalParamId: 'contactId',
dependsOn: ['credential'],
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
},
{
@@ -78,6 +79,7 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
canonicalParamId: 'contactId',
placeholder: 'Enter Contact ID',
mode: 'advanced',
dependsOn: ['credential'],
condition: { field: 'operation', value: ['read_contact', 'write_task', 'write_note'] },
},
{
+1
View File
@@ -112,6 +112,7 @@ export const ZoomBlock: BlockConfig<ZoomResponse> = {
type: 'short-input',
canonicalParamId: 'meetingId',
placeholder: 'Enter meeting ID',
dependsOn: ['credential'],
mode: 'advanced',
required: true,
condition: {
+93
View File
@@ -66,8 +66,11 @@ vi.mock('@/lib/oauth/utils', () => ({
getScopesForService: vi.fn(() => []),
}))
import type { SubBlockConfig } from '@/blocks/types'
import {
getApiKeyCondition,
getDependsOnFields,
getSubBlocksDependingOnChange,
parseOptionalBooleanInput,
parseOptionalJsonInput,
parseOptionalNumberInput,
@@ -359,3 +362,93 @@ describe('parseOptionalBooleanInput', () => {
expect(parseOptionalBooleanInput('no')).toBeUndefined()
})
})
describe('getDependsOnFields', () => {
it('returns an empty array when dependsOn is unset', () => {
expect(getDependsOnFields(undefined)).toEqual([])
})
it('returns array dependencies unchanged', () => {
expect(getDependsOnFields(['credential', 'projectId'])).toEqual(['credential', 'projectId'])
})
it('flattens all and any dependencies', () => {
expect(getDependsOnFields({ all: ['credential'], any: ['teamId', 'manualTeamId'] })).toEqual([
'credential',
'teamId',
'manualTeamId',
])
})
})
describe('getSubBlocksDependingOnChange', () => {
it('finds direct dependents of a changed subblock', () => {
const subBlocks: SubBlockConfig[] = [
{ id: 'provider', title: 'Provider', type: 'dropdown' },
{ id: 'model', title: 'Model', type: 'dropdown', dependsOn: ['provider'] },
{ id: 'prompt', title: 'Prompt', type: 'long-input' },
]
expect(
getSubBlocksDependingOnChange(subBlocks, 'provider').map((subBlock) => subBlock.id)
).toEqual(['model'])
})
it('matches dependents through canonical basic and advanced siblings', () => {
const subBlocks: SubBlockConfig[] = [
{
id: 'channel',
title: 'Channel',
type: 'channel-selector',
canonicalParamId: 'channelId',
mode: 'basic',
},
{
id: 'manualChannel',
title: 'Channel ID',
type: 'short-input',
canonicalParamId: 'channelId',
mode: 'advanced',
},
{
id: 'messageId',
title: 'Message ID',
type: 'short-input',
dependsOn: ['channelId'],
},
{
id: 'threadTs',
title: 'Thread Timestamp',
type: 'short-input',
dependsOn: ['otherField'],
},
]
expect(
getSubBlocksDependingOnChange(subBlocks, 'manualChannel').map((subBlock) => subBlock.id)
).toEqual(['messageId'])
expect(
getSubBlocksDependingOnChange(subBlocks, 'channel').map((subBlock) => subBlock.id)
).toEqual(['messageId'])
})
it('matches object-form dependencies when any listed dependency changes', () => {
const subBlocks: SubBlockConfig[] = [
{ id: 'credential', title: 'Credential', type: 'oauth-input' },
{ id: 'teamId', title: 'Team', type: 'short-input' },
{
id: 'projectId',
title: 'Project',
type: 'short-input',
dependsOn: { all: ['credential'], any: ['teamId'] },
},
]
expect(
getSubBlocksDependingOnChange(subBlocks, 'credential').map((subBlock) => subBlock.id)
).toEqual(['projectId'])
expect(
getSubBlocksDependingOnChange(subBlocks, 'teamId').map((subBlock) => subBlock.id)
).toEqual(['projectId'])
})
})
+24 -10
View File
@@ -1,6 +1,7 @@
import { toError } from '@sim/utils/errors'
import { isAzureConfigured, isHosted, isOllamaConfigured } from '@/lib/core/config/feature-flags'
import { getScopesForService } from '@/lib/oauth/utils'
import { buildCanonicalIndex } from '@/lib/workflows/subblocks/visibility'
import type { BlockOutput, OutputFieldDefinition, SubBlockConfig } from '@/blocks/types'
import {
getBaseModelProviders,
@@ -63,16 +64,6 @@ export function getModelOptions() {
})
}
/**
* Checks if a field is included in the dependsOn config.
* Handles both simple array format and object format with all/any fields.
*/
export function isDependency(dependsOn: SubBlockConfig['dependsOn'], field: string): boolean {
if (!dependsOn) return false
if (Array.isArray(dependsOn)) return dependsOn.includes(field)
return dependsOn.all?.includes(field) || dependsOn.any?.includes(field) || false
}
/**
* Gets all dependency fields as a flat array.
* Handles both simple array format and object format with all/any fields.
@@ -83,6 +74,29 @@ export function getDependsOnFields(dependsOn: SubBlockConfig['dependsOn']): stri
return [...(dependsOn.all || []), ...(dependsOn.any || [])]
}
/**
* Finds subblocks that depend on a changed field, accounting for canonical pairs.
*/
export function getSubBlocksDependingOnChange(
allSubBlocks: SubBlockConfig[],
changedSubBlockId: string
): SubBlockConfig[] {
const canonicalIndex = buildCanonicalIndex(allSubBlocks)
const canonicalId = canonicalIndex.canonicalIdBySubBlockId[changedSubBlockId]
const group = canonicalId ? canonicalIndex.groupsById[canonicalId] : undefined
const changedFields = new Set<string>([changedSubBlockId])
if (canonicalId) changedFields.add(canonicalId)
if (group?.basicId) changedFields.add(group.basicId)
for (const advancedId of group?.advancedIds || []) {
changedFields.add(advancedId)
}
return allSubBlocks.filter((subBlock) =>
getDependsOnFields(subBlock.dependsOn).some((field) => changedFields.has(field))
)
}
export function resolveOutputType(
outputs: Record<string, OutputFieldDefinition>
): Record<string, BlockOutput> {
+2 -3
View File
@@ -19,6 +19,7 @@ import { getWorkflowStateContract } from '@/lib/api/contracts'
import { useSession } from '@/lib/auth/auth-client'
import { useSocket } from '@/app/workspace/providers/socket-provider'
import { getBlock } from '@/blocks'
import { getSubBlocksDependingOnChange } from '@/blocks/utils'
import { normalizeName, RESERVED_BLOCK_NAMES } from '@/executor/constants'
import { invalidateDeploymentQueries } from '@/hooks/queries/deployments'
import { useUndoRedo } from '@/hooks/use-undo-redo'
@@ -1322,9 +1323,7 @@ export function useCollaborativeWorkflow() {
const blockType = useWorkflowStore.getState().blocks?.[blockId]?.type
const blockConfig = blockType ? getBlock(blockType) : null
if (blockConfig?.subBlocks && Array.isArray(blockConfig.subBlocks)) {
const dependents = blockConfig.subBlocks.filter(
(sb: any) => Array.isArray(sb.dependsOn) && sb.dependsOn.includes(subblockId)
)
const dependents = getSubBlocksDependingOnChange(blockConfig.subBlocks, subblockId)
for (const dep of dependents) {
if (!dep?.id || dep.id === subblockId) continue
const currentDepValue = useSubBlockStore.getState().getValue(blockId, dep.id)
+3 -3
View File
@@ -24,7 +24,7 @@ export const elevenLabsSttTool: ToolConfig<SttParams, SttResponse> = {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'ElevenLabs model to use (scribe_v1, scribe_v1_experimental)',
description: 'ElevenLabs model to use (scribe_v2)',
},
audioFile: {
type: 'file',
@@ -71,7 +71,7 @@ export const elevenLabsSttTool: ToolConfig<SttParams, SttResponse> = {
) => ({
provider: 'elevenlabs',
apiKey: params.apiKey,
model: params.model,
model: 'scribe_v2',
audioFile: params.audioFile,
audioFileReference: params.audioFileReference,
audioUrl: params.audioUrl,
@@ -141,7 +141,7 @@ export const elevenLabsSttV2Tool: ToolConfig<SttV2Params, SttResponse> = {
) => ({
provider: 'elevenlabs',
apiKey: params.apiKey,
model: params.model,
model: 'scribe_v2',
audioFile: params.audioFile,
audioFileReference: params.audioFileReference,
language: params.language || 'auto',