Skip to content

Commit 9d31e1b

Browse files
committed
Rename output mode 'json' to 'structured_output'
1 parent 3235985 commit 9d31e1b

19 files changed

+227
-198
lines changed

backend/src/__tests__/loop-agent-steps.test.ts

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ import * as promptAgentStream from '../prompt-agent-stream'
2424
import * as requestContext from '../websockets/request-context'
2525

2626
import type { AgentTemplate, StepGenerator } from '../templates/types'
27-
import type {
28-
AgentState,
29-
} from '@codebuff/common/types/session-state'
27+
import type { AgentState } from '@codebuff/common/types/session-state'
3028
import type { WebSocket } from 'ws'
3129

3230
describe('loopAgentSteps STEP behavior', () => {
@@ -151,7 +149,7 @@ describe('loopAgentSteps STEP behavior', () => {
151149
parentPrompt: 'Testing',
152150
model: 'claude-3-5-sonnet-20241022',
153151
inputSchema: {},
154-
outputMode: 'json',
152+
outputMode: 'structured_output',
155153
includeMessageHistory: true,
156154
toolNames: ['read_files', 'write_file', 'end_turn'],
157155
subagents: [],
@@ -180,26 +178,29 @@ describe('loopAgentSteps STEP behavior', () => {
180178
clearAgentGeneratorCache()
181179
})
182180

183-
llmCallCount = 0 // Reset LLM call count
181+
llmCallCount = 0 // Reset LLM call count
184182
afterAll(() => {
185183
clearMockedModules()
186184
})
187185

188186
it('should verify correct STEP behavior - LLM called once after STEP', async () => {
189187
// This test verifies that programmatic agents don't call the LLM,
190188
// and that STEP yielding works correctly without LLM involvement
191-
189+
192190
let stepCount = 0
193191
const mockGenerator = (function* () {
194192
stepCount++
195-
193+
196194
if (stepCount === 1) {
197195
// First call: Execute a tool, then STEP
198196
yield { toolName: 'read_files', args: { paths: ['file1.txt'] } }
199197
yield 'STEP' // Should pause here
200198
} else if (stepCount === 2) {
201199
// Second call: Should continue from here, not call LLM
202-
yield { toolName: 'write_file', args: { path: 'output.txt', content: 'test' } }
200+
yield {
201+
toolName: 'write_file',
202+
args: { path: 'output.txt', content: 'test' },
203+
}
203204
yield { toolName: 'end_turn', args: {} }
204205
}
205206
})() as StepGenerator
@@ -213,47 +214,55 @@ describe('loopAgentSteps STEP behavior', () => {
213214
// Mock checkLiveUserInput to return true for multiple iterations
214215
let checkCallCount = 0
215216
const mockCheckLiveUserInput = require('@codebuff/backend/live-user-inputs')
216-
spyOn(mockCheckLiveUserInput, 'checkLiveUserInput').mockImplementation(() => {
217-
checkCallCount++
218-
// Allow enough iterations to see the bug
219-
return checkCallCount <= 3
220-
})
217+
spyOn(mockCheckLiveUserInput, 'checkLiveUserInput').mockImplementation(
218+
() => {
219+
checkCallCount++
220+
// Allow enough iterations to see the bug
221+
return checkCallCount <= 3
222+
},
223+
)
221224

222-
const result = await loopAgentSteps(new MockWebSocket() as unknown as WebSocket, {
223-
userInputId: 'test-user-input',
224-
agentType: 'test-agent',
225-
agentState: mockAgentState,
226-
prompt: 'Test prompt',
227-
params: undefined,
228-
fingerprintId: 'test-fingerprint',
229-
fileContext: mockFileContext,
230-
toolResults: [],
231-
localAgentTemplates,
232-
userId: TEST_USER_ID,
233-
clientSessionId: 'test-session',
234-
onResponseChunk: () => {},
235-
})
225+
const result = await loopAgentSteps(
226+
new MockWebSocket() as unknown as WebSocket,
227+
{
228+
userInputId: 'test-user-input',
229+
agentType: 'test-agent',
230+
agentState: mockAgentState,
231+
prompt: 'Test prompt',
232+
params: undefined,
233+
fingerprintId: 'test-fingerprint',
234+
fileContext: mockFileContext,
235+
toolResults: [],
236+
localAgentTemplates,
237+
userId: TEST_USER_ID,
238+
clientSessionId: 'test-session',
239+
onResponseChunk: () => {},
240+
},
241+
)
236242

237243
console.log(`LLM calls made: ${llmCallCount}`)
238244
console.log(`Step count: ${stepCount}`)
239-
245+
240246
// CORRECT BEHAVIOR: After STEP, LLM should be called once, then no more
241247
// The programmatic agent yields STEP, then LLM runs once
242248
expect(llmCallCount).toBe(1) // LLM called once after STEP
243-
249+
244250
// The programmatic agent should have been called once (yielded STEP)
245251
expect(stepCount).toBe(1)
246-
252+
247253
// After STEP, the LLM should run once, then the loop should continue correctly
248254
})
249255

250256
it('should demonstrate correct behavior when programmatic agent completes without STEP', async () => {
251257
// This test shows that when a programmatic agent doesn't yield STEP,
252258
// it should complete without calling the LLM at all (since it ends with end_turn)
253-
259+
254260
const mockGenerator = (function* () {
255261
yield { toolName: 'read_files', args: { paths: ['file1.txt'] } }
256-
yield { toolName: 'write_file', args: { path: 'output.txt', content: 'test' } }
262+
yield {
263+
toolName: 'write_file',
264+
args: { path: 'output.txt', content: 'test' },
265+
}
257266
yield { toolName: 'end_turn', args: {} }
258267
})() as StepGenerator
259268

@@ -263,20 +272,23 @@ describe('loopAgentSteps STEP behavior', () => {
263272
'test-agent': mockTemplate,
264273
}
265274

266-
const result = await loopAgentSteps(new MockWebSocket() as unknown as WebSocket, {
267-
userInputId: 'test-user-input',
268-
agentType: 'test-agent',
269-
agentState: mockAgentState,
270-
prompt: 'Test prompt',
271-
params: undefined,
272-
fingerprintId: 'test-fingerprint',
273-
fileContext: mockFileContext,
274-
toolResults: [],
275-
localAgentTemplates,
276-
userId: TEST_USER_ID,
277-
clientSessionId: 'test-session',
278-
onResponseChunk: () => {},
279-
})
275+
const result = await loopAgentSteps(
276+
new MockWebSocket() as unknown as WebSocket,
277+
{
278+
userInputId: 'test-user-input',
279+
agentType: 'test-agent',
280+
agentState: mockAgentState,
281+
prompt: 'Test prompt',
282+
params: undefined,
283+
fingerprintId: 'test-fingerprint',
284+
fileContext: mockFileContext,
285+
toolResults: [],
286+
localAgentTemplates,
287+
userId: TEST_USER_ID,
288+
clientSessionId: 'test-session',
289+
onResponseChunk: () => {},
290+
},
291+
)
280292

281293
// Should NOT call LLM since the programmatic agent ended with end_turn
282294
expect(llmCallCount).toBe(0)

backend/src/__tests__/run-agent-step-tools.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ describe('runAgentStep - set_output tool', () => {
307307
parentPrompt: 'Testing handleSteps functionality',
308308
model: 'claude-3-5-sonnet-20241022',
309309
inputSchema: {},
310-
outputMode: 'json' as const,
310+
outputMode: 'structured_output' as const,
311311
includeMessageHistory: true,
312312
toolNames: ['read_files', 'end_turn'],
313313
subagents: [],
@@ -424,7 +424,7 @@ describe('runAgentStep - set_output tool', () => {
424424
parentPrompt: 'Deletes assistant messages',
425425
model: 'claude-3-5-sonnet-20241022',
426426
inputSchema: {},
427-
outputMode: 'json' as const,
427+
outputMode: 'structured_output' as const,
428428
includeMessageHistory: true,
429429
toolNames: ['set_messages', 'end_turn'],
430430
subagents: [],
@@ -466,7 +466,7 @@ describe('runAgentStep - set_output tool', () => {
466466
parentPrompt: 'Parent agent that spawns inline agents',
467467
model: 'claude-3-5-sonnet-20241022',
468468
inputSchema: {},
469-
outputMode: 'json' as const,
469+
outputMode: 'structured_output' as const,
470470
includeMessageHistory: true,
471471
toolNames: ['spawn_agent_inline', 'end_turn'],
472472
subagents: ['message-deleter-agent'],

backend/src/__tests__/run-programmatic-step.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('runProgrammaticStep', () => {
8787
parentPrompt: 'Testing',
8888
model: 'claude-3-5-sonnet-20241022',
8989
inputSchema: {},
90-
outputMode: 'json',
90+
outputMode: 'structured_output',
9191
includeMessageHistory: true,
9292
toolNames: ['read_files', 'write_file', 'end_turn'],
9393
subagents: [],
@@ -820,7 +820,7 @@ describe('runProgrammaticStep', () => {
820820
// Create template with outputSchema
821821
const schemaTemplate = {
822822
...mockTemplate,
823-
outputMode: 'json' as const,
823+
outputMode: 'structured_output' as const,
824824
outputSchema: {
825825
type: 'object',
826826
properties: {
@@ -868,7 +868,7 @@ describe('runProgrammaticStep', () => {
868868
// Create template with strict outputSchema
869869
const schemaTemplate = {
870870
...mockTemplate,
871-
outputMode: 'json' as const,
871+
outputMode: 'structured_output' as const,
872872
outputSchema: {
873873
type: 'object',
874874
properties: {
@@ -949,10 +949,10 @@ describe('runProgrammaticStep', () => {
949949
})
950950
})
951951

952-
it('should work with outputMode json but no outputSchema defined', async () => {
952+
it('should work with outputMode structured_output but no outputSchema defined', async () => {
953953
const schemaWithoutSchemaTemplate = {
954954
...mockTemplate,
955-
outputMode: 'json' as const,
955+
outputMode: 'structured_output' as const,
956956
outputSchema: undefined, // No schema defined
957957
toolNames: ['set_output', 'end_turn'],
958958
}
@@ -986,6 +986,7 @@ describe('runProgrammaticStep', () => {
986986
})
987987
})
988988
})
989+
989990
describe('logging and context', () => {
990991
it('should log agent execution start', async () => {
991992
const mockGenerator = (function* () {

backend/src/__tests__/sandbox-generator.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('QuickJS Sandbox Generator', () => {
3535
displayName: 'Test VM Agent',
3636
parentPrompt: 'Test VM isolation',
3737
model: 'anthropic/claude-4-sonnet-20250522',
38-
outputMode: 'json',
38+
outputMode: 'structured_output',
3939
includeMessageHistory: false,
4040
toolNames: ['set_output'],
4141
subagents: [],

backend/src/templates/agents/agent-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const agentBuilder = (model: Model): Omit<AgentTemplate, 'id'> => {
8686
.passthrough()
8787
.optional(),
8888
},
89-
outputMode: 'json',
89+
outputMode: 'structured_output',
9090
includeMessageHistory: false,
9191
toolNames: [
9292
'write_file',

backend/src/templates/agents/base-agent-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export const baseAgentBuilder = (
120120
.passthrough()
121121
.optional(),
122122
},
123-
outputMode: 'json',
123+
outputMode: 'structured_output',
124124
includeMessageHistory: false,
125125
toolNames: [
126126
'create_plan',

backend/src/templates/agents/file-explorer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const fileExplorer = {
1717
parentPrompt:
1818
'Spawns multiple file picker agents in parallel to comprehensively explore the codebase from different perspectives',
1919
model: 'anthropic/claude-4-sonnet-20250522',
20-
outputMode: 'json',
20+
outputMode: 'structured_output',
2121
includeMessageHistory: false,
2222
toolNames: ['spawn_agents', 'set_output'],
2323
subagents: ['file-picker'],

backend/src/templates/dynamic-agents.knowledge.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ yield { toolName: 'write_file', args: { path: '...', content: '...' } }
198198
// Search code
199199
yield { toolName: 'code_search', args: { pattern: '...' } }
200200

201-
// Set final output (required for outputMode: 'json')
201+
// Set final output (required for outputMode: 'structured_output')
202202
yield { toolName: 'set_output', args: { result: {...} } }
203203
```
204204

backend/src/tools/handlers/tool/spawn-agents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export const handleSpawnAgents = ((params: {
233233
}
234234
let report = ''
235235

236-
if (agentTemplate.outputMode === 'json') {
236+
if (agentTemplate.outputMode === 'structured_output') {
237237
report = JSON.stringify(result.value.agentState.output, null, 2)
238238
} else if (agentTemplate.outputMode === 'last_message') {
239239
const { agentState } = result.value

common/src/__tests__/agent-validation.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('Agent Validation', () => {
6969
displayName: 'Test Agent',
7070
parentPrompt: 'Testing',
7171
model: 'claude-3-5-sonnet-20241022',
72-
outputMode: 'json' as const,
72+
outputMode: 'structured_output' as const,
7373
toolNames: ['set_output'],
7474
subagents: [],
7575
includeMessageHistory: true,
@@ -671,7 +671,7 @@ describe('Agent Validation', () => {
671671
displayName: 'Test Agent',
672672
parentPrompt: 'Testing handleSteps',
673673
model: 'claude-3-5-sonnet-20241022',
674-
outputMode: 'json' as const,
674+
outputMode: 'structured_output' as const,
675675
toolNames: ['set_output'],
676676
systemPrompt: 'You are a test agent',
677677
instructionsPrompt: 'Process: {prompt}',
@@ -746,7 +746,7 @@ describe('Agent Validation', () => {
746746
displayName: 'Test Agent',
747747
parentPrompt: 'Testing',
748748
model: 'claude-3-5-sonnet-20241022',
749-
outputMode: 'json' as const,
749+
outputMode: 'structured_output' as const,
750750
toolNames: ['end_turn'], // Missing set_output
751751
subagents: [],
752752
systemPrompt: 'Test',
@@ -775,7 +775,7 @@ describe('Agent Validation', () => {
775775
displayName: 'Test Agent',
776776
parentPrompt: 'Testing',
777777
model: 'claude-3-5-sonnet-20241022',
778-
outputMode: 'last_message' as const, // Not json
778+
outputMode: 'last_message' as const, // Not structured_output
779779
toolNames: ['end_turn', 'set_output'], // Has set_output
780780
subagents: [],
781781
systemPrompt: 'Test',
@@ -788,7 +788,7 @@ describe('Agent Validation', () => {
788788
if (!result.success) {
789789
const errorMessage = result.error.issues[0]?.message || ''
790790
expect(errorMessage).toContain(
791-
"'set_output' tool requires outputMode to be 'json'",
791+
"'set_output' tool requires outputMode to be 'structured_output'",
792792
)
793793
}
794794
})

0 commit comments

Comments
 (0)