fix(resolver): agent response format, input formats, root level (#2925)

* fix(resolvers): agent response format, input formats, root level

* fix response block initial seeding

* fix tests
This commit is contained in:
Vikhyath Mondreti
2026-01-21 14:55:23 -08:00
committed by GitHub
parent 8bbcf31b83
commit 5157f0bbb2
18 changed files with 203 additions and 330 deletions

View File

@@ -6,6 +6,10 @@ import type { ResolutionContext } from './reference'
vi.mock('@sim/logger', () => loggerMock)
vi.mock('@/lib/workflows/blocks/block-outputs', () => ({
getBlockOutputs: vi.fn(() => ({})),
}))
function createTestWorkflow(
blocks: Array<{
id: string
@@ -140,16 +144,21 @@ describe('BlockResolver', () => {
expect(resolver.resolve('<source.nonexistent>', ctx)).toBeUndefined()
})
it.concurrent('should throw error for path not in output schema', () => {
it.concurrent('should throw error for path not in output schema', async () => {
const { getBlockOutputs } = await import('@/lib/workflows/blocks/block-outputs')
const mockGetBlockOutputs = vi.mocked(getBlockOutputs)
const customOutputs = {
validField: { type: 'string', description: 'A valid field' },
nested: {
child: { type: 'number', description: 'Nested child' },
},
}
mockGetBlockOutputs.mockReturnValue(customOutputs as any)
const workflow = createTestWorkflow([
{
id: 'source',
outputs: {
validField: { type: 'string', description: 'A valid field' },
nested: {
child: { type: 'number', description: 'Nested child' },
},
},
outputs: customOutputs,
},
])
const resolver = new BlockResolver(workflow)
@@ -161,6 +170,8 @@ describe('BlockResolver', () => {
/"invalidField" doesn't exist on block "source"/
)
expect(() => resolver.resolve('<source.invalidField>', ctx)).toThrow(/Available fields:/)
mockGetBlockOutputs.mockReturnValue({})
})
it.concurrent('should return undefined for path in schema but missing in data', () => {
@@ -298,45 +309,6 @@ describe('BlockResolver', () => {
})
})
describe('tryParseJSON', () => {
it.concurrent('should parse valid JSON object string', () => {
const resolver = new BlockResolver(createTestWorkflow())
expect(resolver.tryParseJSON('{"key": "value"}')).toEqual({ key: 'value' })
})
it.concurrent('should parse valid JSON array string', () => {
const resolver = new BlockResolver(createTestWorkflow())
expect(resolver.tryParseJSON('[1, 2, 3]')).toEqual([1, 2, 3])
})
it.concurrent('should return original value for non-string input', () => {
const resolver = new BlockResolver(createTestWorkflow())
const obj = { key: 'value' }
expect(resolver.tryParseJSON(obj)).toBe(obj)
expect(resolver.tryParseJSON(123)).toBe(123)
expect(resolver.tryParseJSON(null)).toBe(null)
})
it.concurrent('should return original string for non-JSON strings', () => {
const resolver = new BlockResolver(createTestWorkflow())
expect(resolver.tryParseJSON('plain text')).toBe('plain text')
expect(resolver.tryParseJSON('123')).toBe('123')
expect(resolver.tryParseJSON('')).toBe('')
})
it.concurrent('should return original string for invalid JSON', () => {
const resolver = new BlockResolver(createTestWorkflow())
expect(resolver.tryParseJSON('{invalid json}')).toBe('{invalid json}')
expect(resolver.tryParseJSON('[1, 2,')).toBe('[1, 2,')
})
it.concurrent('should handle whitespace around JSON', () => {
const resolver = new BlockResolver(createTestWorkflow())
expect(resolver.tryParseJSON(' {"key": "value"} ')).toEqual({ key: 'value' })
expect(resolver.tryParseJSON('\n[1, 2]\n')).toEqual([1, 2])
})
})
describe('Response block backwards compatibility', () => {
it.concurrent('should resolve new format: <responseBlock.data>', () => {
const workflow = createTestWorkflow([

View File

@@ -1,3 +1,4 @@
import { getBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
import { USER_FILE_ACCESSIBLE_PROPERTIES } from '@/lib/workflows/types'
import {
isReference,
@@ -229,9 +230,15 @@ export class BlockResolver implements Resolver {
}
}
const blockType = block?.metadata?.id
const params = block?.config?.params as Record<string, unknown> | undefined
const subBlocks = params
? Object.fromEntries(Object.entries(params).map(([k, v]) => [k, { value: v }]))
: undefined
const toolId = block?.config?.tool
const toolConfig = toolId ? getTool(toolId) : undefined
const outputSchema = toolConfig?.outputs ?? block?.outputs
const outputSchema =
toolConfig?.outputs ?? (blockType ? getBlockOutputs(blockType, subBlocks) : block?.outputs)
const schemaFields = getSchemaFieldNames(outputSchema)
if (schemaFields.length > 0 && !isPathInOutputSchema(outputSchema, pathParts)) {
throw new Error(
@@ -336,21 +343,4 @@ export class BlockResolver implements Resolver {
}
return String(value)
}
tryParseJSON(value: any): any {
if (typeof value !== 'string') {
return value
}
const trimmed = value.trim()
if (trimmed.length > 0 && (trimmed.startsWith('{') || trimmed.startsWith('['))) {
try {
return JSON.parse(trimmed)
} catch {
return value
}
}
return value
}
}