v7.2.73+ always injects native x_search and breaks client function tool web_search on /v1/responses
已打开 01:24PM - 15 Jul 26 UTC
**Is it a request payload issue?**
[x] Yes, this is a request payload issue. I… am using a client/cURL to send a request payload, but I received an unexpected error.
[ ] No, it's another issue.
**If it's a request payload issue, you MUST know**
Our team doesn't have any GODs or ORACLEs or MIND READERs. Please make sure to attach the request log or curl payload.
**Describe the bug**
From `v7.2.73` onward, the xAI executor always injects native `{"type":"x_search"}` into `/v1/responses` requests via `ensureXAINativeXSearchTool`, even when `config.yaml` has no payload rules and the client already provides its own function tools.
When the client declares a function tool named `web_search` (common in agent frameworks like pi-agent), this causes:
1. Final tools become: client `function/web_search` + injected `x_search`
2. Upstream may return: `400 {"code":"invalid-argument","error":"Duplicate tool names: web_search"}`
3. Or the model emits native `web_search_call` that stays `status=in_progress` with empty sources
4. The client never receives a normal `function_call` for its local tool, so the agent turn aborts
On `v7.2.71` / `v7.2.72`, the same request returns a normal `function_call` for `web_search`.
Payload config cannot disable this, because injection runs after `ApplyPayloadConfigWithRequest`. I verified `payload.filter` removing `tools.#(type=="x_search")` does not help.
**CLI Type**
xAI / Grok OAuth
**Model Name**
`grok-4.5`
**LLM Client**
pi-agent (OpenAI Responses API client with local function tools)
**Request Information**
```bash
curl -N http://127.0.0.1:8317/v1/responses \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "User-Agent: pi-agent" \
-d '{
"model": "grok-4.5",
"stream": true,
"input": [
{"role": "user", "content": "Call web_search with query hello"}
],
"tools": [
{
"type": "function",
"name": "web_search",
"description": "Search the web",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}
],
"tool_choice": "required",
"reasoning": {"effort": "low"}
}'
```
**Result on `v7.2.71` / `v7.2.72`:**
`response.completed` contains `function_call` with `name=web_search`.
**Result on `v7.2.73+` / `latest` (`v7.2.77`):**
`response.completed.tools` includes both the client function tool and `{"type":"x_search"}`. Output often contains:
```json
{
"type": "web_search_call",
"status": "in_progress",
"action": {
"type": "search",
"query": "hello",
"sources": []
}
}
```
No client-side `function_call` is returned.
Related log:
```text
request error, error status: 400, error message: {"code":"invalid-argument","error":"Duplicate tool names: web_search"}
```
Control checks:
- Rename client tool to `pi_web_search` → client `function_call` works, but `x_search` is still injected
- Send request with no client tools on `v7.2.73+` → tools still become `[{"type":"x_search"}]`
Code path in `internal/runtime/executor/xai_executor.go` (since `v7.2.73`):
```go
// Always inject native x_search when the client did not declare it ...
body = helps.ApplyPayloadConfigWithRequest(...)
body = ensureXAINativeXSearchTool(body)
```
**Expected behavior**
1. Do not force-inject native `x_search` for all xAI requests by default, or
2. Provide a config switch to disable it (for example `xai.inject-x-search: false`), and
3. When a client already provides function tools, especially one named `web_search`, do not break the client tool loop with native `web_search_call` / duplicate tool name errors.
**Screenshots**
N/A
**OS Type**
- OS: Linux (Docker)
- Version: Ubuntu host, image `eceasy/cli-proxy-api`
**Additional context**
- Working: `eceasy/cli-proxy-api:v7.2.71`
- Broken: `eceasy/cli-proxy-api:latest` (`v7.2.77`)
- First version with forced injection: `v7.2.73` (`ensureXAINativeXSearchTool`)
- Related issues: #4282, #3704
- Current workaround: pin image to `<= v7.2.72`, or rename client tool away from `web_search`