Skip to content

Commit f1a0e96

Browse files
committed
test(ai): add test for mixed pending batch with server and client tools
1 parent e9ae589 commit f1a0e96

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

packages/typescript/ai/tests/chat.test.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,79 @@ describe('chat()', () => {
809809
expect(argsIdx).toBeLessThan(endIdx)
810810
}
811811
})
812+
813+
it('should emit TOOL_CALL_START and TOOL_CALL_ARGS for the server tool in a mixed pending batch', async () => {
814+
const weatherSpy = vi.fn().mockReturnValue({ temp: 72 })
815+
816+
const { adapter } = createMockAdapter({ iterations: [] })
817+
818+
const stream = chat({
819+
adapter,
820+
messages: [
821+
{ role: 'user', content: 'Weather and notify?' },
822+
{
823+
role: 'assistant',
824+
content: '',
825+
toolCalls: [
826+
{
827+
id: 'call_server',
828+
type: 'function' as const,
829+
function: { name: 'getWeather', arguments: '{"city":"NYC"}' },
830+
},
831+
{
832+
id: 'call_client',
833+
type: 'function' as const,
834+
function: {
835+
name: 'showNotification',
836+
arguments: '{"message":"done"}',
837+
},
838+
},
839+
],
840+
},
841+
// No tool results -> both pending
842+
],
843+
tools: [
844+
serverTool('getWeather', weatherSpy),
845+
clientTool('showNotification'),
846+
],
847+
})
848+
849+
const chunks = await collectChunks(stream as AsyncIterable<StreamChunk>)
850+
851+
// Server tool should have executed
852+
expect(weatherSpy).toHaveBeenCalledTimes(1)
853+
854+
// The executed server tool should get the full START -> ARGS -> END
855+
const starts = chunks.filter(
856+
(c) =>
857+
c.type === 'TOOL_CALL_START' &&
858+
(c as any).toolCallId === 'call_server',
859+
)
860+
expect(starts).toHaveLength(1)
861+
expect((starts[0] as any).toolName).toBe('getWeather')
862+
863+
const argChunks = chunks.filter(
864+
(c) =>
865+
c.type === 'TOOL_CALL_ARGS' &&
866+
(c as any).toolCallId === 'call_server',
867+
)
868+
expect(argChunks).toHaveLength(1)
869+
expect((argChunks[0] as any).delta).toBe('{"city":"NYC"}')
870+
871+
const ends = chunks.filter(
872+
(c) =>
873+
c.type === 'TOOL_CALL_END' &&
874+
(c as any).toolCallId === 'call_server',
875+
)
876+
expect(ends).toHaveLength(1)
877+
878+
// Verify ordering
879+
const startIdx = chunks.indexOf(starts[0]!)
880+
const argsIdx = chunks.indexOf(argChunks[0]!)
881+
const endIdx = chunks.indexOf(ends[0]!)
882+
expect(startIdx).toBeLessThan(argsIdx)
883+
expect(argsIdx).toBeLessThan(endIdx)
884+
})
812885
})
813886

814887
// ==========================================================================

0 commit comments

Comments
 (0)