fix(messages): make /v1/messages a drop-in for Anthropic SDK clients - #543
Conversation
Tested GoModel end-to-end with the official Anthropic Python SDK across five providers and fixed every gap between "works" and "drop-in": - Auth: accept the Anthropic-native x-api-key header as a fallback when no Authorization header is present, so Anthropic(api_key=...) works unchanged on translated and passthrough routes. - stop_sequence: the anthropic provider now parses the natively reported matched sequence and carries it as a stop_sequence choice/delta extension (same pattern as reasoning_content); the Messages dialect maps it back to stop_reason "stop_sequence" plus the matched value, stream and non-stream. OpenAI-family providers structurally cannot report it (finish_reason conflates) and keep end_turn - documented. - Streaming usage: message_start now seeds usage.input_tokens with the chars/4 heuristic estimate; authoritative usage still arrives in message_delta, which SDK accumulators prefer. - Models: GET /v1/models renders the Anthropic list shape (type, display_name, created_at, has_more/first_id/last_id) when the request carries the anthropic-version header Anthropic SDKs always send. - 404s: unknown routes return the canonical error envelope (Anthropic shape for Anthropic-dialect callers) instead of echo's default body, so SDK clients raise clean typed errors. Documented remaining translation limitations (cache_control dropped, heuristic count_tokens, unsigned thinking blocks, no Messages batches API) in docs/advanced/anthropic-messages-api.mdx and amended ADR-0007. Full findings and retest results in docs/dev/2026-07-17_anthropic-sdk-compat-findings.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR extends Anthropic compatibility for stop sequences, streaming token estimates, authentication, model listing, unknown-route errors, and SDK compatibility documentation. ChangesAnthropic response and streaming compatibility
Server dialect handling
Compatibility documentation
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant AnthropicSDK
participant MessagesHandler
participant Provider
participant StreamConverter
AnthropicSDK->>MessagesHandler: POST /v1/messages
MessagesHandler->>Provider: Request streaming completion
Provider-->>StreamConverter: Stream chunks and usage
StreamConverter-->>AnthropicSDK: message_start estimate and final message_delta
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
Registering a wildcard RouteNotFound route made echo's router prefer it over the 405 method-not-allowed fallback during backtracking, so wrong- method requests on known paths (e.g. GET /v1/chat/completions) returned 404. Move the dialect-aware canonical 404 handler to the router-level NotFoundHandler config, which fires only when no route matches at all. Also update the anthropic contract golden for the intentional stop_sequence choice extension introduced in the previous commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.
Inline comments:
In `@internal/anthropicapi/request_test.go`:
- Around line 458-473: Refactor TestEstimateChatInputTokens into a table-driven
test covering the existing nil and message-only cases, and add a case containing
both Tools and ToolCalls. Set the expected token estimate for the new case
according to EstimateChatInputTokens behavior, preserving the current assertions
and using descriptive test names.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 724f5e77-d8e9-417a-9c9a-0c8b700dd41f
📒 Files selected for processing (24)
docs/adr/0007-anthropic-messages-ingress.mddocs/advanced/anthropic-messages-api.mdxdocs/dev/2026-07-17_anthropic-sdk-compat-findings.mdinternal/anthropicapi/models.gointernal/anthropicapi/request.gointernal/anthropicapi/request_test.gointernal/anthropicapi/response.gointernal/anthropicapi/response_test.gointernal/anthropicapi/stream.gointernal/anthropicapi/stream_test.gointernal/core/types.gointernal/providers/anthropic/anthropic_test.gointernal/providers/anthropic/chat.gointernal/providers/anthropic/chat_stream.gointernal/providers/anthropic/types.gointernal/server/auth.gointernal/server/auth_test.gointernal/server/error_support.gointernal/server/error_support_test.gointernal/server/handlers.gointernal/server/handlers_test.gointernal/server/http.gointernal/server/messages_handler.gotests/contract/testdata/golden/anthropic/messages_with_params.golden.json
Review follow-up: convert the estimate test to the table-driven form and add a case covering tool calls and tool definitions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Tested GoModel extensively with the official Anthropic Python SDK (
anthropic0.117.0) pointed at/v1/messages, across cheap models on five providers (OpenAI, Gemini, Groq, DeepSeek, Anthropic). The core contract held up (52 checks); this PR fixes every gap between "works" and "drop-in". Full findings + retest results:docs/dev/2026-07-17_anthropic-sdk-compat-findings.md.User-visible impact
Anthropic(api_key=...)now works unchanged. The SDK sends the key asx-api-key; the auth middleware now accepts that header as a fallback when noAuthorizationheader is present (Bearer keeps precedence). Applies to translated and passthrough routes.stop_reason: "stop_sequence"+ the matched sequence are reported when the serving provider knows them natively (Anthropic), stream and non-stream. The value rides the canonical dialect as astop_sequencechoice/delta extension field — same pattern as the existingreasoning_contentrelay. OpenAI-family providers structurally can't distinguish stop-parameter hits from natural stops, so they keepend_turn(documented).message_startcarries a realusage.input_tokens(the chars/4 heuristic estimate) instead of 0; authoritative usage still arrives inmessage_delta, which SDK accumulators prefer — final totals stay exact.client.models.list()returns proper Anthropic-shaped models (type: "model",display_name, RFC3339created_at,has_more/first_id/last_id) — negotiated via theanthropic-versionheader Anthropic SDKs always send; OpenAI clients see the unchanged OpenAI shape.{"message":"Not Found"}, so SDKs raise clean typed errors.Provider-specific behavior
stop_sequenceextension field (omitted unless a sequence matched), consistent with the existingreasoning_contentextension.Docs
docs/advanced/anthropic-messages-api.mdx: new Authentication section with an Anthropic-SDK snippet, models-endpoint row, and updated limitations (cache_control dropped, heuristic count_tokens, no Messages batches API — use/v1/batches).Testing
go test ./internal/...green; gofmt/vet clean.stop_sequencefidelity on the Claude path in both modes.🤖 Generated with Claude Code
Summary by CodeRabbit
/v1/models.x-api-key.message_startwith heuristic input-token estimates./v1/messagescompatibility, authentication, limitations, and added dated compatibility findings.