Skip to content

deps: migrate AI SDK v5 to v7 - #441

Merged
filopedraz merged 2 commits into
mainfrom
kosuke/chat-xhnrvi
Sep 7, 2026
Merged

filopedraz merged 2 commits into
mainfrom
kosuke/chat-xhnrvi

Conversation

@filopedraz

Copy link
Copy Markdown
Member

What's Changed

Migrates the Vercel AI SDK from v5 to v7, two majors, using the official codemods for both hops plus a manual review of every changed line.

ai             ^5.0.108  ->  ^7.x
@ai-sdk/react  ^2.0.115  ->  ^4.x
@ai-sdk/google  ^2.0.47  ->  ^4.x

The v5-to-v6 hop moved useChat to a message-parts model, where a message is an array of typed parts and a UI that does not render every part type silently drops tool-call output. That was the change most likely to break this app quietly rather than loudly, so it got the most attention.

This supersedes #384, #385 and #387, which each bumped one package to the v6 line without the code migration, which is why all three failed CI. All three are now closed.

Type of Change

  • 🔧 enhancement - Improvement to existing feature
  • 🔒 security - Security fix (two majors of upstream fixes)

Testing

  • Added new tests for changes (if applicable)

typecheck, lint, format:check, build pass. 580 tests (575 + 5 new, on extractRequestMetadata, where the shape actually changed).

__tests__/lib/ai/utils.test.ts and __tests__/lib/services/rag-service.test.ts pass unmodified in substance, and it is worth being precise about why: neither asserts an AI SDK message shape. They exercise @google/genai grounding metadata and File Search store operations, and @google/genai is a separate SDK that was deliberately not bumped. There was no old shape to update.

What was driven end to end, and what was not

There is no GOOGLE_AI_API_KEY in the build sandbox. Rather than stop at a typecheck, a local HTTPS stub speaking the real Google streaming wire protocol was placed behind generativelanguage.googleapis.com, so the unmodified route, provider and UI ran for real against Chromium and real Postgres:

  • tokens streamed incrementally and assembled from text parts
  • the file_search tool call was emitted and persisted
  • grounding metadata reached messageMetadata and rendered as "Used 2 sources" with both documents linked
  • the conversation persisted and survived a reload
  • the LLM log recorded promptTokens, completionTokens, reasoningTokens and cachedInputTokens through the new inputTokenDetails / outputTokenDetails paths
  • generationConfig and the system prompt persisted, confirming instructions: still produces systemInstruction on the wire

Not exercised against real Google infrastructure: actual Gemini generation, real File Search retrieval, real grounding, and the quota / rate-limit / auth error paths. The stub reproduces the protocol, not Google's behavior. All verification scaffolding (hosts entry, iptables rule, CA bundle, stub process) was torn down and re-verified as reverted.

One pre-existing bug fixed, out of scope but blocking

bun run build was already broken on main: app/(logged-in)/org/[slug]/layout.tsx typed params: { slug: string } while awaiting it, a Next 16 async-params error. Proved pre-existing by building HEAD in a clean worktree. One line, fixed here rather than buried, because the build gate cannot pass without it.

Lockfile scope

The lockfile diff touches only the three AI SDK packages and their transitive dependencies. eslint, vitest, stripe, bullmq, better-auth, next and react are untouched, each having its own migration PR coming.


🤖 Generated with Claude Code

https://claude.ai/code/session_01GyBfVKhYQGLZhkcWiHaruY

filopedraz and others added 2 commits September 7, 2026 14:24
Bumps ai 5.0.108 -> 7.0.93, @ai-sdk/react 2.0.115 -> 4.0.96 and
@ai-sdk/google 2.0.47 -> 4.0.64 (the @ai-sdk/* line runs three majors
behind the core, verified against the published dist-tags).

Applied the official @ai-sdk/codemod v6 and v7 bundles and reviewed every
line. The codemods needed correcting in two places: they rewrote
reasoningTokens/cachedInputTokens on our own Drizzle columns and DB rows
in lib/services/llm-logs-service.ts and the admin RAG components, and
they renamed useChat's onFinish to onEnd even though ChatInit still uses
onFinish (onEnd is the server-side stream option). Both were reverted.

Chat route changes:
- convertToModelMessages is async in v6+
- system -> instructions
- toUIMessageStreamResponse -> toUIMessageStream + createUIMessageStreamResponse
- stream onFinish -> onEnd, whose finishReason is now optional
- result.totalUsage -> result.usage, result.request -> result.finalStep.request
- usage token details moved to inputTokenDetails/outputTokenDetails
- createGoogleGenerativeAI -> createGoogle
- dropped a @ts-expect-error the provider no longer needs
- opt into include: { requestBody: true }; v7 omits request bodies by default

Fixes a silent regression the compiler cannot catch: request.body is now
a parsed object rather than a JSON string, so the route's typeof === 'string'
guard nulled generationConfig and systemPrompt in every LLM log. Extracted
into extractRequestMetadata with tests asserting the new object shape.

Also raises engines.node to >=22 (required by v7) and types the org layout
params as a Promise, which was already failing next build on this branch.

Verified end to end against a local stub of the Google streaming API:
tokens stream, the file_search tool call is emitted, grounding metadata
renders as sources, and the conversation persists and reloads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Resolves bun.lock by regeneration; package.json auto-merged cleanly and
keeps the AI SDK v7 pins alongside husky 9, exceljs and Node 22 from #436.

Fixes a semantic conflict git could not see. #438 added
__tests__/api/chat/route.test.ts with mocks written against the v5 AI SDK
surface, while this branch migrated the route to v7:

  - @ai-sdk/google: createGoogleGenerativeAI -> createGoogle
  - ai: result.toUIMessageStreamResponse() ->
    createUIMessageStreamResponse({ stream: toUIMessageStream(...) })

Both mocks now match the surface the route actually calls. The merge was
textually clean and the suite failed at collection, which is the only
reason this was caught.

Gate verified: typecheck, lint, 744 tests across 55 files, format:check, knip.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GyBfVKhYQGLZhkcWiHaruY
@filopedraz
filopedraz merged commit a001a5e into main Sep 7, 2026
4 checks passed
@filopedraz
filopedraz deleted the kosuke/chat-xhnrvi branch September 7, 2026 15:15
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