diff --git a/packages/ai/CHANGELOG.md b/packages/ai/CHANGELOG.md index 86b9ebaa..241b7792 100644 --- a/packages/ai/CHANGELOG.md +++ b/packages/ai/CHANGELOG.md @@ -5,6 +5,11 @@ ### Added - Added `onResponse` to `StreamOptions` so callers can inspect provider HTTP status and headers after each response arrives and before the response stream is consumed ([#3128](https://github.com/badlogic/pi-mono/issues/3128)) +- Added `thinkingDisplay` (`"summarized" | "omitted"`) to `AnthropicOptions` and `BedrockOptions`, wiring it through to the Anthropic/Bedrock `thinking` config. Defaults to `"summarized"` so Claude Opus 4.7 and Mythos Preview keep returning thinking text; set it to `"omitted"` to skip thinking streaming for faster time-to-first-text-token. + +### Changed + +- Bumped `@anthropic-ai/sdk` to `0.90.0` so `ThinkingConfigAdaptive.display` and `ThinkingConfigEnabled.display` are typed by the SDK. ## [0.67.5] - 2026-04-16 diff --git a/packages/ai/src/index.ts b/packages/ai/src/index.ts index 1c885a85..6a1ee2c0 100644 --- a/packages/ai/src/index.ts +++ b/packages/ai/src/index.ts @@ -4,8 +4,8 @@ export { Type } from "@sinclair/typebox"; export * from "./api-registry.js"; export * from "./env-api-keys.js"; export * from "./models.js"; -export type { BedrockOptions } from "./providers/amazon-bedrock.js"; -export type { AnthropicOptions } from "./providers/anthropic.js"; +export type { BedrockOptions, BedrockThinkingDisplay } from "./providers/amazon-bedrock.js"; +export type { AnthropicEffort, AnthropicOptions, AnthropicThinkingDisplay } from "./providers/anthropic.js"; export type { AzureOpenAIResponsesOptions } from "./providers/azure-openai-responses.js"; export * from "./providers/faux.js"; export type { GoogleOptions } from "./providers/google.js"; diff --git a/packages/ai/src/providers/amazon-bedrock.ts b/packages/ai/src/providers/amazon-bedrock.ts index f72356b5..4b2e61f6 100644 --- a/packages/ai/src/providers/amazon-bedrock.ts +++ b/packages/ai/src/providers/amazon-bedrock.ts @@ -46,6 +46,8 @@ import { sanitizeSurrogates } from "../utils/sanitize-unicode.js"; import { adjustMaxTokensForThinking, buildBaseOptions, clampReasoning } from "./simple-options.js"; import { transformMessages } from "./transform-messages.js"; +export type BedrockThinkingDisplay = "summarized" | "omitted"; + export interface BedrockOptions extends StreamOptions { region?: string; profile?: string; @@ -56,6 +58,17 @@ export interface BedrockOptions extends StreamOptions { thinkingBudgets?: ThinkingBudgets; /* Only supported by Claude 4.x models, see https://docs.aws.amazon.com/bedrock/latest/userguide/claude-messages-extended-thinking.html#claude-messages-extended-thinking-tool-use-interleaved */ interleavedThinking?: boolean; + /** + * Controls how Claude's thinking content is returned in responses. + * - "summarized": Thinking blocks contain summarized thinking text (default here). + * - "omitted": Thinking content is redacted but the signature still travels back + * for multi-turn continuity, reducing time-to-first-text-token. + * + * Note: Anthropic's API default for Claude Opus 4.7 and Mythos Preview is + * "omitted". We default to "summarized" here to keep behavior consistent with + * older Claude 4 models. Only applies to Claude models on Bedrock. + */ + thinkingDisplay?: BedrockThinkingDisplay; /** Key-value pairs attached to the inference request for cost allocation tagging. * Keys: max 64 chars, no `aws:` prefix. Values: max 256 chars. Max 50 pairs. * Tags appear in AWS Cost Explorer split cost allocation data. @@ -759,9 +772,12 @@ function buildAdditionalModelRequestFields( } if (model.id.includes("anthropic.claude") || model.id.includes("anthropic/claude")) { + // Default to "summarized" so Opus 4.7 and Mythos Preview behave like + // older Claude 4 models (whose API default is also "summarized"). + const display: BedrockThinkingDisplay = options.thinkingDisplay ?? "summarized"; const result: Record = supportsAdaptiveThinking(model.id) ? { - thinking: { type: "adaptive" }, + thinking: { type: "adaptive", display }, output_config: { effort: mapThinkingLevelToEffort(options.reasoning, model.id) }, } : (() => { @@ -781,6 +797,7 @@ function buildAdditionalModelRequestFields( thinking: { type: "enabled", budget_tokens: budget, + display, }, }; })(); diff --git a/packages/ai/src/providers/anthropic.ts b/packages/ai/src/providers/anthropic.ts index 861bab8a..47686327 100644 --- a/packages/ai/src/providers/anthropic.ts +++ b/packages/ai/src/providers/anthropic.ts @@ -156,6 +156,8 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]): export type AnthropicEffort = "low" | "medium" | "high" | "xhigh" | "max"; +export type AnthropicThinkingDisplay = "summarized" | "omitted"; + export interface AnthropicOptions extends StreamOptions { /** * Enable extended thinking. @@ -179,6 +181,18 @@ export interface AnthropicOptions extends StreamOptions { * Ignored for older models. */ effort?: AnthropicEffort; + /** + * Controls how thinking content is returned in API responses. + * - "summarized": Thinking blocks contain summarized thinking text (default here). + * - "omitted": Thinking blocks return an empty thinking field; the encrypted + * signature still travels back for multi-turn continuity. Use for faster + * time-to-first-text-token when your UI does not surface thinking. + * + * Note: Anthropic's API default for Claude Opus 4.7 and Claude Mythos Preview + * is "omitted". We default to "summarized" here to keep behavior consistent + * with older Claude 4 models. Set this explicitly to "omitted" to opt in. + */ + thinkingDisplay?: AnthropicThinkingDisplay; interleavedThinking?: boolean; toolChoice?: "auto" | "any" | "none" | { type: "tool"; name: string }; /** @@ -678,11 +692,14 @@ function buildParams( // budget-based (older models), or explicitly disabled. if (model.reasoning) { if (options?.thinkingEnabled) { + // Default to "summarized" so Opus 4.7 and Mythos Preview behave like + // older Claude 4 models (whose API default is also "summarized"). + const display: AnthropicThinkingDisplay = options.thinkingDisplay ?? "summarized"; if (supportsAdaptiveThinking(model.id)) { // Adaptive thinking: Claude decides when and how much to think. - // The Anthropic SDK types can lag newly supported effort values such as "xhigh". - params.thinking = { type: "adaptive" }; + params.thinking = { type: "adaptive", display }; if (options.effort) { + // The Anthropic SDK types can lag newly supported effort values such as "xhigh". params.output_config = options.effort === "xhigh" ? ({ effort: options.effort } as unknown as NonNullable< @@ -695,6 +712,7 @@ function buildParams( params.thinking = { type: "enabled", budget_tokens: options.thinkingBudgetTokens || 1024, + display, }; } } else if (options?.thinkingEnabled === false) {