Skip to content

Commit 5c1005c

Browse files
committed
fix(agents-core): skip final output while tools pending
1 parent bddda90 commit 5c1005c

File tree

2 files changed

+84
-35
lines changed

2 files changed

+84
-35
lines changed

packages/agents-core/src/runImplementation.ts

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -583,44 +583,45 @@ export async function executeToolsAndSideEffects<TContext>(
583583
);
584584
}
585585

586-
if (
587-
agent.outputType === 'text' &&
588-
!processedResponse.hasToolsOrApprovalsToRun()
589-
) {
590-
return new SingleStepResult(
591-
originalInput,
592-
newResponse,
593-
preStepItems,
594-
newItems,
595-
{
596-
type: 'next_step_final_output',
597-
output: potentialFinalOutput,
598-
},
599-
);
600-
} else if (agent.outputType !== 'text' && potentialFinalOutput) {
601-
// Structured output schema => always leads to a final output if we have text
602-
const { parser } = getSchemaAndParserFromInputType(
603-
agent.outputType,
604-
'final_output',
605-
);
606-
const [error] = await safeExecute(() => parser(potentialFinalOutput));
607-
if (error) {
608-
addErrorToCurrentSpan({
609-
message: 'Invalid output type',
610-
data: {
611-
error: String(error),
586+
if (!processedResponse.hasToolsOrApprovalsToRun()) {
587+
if (agent.outputType === 'text') {
588+
return new SingleStepResult(
589+
originalInput,
590+
newResponse,
591+
preStepItems,
592+
newItems,
593+
{
594+
type: 'next_step_final_output',
595+
output: potentialFinalOutput,
612596
},
613-
});
614-
throw new ModelBehaviorError('Invalid output type');
597+
);
615598
}
616599

617-
return new SingleStepResult(
618-
originalInput,
619-
newResponse,
620-
preStepItems,
621-
newItems,
622-
{ type: 'next_step_final_output', output: potentialFinalOutput },
623-
);
600+
if (agent.outputType !== 'text' && potentialFinalOutput) {
601+
// Structured output schema => always leads to a final output if we have text.
602+
const { parser } = getSchemaAndParserFromInputType(
603+
agent.outputType,
604+
'final_output',
605+
);
606+
const [error] = await safeExecute(() => parser(potentialFinalOutput));
607+
if (error) {
608+
addErrorToCurrentSpan({
609+
message: 'Invalid output type',
610+
data: {
611+
error: String(error),
612+
},
613+
});
614+
throw new ModelBehaviorError('Invalid output type');
615+
}
616+
617+
return new SingleStepResult(
618+
originalInput,
619+
newResponse,
620+
preStepItems,
621+
newItems,
622+
{ type: 'next_step_final_output', output: potentialFinalOutput },
623+
);
624+
}
624625
}
625626

626627
return new SingleStepResult(

packages/agents-core/test/runImplementation.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
TEST_MODEL_RESPONSE_WITH_FUNCTION,
4242
TEST_TOOL,
4343
FakeModelProvider,
44+
fakeModelMessage,
4445
} from './stubs';
4546
import { computerTool } from '../src/tool';
4647
import * as protocol from '../src/types/protocol';
@@ -903,6 +904,53 @@ describe('executeToolsAndSideEffects', () => {
903904
expect(result.nextStep.type).toBe('next_step_run_again');
904905
});
905906

907+
it('continues execution when structured agent has tools pending', async () => {
908+
const structuredAgent = new Agent({
909+
name: 'StructuredAgent',
910+
outputType: z.object({
911+
foo: z.string(),
912+
}),
913+
});
914+
915+
const structuredResponse: ModelResponse = {
916+
output: [
917+
{ ...TEST_MODEL_FUNCTION_CALL },
918+
fakeModelMessage('{"foo":"bar"}'),
919+
],
920+
usage: new Usage(),
921+
} as any;
922+
923+
const processedResponse = processModelResponse(
924+
structuredResponse,
925+
structuredAgent,
926+
[TEST_TOOL],
927+
[],
928+
);
929+
930+
expect(processedResponse.hasToolsOrApprovalsToRun()).toBe(true);
931+
932+
const structuredState = new RunState(
933+
new RunContext(),
934+
'test input',
935+
structuredAgent,
936+
1,
937+
);
938+
939+
const result = await withTrace('test', () =>
940+
executeToolsAndSideEffects(
941+
structuredAgent,
942+
'test input',
943+
[],
944+
structuredResponse,
945+
processedResponse,
946+
runner,
947+
structuredState,
948+
),
949+
);
950+
951+
expect(result.nextStep.type).toBe('next_step_run_again');
952+
});
953+
906954
it('returns final output when text agent has no tools pending', async () => {
907955
const textAgent = new Agent({ name: 'TextAgent', outputType: 'text' });
908956
const response: ModelResponse = {

0 commit comments

Comments
 (0)