11import type { Anthropic } from "@anthropic-ai/sdk"
2- import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
32
43import { moonshotDefaultModelId } from "@roo-code/types"
54
@@ -168,14 +167,20 @@ describe("MoonshotHandler", () => {
168167 } )
169168
170169 it ( "omits temperature and sends required max reasoning for Kimi K3" , async ( ) => {
171- async function * mockFullStream ( ) {
172- yield { type : "text-delta" , text : "K3 response" }
170+ async function * mockStream ( ) {
171+ yield {
172+ choices : [ { delta : { content : "K3 response" } , finish_reason : null } ] ,
173+ usage : null ,
174+ }
173175 }
174176
175- mockStreamText . mockReturnValue ( {
176- fullStream : mockFullStream ( ) ,
177- usage : Promise . resolve ( { inputTokens : 1 , outputTokens : 1 , details : { } , raw : { } } ) ,
178- } )
177+ const mockClient = {
178+ chat : {
179+ completions : {
180+ create : vi . fn ( ) . mockResolvedValue ( mockStream ( ) ) ,
181+ } ,
182+ } ,
183+ }
179184
180185 const k3Handler = new MoonshotHandler ( {
181186 ...mockOptions ,
@@ -184,26 +189,57 @@ describe("MoonshotHandler", () => {
184189 reasoningEffort : "disable" ,
185190 enableReasoningEffort : false ,
186191 } )
192+ ; ( k3Handler as any ) . client = mockClient
193+
187194 for await ( const _chunk of k3Handler . createMessage ( systemPrompt , messages ) ) {
188195 void _chunk
189196 }
190197
191- expect ( mockStreamText ) . toHaveBeenCalledWith (
192- expect . objectContaining ( {
193- temperature : undefined ,
194- maxOutputTokens : 131_072 ,
195- providerOptions : { openaiCompatible : { reasoningEffort : "max" } } ,
196- } ) ,
197- )
198+ const [ requestOptions ] = mockClient . chat . completions . create . mock . calls [ 0 ]
199+ expect ( requestOptions ) . toMatchObject ( {
200+ model : "kimi-k3" ,
201+ reasoning_effort : "max" ,
202+ max_tokens : 131_072 ,
203+ } )
204+ expect ( requestOptions ) . not . toHaveProperty ( "temperature" )
198205 } )
199206
200- it ( "serializes retained Kimi K3 reasoning through the installed AI SDK" , async ( ) => {
201- const actualAi = await vi . importActual < typeof import ( "ai" ) > ( "ai" )
202- const actualOpenAICompatible =
203- await vi . importActual < typeof import ( "@ai-sdk/openai-compatible" ) > ( "@ai-sdk/openai-compatible" )
204- vi . mocked ( createOpenAICompatible ) . mockImplementationOnce ( actualOpenAICompatible . createOpenAICompatible )
205- mockStreamText . mockImplementationOnce ( actualAi . streamText )
207+ it ( "sends required max reasoning for Kimi K3 when streaming is disabled" , async ( ) => {
208+ const mockClient = {
209+ chat : {
210+ completions : {
211+ create : vi . fn ( ) . mockResolvedValue ( {
212+ choices : [ { message : { content : "K3 response" } } ] ,
213+ usage : { prompt_tokens : 1 , completion_tokens : 1 } ,
214+ } ) ,
215+ } ,
216+ } ,
217+ }
206218
219+ const k3Handler = new MoonshotHandler ( {
220+ ...mockOptions ,
221+ apiModelId : "kimi-k3" ,
222+ modelTemperature : 0.9 ,
223+ reasoningEffort : "disable" ,
224+ enableReasoningEffort : false ,
225+ openAiStreamingEnabled : false ,
226+ } )
227+ ; ( k3Handler as any ) . client = mockClient
228+
229+ for await ( const _chunk of k3Handler . createMessage ( systemPrompt , messages ) ) {
230+ void _chunk
231+ }
232+
233+ const [ requestOptions ] = mockClient . chat . completions . create . mock . calls [ 0 ]
234+ expect ( requestOptions ) . toMatchObject ( {
235+ model : "kimi-k3" ,
236+ reasoning_effort : "max" ,
237+ max_tokens : 131_072 ,
238+ } )
239+ expect ( requestOptions ) . not . toHaveProperty ( "temperature" )
240+ } )
241+
242+ it ( "serializes retained Kimi K3 reasoning through the installed OpenAI SDK" , async ( ) => {
207243 let requestBody : Record < string , any > | undefined
208244 const fetchMock = vi . fn ( async ( _input : string | URL | Request , init ?: RequestInit ) => {
209245 requestBody = JSON . parse ( String ( init ?. body ) )
@@ -236,24 +272,20 @@ describe("MoonshotHandler", () => {
236272 } ,
237273 ] as Anthropic . Messages . MessageParam [ ]
238274
239- try {
240- for await ( const _chunk of k3Handler . createMessage ( "system" , retainedMessages ) ) {
241- // Drain the stream so the installed SDK serializes the HTTP request.
242- }
243- } catch ( error ) {
244- // The synthetic SSE only needs to support request serialization.
245- expect ( ( error as Error ) . name ) . toBe ( "AI_NoOutputGeneratedError" )
275+ for await ( const _chunk of k3Handler . createMessage ( "system" , retainedMessages ) ) {
276+ void _chunk
246277 }
247278
248279 expect ( requestBody ) . toMatchObject ( {
249280 model : "kimi-k3" ,
250281 reasoning_effort : "max" ,
282+ max_tokens : 131_072 ,
251283 messages : [
252284 { role : "system" , content : "system" } ,
253285 { role : "user" , content : "Inspect the file" } ,
254286 {
255287 role : "assistant" ,
256- content : null ,
288+ content : "" ,
257289 reasoning_content : "I need the file contents first." ,
258290 tool_calls : [
259291 {
@@ -365,24 +397,35 @@ describe("MoonshotHandler", () => {
365397 } )
366398
367399 it ( "omits temperature and sends required max reasoning for Kimi K3" , async ( ) => {
368- mockGenerateText . mockResolvedValue ( { text : "K3 completion" } )
400+ const mockClient = {
401+ chat : {
402+ completions : {
403+ create : vi . fn ( ) . mockResolvedValue ( {
404+ choices : [ { message : { content : "K3 completion" } } ] ,
405+ } ) ,
406+ } ,
407+ } ,
408+ }
409+
369410 const k3Handler = new MoonshotHandler ( {
370411 ...mockOptions ,
371412 apiModelId : "kimi-k3" ,
372413 modelTemperature : 0.9 ,
373414 reasoningEffort : "disable" ,
374415 enableReasoningEffort : false ,
375416 } )
417+ ; ( k3Handler as any ) . client = mockClient
376418
377- await k3Handler . completePrompt ( "Test prompt" )
419+ const result = await k3Handler . completePrompt ( "Test prompt" )
378420
379- expect ( mockGenerateText ) . toHaveBeenCalledWith (
380- expect . objectContaining ( {
381- temperature : undefined ,
382- maxOutputTokens : 131_072 ,
383- providerOptions : { openaiCompatible : { reasoningEffort : "max" } } ,
384- } ) ,
385- )
421+ expect ( result ) . toBe ( "K3 completion" )
422+ const [ requestOptions ] = mockClient . chat . completions . create . mock . calls [ 0 ]
423+ expect ( requestOptions ) . toMatchObject ( {
424+ model : "kimi-k3" ,
425+ reasoning_effort : "max" ,
426+ max_tokens : 131_072 ,
427+ } )
428+ expect ( requestOptions ) . not . toHaveProperty ( "temperature" )
386429 } )
387430 } )
388431
0 commit comments