Skip to content

feat(gateway): add Anthropic Messages API support to openrouter proxy#1122

Merged
chrarnoldus merged 26 commits intomainfrom
feat/anthropic-messages-api
Mar 18, 2026
Merged

feat(gateway): add Anthropic Messages API support to openrouter proxy#1122
chrarnoldus merged 26 commits intomainfrom
feat/anthropic-messages-api

Conversation

@kilo-code-bot
Copy link
Contributor

@kilo-code-bot kilo-code-bot bot commented Mar 16, 2026

Summary

Adds support for the Anthropic Messages API to the OpenRouter proxy route (/api/openrouter/messages and /api/gateway/messages), in addition to the existing OpenAI chat completions (/chat/completions) and Responses API (/responses) support.

Key changes:

  • types.ts: Added GatewayMessagesRequest type (Anthropic Messages format with model, max_tokens, messages, system, stream, tools, etc.) and extended the GatewayRequest discriminated union with the messages kind.
  • route.ts: Extended validatePath to accept /messages, added body parsing for the messages format, applied the same admin-only guard as the Responses API, handled prompt info extraction and free-model rewriting for the new kind.
  • processUsage.messages.ts (new file): Streaming and non-streaming usage parsing for Anthropic's SSE format (message_start → input tokens, message_delta → output tokens + stop reason), with OpenRouter cost field handling mirroring the existing chat completions and responses parsers.
  • processUsage.ts: Wired the new messages api_kind into countAndStoreUsage.
  • request-helpers.ts: getMaxTokens now handles the messages kind (returns max_tokens).
  • api-metrics.server.ts: getToolsAvailable and getToolsUsed handle Anthropic tool format (tools have a top-level name, tool use appears as tool_use content blocks in assistant messages).
  • abuse-service.ts: extractFullPrompts handles the messages kind (top-level system field + user message content extraction).
  • providers/index.ts: openRouterRequest body parameter type updated to include GatewayMessagesRequest.

The Messages API endpoint is gated behind is_admin (same as the Responses API) while it's experimental.

Verification

  • Manual code review for correctness against the Anthropic Messages API streaming spec and OpenRouter's cost field conventions.
  • Verified the SSE event shape matches Anthropic's streaming format: message_start carries usage.input_tokens, message_delta carries usage.output_tokens.
  • TypeScript typecheck could not be run (tsgo binary unavailable in this environment); please verify during CI.

Visual Changes

N/A

Reviewer Notes

  • The Messages API path (/messages) is forwarded as-is to OpenRouter at ${provider.apiUrl}/messages — OpenRouter supports the native Anthropic Messages API format at this path.
  • Free model response rewriting is a no-op for the messages kind (free models don't use the Anthropic Messages API today), so it falls through to wrapInSafeNextResponse.
  • Custom LLM providers return a 404 for the messages kind (same as responses), since customLlmRequest only handles chat completions.
  • Cache breakpoints and tool call ID normalization in applyAnthropicModelSettings are already guarded behind kind === 'chat_completions'; clients using the native Messages API are responsible for their own cache control markup.

@kilo-code-bot
Copy link
Contributor Author

kilo-code-bot bot commented Mar 16, 2026

Code Review Summary

Status: 16 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 15
SUGGESTION 0

Fix these issues in Kilo Cloud

Issue Details (click to expand)

CRITICAL

File Line Issue
cloudflare-gastown/src/gastown.worker.ts 418 /api/users/* routes still trust the caller-supplied :userId, so authenticated users can target another user's town DO

WARNING

File Line Issue
src/app/api/openrouter/[...path]/route.ts 133 Invalid /messages payloads can crash before a 400 is returned
src/app/api/openrouter/[...path]/route.ts 436 Custom models no longer fail closed on unsupported /responses and /messages requests
src/lib/abuse-service.ts 108 Tool-result turns erase the user prompt for abuse checks
src/lib/processUsage.messages.ts 39 Vercel-routed Messages requests are recorded with zero cost
src/lib/processUsage.messages.ts 99 Streaming error events are recorded as successful usage
src/lib/processUsage.messages.ts 129 Streamed Messages errors are recorded as successful usage
src/lib/processUsage.messages.ts 217 Non-streaming error responses throw during usage parsing
src/lib/processUsage.messages.ts 249 Follow-up tool calls lose the logged user prompt
cloudflare-gastown/src/handlers/org-towns.handler.ts 45 Failed TownDO initialization leaves an orphaned org town record
Other Observations (not in diff)

Issues found in unchanged code that cannot receive inline comments:

File Line Issue
src/app/api/openrouter/[...path]/route.ts N/A /messages can still hit gateways that only support chat-completions
src/app/api/openrouter/[...path]/route.ts N/A Non-standard Messages fields are sent to Anthropic-compatible backends
cloudflare-gastown/src/handlers/rig-agents.handler.ts 304 target_agent_id is not constrained to the authenticated rig, so agents can nudge known agents outside params.rigId
cloudflare-gastown/src/handlers/rig-agents.handler.ts 327 nudge-delivered acknowledges by nudge_id alone, so a sender can suppress another agent's nudge before it is seen
src/components/gastown/useXtermPty.ts 199 Reconnects keep stacking xterm onData listeners, so long-lived terminal sessions pay growing per-keystroke overhead after each reconnect
src/lib/models.ts 40 preferredModels now uses minimax/minimax-m2.7, but the rest of the Minimax integration still points at minimax/minimax-m2.5
Files Reviewed (68 files)
  • src/app/api/openrouter/[...path]/route.ts, src/lib/processUsage.messages.ts, src/lib/abuse-service.ts, src/lib/kilo-auto-model.ts, and related provider/type updates - no new incremental issues in the current PR diff; previous findings remain open except the /messages auto-model reasoning regression, which is fixed
  • cloudflare-gastown/src/handlers/rig-agents.handler.ts - 2 new warnings in incremental changes outside the current PR diff
  • cloudflare-gastown/src/gastown.worker.ts - previous critical issue still open; no additional new diff-commentable issues
  • kiloclaw/controller/src/*, cloudflare-gastown/container/*, packages/db/*, and the supporting config/docs/version updates - no new issues found in reviewed incremental changes

Reviewed by gpt-5.4-20260305 · 1,938,755 tokens

@chrarnoldus chrarnoldus force-pushed the feat/anthropic-messages-api branch from e8761fe to 7602d6d Compare March 18, 2026 12:34
const inferredUpstream_USD = openrouterCost_USD * OPENROUTER_BYOK_COST_MULTIPLIER;
const microdollar_error = (inferredUpstream_USD - upstream_inference_cost_USD) * 1000000;
if (
(is_byok == null && (openrouterCost_USD || upstream_inference_cost_USD)) ||
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like we should at least a comment on some of this logic. So when the request isn't byok and there is either a openrouter cost reported or upstream cost reported, then it's considered suspicious? Why?

Copy link
Contributor

Choose a reason for hiding this comment

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

this file is vibe copied, I'll go through it for the next PR (the feature is only accessible for kilo accounts for now)

@chrarnoldus chrarnoldus merged commit 18a782b into main Mar 18, 2026
18 checks passed
@chrarnoldus chrarnoldus deleted the feat/anthropic-messages-api branch March 18, 2026 21:16
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.

2 participants