SpyBara
Go Premium

Documentation 2026-07-14 21:01 UTC to 2026-07-16 09:01 UTC

6 files changed +172 −26. View all changes and history on the product overview
2026
Sat 18 00:00 Thu 16 09:01 Tue 14 21:01 Sun 12 00:02 Thu 9 13:03 Wed 8 18:58 Mon 6 22:58 Sat 4 20:00 Thu 2 16:59 Wed 1 19:00

faq/security.md +34 −12

Details

8>8>

9> xAI never trains on your API inputs or outputs without your explicit permission.9> xAI never trains on your API inputs or outputs without your explicit permission.

10 10 

11API requests and responses are temporarily stored on our servers for 30 days in case they need to be audited for potential abuse or misuse. This data is automatically deleted after 30 days.11By default, all API requests and responses are stored on our servers (encrypted at rest) for 30 days for auditing purposes in the event of suspected abuse or misuse. xAI does not train on this data, and it is automatically deleted after 30 days.

12 12 

13For teams that require stricter data handling, see [Zero Data Retention (ZDR)](#what-is-zero-data-retention-zdr) below.13For teams that require stricter data handling, see [Zero Data Retention (ZDR)](#what-is-zero-data-retention-zdr) below.

14 14 

15## What is Zero Data Retention (ZDR)?15## What is Zero Data Retention (ZDR)?

16 16 

17Zero Data Retention (ZDR) is an enterprise feature that prevents xAI from storing any API request or response data. ZDR is exclusively available to enterprise accounts. When ZDR is enabled for your team, your prompts, completions, and associated metadata are processed in real time but never persisted to our servers; once a response is delivered, no record of the exchange remains.17> [!WARNING]

18>

19> **For most customers, we do not recommend enabling ZDR**. ZDR disables important API features that depend on stored data, including the stateful Responses API, Files and Collections, and the Batch API (see the full list below). Because content deleted under ZDR can never be recovered or produced by xAI, only enable it when your compliance requirements truly demand it. For most teams, the default 30-day retention is the better choice.

20 

21* **No storage of prompts:** ZDR ensures that API request inputs (i.e., your prompt) and outputs (i.e., the tokens generated by the LLM) are never persisted to disk. The 30-day audit retention described above does not apply to ZDR-enabled teams.

22* **Applied at a team level:** If enabled, ZDR is team-wide. There is no ability to enable ZDR for specific API keys.

23* **Self-serve:** Where available, a team admin can turn it on or off directly from the [xAI Console](https://console.x.ai/team/default/settings/team). When enabled, it applies to every API key on the team.

18 24 

19For more information about ZDR and enterprise plans, please contact [sales@x.ai](mailto:sales@x.ai).25### Features not supported with ZDR

20 26 

21### How it works27Any capability that relies on xAI storing data is unavailable while ZDR is enabled:

22 28 

23* **No logging:** API inputs and outputs are not written to any datastore. The 30-day audit retention described above does not apply to ZDR-enabled teams.29| Feature | Why it is unavailable under ZDR |

24* **Moderation still runs:** Safety and content moderation checks are performed in real time, but moderation results are not stored.30| --- | --- |

25* **Response header:** Every API response includes an `x-zero-data-retention` header set to `"true"` or `"false"`, so your application can programmatically confirm that ZDR is active.31| **Per-API-key request logging** | Recording the full request and response for each API key so you can review them in the Console. Under ZDR, request and response logs are not retained, and any existing per-key request-logging flags are cleared when ZDR is enabled. |

32| **[Stateful Responses API](/developers/model-capabilities/text/generate-text#chaining-the-conversation)** | A mode where xAI stores your conversation server-side so you can continue it later by referencing a previous response. That history cannot be stored under ZDR (`store_messages`, `previous_response_id`), so the client must store conversation history on the client side instead. Please remember to enable `use_encrypted_content` for [agentic tool-calling state](/developers/tools/advanced-usage#append-the-encrypted-agentic-tool-calling-states). |

33| **[Files API](/developers/files)** | Lets you upload a file to xAI once and reference it by `file_id` across later requests. Uploaded files are stored server-side, so under ZDR new uploads and `file_id` attachments are blocked. Existing files can still be viewed and deleted. |

34| **[Collections API](/developers/files/collections)** | Stores your documents on xAI so Grok can search and retrieve from them (RAG). Those documents must be stored to be retrieved, which ZDR does not allow. Existing collections can still be viewed and deleted. |

35| **[Batch API](/developers/advanced-api-usage/batch-api)** | Processes large sets of requests asynchronously at a discounted price. Batched requests are stored until they are processed, so the Batch API is unavailable under ZDR. |

36| **[Deferred completions](/developers/advanced-api-usage/deferred-chat-completions)** | Return a request ID right away and let you fetch the result later instead of holding the connection open. That result is stored until you retrieve it, which ZDR does not permit. Concurrent client-side requests (for example via `AsyncClient`) are unaffected. |

37| **Stored [image](/developers/model-capabilities/images/generation) and [video](/developers/model-capabilities/video/generation) outputs** | Image and video generation can have xAI host the generated media and return it as a `file_id` or URL. Under ZDR there is no xAI-side output storage: no `file_id` inputs, no stored outputs, and no URL-format image results. For images, return base64 only. For video, you must supply your own `output.upload_url` (xAI uploads the result to your URL). Image generation is also limited to `grok-imagine` models and excludes agentic image generation. Video generation in the Console playground is unavailable for the same reason. |

38| **Voice agent conversation history** | Voice agent conversations are not persisted when ZDR is enabled. |

26 39 

27### How to enable ZDR40### How to enable ZDR

28 41 

29ZDR is only available to enterprise accounts. To learn more or enable ZDR for your organization, please reach out to [sales@x.ai](mailto:sales@x.ai). Once enabled, ZDR applies automatically to all API requests made with that team's API keys—no code changes are required.42Team admins can enable or disable ZDR from the Console:

43 

441. Sign in to the [xAI Console](https://console.x.ai/) as a team admin and select the team you want to change in the team picker.

452. Delete any existing [Files](/developers/files) and [Collections](/developers/files/collections) for that team. The Console blocks Enable while stored files or collections remain.

463. Open **Team Settings** and find the **Zero Data Retention (ZDR)** row, then click **Enable**.

474. Review the acknowledgments in the confirmation dialog, including that deleted User Content cannot be recovered, and accept the Enterprise Terms and Privacy Policy. Click **Enable** to confirm.

48 

49Once enabled, ZDR applies automatically to all API requests made with that team's API keys—no code changes are required. To turn it off later, use the same **Zero Data Retention (ZDR)** row and choose **Disable**.

30 50 

31You can verify ZDR is active for your team in the [xAI Console](https://console.x.ai/) team picker, which displays a "Zero Data Retention" label beneath your team name.51If you do not see the ZDR option, you may not be a team admin, self-serve ZDR may not be enabled for your environment yet, or your team may be on a negotiated Enterprise Customer Agreement that governs retention separately—contact [sales@x.ai](mailto:sales@x.ai) in that case.

32 52 

33### Considerations53### How to check that ZDR is enabled

34 54 

35* **No server-side conversation history:** Because requests are not stored, features that rely on server-side state—such as the Responses API's automatic conversation threading via `previous_response_id`—are unavailable. You must manage conversation context client-side, e.g., by using `use_encrypted_content` for [agentic tool-calling state](/developers/tools/advanced-usage#append-the-encrypted-agentic-tool-calling-states).55* **Team Settings:** the **Zero Data Retention (ZDR)** row shows an **Active** badge.

36* **No audit log entries for request content:** Audit logs will still record administrative events (key creation, team changes, etc.), but the content of API requests and responses will not appear.56* **Team picker:** the [xAI Console](https://console.x.ai/) team picker shows a **ZDR** badge next to your team name (tooltip: Zero Data Retention).

57* **Response header:** every API response includes an `x-zero-data-retention` header set to `"true"` or `"false"`, so your application can programmatically confirm whether ZDR is active.

58* **Audit logs:** administrative events (key creation, team changes, etc.) still appear in the Console audit log, but the content of API requests and responses does not.

37 59 

38## Is the xAI API HIPAA compliant?60## Is the xAI API HIPAA compliant?

39 61 

Details

156| `turn_detection.prefix_padding_ms` | number | optional | Amount of audio (in ms) to include before the detected start of speech (0–10000). Helps capture the beginning of words that might otherwise be clipped by the VAD. Default: `333`. |156| `turn_detection.prefix_padding_ms` | number | optional | Amount of audio (in ms) to include before the detected start of speech (0–10000). Helps capture the beginning of words that might otherwise be clipped by the VAD. Default: `333`. |

157| `turn_detection.idle_timeout_ms` | number | optional | When set, the server proactively re-engages the user if no speech is detected for this many milliseconds after the assistant finishes responding. The timer re-arms after every response, so it fires repeatedly each `idle_timeout_ms` until the user speaks. Default: `null`. |157| `turn_detection.idle_timeout_ms` | number | optional | When set, the server proactively re-engages the user if no speech is detected for this many milliseconds after the assistant finishes responding. The timer re-arms after every response, so it fires repeatedly each `idle_timeout_ms` until the user speaks. Default: `null`. |

158| `resumption.enabled` | boolean | optional | Opt in to [Session Resumption](#session-resumption): the server caches conversation turns keyed by `conversation_id` and replays them on reconnect so the model stays conditioned on prior context. Defaults to `false`. See [Session Resumption](#session-resumption). |158| `resumption.enabled` | boolean | optional | Opt in to [Session Resumption](#session-resumption): the server caches conversation turns keyed by `conversation_id` and replays them on reconnect so the model stays conditioned on prior context. Defaults to `false`. See [Session Resumption](#session-resumption). |

159| `audio.input.format.type` | string | Input format: `"audio/pcm"`, `"audio/pcmu"`, or `"audio/pcma"` |159| `audio.input.format.type` | string | Input codec: `"audio/pcm"`, `"audio/pcmu"`, `"audio/pcma"`, or `"audio/opus"` |

160| `audio.input.format.rate` | number | Input sample rate (PCM only): 8000, 16000, 22050, 24000, 32000, 44100, 48000 |160| `audio.input.format.rate` | number | Input sample rate (PCM only): 8000, 16000, 22050, 24000, 32000, 44100, 48000 |

161| `audio.output.format.type` | string | Output format: `"audio/pcm"`, `"audio/pcmu"`, or `"audio/pcma"` |161| `audio.input.transport` | `"json"` | `"binary"` | optional | Wire path for input audio. Default: `"json"` (base64 in `input_audio_buffer.append`). `"binary"`: raw codec bytes as WebSocket binary frames. See [Audio Transport](#audio-transport). |

162| `audio.output.format.type` | string | Output codec: `"audio/pcm"`, `"audio/pcmu"`, `"audio/pcma"`, or `"audio/opus"` |

162| `audio.output.format.rate` | number | Output sample rate (PCM only): 8000, 16000, 22050, 24000, 32000, 44100, 48000 |163| `audio.output.format.rate` | number | Output sample rate (PCM only): 8000, 16000, 22050, 24000, 32000, 44100, 48000 |

164| `audio.output.transport` | `"json"` | `"binary"` | optional | Wire path for assistant audio. Default: `"json"` (base64 in `response.output_audio.delta` / `response.audio.delta`). `"binary"`: raw codec bytes as WebSocket binary frames. Mid-session changes apply at the next response boundary. See [Audio Transport](#audio-transport). |

163| `audio.input.transcription.language_hint` | string | BCP-47 language code (e.g. `"ja"`, `"ar"`, `"es-MX"`, `"pt-BR"`) to bias ASR transcription toward a specific language. Can be updated mid-session. See [Language Hint](#language-hint). |165| `audio.input.transcription.language_hint` | string | BCP-47 language code (e.g. `"ja"`, `"ar"`, `"es-MX"`, `"pt-BR"`) to bias ASR transcription toward a specific language. Can be updated mid-session. See [Language Hint](#language-hint). |

164| `audio.input.transcription.keyterms` | array | List of key terms (e.g. product names, proper nouns) to bias transcription toward. Max 100 terms, each up to 50 characters. Can be updated mid-session. See [Keyterms](#keyterms). |166| `audio.input.transcription.keyterms` | array | List of key terms (e.g. product names, proper nouns) to bias transcription toward. Max 100 terms, each up to 50 characters. Can be updated mid-session. See [Keyterms](#keyterms). |

165| `audio.output.speed` | number | Playback speed multiplier for assistant audio output. Range: 0.7–1.5. Default: `1.0`. Values below 1.0 slow down speech; values above 1.0 speed it up. |167| `audio.output.speed` | number | Playback speed multiplier for assistant audio output. Range: 0.7–1.5. Default: `1.0`. Values below 1.0 slow down speech; values above 1.0 speed it up. |


221 223 

222### Configuring Audio Format224### Configuring Audio Format

223 225 

224Specify the audio format and sample rate in the `audio` [session parameters](#session-parameters). Input and output are specified separately and do not need to match.226Specify the audio codec and sample rate in the `audio` [session parameters](#session-parameters). Input and output are specified separately and do not need to match. Codec (`format`) is independent of wire path (`transport`); see [Audio Transport](#audio-transport).

225 227 

226| Format | Encoding | Container Types | Sample Rate |228| Format | Encoding | Container Types | Sample Rate |

227|--------|----------|-----------------|-------------|229|--------|----------|-----------------|-------------|

228| **`audio/pcm`** (Default) | Linear16, Little-endian | Raw, WAV, AIFF | Configurable (see below) |230| **`audio/pcm`** (Default) | Linear16, Little-endian | Raw, WAV, AIFF | Configurable (see below) |

229| **`audio/pcmu`** | G.711 μ-law (Mulaw) | Raw | 8000 Hz |231| **`audio/pcmu`** | G.711 μ-law (Mulaw) | Raw | 8000 Hz |

230| **`audio/pcma`** | G.711 A-law | Raw | 8000 Hz |232| **`audio/pcma`** | G.711 A-law | Raw | 8000 Hz |

233| **`audio/opus`** | Opus | Raw packets (one packet per payload) | 24000 Hz |

231 234 

232When using the `audio/pcm` format, you can configure the sample rate to one of the following supported values:235When using the `audio/pcm` format, you can configure the sample rate to one of the following supported values:

233 236 


295ws.send(JSON.stringify(sessionConfig));298ws.send(JSON.stringify(sessionConfig));

296```299```

297 300 

301### Audio Transport

302 

303`format` selects the **codec**. `transport` selects how those bytes travel on the WebSocket:

304 

305| | **Input** | **Output** |

306|---|-----------|------------|

307| **`json`** (default) | Base64 in [`input_audio_buffer.append`](/developers/rest-api-reference/inference/voice#input_audio_buffer.append) | Base64 in [`response.output_audio.delta`](/developers/rest-api-reference/inference/voice#response.output_audio.delta) / `response.audio.delta` |

308| **`binary`** | Raw codec bytes as WebSocket **binary** frames (no protocol header) | Same binary frames; lifecycle events (`response.created`, `response.done`, transcripts, etc.) stay JSON |

309 

310Omit `transport` (or set `"json"`) to keep existing clients unchanged.

311 

312**Input dual-accept:** when input format is configured, the server accepts **both** JSON append and binary frames for that codec. Use `input.transport` as the preferred send path for your client; you do not need to drain one channel before the other mid-session.

313 

314**Output is strict:** assistant audio is emitted only on `output.transport`. Changing `output.transport` mid-session applies at the **next response boundary** so a single utterance never mixes JSON deltas and binary frames.

315 

316**Opus:** each JSON `delta` / `audio` field or each binary frame is one raw Opus packet (24 kHz mono). There is no extra framing header on binary frames.

317 

318Example: PCM over binary on both directions:

319 

320```pythonWithoutSDK

321session_config = {

322 "type": "session.update",

323 "session": {

324 "audio": {

325 "input": {

326 "format": {"type": "audio/pcm", "rate": 24000},

327 "transport": "binary",

328 },

329 "output": {

330 "format": {"type": "audio/pcm", "rate": 24000},

331 "transport": "binary",

332 },

333 },

334 },

335}

336await ws.send(json.dumps(session_config))

337 

338# Send mic audio as raw PCM16 little-endian frames (not base64 JSON)

339await ws.send(pcm16_bytes) # WebSocket binary message

340 

341# Receive: binary messages are audio; text messages are JSON events

342async for message in ws:

343 if isinstance(message, bytes):

344 # raw PCM16 (or Opus packets if format is audio/opus)

345 play(message)

346 else:

347 event = json.loads(message)

348 # response.done, transcripts, etc.

349```

350 

351```javascriptWithoutSDK

352const sessionConfig = {

353 type: "session.update",

354 session: {

355 audio: {

356 input: {

357 format: { type: "audio/pcm", rate: 24000 },

358 transport: "binary",

359 },

360 output: {

361 format: { type: "audio/pcm", rate: 24000 },

362 transport: "binary",

363 },

364 },

365 },

366};

367ws.send(JSON.stringify(sessionConfig));

368 

369// Send mic audio as raw PCM16 little-endian frames

370ws.send(pcm16ArrayBuffer); // binary WebSocket frame

371 

372ws.binaryType = "arraybuffer";

373ws.on("message", (data) => {

374 if (data instanceof ArrayBuffer || Buffer.isBuffer(data)) {

375 // raw PCM16 (or Opus packets if format is audio/opus)

376 play(data);

377 return;

378 }

379 const event = JSON.parse(data.toString());

380 // response.done, transcripts, etc.

381});

382```

383 

298### Receiving and Playing Audio384### Receiving and Playing Audio

299 385 

300Decode and play base64 PCM16 audio received from the API. Use the same sample rate as configured:386Decode and play base64 PCM16 audio received from the API when `output.transport` is `"json"` (default). Use the same sample rate as configured. For `transport: "binary"`, play the binary frame payload directly (same codec bytes, no base64).

301 387 

302```pythonWithoutSDK388```pythonWithoutSDK

303import base64389import base64


600 686 

601### Web Search and X Search687### Web Search and X Search

602 688 

603Configure web search and X search tools to give your voice agent access to current information from the web and X (Twitter).689Configure web search and X search tools to give your voice agent access to current information from the web and X (Twitter). Both tools run server-side — enable them by listing them in `session.tools`, optionally with the same filtering parameters as the text-API [Web Search](/developers/tools/web-search) and [X Search](/developers/tools/x-search) tools.

604 690 

605```pythonWithoutSDK691```pythonWithoutSDK

606session_config = {692session_config = {


610 "tools": [696 "tools": [

611 {697 {

612 "type": "web_search",698 "type": "web_search",

699 "allowed_domains": ["x.ai", "docs.x.ai"],

700 "location": {"country": "US", "city": "San Francisco"},

613 },701 },

614 {702 {

615 "type": "x_search",703 "type": "x_search",

704 "allowed_x_handles": ["xai"],

705 "from_date": "2025-01-01",

706 "to_date": "2025-06-01",

616 },707 },

617 ],708 ],

618 },709 },


627 tools: [718 tools: [

628 {719 {

629 type: "web_search",720 type: "web_search",

721 allowed_domains: ["x.ai", "docs.x.ai"],

722 location: { country: "US", city: "San Francisco" },

630 },723 },

631 {724 {

632 type: "x_search",725 type: "x_search",

726 allowed_x_handles: ["xai"],

727 from_date: "2025-01-01",

728 to_date: "2025-06-01",

633 },729 },

634 ],730 ],

635 },731 },

636};732};

637```733```

638 734 

735#### Web Search Parameters

736 

737| Parameter | Required | Description |

738|-----------|----------|-------------|

739| `allowed_domains` | No | Only include results from these domains (no protocol or path, e.g. `example.com`). Max 5. Mutually exclusive with `excluded_domains` — do not set both on the same tool. |

740| `excluded_domains` | No | Exclude results from these domains. Max 5. Mutually exclusive with `allowed_domains` — do not set both on the same tool. |

741| `enable_image_understanding` | No | Let the agent view images found during web search. |

742| `location` | No | Bias results by location: `country` (ISO 3166-1 alpha-2 or full name), `city`, `region`, `timezone` (IANA, e.g. `America/Los_Angeles`). Also accepted under the text-API name `user_location`. |

743 

744#### X Search Parameters

745 

746| Parameter | Required | Description |

747|-----------|----------|-------------|

748| `allowed_x_handles` | No | Only include posts from these X handles (without `@`). Max 20. Mutually exclusive with `excluded_x_handles` — do not set both on the same tool. |

749| `excluded_x_handles` | No | Exclude posts from these X handles. Max 20. Mutually exclusive with `allowed_x_handles` — do not set both on the same tool. |

750| `from_date` | No | Only consider posts from this date, ISO-8601 `YYYY-MM-DD`. Must not be later than `to_date`. |

751| `to_date` | No | Only consider posts up to this date, ISO-8601 `YYYY-MM-DD`. |

752| `enable_image_understanding` | No | Let the agent view images in posts. |

753| `enable_video_understanding` | No | Let the agent view videos in posts. |

754 

755An invalid configuration — too many entries, `allowed_*` and `excluded_*` set together, or a malformed/inverted date window — is rejected with an `error` event that describes the problem. The session stays connected and its previous configuration remains in effect.

756 

639### Remote MCP Tools757### Remote MCP Tools

640 758 

641Use the `mcp` tool type to connect your voice agent to external [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) servers. This lets you extend your voice agent with third-party or custom tools without implementing them as client-side functions — xAI manages the MCP server connection and tool execution on your behalf.759Use the `mcp` tool type to connect your voice agent to external [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) servers. This lets you extend your voice agent with third-party or custom tools without implementing them as client-side functions — xAI manages the MCP server connection and tool execution on your behalf.

Details

6 6 

7### 1. Register the phone number7### 1. Register the phone number

8 8 

9Create a Direct SIP phone number and include the webhook details that should receive incoming-call events. Use `origin: "byo_trunk"` for a customer-owned number. xAI creates the webhook endpoint alongside the phone-number route and returns the webhook signing secret once in the response.9Create a Direct SIP phone number and include the webhook details that should receive incoming-call events. Use `origin: "byo_trunk"` for a customer-owned number. Provisioning xAI phone numbers via API is not supported. xAI returns the webhook signing secret in the response.

10 10 

11Choose one SIP authentication method.11Choose one SIP authentication method.

12 12 

rate-limits.md +1 −1

Details

39| grok-build-0.1 | T0: 37, T1: 50, T2: 75, T3: 125, T4: 208 | T0: 10M, T1: 15M, T2: 25M, T3: 45M, T4: 85M |39| grok-build-0.1 | T0: 37, T1: 50, T2: 75, T3: 125, T4: 208 | T0: 10M, T1: 15M, T2: 25M, T3: 45M, T4: 85M |

40| grok-4.20-multi-agent-0309 | T0: 9, T1: 12, T2: 18, T3: 31, T4: 56 | T0: 2.5M, T1: 3.7M, T2: 6.2M, T3: 11M, T4: 21M |40| grok-4.20-multi-agent-0309 | T0: 9, T1: 12, T2: 18, T3: 31, T4: 56 | T0: 2.5M, T1: 3.7M, T2: 6.2M, T3: 11M, T4: 21M |

41| grok-4.5 | T0: 150, T1: 172, T2: 208, T3: 312, T4: 500 | T0: 50M, T1: 53M, T2: 60M, T3: 74M, T4: 100M |41| grok-4.5 | T0: 150, T1: 172, T2: 208, T3: 312, T4: 500 | T0: 50M, T1: 53M, T2: 60M, T3: 74M, T4: 100M |

42| grok-imagine-image-quality | 5 | — |

43| grok-imagine-image | 5 | — |42| grok-imagine-image | 5 | — |

43| grok-imagine-image-quality | 5 | — |

44| grok-imagine-video | 10 | — |44| grok-imagine-video | 10 | — |

45| grok-imagine-video-1.5 | 10 | — |45| grok-imagine-video-1.5 | 10 | — |

46 46 

Details

350 If not set, the new secret inherits the old secret's expiration time.350 If not set, the new secret inherits the old secret's expiration time.

351 351 

352* `oldSecretExpireTime` (string) — The time at which the old secret stops being accepted.352* `oldSecretExpireTime` (string) — The time at which the old secret stops being accepted.

353 Defaults to 24 hours from now if not set. Must not be more than 7 days from now.353 Defaults to 24 hours from now if not set for non-expired keys. Must not be more than 7 days from now.

354 354 

355### Response Body355### Response Body

356 356 

Details

273 "x_search_call",273 "x_search_call",

274 "code_interpreter_call",274 "code_interpreter_call",

275 "file_search_call",275 "file_search_call",

276 "mcp_call"276 "mcp_call",

277 ):277 ):

278 print(278 # Server-side items expose type-specific fields (e.g. action),

279 f"Server-side tool call: {item.name} with arguments: {item.arguments}"279 # not name/arguments like client-side function_call items.

280 details = getattr(item, "action", None) or item.model_dump(

281 exclude={"id", "type", "status"}, exclude_none=True

280 )282 )

283 print(f"Server-side tool call: {item.type} {details}")

281 284 

282 if not tool_outputs:285 if not tool_outputs:

283 break286 break


332 "x_search_call",335 "x_search_call",

333 "code_interpreter_call",336 "code_interpreter_call",

334 "file_search_call",337 "file_search_call",

335 "mcp_call"338 "mcp_call",

336 ):339 ):

337 print(340 # Server-side items expose type-specific fields (e.g. action),

338 f"Server-side tool call: {item.name} with arguments: {item.arguments}"341 # not name/arguments like client-side function_call items.

342 details = getattr(item, "action", None) or item.model_dump(

343 exclude={"id", "type", "status"}, exclude_none=True

339 )344 )

345 print(f"Server-side tool call: {item.type} {details}")

340 346 

341 if not tool_outputs:347 if not tool_outputs:

342 break348 break