fix(coding-agent): omit unsupported search temperature
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
| `recency` | `"day" \| "week" \| "month" \| "year"` | No | Time filter. Only providers that implement it use it; code maps it for Brave, Perplexity, Tavily, SearXNG, Kagi, TinyFish, Firecrawl, and xAI. |
|
||||
| `limit` | `number` | No | Max results to return. Usually becomes the provider request's result-count parameter when `num_search_results` is absent. TinyFish uses it for paginated fetches before slicing; xAI sends it as `search_parameters.max_search_results` when `num_search_results` is absent and also caps parsed sources/citations locally, defaulting to `10` and max `30`. |
|
||||
| `max_tokens` | `number` | No | Passed through as provider token caps (`maxOutputTokens`, `max_tokens`, or xAI `max_output_tokens`) only by Anthropic, Gemini, xAI, and Perplexity API-key mode. Ignored by the other providers. |
|
||||
| `temperature` | `number` | No | Passed through only by Anthropic, Gemini, xAI, and Perplexity API-key mode. Ignored by the other providers. |
|
||||
| `temperature` | `number` | No | Passed through only by Anthropic models that support sampling parameters, Gemini, xAI, and Perplexity API-key mode. Ignored or omitted by the other provider/model paths. |
|
||||
| `num_search_results` | `number` | No | Requested search breadth or local result cap. Most providers send it upstream. TinyFish clamps to `1..20` with default `10`, sends it as `num_results` per page, and uses paginated fetches before slicing. xAI sends it as `search_parameters.max_search_results` and caps parsed sources/citations locally with default `10` and max `30`. |
|
||||
|
||||
## Outputs
|
||||
@@ -126,7 +126,7 @@ Streaming: none. `WebSearchTool.execute()` forwards its `AbortSignal` into `exec
|
||||
- `ANTHROPIC_SEARCH_BASE_URL` — search-only base URL for either `ANTHROPIC_SEARCH_API_KEY` or fallback Anthropic credentials; overrides `ANTHROPIC_BASE_URL` (and `FOUNDRY_BASE_URL` in Foundry mode); defaults to `https://api.anthropic.com`.
|
||||
- `ANTHROPIC_SEARCH_MODEL` — search model; defaults to `claude-haiku-4-5`.
|
||||
- Querying: Claude Messages API with web-search tool enabled.
|
||||
- `max_tokens` and `temperature` pass through.
|
||||
- `max_tokens` passes through. `temperature` passes through only for models that support sampling parameters; it is omitted for Opus 4.7+, Sonnet 5+, and Fable/Mythos 5+ because those APIs reject sampling parameters.
|
||||
- `limit` and `num_search_results` are collapsed together before dispatch: `num_results = params.numSearchResults ?? params.limit`.
|
||||
- Output may include `answer`, `sources`, `citations`, `searchQueries`, `usage.searchRequests`, `model`, `requestId`.
|
||||
- **Codex** — `packages/coding-agent/src/web/search/providers/codex.ts`
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Anthropic web search sending unsupported temperature parameters to sampling-restricted Claude models.
|
||||
|
||||
## [17.2.2] - 2026-07-31
|
||||
|
||||
### Added
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
withAuth,
|
||||
wrapFetchForCch,
|
||||
} from "@oh-my-pi/pi-ai";
|
||||
import { hasOpus47ApiRestrictions } from "@oh-my-pi/pi-catalog/identity/family";
|
||||
import { $env } from "@oh-my-pi/pi-utils";
|
||||
import type {
|
||||
AnthropicApiResponse,
|
||||
@@ -176,7 +177,8 @@ async function callSearch(
|
||||
body.metadata = { user_id: metadataUserId };
|
||||
}
|
||||
|
||||
if (temperature !== undefined) {
|
||||
// Opus 4.7+, Sonnet 5+, and Fable/Mythos 5 reject sampling parameters with a 400.
|
||||
if (temperature !== undefined && !hasOpus47ApiRestrictions(model)) {
|
||||
body.temperature = temperature;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,20 @@ function makeCaptureFetch(): { fetch: FetchImpl; body: () => Record<string, unkn
|
||||
return { fetch, body: () => captured };
|
||||
}
|
||||
|
||||
function withSearchModel(model: string) {
|
||||
const original = Bun.env.ANTHROPIC_SEARCH_MODEL;
|
||||
Bun.env.ANTHROPIC_SEARCH_MODEL = model;
|
||||
return {
|
||||
[Symbol.dispose]() {
|
||||
if (original === undefined) {
|
||||
delete Bun.env.ANTHROPIC_SEARCH_MODEL;
|
||||
} else {
|
||||
Bun.env.ANTHROPIC_SEARCH_MODEL = original;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("Anthropic search request body", () => {
|
||||
it("forwards the raw session id as metadata.user_id for API-key auth", async () => {
|
||||
using tempDir = TempDir.createSync("@pi-anthropic-search-apikey-");
|
||||
@@ -127,4 +141,50 @@ describe("Anthropic search request body", () => {
|
||||
authStorage.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("omits temperature for sampling-restricted models", async () => {
|
||||
using _model = withSearchModel("claude-opus-5");
|
||||
using tempDir = TempDir.createSync("@pi-anthropic-search-opus-");
|
||||
const authStorage = await CodingAuthStorage.create(path.join(tempDir.path(), "auth.db"));
|
||||
try {
|
||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||
|
||||
const cap = makeCaptureFetch();
|
||||
await searchAnthropic({
|
||||
query: "sampling compatibility",
|
||||
systemPrompt: "Use web search.",
|
||||
temperature: 0.1,
|
||||
authStorage,
|
||||
fetch: cap.fetch,
|
||||
});
|
||||
|
||||
expect(cap.body()?.model).toBe("claude-opus-5");
|
||||
expect(cap.body()).not.toHaveProperty("temperature");
|
||||
} finally {
|
||||
authStorage.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves temperature for compatible models", async () => {
|
||||
using _model = withSearchModel("claude-haiku-4-5");
|
||||
using tempDir = TempDir.createSync("@pi-anthropic-search-haiku-");
|
||||
const authStorage = await CodingAuthStorage.create(path.join(tempDir.path(), "auth.db"));
|
||||
try {
|
||||
authStorage.setRuntimeApiKey("anthropic", "test-key");
|
||||
|
||||
const cap = makeCaptureFetch();
|
||||
await searchAnthropic({
|
||||
query: "sampling compatibility",
|
||||
systemPrompt: "Use web search.",
|
||||
temperature: 0.1,
|
||||
authStorage,
|
||||
fetch: cap.fetch,
|
||||
});
|
||||
|
||||
expect(cap.body()?.model).toBe("claude-haiku-4-5");
|
||||
expect(cap.body()?.temperature).toBe(0.1);
|
||||
} finally {
|
||||
authStorage.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user