Skip to content

🤖 chore: upgrade AI SDK to v7 - #3707

Merged
ThomasK33 merged 5 commits into
mainfrom
ai-sdk-nchz
Jul 10, 2026
Merged

🤖 chore: upgrade AI SDK to v7#3707
ThomasK33 merged 5 commits into
mainfrom
ai-sdk-nchz

Conversation

@ThomasK33

Copy link
Copy Markdown
Member

Summary

Upgrades the AI SDK from v6 (ai@6.0.175) to v7 (ai@7.0.19), bumping all @ai-sdk/* providers to their v7-compatible majors, and adapts mux's usage accounting, streaming, middleware, and provider configuration to the v7 breaking changes — while keeping mux's persisted data shapes (chat.jsonl, session-usage.json, analytics ETL) fully backward compatible with no data migration.

Background

AI SDK 7 shipped 2026-06-25 with several breaking changes that affect mux directly:

  • Nested usage shape: token details moved into usage.inputTokenDetails.{noCache,cacheRead,cacheWrite}Tokens and usage.outputTokenDetails.reasoningTokens; Anthropic cache-write tokens moved off providerMetadata.anthropic.cacheCreationInputTokens onto usage.
  • Multi-step result semantics: top-level result.usage now accumulates across ALL steps (old totalUsage, now deprecated); final-step values moved to result.finalStep.
  • { role: "system" } inside messages throws unless allowSystemInMessages: true — mux prepends a cached system message for Anthropic prompt caching.
  • Provider spec v4: all providers now emit specificationVersion: "v4" models; LanguageModelV3Middleware gates no longer match.
  • Behavior default flips: xAI models default to the Responses API, OpenAI Responses reasoningSummary defaults to "detailed" whenever reasoning effort is set, and MCP HTTP/SSE transports reject redirects by default.
  • Type changes: ToolExecutionOptions<CONTEXT> is now generic with a required context field; tool-result media parts were removed from ToolResultOutput.

Notably, ESM-only ai@7 is NOT a blocker for mux's CommonJS main-process build: require(esm) works on Node 22 / Electron 40, verified against the compiled dist/ output.

Implementation

  • Normalization boundary (src/common/utils/tokens/usageHelpers.ts): new normalizeUsage() and withCacheWriteMetadata() convert live v7 usage into mux's persisted flat V2 shape and re-inject Anthropic cache-write tokens into providerMetadata.anthropic.cacheCreationInputTokens. Applied at every ingestion point: StreamManager (finish-step events, getStreamMetadata, getAggregatedProviderMetadata), SessionUsageService.recordHeadlessUsage (choke point for all headless callers), advisor tool, and /btw side questions. Downstream consumers (pricing, IPC, ETL, historical rows) are untouched; createDisplayUsage additionally self-heals nested shapes defensively.
  • Result semantics: getStreamMetadata reads usage (all-steps) + finalStep.usage/finalStep.providerMetadata (context window display); deprecated totalUsage readers migrated (memory consolidation/harvest, status generator).
  • System messages: allowSystemInMessages: true on the two streamText calls that receive prepended cached system messages (StreamManager, advisor transcript).
  • Middleware/spec: DevTools middleware migrated LanguageModelV3*LanguageModelV4* (structurally identical spec); v3 spec gates in aiService/providerModelFactory now check "v4".
  • Preserved provider behavior: xAI pinned to provider.chat(); reasoningSummary: null sent explicitly for models that reject the parameter; MCP transports pass redirect: "follow" (user-configured, trusted URLs).
  • Type churn: ToolExecutionOptions<unknown> + context: undefined across ~35 test files and 3 production call sites; media tool-result fixtures cast (production rewrite path extractToolMedia* already strips them before providers see them).

Validation

  • Live smoke in a dev-server sandbox via CLI and full browser-driven product flow (agent-browser): workspace creation from chat, live streaming with usage-delta updates, a 4-tool multi-step turn including tool-failure recovery, Mux Gateway routing, /btw side question, and a follow-up turn.
  • Anthropic cache accounting verified end-to-end: Stats panel (Cache Create 19.9k/$0.12, Cache Read 77.8k/$0.04) matches session-usage.json exactly; second turn showed 96.9k cache reads, proving prompt caching still hits across turns under allowSystemInMessages.
  • OpenAI gpt-5.2 reasoning turn (normalized reasoningTokens), DevTools rawRequest capture through the V4 middleware, and headless (title/status generator) spend recording all verified against sandbox session files.
  • Compiled CJS require() smoke test of dist/node/services/streamManager.js + friends against the ESM-only ai@7 graph.

Risks

  • Highest risk: usage/cost accounting. If an ingestion path is missed, Anthropic cache-write tokens would silently price as $0 (they no longer arrive via providerMetadata). Mitigated by the single normalization boundary + choke-point placement, behavioral tests, and live verification above. Severity: moderate (cost display/analytics only, no data loss; ETL rebuilds can reprice).
  • Provider behavior pins (xAI chat-completions, reasoningSummary null, MCP redirect-follow) intentionally preserve pre-upgrade behavior; switching xAI to the Responses API can be evaluated separately.
  • Unrelated trunk failures observed during validation: Storybook snapshot-budget test (297 > 293 on trunk, no story files touched here) and one Bun allocator panic flake on a full-suite run.

Pains

  • v7's Tool type defaults its context generic to any, which infects streamText's inferred result type and trips no-unsafe-return; fixed by explicitly pinning streamText<ToolSet>.
  • @ai-sdk/mcp v2 strictly validates OAuth metadata issuers (RFC 8414 canonical form), which surfaced as opaque startDesktopFlow failures in tests until the underlying error was extracted.
  • AI SDK provider bundles now prefix internal class names with _ (e.g. _OpenAIChatLanguageModel), breaking constructor.name equality assertions in tests.

Generated with mux • Model: anthropic:claude-fable-5 • Thinking: xhigh • Cost: $79.25

ThomasK33 added 2 commits July 9, 2026 19:32
- Bump ai 6.0.175 → 7.0.19 and @ai-sdk/* providers to v7-compatible majors
- Normalize AI SDK 7 nested usage (inputTokenDetails/outputTokenDetails) into
  mux's persisted flat shape at all ingestion boundaries; re-inject Anthropic
  cache-write tokens into providerMetadata for stable pricing/display
- streamText result semantics: usage now spans all steps (old totalUsage);
  final-step values read from finalStep
- allowSystemInMessages for cached system-message prepending (streamManager, advisor)
- Migrate devtools middleware + spec gates from LanguageModelV3 to V4
- Pin xai chat-completions API (v7 defaults to Responses API)
- Explicitly disable OpenAI reasoningSummary for unsupported models (v7 defaults to detailed)
- Preserve MCP redirect-following for user-configured servers (v7 defaults to error)
- ToolExecutionOptions is now generic with required context; media tool-result parts removed
- Migrate deprecated stream.totalUsage readers to stream.usage (memory
  consolidation/harvest, status generator)
- Pin streamText<ToolSet> generics so Tool's any-context doesn't infect the
  inferred result type (no-unsafe-return)
- Add behavioral tests for normalizeUsage/withCacheWriteMetadata
- Fix tests hit by v7 behavior changes: RFC 8414 canonical issuer for MCP
  OAuth metadata, provider class-name suffix matching, v7 usage promise name
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

CI's flake-hash-check computed the new offlineCache outputHash after the
bun.lock changes from the AI SDK v7 dependency bumps.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c4828eee89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread package.json
The 2.9.x line peers with ai@^6; 3.0.0 targets ai@^7 and emits v4-spec
models consistent with the other upgraded providers. Addresses Codex
review feedback on PR #3707.
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: e8a123dcd5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

AI SDK 7's convertToModelMessages requires FileUIPart.url to parse as a
real URL (new URL(part.url)) and inlines data: URLs itself. The v6-era
rewrite to raw base64 now throws 'Invalid URL', breaking every image/PDF
attachment send. Rebuild the canonical data:<mediaType>;base64,<payload>
form instead — still normalizing URL-encoded (non-base64) data URIs,
which the SDK's splitDataUrl would otherwise corrupt.

Also update flake.nix offline-cache hash for the OpenRouter bump's
bun.lock change (hash from CI's flake-hash-check diff).

Verified: tests/ipc/streaming/sendMessage.images.test.ts 5/5 pass locally
(with bridge OPENAI_BASE_URL unset; the 403s under the bridge URL are a
local env mismatch, not a product issue).
@ThomasK33
ThomasK33 added this pull request to the merge queue Jul 10, 2026
Merged via the queue into main with commit ec47caf Jul 10, 2026
40 of 42 checks passed
@ThomasK33
ThomasK33 deleted the ai-sdk-nchz branch July 10, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant