AI SDK migration#15
Conversation
- Added new dependencies: `@ai-sdk/openai-compatible`, `@ai-sdk/react`, and `ai`. - Removed the `openai` dependency in favor of the new SDK. - Updated the README to reflect the integration of Vercel AI SDK. - Refactored the App component to utilize the new SDK for model management and API interactions. - Introduced a new `ModelInfo` type for better type safety in model handling.
… SDK - Changed the type from `CoreMessage` to `ModelMessage` in the App component. - Adjusted the role assignment to ensure compatibility with the new message structure.
…ompt - Introduced a new patch for the @ai-sdk/openai-compatible package to address compatibility issues. - Updated bun.lock and package.json to include the new patchedDependencies section. - vercel/ai#10272
- Introduced a new module for OpenAI compatibility, encapsulating provider creation and model fetching logic. - Replaced direct API calls in the App component with utility functions for improved readability and maintainability. - Updated the openAIProvider initialization to utilize the new buildOpenAICompatibleProvider function.
WalkthroughThe application is migrated from the OpenAI SDK to the Vercel AI SDK's OpenAI-compatible provider, introducing utilities for model fetching and streaming completions. A patch is applied to the dependency to refactor completion prompt handling with a generalized content-part mapper. Changes
Sequence DiagramsequenceDiagram
participant User
participant App
participant OpenAICompatibleProvider
participant RemoteAPI as Remote API<br/>(OpenAI-compatible)
rect rgb(240, 248, 255)
Note over App,RemoteAPI: Model Synchronization Flow
User->>App: Load app / Configure base URL
App->>App: Check stored models
alt No stored models or baseURL changed
App->>App: buildOpenAICompatibleProvider(baseURL, apiKey)
activate OpenAICompatibleProvider
App->>OpenAICompatibleProvider: fetchOpenAICompatibleModels()
OpenAICompatibleProvider->>RemoteAPI: GET /models<br/>(+ Authorization header)
RemoteAPI-->>OpenAICompatibleProvider: { data: ModelInfo[] }
deactivate OpenAICompatibleProvider
OpenAICompatibleProvider-->>App: ModelInfo[]
App->>App: Persist to storage<br/>Set active model
else Stored models exist
App->>App: Load from storage
end
end
rect rgb(255, 240, 245)
Note over App,RemoteAPI: Completion/Chat Streaming Flow
User->>App: Request completion or chat
App->>App: Guard: baseURL & provider configured?
alt Conditions met
App->>OpenAICompatibleProvider: streamText({ prompt/messages, model, ... })
OpenAICompatibleProvider->>RemoteAPI: Stream request
RemoteAPI-->>OpenAICompatibleProvider: Stream deltas
OpenAICompatibleProvider-->>App: fullStream/textStream
App->>App: Iterate & append deltas to content
App-->>User: Render streamed response
else Missing config
App-->>User: Show error/toast
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/ai/openaiCompatible.ts (1)
36-39: Consider skipping the Authorization header when no API key is set.Right now you always send
Authorization: Bearer _PLACEHOLDER_whenapiKeyis empty. Some self‑hosted or keyless endpoints may treat a missing header differently from an invalid token. You might want to only add the header whenapiKeyis truthy to maximize compatibility..bun-patches/@ai-sdk%2Fopenai-compatible@1.0.27.patch (1)
19-35: Patched completion prompt logic changes semantics; confirm it matches your usage.The new
toText/promptTextpath now:
- Flattens system/user/assistant content into plain text lines without role prefixes.
- Treats any non‑
textcontent (aside from explicittool-callerror) as anInvalidPromptError.- Returns
stopSequences: []instead of stopping on the next user turn.This is a substantial behavioral change from the original role‑tagged, stop‑sequence‑driven format. It’s fine if your callers only need a raw text prompt, but be aware that:
- Any future content part types (e.g., images) will now throw.
- Other code using this patched build may see different completion behavior than the upstream library.
Worth double‑checking this aligns with how you use
completionModeltoday.Also applies to: 52-92
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
.bun-patches/@ai-sdk%2Fopenai-compatible@1.0.27.patch(1 hunks)README.md(1 hunks)package.json(2 hunks)src/App.tsx(7 hunks)src/ai/openaiCompatible.ts(1 hunks)src/components/Header.tsx(2 hunks)src/types.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/ai/openaiCompatible.ts (1)
src/types.ts (1)
ModelInfo(14-19)
src/components/Header.tsx (1)
src/types.ts (1)
ModelInfo(14-19)
src/App.tsx (2)
src/types.ts (1)
ModelInfo(14-19)src/ai/openaiCompatible.ts (2)
buildOpenAICompatibleProvider(7-23)fetchOpenAICompatibleModels(25-46)
🔇 Additional comments (8)
package.json (1)
12-19: Patched dependency version coupling looks intentional; note upgrade implications.Using
^1.0.27for@ai-sdk/openai-compatiblewhile patching@ai-sdk/openai-compatible@1.0.27is fine, but once the installed version drifts above1.0.27the patch will silently stop applying. Consider pinning the dependency or adding a brief comment in the repo about keeping the patch and version in sync.Also applies to: 51-54
README.md (1)
27-33: Updated security and tech‑stack notes look accurate and clear.The README now correctly reflects that keys are stored in the browser and that the app uses a client‑side OpenAI‑compatible flow via the Vercel AI SDK; this aligns with the new implementation.
src/types.ts (1)
13-19: Good centralization of model metadata viaModelInfo.Defining
ModelInfoonce and reusing it across the app (provider, App, Header) is a clean way to keep model typing consistent while allowing provider‑specific fields via optionals.src/components/Header.tsx (1)
2-6: Header typing and model select mapping look solid.Using
models: ModelInfo[]and derivingSelectoptions from{ value: model.id, label: model.name || model.id }fits the new shared type well, and the simplified icon span witharia-hidden="true"keeps the segmented control accessible.Also applies to: 41-45, 60-62
src/ai/openaiCompatible.ts (1)
4-23: Provider factory and model‑fetch helper are well‑factored and safe by default.The base‑URL normalization plus
null/error guards make it hard to misconfigure the provider, andfetchOpenAICompatibleModels’s error handling and shape check before returningModelInfo[]are straightforward and robust.Also applies to: 25-46
src/App.tsx (3)
3-12: Provider wiring and model sync flow look clean and resilient.Using
ModelInfo[]formodels, memoizingopenAIProviderfrom{ baseURL, apiKey }, and centralizing model fetching/persistence insyncModels(with overrides and asilentflag) gives you a nicely contained integration with the OpenAI‑compatible backend. The initial effect that restores base URL, key, and models from IndexedDB, then conditionally syncs, also reads well and has sensible early‑return behavior when cached models exist.Also applies to: 17-20, 28-40, 42-49, 51-101, 103-131
530-551: Settings save + model resync flow is sensible.On save you persist the new base URL and key, then immediately call
syncModelswith explicit overrides in silent mode. That ensures the local model list stays in sync with the new endpoint without spamming toasts, while still letting the user manually resync via the button when desired.
376-401: The code at lines 472-476 is correct as written and requires no changes.The review suggested changing
reasoningtoreasoning_contentin theappendToNodecall, but this is incorrect. TheappendToNodefunction signature expectsdelta: { content?: string; reasoning?: string }, and the code properly uses thereasoningfield. Internally, the implementation mapsdelta.reasoningtonode.reasoningContent, correctly bridging the naming conventions between the delta parameter and the node structure.The identical pattern correctly appears at all mentioned locations (428-437, 452-457, 459-477).
Summary by CodeRabbit
Release Notes
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.