11import { createServer , type IncomingMessage , type Server , type ServerResponse } from "node:http" ;
22import type { AddressInfo } from "node:net" ;
3- import { afterAll , beforeAll , describe , expect , it } from "vitest" ;
3+ import { afterAll , beforeAll , beforeEach , describe , expect , it } from "vitest" ;
44import { generatePrivateKey } from "viem/accounts" ;
55
66import { startProxy , type ProxyHandle } from "./proxy.js" ;
@@ -10,6 +10,20 @@ describe("tool forwarding", () => {
1010 let proxy : ProxyHandle ;
1111 let upstreamUrl = "" ;
1212 let receivedBody : Record < string , unknown > | null = null ;
13+ let upstreamResponse : Record < string , unknown > = {
14+ id : "chatcmpl-tool-forwarding" ,
15+ object : "chat.completion" ,
16+ created : Math . floor ( Date . now ( ) / 1000 ) ,
17+ model : "openai/gpt-4o" ,
18+ choices : [
19+ {
20+ index : 0 ,
21+ message : { role : "assistant" , content : "ok" } ,
22+ finish_reason : "stop" ,
23+ } ,
24+ ] ,
25+ usage : { prompt_tokens : 10 , completion_tokens : 1 , total_tokens : 11 } ,
26+ } ;
1327
1428 beforeAll ( async ( ) => {
1529 upstream = createServer ( async ( req : IncomingMessage , res : ServerResponse ) => {
@@ -21,22 +35,7 @@ describe("tool forwarding", () => {
2135 receivedBody = JSON . parse ( Buffer . concat ( chunks ) . toString ( ) ) as Record < string , unknown > ;
2236
2337 res . writeHead ( 200 , { "Content-Type" : "application/json" } ) ;
24- res . end (
25- JSON . stringify ( {
26- id : "chatcmpl-tool-forwarding" ,
27- object : "chat.completion" ,
28- created : Math . floor ( Date . now ( ) / 1000 ) ,
29- model : "openai/gpt-4o" ,
30- choices : [
31- {
32- index : 0 ,
33- message : { role : "assistant" , content : "ok" } ,
34- finish_reason : "stop" ,
35- } ,
36- ] ,
37- usage : { prompt_tokens : 10 , completion_tokens : 1 , total_tokens : 11 } ,
38- } ) ,
39- ) ;
38+ res . end ( JSON . stringify ( upstreamResponse ) ) ;
4039 } ) ;
4140
4241 await new Promise < void > ( ( resolve ) => upstream . listen ( 0 , "127.0.0.1" , resolve ) ) ;
@@ -51,6 +50,23 @@ describe("tool forwarding", () => {
5150 } ) ;
5251 } , 10_000 ) ;
5352
53+ beforeEach ( ( ) => {
54+ upstreamResponse = {
55+ id : "chatcmpl-tool-forwarding" ,
56+ object : "chat.completion" ,
57+ created : Math . floor ( Date . now ( ) / 1000 ) ,
58+ model : "openai/gpt-4o" ,
59+ choices : [
60+ {
61+ index : 0 ,
62+ message : { role : "assistant" , content : "ok" } ,
63+ finish_reason : "stop" ,
64+ } ,
65+ ] ,
66+ usage : { prompt_tokens : 10 , completion_tokens : 1 , total_tokens : 11 } ,
67+ } ;
68+ } ) ;
69+
5470 afterAll ( async ( ) => {
5571 await proxy ?. close ( ) ;
5672 await new Promise < void > ( ( resolve ) => upstream . close ( ( ) => resolve ( ) ) ) ;
@@ -98,4 +114,67 @@ describe("tool forwarding", () => {
98114 expect ( parsedTools ) . toHaveLength ( 1 ) ;
99115 expect ( parsedTools [ 0 ] ?. function ?. name ) . toBe ( "web_search" ) ;
100116 } ) ;
117+
118+ it ( "suppresses assistant content when upstream returns tool_calls" , async ( ) => {
119+ upstreamResponse = {
120+ id : "chatcmpl-tool-content" ,
121+ object : "chat.completion" ,
122+ created : Math . floor ( Date . now ( ) / 1000 ) ,
123+ model : "moonshot/kimi-k2.6" ,
124+ choices : [
125+ {
126+ index : 0 ,
127+ message : {
128+ role : "assistant" ,
129+ content :
130+ "The user wants the current time. I should call get_current_time with Chicago." ,
131+ tool_calls : [
132+ {
133+ id : "get_current_time:0" ,
134+ type : "function" ,
135+ function : {
136+ name : "get_current_time" ,
137+ arguments : '{"city":"Chicago"}' ,
138+ } ,
139+ } ,
140+ ] ,
141+ } ,
142+ finish_reason : "tool_calls" ,
143+ } ,
144+ ] ,
145+ usage : { prompt_tokens : 10 , completion_tokens : 20 , total_tokens : 30 } ,
146+ } ;
147+
148+ const res = await fetch ( `${ proxy . baseUrl } /v1/chat/completions` , {
149+ method : "POST" ,
150+ headers : { "Content-Type" : "application/json" } ,
151+ body : JSON . stringify ( {
152+ model : "moonshot/kimi-k2.6" ,
153+ stream : false ,
154+ messages : [ { role : "user" , content : "What time is it in Chicago? Use the tool." } ] ,
155+ tools : [
156+ {
157+ type : "function" ,
158+ function : {
159+ name : "get_current_time" ,
160+ description : "Get current time" ,
161+ parameters : { type : "object" } ,
162+ } ,
163+ } ,
164+ ] ,
165+ } ) ,
166+ } ) ;
167+
168+ expect ( res . status ) . toBe ( 200 ) ;
169+ const json = ( await res . json ( ) ) as {
170+ choices ?: Array < {
171+ message ?: {
172+ content ?: string ;
173+ tool_calls ?: unknown [ ] ;
174+ } ;
175+ } > ;
176+ } ;
177+ expect ( json . choices ?. [ 0 ] ?. message ?. content ) . toBe ( "" ) ;
178+ expect ( json . choices ?. [ 0 ] ?. message ?. tool_calls ) . toHaveLength ( 1 ) ;
179+ } ) ;
101180} ) ;
0 commit comments