agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
106 lines • 5.94 kB
TypeScript
/**
* anthropicThinkingWire — the one translation of `LLMRequest.thinking` onto
* the Messages body, in the shape each Claude model takes.
*
* Pattern: pure translation over a declaration (the `anthropicCacheWire.ts` twin).
* Role: Outer ring. Two jobs, one file — which mode each model takes is
* the `thinking` column of the ONE model table
* (`anthropicModels.ts` · `ANTHROPIC_MODELS`):
* 1. `thinkingModeWith` — the ONE mode function an adapter declares
* (`LLMProvider.thinkingMode`) and builds its body with: the app's own
* answer for an id the table cannot read, else the table's.
* 2. `anthropicThinkingPlan` — the `thinking` field a request gets on that
* model, or a typed refusal before anything is sent.
*
* Why it exists: `.thinking({ budget })` used to send
* `{ type: 'enabled', budget_tokens }` to every model, and Claude 4.7 and later
* reject that with HTTP 400 — every call of every agent that asked to think
* failed. The models disagree, so the shape is decided per model, here, for
* the three adapters that build this body (`anthropic()`, `browserAnthropic()`,
* `invokeModelGateway()`).
*
* The rules, from Anthropic's documentation (fetched 2026-10-08):
* - per-model thinking types and the ones each model rejects with a 400 —
* https://platform.claude.com/docs/en/build-with-claude/thinking-troubleshooting
* ("Thinking support, defaults, and rejected configurations by model") and
* https://platform.claude.com/docs/en/build-with-claude/thinking ("Configuring thinking");
* - "Claude 4.7 and later models do not support it [budget_tokens] and reject
* requests that use it, returning a 400 error"; deprecated but accepted on
* the Claude 4.6 models; the only mode on Claude 4.5 and earlier, where
* `type: "adaptive"` is a 400 —
* https://platform.claude.com/docs/en/build-with-claude/extended-thinking;
* - `budget_tokens` at least 1,024 and below `max_tokens`; forced tool use is
* incompatible with manual (budget) thinking; while thinking, older models
* take no `temperature` but the default and Claude 4.7 and later reject a
* non-default one on every request — same pages ("Budget rules and
* tuning", "Limits and feature compatibility");
* - `display` defaults to `"omitted"` (empty thinking text) on Claude 4.7 and
* later; `"summarized"` returns readable thinking on every adaptive model —
* https://platform.claude.com/docs/en/build-with-claude/thinking ("Controlling thinking display");
* - model ids per platform —
* https://platform.claude.com/docs/en/about-claude/models/overview,
* https://platform.claude.com/docs/en/about-claude/model-deprecations,
* https://platform.claude.com/docs/en/build-with-claude/claude-on-amazon-bedrock-legacy,
* https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai.
*
* @example
* const modeOf = thinkingModeWith(undefined); // the table's answer, per model
* anthropicThinkingPlan({ thinking: { budget: 2048 } }, {
* model: 'claude-opus-5-5', provider: 'anthropic', maxTokensDefault: 4096, thinkingMode: modeOf,
* }); // { thinking: { type: 'adaptive', display: 'summarized' }, maxTokens: 4096 }
*/
import type { LLMRequest } from '../../provider-contract/types.js';
import type { ThinkingMode } from '../../thinking/types.js';
/**
* An adapter's ONE mode function: the app's own answer for an id the table
* cannot read (the adapter's `thinkingMode` option), else the table's. The
* adapter declares it as `LLMProvider.thinkingMode` AND builds its body with
* it, so what the agent checks at build and what reaches the wire cannot
* disagree.
*
* @example
* const modeOf = thinkingModeWith((id) => (id === 'haiku-fast' ? 'budget' : undefined));
* modeOf('haiku-fast'); // 'budget' — the app's answer
* modeOf('claude-opus-5-5'); // 'adaptive' — the table's
*/
export declare function thinkingModeWith(own: ((model: string) => ThinkingMode | undefined) | undefined): (model: string) => ThinkingMode;
/** The `thinking` field of a Messages body, in the two shapes this wire sends. */
export type AnthropicThinkingParam = {
type: 'enabled';
budget_tokens: number;
} | {
type: 'adaptive';
display: 'summarized';
};
/**
* The `thinking` field and `max_tokens` for one request on one model, or
* `undefined` when the request does not ask to think.
*
* - `'budget'`: `{ type: 'enabled', budget_tokens: budget }`.
* - `'adaptive'`: `{ type: 'adaptive', display: 'summarized' }` — no budget
* (the wire has no field for one), and `display: 'summarized'` because
* `.thinking()` asks for readable thinking and these models otherwise
* return it empty.
* - Either way `max_tokens` stays above the budget: one at or below it
* becomes `budget + 1024` (on an adaptive model the budget only sizes this).
*
* `thinkingMode` is the adapter's own mode function ({@link thinkingModeWith})
* — the one it declares — never the table read on the side.
*
* @throws UnsupportedThinkingError — before anything is sent — when the model
* cannot think, when a budget model is given a budget `budget_tokens` does
* not accept or a forced tool choice, or when a non-default `temperature`
* rides along (rejected whenever thinking is on).
* @throws TypeError when the adapter's `thinkingMode` option answers
* something other than `'adaptive'`, `'budget'` or `'none'`.
*/
export declare function anthropicThinkingPlan(req: Pick<LLMRequest, 'thinking' | 'temperature' | 'toolChoice' | 'tools' | 'maxTokens'>, args: {
readonly model: string;
readonly provider: string;
readonly maxTokensDefault: number;
readonly thinkingMode: (model: string) => ThinkingMode;
}): {
readonly thinking: AnthropicThinkingParam;
readonly maxTokens: number;
} | undefined;
//# sourceMappingURL=anthropicThinkingWire.d.ts.map