@@ -11,7 +11,8 @@ import { sleep, waitFor, waitUntilAborted } from "../utils"
1111const XAI_API_KEY = process . env . XAI_API_KEY
1212const XAI_BASE_URL = "https://api.x.ai/v1"
1313const XAI_RESPONSES_URL = `${ XAI_BASE_URL } /responses`
14- const XAI_MODEL_ID = "grok-4.20"
14+ const XAI_MODEL_ID = "grok-4-1-fast-non-reasoning"
15+ const XAI_REASONING_MODEL_ID = "grok-4-1-fast-reasoning"
1516
1617type CapturedXAIRequest = {
1718 model ?: string
@@ -343,6 +344,9 @@ suite("xAI provider", function () {
343344 const requests : CapturedXAIRequest [ ] = [ ]
344345
345346 suiteSetup ( async ( ) => {
347+ // readCallId must match what the first-turn fixture returns so the second-turn
348+ // fixture can recognise it in functionCallOutputIds. Only relevant in mock mode;
349+ // passthrough ignores the fixture resolver entirely.
346350 const readCallId = "call_xai_read_001"
347351 restoreFetch = installXAIFetchInterceptor (
348352 requests ,
@@ -461,4 +465,65 @@ suite("xAI provider", function () {
461465 `Task should reach the completion_result ask after the xAI tool loop.\n${ diagnostics } ` ,
462466 )
463467 } )
468+
469+ test ( "Should complete a tool-using task end-to-end via xAI Responses API (reasoning model)" , async ( ) => {
470+ const { result } = await runXAIToolProbe ( XAI_REASONING_MODEL_ID , requests )
471+ const diagnostics = formatDiagnostics ( result )
472+ const [ firstRequest , secondRequest ] = result . requests
473+
474+ assert . ok ( firstRequest , `xAI reasoning model should issue an initial API request.\n${ diagnostics } ` )
475+ assert . ok (
476+ secondRequest ,
477+ `xAI reasoning model should issue a follow-up request after the tool result.\n${ diagnostics } ` ,
478+ )
479+ assert . strictEqual (
480+ firstRequest . model ,
481+ XAI_REASONING_MODEL_ID ,
482+ `xAI should request the expected reasoning model.\n${ diagnostics } ` ,
483+ )
484+ assert . strictEqual (
485+ firstRequest . maxOutputTokens ,
486+ 65_536 ,
487+ `xAI reasoning model should request the model's documented max output tokens.\n${ diagnostics } ` ,
488+ )
489+ assert . deepStrictEqual (
490+ firstRequest . include ,
491+ [ "reasoning.encrypted_content" ] ,
492+ `xAI reasoning model should request encrypted reasoning content from the Responses API.\n${ diagnostics } ` ,
493+ )
494+ assert . strictEqual (
495+ firstRequest . toolChoice ,
496+ "auto" ,
497+ `xAI reasoning model should enable auto tool choice.\n${ diagnostics } ` ,
498+ )
499+ assert . strictEqual (
500+ firstRequest . parallelToolCalls ,
501+ true ,
502+ `xAI reasoning model should keep parallel tool calls enabled.\n${ diagnostics } ` ,
503+ )
504+ assert . strictEqual (
505+ firstRequest . hasTools ,
506+ true ,
507+ `xAI reasoning model should advertise tools on the initial request.\n${ diagnostics } ` ,
508+ )
509+ assert . ok (
510+ secondRequest . functionCallOutputIds . length > 0 ,
511+ `xAI reasoning model should send the read_file tool result back to the Responses API.\n${ diagnostics } ` ,
512+ )
513+ assert . ok ( result . completed , `Task should complete cleanly.\n${ diagnostics } ` )
514+ assert . strictEqual (
515+ result . mistakeLimitReached ,
516+ false ,
517+ `Task should not hit the consecutive mistake limit.\n${ diagnostics } ` ,
518+ )
519+ assert . strictEqual (
520+ result . noToolErrors ,
521+ 0 ,
522+ `Task should not emit MODEL_NO_TOOLS_USED while handling a tool-using probe.\n${ diagnostics } ` ,
523+ )
524+ assert . ok (
525+ result . transcript . some ( ( line ) => line . startsWith ( "completion_result:" ) ) ,
526+ `Task should reach the completion_result ask after the xAI tool loop.\n${ diagnostics } ` ,
527+ )
528+ } )
464529} )
0 commit comments