Skip to content

[sglang-miles] Extract Anthropic conversion into standalone utils - #35127

Merged
guapisolo merged 3 commits into
sgl-project:sglang-milesfrom
guapisolo:refactor/anthropic-conversion-utils
Aug 30, 2026
Merged

guapisolo merged 3 commits into
sgl-project:sglang-milesfrom
guapisolo:refactor/anthropic-conversion-utils

Conversation

@guapisolo

@guapisolo guapisolo commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

Expose SGLang's Anthropic/OpenAI conversion for reuse by external frontends.

Motivation

External OpenAI-compatible frontends that must interpret Anthropic Messages exactly as this server does (e.g. Miles PR #2358) previously had to vendor protocol.py/serving.py and maintain a derived codec, plus a sync ledger to keep the copies honest. This gives the server and external callers one conversion implementation, maintained here.

Usage

An external handler validates AnthropicMessagesRequest, then wraps its own OpenAI path with the shared converters:

from sglang.srt.entrypoints.anthropic.serving import (
    convert_response,
    convert_to_chat_completion_request,
)

chat_request = convert_to_chat_completion_request(
    anthropic_request,
    merge_inline_system=merge_inline_system,
)
chat_response = await openai_backend(chat_request)
anthropic_response = convert_response(chat_response)

The result uses the same conversion as SGLang's /v1/messages; endpoint validation, routing, and transport remain caller-owned.

Design Notes

  • Mental model: An external /v1/messages handler parses AnthropicMessagesRequest; passes its deployment's merge_inline_system choice plus optional reasoning callbacks into convert_to_chat_completion_request; runs the resulting ChatCompletionRequest through its OpenAI-compatible backend; then converts the completed response with convert_response or to_anthropic_fake_sse_events. SGLang owns format conversion; the caller owns endpoint policy, session state, and transport.
  • Runtime boundary: convert_to_chat_completion_request receives request data plus explicit inline-system policy and optional reasoning hooks; convert_response is data-only. AnthropicServing invokes both while supplying hooks from OpenAIServingChat, preserving server routing plus live streaming.
  • Utility boundary: utils.py contains Anthropic error-envelope mapping plus eager fake-SSE construction from a completed OpenAI response. Raw-body parsing, feature policy, routing, and state remain caller-owned; no fake serving instance, AnthropicRequestContext, or allow_* matrix is exposed.
  • Streaming boundary: Eager fake SSE materializes one delta per block after completion; SGLang's token-by-token streaming path is unchanged.

Verification

  • PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=python python -m pytest -p no:cacheprovider -q test/registered/unit/entrypoints/anthropic/test_utils.py test/registered/unit/entrypoints/anthropic/test_serving.py: 84 passed, 55 subtests passed; covers request/response goldens, existing serving behavior, error mapping, fake SSE, and import hygiene.
  • Miles tests/fast/router/test_session_anthropic.py plus tests/fast/utils/test_utils/test_mock_sglang_server.py: 58 passed, 1 skipped; verifies the paired session/TITO integration.
  • pre-commit run --files python/sglang/srt/entrypoints/anthropic/utils.py test/registered/unit/entrypoints/anthropic/test_utils.py: passed.

Review Focus

  • convert_to_chat_completion_request / AnthropicServing: reasoning-callback injection and preservation of server conversion behavior.
  • utils.py: error mapping, message scrubbing, and the eager fake-SSE boundary.
  • Cross-repo ABI with Miles [Bug] runtime_endpoint encounter a JSONDecodeError #2358: merge_inline_system, request/response types, and completed-response streaming.

CI States

Latest PR Test (Base): ❌ Run #33150255041
Latest PR Test (Extra): ❌ Run #33150254812
Latest PR Test (AMD ROCm 7.2): ❌ Run #33150255067

External OpenAI-compatible frontends that must interpret Anthropic
Messages exactly as this server does (e.g. token-in-token-out session
routers) previously had to vendor protocol.py/serving.py and maintain
a derived codec. Add anthropic/utils.py so the server and external
callers share one conversion implementation, with serving.py and
protocol.py untouched:

- to_openai_request / to_anthropic_response delegate to the original
  AnthropicServing methods through a runtime-detached instance (the
  conversion paths only read _merge_inline_system; the reasoning
  paths that need OpenAIServingChat are rejected up front)
- AnthropicRequestContext feature gates let a frontend that verified
  only a subset of typed features fail closed with a 400 instead of
  the server's accept-and-log policy; thinking is always rejected
- to_anthropic_error returns the envelope DTO against a composite
  map: serving.py's ERROR_TYPE_MAP plus the 413/422 entries the HTTP
  layer maps in http_server.py's /v1/messages exception handler
- to_anthropic_fake_sse_events eagerly synthesizes the Anthropic SSE
  event sequence from one complete ChatCompletionResponse for callers
  that proxy a finished non-streaming response

test_utils.py pins delegation equivalence against a normally
constructed AnthropicServing (so new instance-state dependencies in
the serving methods fail loudly), absolute goldens for the conversion
semantics, a depth-2 gate canary, and an AST cross-check of the
413/422 policy against http_server.py's handler source.

Co-authored-by: Shi Dong <shi.dong@radixark.ai>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex Temporary added 2 commits August 20, 2026 03:50
Move request and response conversion into module-level functions used by both
AnthropicServing and standalone utilities. This removes the half-initialized
conversion-only serving instance while preserving reasoning hooks and wire
behavior.
Keep request and response conversion in the module-level serving helpers.
Limit utils to error mapping and eager fake-SSE adaptation, removing the
unused request context and deployment-policy matrix.
@guapisolo
guapisolo force-pushed the refactor/anthropic-conversion-utils branch from 1ed0c90 to e95eae4 Compare August 28, 2026 07:06
@guapisolo
guapisolo merged commit 4e230c3 into sgl-project:sglang-miles Aug 30, 2026
81 of 90 checks passed
Kh4L pushed a commit to Kh4L/sglang that referenced this pull request Sep 8, 2026
…l-project#35127)

Co-authored-by: Shi Dong <shi.dong@radixark.ai>
(cherry picked from commit 4e230c3)
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