Skip to content

Commit db654d8

Browse files
committed
fix(resolvers): agent response format, input formats, root level
1 parent 0ea0256 commit db654d8

File tree

16 files changed

+130
-278
lines changed

16 files changed

+130
-278
lines changed

apps/sim/app/api/function/execute/route.ts

Lines changed: 57 additions & 157 deletions
Large diffs are not rendered by default.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/code/code.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ import { WandPromptBar } from '@/app/workspace/[workspaceId]/w/[workflowId]/comp
3535
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
3636
import { useWand } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-wand'
3737
import type { GenerationType } from '@/blocks/types'
38+
import { normalizeName } from '@/executor/constants'
3839
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
3940
import { useTagSelection } from '@/hooks/kb/use-tag-selection'
40-
import { normalizeName } from '@/stores/workflows/utils'
4141

4242
const logger = createLogger('Code')
4343

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/condition-input/condition-input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ import {
3232
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
3333
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
3434
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
35+
import { normalizeName } from '@/executor/constants'
3536
import { createEnvVarPattern, createReferencePattern } from '@/executor/utils/reference-validation'
3637
import { useTagSelection } from '@/hooks/kb/use-tag-selection'
37-
import { normalizeName } from '@/stores/workflows/utils'
3838
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
3939

4040
const logger = createLogger('ConditionInput')

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
import type { ReactNode } from 'react'
44
import { splitReferenceSegment } from '@/lib/workflows/sanitization/references'
5-
import { REFERENCE } from '@/executor/constants'
5+
import { normalizeName, REFERENCE } from '@/executor/constants'
66
import { createCombinedPattern } from '@/executor/utils/reference-validation'
7-
import { normalizeName } from '@/stores/workflows/utils'
87

98
export interface HighlightContext {
109
accessiblePrefixes?: Set<string>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ import type {
3535
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
3636
import { getBlock } from '@/blocks'
3737
import type { BlockConfig } from '@/blocks/types'
38+
import { normalizeName } from '@/executor/constants'
3839
import type { Variable } from '@/stores/panel'
3940
import { useVariablesStore } from '@/stores/panel'
4041
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
4142
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
42-
import { normalizeName } from '@/stores/workflows/utils'
4343
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
4444
import type { BlockState } from '@/stores/workflows/workflow/types'
4545

apps/sim/executor/handlers/human-in-the-loop/human-in-the-loop-handler.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from '@/executor/human-in-the-loop/utils'
1818
import type { BlockHandler, ExecutionContext, PauseMetadata } from '@/executor/types'
1919
import { collectBlockData } from '@/executor/utils/block-data'
20+
import { parseObjectStrings } from '@/executor/utils/json'
2021
import type { SerializedBlock } from '@/serializer/types'
2122
import { executeTool } from '@/tools'
2223

@@ -265,7 +266,7 @@ export class HumanInTheLoopBlockHandler implements BlockHandler {
265266

266267
if (dataMode === 'structured' && inputs.builderData) {
267268
const convertedData = this.convertBuilderDataToJson(inputs.builderData)
268-
return this.parseObjectStrings(convertedData)
269+
return parseObjectStrings(convertedData)
269270
}
270271

271272
return inputs.data || {}
@@ -485,29 +486,6 @@ export class HumanInTheLoopBlockHandler implements BlockHandler {
485486
)
486487
}
487488

488-
private parseObjectStrings(data: any): any {
489-
if (typeof data === 'string') {
490-
try {
491-
const parsed = JSON.parse(data)
492-
if (typeof parsed === 'object' && parsed !== null) {
493-
return this.parseObjectStrings(parsed)
494-
}
495-
return parsed
496-
} catch {
497-
return data
498-
}
499-
} else if (Array.isArray(data)) {
500-
return data.map((item) => this.parseObjectStrings(item))
501-
} else if (typeof data === 'object' && data !== null) {
502-
const result: any = {}
503-
for (const [key, value] of Object.entries(data)) {
504-
result[key] = this.parseObjectStrings(value)
505-
}
506-
return result
507-
}
508-
return data
509-
}
510-
511489
private parseStatus(status?: string): number {
512490
if (!status) return HTTP.STATUS.OK
513491
const parsed = Number(status)

apps/sim/executor/handlers/response/response-handler.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createLogger } from '@sim/logger'
22
import { BlockType, HTTP, REFERENCE } from '@/executor/constants'
33
import type { BlockHandler, ExecutionContext, NormalizedBlockOutput } from '@/executor/types'
4+
import { parseObjectStrings } from '@/executor/utils/json'
45
import type { SerializedBlock } from '@/serializer/types'
56

67
const logger = createLogger('ResponseBlockHandler')
@@ -73,7 +74,7 @@ export class ResponseBlockHandler implements BlockHandler {
7374

7475
if (dataMode === 'structured' && inputs.builderData) {
7576
const convertedData = this.convertBuilderDataToJson(inputs.builderData)
76-
return this.parseObjectStrings(convertedData)
77+
return parseObjectStrings(convertedData)
7778
}
7879

7980
return inputs.data || {}
@@ -222,29 +223,6 @@ export class ResponseBlockHandler implements BlockHandler {
222223
)
223224
}
224225

225-
private parseObjectStrings(data: any): any {
226-
if (typeof data === 'string') {
227-
try {
228-
const parsed = JSON.parse(data)
229-
if (typeof parsed === 'object' && parsed !== null) {
230-
return this.parseObjectStrings(parsed)
231-
}
232-
return parsed
233-
} catch {
234-
return data
235-
}
236-
} else if (Array.isArray(data)) {
237-
return data.map((item) => this.parseObjectStrings(item))
238-
} else if (typeof data === 'object' && data !== null) {
239-
const result: any = {}
240-
for (const [key, value] of Object.entries(data)) {
241-
result[key] = this.parseObjectStrings(value)
242-
}
243-
return result
244-
}
245-
return data
246-
}
247-
248226
private parseStatus(status?: string): number {
249227
if (!status) return HTTP.STATUS.OK
250228
const parsed = Number(status)

apps/sim/executor/utils/json.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,30 @@ export function isJSONString(value: string): boolean {
4040
const trimmed = value.trim()
4141
return trimmed.startsWith('{') || trimmed.startsWith('[')
4242
}
43+
44+
/**
45+
* Recursively parses JSON strings within an object or array.
46+
* Useful for normalizing data that may contain stringified JSON at various levels.
47+
*/
48+
export function parseObjectStrings(data: unknown): unknown {
49+
if (typeof data === 'string') {
50+
try {
51+
const parsed = JSON.parse(data)
52+
if (typeof parsed === 'object' && parsed !== null) {
53+
return parseObjectStrings(parsed)
54+
}
55+
return parsed
56+
} catch {
57+
return data
58+
}
59+
} else if (Array.isArray(data)) {
60+
return data.map((item) => parseObjectStrings(item))
61+
} else if (typeof data === 'object' && data !== null) {
62+
const result: Record<string, unknown> = {}
63+
for (const [key, value] of Object.entries(data)) {
64+
result[key] = parseObjectStrings(value)
65+
}
66+
return result
67+
}
68+
return data
69+
}

apps/sim/executor/variables/resolvers/block.test.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -298,45 +298,6 @@ describe('BlockResolver', () => {
298298
})
299299
})
300300

301-
describe('tryParseJSON', () => {
302-
it.concurrent('should parse valid JSON object string', () => {
303-
const resolver = new BlockResolver(createTestWorkflow())
304-
expect(resolver.tryParseJSON('{"key": "value"}')).toEqual({ key: 'value' })
305-
})
306-
307-
it.concurrent('should parse valid JSON array string', () => {
308-
const resolver = new BlockResolver(createTestWorkflow())
309-
expect(resolver.tryParseJSON('[1, 2, 3]')).toEqual([1, 2, 3])
310-
})
311-
312-
it.concurrent('should return original value for non-string input', () => {
313-
const resolver = new BlockResolver(createTestWorkflow())
314-
const obj = { key: 'value' }
315-
expect(resolver.tryParseJSON(obj)).toBe(obj)
316-
expect(resolver.tryParseJSON(123)).toBe(123)
317-
expect(resolver.tryParseJSON(null)).toBe(null)
318-
})
319-
320-
it.concurrent('should return original string for non-JSON strings', () => {
321-
const resolver = new BlockResolver(createTestWorkflow())
322-
expect(resolver.tryParseJSON('plain text')).toBe('plain text')
323-
expect(resolver.tryParseJSON('123')).toBe('123')
324-
expect(resolver.tryParseJSON('')).toBe('')
325-
})
326-
327-
it.concurrent('should return original string for invalid JSON', () => {
328-
const resolver = new BlockResolver(createTestWorkflow())
329-
expect(resolver.tryParseJSON('{invalid json}')).toBe('{invalid json}')
330-
expect(resolver.tryParseJSON('[1, 2,')).toBe('[1, 2,')
331-
})
332-
333-
it.concurrent('should handle whitespace around JSON', () => {
334-
const resolver = new BlockResolver(createTestWorkflow())
335-
expect(resolver.tryParseJSON(' {"key": "value"} ')).toEqual({ key: 'value' })
336-
expect(resolver.tryParseJSON('\n[1, 2]\n')).toEqual([1, 2])
337-
})
338-
})
339-
340301
describe('Response block backwards compatibility', () => {
341302
it.concurrent('should resolve new format: <responseBlock.data>', () => {
342303
const workflow = createTestWorkflow([

apps/sim/executor/variables/resolvers/block.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
12
import { USER_FILE_ACCESSIBLE_PROPERTIES } from '@/lib/workflows/types'
23
import {
34
isReference,
@@ -229,9 +230,15 @@ export class BlockResolver implements Resolver {
229230
}
230231
}
231232

233+
const blockType = block?.metadata?.id
234+
const params = block?.config?.params as Record<string, unknown> | undefined
235+
const subBlocks = params
236+
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, { value: v }]))
237+
: undefined
232238
const toolId = block?.config?.tool
233239
const toolConfig = toolId ? getTool(toolId) : undefined
234-
const outputSchema = toolConfig?.outputs ?? block?.outputs
240+
const outputSchema =
241+
toolConfig?.outputs ?? (blockType ? getBlockOutputs(blockType, subBlocks) : block?.outputs)
235242
const schemaFields = getSchemaFieldNames(outputSchema)
236243
if (schemaFields.length > 0 && !isPathInOutputSchema(outputSchema, pathParts)) {
237244
throw new Error(
@@ -336,21 +343,4 @@ export class BlockResolver implements Resolver {
336343
}
337344
return String(value)
338345
}
339-
340-
tryParseJSON(value: any): any {
341-
if (typeof value !== 'string') {
342-
return value
343-
}
344-
345-
const trimmed = value.trim()
346-
if (trimmed.length > 0 && (trimmed.startsWith('{') || trimmed.startsWith('['))) {
347-
try {
348-
return JSON.parse(trimmed)
349-
} catch {
350-
return value
351-
}
352-
}
353-
354-
return value
355-
}
356346
}

0 commit comments

Comments
 (0)