refactor(providers): migrate anthropic to the official @anthropic-ai/sdk - #1075
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 54 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThis PR migrates the Anthropic provider from ChangesAnthropic SDK Migration
🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/providers/anthropic.ts (1)
477-482: 💤 Low valueEmpty string fallback for
toolCallIdandtoolNamemay cause API errors.Anthropic's API requires valid
idandnamefields fortool_useblocks. If the source data has undefined values, passing empty strings could result in API validation errors or unexpected behavior. Consider logging a warning when these fields are missing.🛡️ Proposed defensive handling
} else if (p?.type === "tool-call") { let input: unknown = p.input; if (typeof input === "string") { try { input = JSON.parse(input); } catch { input = {}; } } + const toolCallId = p.toolCallId ?? ""; + const toolName = p.toolName ?? ""; + if (!toolCallId || !toolName) { + logger.warn("[messagesToAnthropic] tool-call missing id or name", { + hasToolCallId: !!p.toolCallId, + hasToolName: !!p.toolName, + }); + } blocks.push({ type: "tool_use", - id: p.toolCallId ?? "", - name: p.toolName ?? "", + id: toolCallId, + name: toolName, input: input ?? {}, }); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/providers/anthropic.ts` around lines 477 - 482, The current blocks.push call creates a "tool_use" block with empty-string fallbacks for p.toolCallId and p.toolName which can trigger Anthropic validation errors; update the logic around the push in the function handling p (the code that builds the blocks array and references p.toolCallId/p.toolName/input) to validate that p.toolCallId and p.toolName are present and non-empty before adding the block, and if either is missing log a warning (use the existing logger) and skip adding the "tool_use" block (or alternatively surface an error) so you never send empty id/name to the API.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/providers/anthropic.ts`:
- Around line 477-482: The current blocks.push call creates a "tool_use" block
with empty-string fallbacks for p.toolCallId and p.toolName which can trigger
Anthropic validation errors; update the logic around the push in the function
handling p (the code that builds the blocks array and references
p.toolCallId/p.toolName/input) to validate that p.toolCallId and p.toolName are
present and non-empty before adding the block, and if either is missing log a
warning (use the existing logger) and skip adding the "tool_use" block (or
alternatively surface an error) so you never send empty id/name to the API.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1291a576-0817-4c7a-9cb7-836885e006f4
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
package.jsonscripts/build-browser.mjssrc/lib/providers/anthropic.tssrc/lib/providers/anthropicBaseProvider.tssrc/lib/utils/noOutputSentinel.tstest/continuous-test-suite-context.tstest/continuous-test-suite-stream-span.ts
💤 Files with no reviewable changes (1)
- src/lib/providers/anthropicBaseProvider.ts
There was a problem hiding this comment.
Pull request overview
Migrates the Anthropic provider implementation from @ai-sdk/anthropic/streamText to the official @anthropic-ai/sdk (Messages API), removes the unused anthropicBaseProvider.ts, and updates associated audits/build tooling to reflect the new provider surface.
Changes:
- Replaces Anthropic client construction and generation/streaming paths to use the official
@anthropic-ai/sdkMessages API directly. - Deletes the dead
src/lib/providers/anthropicBaseProvider.tsand updates continuous test-suite wiring/audits to point atproviders/anthropic. - Adds
@anthropic-ai/sdkto dependencies and stubsstandardwebhooksfor the browser bundle build.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/continuous-test-suite-stream-span.ts | Updates provider error audit target list after removing anthropicBaseProvider. |
| test/continuous-test-suite-context.ts | Updates “all providers wired” / span-stamping / error-capture audit targets to providers/anthropic. |
| src/lib/utils/noOutputSentinel.ts | Updates documentation references from anthropicBaseProvider to anthropic. |
| src/lib/providers/anthropicBaseProvider.ts | Deleted unused provider implementation. |
| src/lib/providers/anthropic.ts | Main migration to official Anthropic SDK; adds Messages API conversions, V3 delegating model for generate(), and native streaming/tool loop. |
| scripts/build-browser.mjs | Stubs standardwebhooks (optional peer) and exposes Webhook in the proxy stub exports. |
| pnpm-lock.yaml | Locks @anthropic-ai/sdk@0.102.0 and its new transitive deps. |
| package.json | Adds @anthropic-ai/sdk dependency. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const p = part as { | ||
| type?: string; | ||
| text?: string; | ||
| image?: unknown; | ||
| data?: unknown; | ||
| url?: unknown; | ||
| }; | ||
| if (p?.type === "text" && typeof p.text === "string") { | ||
| blocks.push({ type: "text", text: p.text }); | ||
| } else if (p?.type === "image" || p?.type === "image_url") { | ||
| const img = toAnthropicImageBlock(p.image ?? p.data ?? p.url); | ||
| if (img) { | ||
| blocks.push(img); | ||
| } | ||
| } |
There was a problem hiding this comment.
Confirmed real — MessageBuilder marks system messages (and GenerationHandler the last tool definition) with providerOptions.anthropic.cacheControl, so dropping them silently disabled prompt caching. Fixed in f66769a: a cacheControlOf helper now honors the contract everywhere — system messages (block-form system emitted only when breakpoints exist, plain string otherwise for wire-compat), per-part AND message-level on user/assistant blocks (message-level applies to the last block, the AI-SDK convention), and tool definitions on both the V3 doGenerate path and the stream path.
| const p = part as { | ||
| type?: string; | ||
| text?: string; | ||
| toolCallId?: string; | ||
| toolName?: string; | ||
| input?: unknown; | ||
| }; | ||
| if (p?.type === "text" && typeof p.text === "string") { | ||
| if (p.text.length > 0) { | ||
| blocks.push({ type: "text", text: p.text }); | ||
| } |
There was a problem hiding this comment.
Fixed in f66769a — assistant text parts now carry their per-part cache_control, and message-level cacheControl is applied to the message's last block (same as the user path).
Tara-ag
left a comment
There was a problem hiding this comment.
Review Complete: Approve with Minor Suggestion
Summary
Successfully reviewed the migration of Anthropic provider from @ai-sdk/anthropic to the official @anthropic-ai/sdk. This is a substantial refactor (+994/-548 lines) that removes the last @ai-sdk provider-package import from the providers tree.
Issues Found: 1 Minor
| Severity | Count | Description |
|---|---|---|
| 🔒 CRITICAL | 0 | None |
| 0 | None | |
| 💡 MINOR | 1 | Debug log at line ~760 logs ANTHROPIC_BASE_URL without credential redaction |
| 💬 SUGGESTION | 0 | None |
Verification Against CLAUDE.md Rules
| Rule | Status | Notes |
|---|---|---|
| Rule 1 (dynamic imports) | ✅ N/A | Provider file - registry handles dynamic imports |
| Rule 3 (Gemini tools+JSON) | ✅ N/A | Anthropic provider |
| Rule 4 (CLI≠SDK) | ✅ Pass | No CLI concerns leaked |
| Rule 5 (backward compat) | ✅ Pass | Public API preserved per PR description |
| Rule 6 (formatProviderError returns) | ✅ Pass | Correctly returns typed errors, never throws |
| Rules 7-13 | ✅ Pass | Enforced by ESLint/CI |
Key Positive Findings
-
Proper Error Handling:
formatProviderErrorcorrectly maps SDK errors to typed errors (NetworkError,AuthenticationError,RateLimitError,ProviderError) and returns them rather than throwing. -
Streaming Architecture:
executeStreamcorrectly uses the BaseProvider tool-merge pattern - receives pre-merged tools viaoptions.toolsand converts them for the native SDK. -
No-Output Sentinel: Correctly implements
buildNoOutputSentinel,stampNoOutputSpan, andcapturedProviderErrorwiring per the test audit requirements (verified in dist audit). -
Browser Build: Properly stubs
standardwebhooks(optional peer dep from SDK's beta webhooks module) for browser bundles. -
BASE_URL Handling: Correctly inverts the version suffix handling - strips
/vNsince the official SDK appends/v1itself, maintaining backward compatibility with both historical env var forms.
Recommended Follow-up
Consider using redactUrlCredentials() from ../utils/logSanitize.js when logging ANTHROPIC_BASE_URL in debug statements (line ~760) to align with the pattern used by other providers (OpenAI, Groq, Ollama, etc.). This is a defensive practice, not a blocking issue.
Decision
APPROVE - The migration is well-architected, preserves backward compatibility, and follows all critical project rules. The one minor issue identified is non-blocking and can be addressed in a follow-up if desired.
| // a version-suffixed base URL — the form the previous @ai-sdk/anthropic | ||
| // implementation REQUIRED (`https://api.anthropic.com/v1`) — would | ||
| // double up as `/v1/v1/messages`. Normalize the inverse way now: strip | ||
| // a trailing `/vN` segment when present so both historical forms of |
There was a problem hiding this comment.
💡 MINOR: Credential exposure in debug log
The debug log at line 760 logs baseURL directly without redacting potential credentials:
logger.debug(
"[AnthropicProvider] Stripping the version suffix from " +
"ANTHROPIC_BASE_URL — the official Anthropic SDK appends /v1 " +
"to the base URL itself.",
{ baseURL: raw, rewrittenTo: stripped }, // <-- raw URL may contain credentials
);Risk: If ANTHROPIC_BASE_URL contains credentials (e.g., https://user:pass@proxy.example.com), they will appear in debug logs.
Suggestion: Import and use redactUrlCredentials from ../utils/logSanitize.js (already used by other providers like OpenAI, Groq, etc.):
import { redactUrlCredentials } from "../utils/logSanitize.js";
// Then in the log:
logger.debug(
"[AnthropicProvider] Stripping the version suffix from ANTHROPIC_BASE_URL...",
{ baseURL: redactUrlCredentials(raw), rewrittenTo: redactUrlCredentials(stripped) },
);This aligns with the pattern established in other providers (see openAI.ts, groq.ts, ollama.ts, etc.).
| } as unknown as LanguageModel; | ||
| } | ||
|
|
||
| protected formatProviderError(error: unknown): Error { |
There was a problem hiding this comment.
✅ Verified: formatProviderError correctly returns errors
The implementation correctly follows CLAUDE.md Critical Rule 6: formatProviderError returns typed errors rather than throwing them. The method properly maps:
TimeoutError→NetworkErrorAPI_KEY_INVALID/Invalid API key→AuthenticationError- Rate limit patterns →
RateLimitError - Network errors (
ECONNRESET,ENOTFOUND, etc.) →NetworkError - Server errors (5xx) →
ProviderError
The error messages appropriately reference "configured credentials" rather than specific env vars (consistent with bootstrapped standards). Good practice.
There was a problem hiding this comment.
Review Summary for anthropic.ts
✅ Positive Findings
-
CLAUDE.md Rule 6 Compliance:
formatProviderErrorcorrectly returns typed errors rather than throwing them. Properly maps toNetworkError,AuthenticationError,RateLimitError, andProviderError. -
Streaming Tool-Merge Contract: The
executeStreamimplementation correctly uses pre-merged tools fromBaseProvider.stream()viaoptions.tools, maintaining the tool-merge contract. -
No-Output Sentinel Pattern: Correctly implements
buildNoOutputSentinel,stampNoOutputSpan, andcapturedProviderErrorwiring per the test audit requirements. -
Backward Compatibility: Public API preserved (
getAuthHeaders,validateModelAccess, usage/auth/tier getters,refreshAuthIfNeeded, etc.).
⚠️ Minor Issues
- Credential Exposure in Debug Log (line ~760): The debug log when stripping version suffix from
ANTHROPIC_BASE_URLlogs the raw URL without redacting potential credentials. Should useredactUrlCredentials()from../utils/logSanitize.js(pattern used by other providers like OpenAI, Groq, etc.).
Overall Assessment
Well-architected migration that removes the @ai-sdk/anthropic dependency while preserving all existing functionality. The native SDK integration follows established patterns in the codebase.
| 'which','pdf-to-img','express-rate-limit','@hono/node-server','powershell-utils', | ||
| 'wsl-utils','default-browser','default-browser-id','run-applescript','open', | ||
| '@langfuse/langfuse','undici','bullmq','croner','ioredis', | ||
| // Optional peer of @anthropic-ai/sdk's beta webhooks module (server-only |
There was a problem hiding this comment.
✅ Verified: Browser stub for standardwebhooks
Correctly adds standardwebhooks to the npm stubs list for the browser bundle. This is the proper approach for handling the optional peer dependency from @anthropic-ai/sdk's beta webhooks module (server-only webhook signature verification) that would otherwise fail in browser builds.
The comment explaining why this is needed is helpful for future maintainers.
Replace the @ai-sdk/anthropic + streamText runtime with the official @anthropic-ai/sdk Messages API. anthropic.ts now drives client.messages.create directly on both paths; the dead AnthropicProviderV2 (anthropicBaseProvider.ts — no registry entry, no runtime importers) is deleted. This removes the last @ai-sdk provider-package import from the providers tree. Faithful migration — all behavior preserved: - Auth machinery untouched by construction: OAuth detection/tiers (~/.neurolink credentials, ANTHROPIC_OAUTH_TOKEN, scope-based tier detection, model downgrade per tier), token refresh, beta headers, and the proven createOAuthFetch wrapper (Bearer auth via current-token getter, UA spoofing, ?beta=true) now wrap the official client. - ANTHROPIC_BASE_URL normalization is inverted to match the new SDK: the official client appends /v1 itself, so a version-suffixed base URL (the form @ai-sdk/anthropic REQUIRED) gets its trailing /vN stripped — both historical env forms keep working. - generate(): getAISDKModel() returns a V3 delegating adapter over messages.create, so BaseProvider.generate + middleware + GenerationHandler keep working unchanged. Thinking blocks map to V3 reasoning parts (result.reasoning / reasoningTokens), cache_read/cache_creation tokens map to V3 cache usage, and json responseFormat is emulated with the same forced-tool strategy @ai-sdk/anthropic used. - executeStream(): native Messages streaming loop — text_delta → content chunks, thinking_delta → the reasoning chunk channel (from #1073), signature_delta captured so thinking blocks replay correctly when tool use continues a turn, tool_use accumulation → execution → tool_result user turn, up to maxSteps. OTel span keeps the neurolink.provider.streamText name for dashboard continuity; tool:end emission + tool-execution storage match the old onStepFinish hooks; NoOutput sentinel + capturedProviderError wiring match the cohort (and the test:context 6.x audits, which now point at providers/anthropic). - formatProviderError taxonomy unchanged (typed errors only). - Public API preserved: getAuthHeaders, validateModelAccess, getUsageInfo, subscription/auth getters, refreshAuthIfNeeded, getLastResponseMetadata, the anthropicModels re-exports, and ANTHROPIC_BETA_HEADERS. The rule-violating `export default` declarations are removed (named exports only; nothing imported the defaults). Build: @anthropic-ai/sdk's beta webhooks module imports the optional standardwebhooks package — stubbed in the browser bundle (server-only webhook verification, irrelevant in the browser). The @ai-sdk/anthropic dependency is retained solely for the browser entry's createAnthropic re-export until the planned breaking browser-surface removal.
9eb573e to
f66769a
Compare
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
Tara-ag
left a comment
There was a problem hiding this comment.
Review Summary
This PR successfully migrates the Anthropic provider from @ai-sdk/anthropic to the official @anthropic-ai/sdk.
✅ Approved - No Blocking Issues
Architecture Compliance:
- CLAUDE.md Rule 1: Dynamic imports only in registry ✓
- CLAUDE.md Rule 6:
formatProviderErrorreturns errors (never throws) ✓ - Tool-merge contract respected via
BaseProvider.stream()✓ - No-output sentinel patterns correctly implemented ✓
Security:
- No hardcoded secrets or credentials ✓
- URL credentials properly redacted in logs using
redactUrlCredentials()✓
Backward Compatibility:
- Public API preserved (
getAuthHeaders,validateModelAccess,refreshAuthIfNeeded, etc.) ✓ - Named exports only (removed rule-violating
export default) ✓
Code Quality:
- Dead code (
anthropicBaseProvider.ts) correctly removed ✓ - Shared utilities properly imported from
openaiChatCompletionsClient.ts✓ - Cache control preservation fixed per reviewer feedback ✓
Build & Tests:
- Browser stub correctly added for
standardwebhooks✓ - Test references updated from
anthropicBaseProvidertoanthropic✓
All previously raised review comments have been addressed. The migration follows established patterns in the codebase and maintains dashboard continuity through consistent OTel span naming.
|
🎉 This PR is included in version 9.69.2 🎉 The release is available on: Your semantic-release bot 📦🚀 |
What & why
Migrates anthropic off
@ai-sdk/anthropic+streamTextonto the official@anthropic-ai/sdk(Messages API) — removing the last@ai-sdkprovider-package import from the providers tree. Also deletes the deadanthropicBaseProvider.ts(AnthropicProviderV2: no registry entry, no runtime importers — only stale audit-list mentions).This was scoped first (full inventory of the 1,296-line provider + the official-SDK mapping); the OAuth/tier machinery ports verbatim by construction — only the client construction and the two generation paths change.
Design
Auth (preserved verbatim): OAuth detection/tiers (
~/.neurolinkcredentials,ANTHROPIC_OAUTH_TOKEN, scope-based tier detection + model downgrade), token refresh, beta headers, and the provencreateOAuthFetchwrapper (current-token getter, UA spoofing,?beta=true) now wrap the official client. API-key path:new Anthropic({ apiKey, defaultHeaders, baseURL, fetch: proxyFetch }).ANTHROPIC_BASE_URLinversion: the official SDK appends/v1itself — the opposite of@ai-sdk/anthropic, which required a version-suffixed base. A trailing/vNis now stripped (with a debug note), so both historical env forms keep working.generate():getAISDKModel()returns a V3 delegating adapter overmessages.create—BaseProvider.generate+ middleware +GenerationHandlerwork unchanged. Thinking blocks → V3 reasoning parts (result.reasoning/reasoningTokens);cache_read/cache_creationtokens → V3 cache usage (Langfuse cost dashboards keep cache metrics);jsonresponseFormat emulated with the same forced-tool strategy@ai-sdk/anthropicused.executeStream(): native Messages streaming loop —text_delta→ content chunks;thinking_delta→ thereasoningchunk channel (from feat(providers): surface reasoning_content natively in the SSE client + base #1073);signature_deltacaptured so thinking blocks replay correctly when tool use continues a turn (API requirement);tool_useaccumulation → execution →tool_resultuser turn → loop tomaxSteps;neurolink.provider.streamTextname (dashboard continuity);tool:endemission + tool-execution storage match the oldonStepFinishhooks; NoOutput sentinel +capturedProviderErrorwiring match the cohort — thetest:context6.x sentinel audits now point atproviders/anthropicand the patterns are verified present in the built output.Public API preserved:
getAuthHeaders,validateModelAccess, usage/auth/tier getters,refreshAuthIfNeeded,getLastResponseMetadata, theanthropicModelsre-exports,ANTHROPIC_BETA_HEADERS. The rule-violatingexport defaults are removed (named exports only; nothing imported them).Build note
@anthropic-ai/sdk's beta webhooks module imports the optionalstandardwebhookspackage — stubbed in the browser bundle (server-only webhook verification), following the build script's existing npm-stub pattern.@ai-sdk/anthropicstays inpackage.jsonsolely for the browser entry'screateAnthropicre-export, until the planned breaking browser-surface removal later in this series.Validation
pnpm run check(svelte-check +tsc --strict) — pass, 0 errorspnpm run build+ publint — pass ("All good!")stampNoOutputSpan,capturedProviderError = error,buildNoOutputSentinel) — all present indist/lib/providers/anthropic.jsSingle commit, per the repo's single-commit policy.
Summary by CodeRabbit
New Features
Chores