Skip to content

The sgalng api specs - #41585

Merged
merrymercy merged 23 commits into
mainfrom
rainj-me/api-spec
Oct 7, 2026
Merged

merrymercy merged 23 commits into
mainfrom
rainj-me/api-spec

Conversation

@rainj-me

@rainj-me rainj-me commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

The Rust server and the Python server each hand-wrote their own /generate request and response types, and the Rust gRPC adapter served a third vocabulary (sglang.runtime.v1) with its own conversion layer. Nothing held the three in sync, and drift showed up as silent behavior differences: fields accepted by one server and dropped by the other, and meta_info shapes that only matched by convention.

This PR makes proto/sglang/api/v1 the single contract for both servers. One generator emits the Rust types, their serde implementations, the tonic service, and the Python types from the schema; the Rust HTTP, Rust gRPC, and Python HTTP entrypoints all decode and encode through those generated types; and parity tests pin the three against each other and against the hand-written Python structs.

Modifications

Schema and generator

  • proto/sglang/api/v1/ defines the native API: GenerateRequest, SamplingParams, the scalar-or-list carriers, typed media inputs (MediaInput, MediaItem, MediaRef), GenerateResponse / GenerateMetaInfo with typed logprob entries and finish reasons, GenerateStreamItem, and the SglangService with Generate, HealthCheck, GetModelInfo, and GetServerInfo.
  • proto/sglang/json/v1/options.proto is the JSON-fidelity vocabulary: the MessageShape, OneofShape, NullPolicy, and UnknownFields enums plus per-field flags (emit_null_when_absent, raw_json, defaults). Proto3's canonical JSON mapping is not used; these options are the contract. Enums replace the earlier boolean trios so two shapes cannot be set at once.
  • rust/sglang-api-codegen (cargo gen-api) runs protoc once and emits rust/sglang-api-types/src/generated/* (prost, tonic, and schema-driven serde for api.v1; prost and tonic for runtime.v1) and python/sglang/api/v1/api_types.py (frozen msgspec structs with from_json_value / to_json_value codecs that mirror the Rust serde byte for byte). All output is checked in.
  • rust/sglang-api-types replaces the old proto/ crate (sglang-grpc-types); sglang-grpc now depends on it. prost and tonic move to 0.14.

Rust server

  • message/wire.rs is the one bridge from the generated request to the internal types, and into_requests fans out the generated GenerateRequest. The hand-written GenerateBody, SamplingParamsInput, processor_extensions, and the Qwen packing round trip are gone. Wire token ids are int64.
  • api_server is reorganized as core (the transport-neutral handle, events, errors, and the CoreOutput to api.v1 frame builder), http, and grpc. The gRPC adapter now serves api.v1 over the same fan-out and admission path as HTTP; the runtime.v1 conversion layer and its 26 stubs are deleted. Batch items fail independently as in-stream error items on both transports.
  • HTTP responses are the generated GenerateResponse serialized through serde. The hand-written meta_info JSON and the OutputAccumulator cache are removed; a small memo for the two growing columns (text, output_ids) keeps cumulative text-only streams O(1) per frame, pinned byte for byte against the typed frame.
  • ModelInfo / ServerInfo moved to message/info.rs; CoreError::http_status and CoreError::stream_error map errors for both transports.
  • gRPC applies schema defaults to unset protobuf fields (apply_absent_defaults) after the preferred-sampling fill, so an unset max_new_tokens is 128, not unbounded.

Python server

  • /generate validates the parsed body against the generated GenerateRequest before the dataclass path and answers a 400 with the schema's error text on a contract violation (entrypoints/api_contract.py).

Tests

  • rust/sglang-api-types/tests/json_contract.rs and test/registered/unit/api/v1/test_json_contract.py: per-option golden tests, mirrored case for case across the two emitters.
  • rust/sglang-api-types/testdata/json_parity.json with tests/json_parity.rs and test/registered/unit/api/v1/test_parity.py: 17 shared fixtures that both generators must decode to the same canonical bytes, plus field-set parity against GenerateReqInput and SamplingParams, dataclass normalization of schema bodies, Python meta_info decoding to the schema subset, and key-order pins.
  • gRPC adapter tests cover streaming, unary folding, drop-abort, in-stream errors, timeouts, batch indexing, preferred params, defaults, and health.

Three contract bugs the parity tests found are fixed in the same change: null_is_none fields now round-trip an explicit null; preferred_sampling_params is raw_json to match the HTTP shape; raw passthroughs keep insertion order regardless of which crate links serde_json.

Behavior changes to note

  • The Rust server's gRPC endpoint speaks sglang.api.v1, not sglang.runtime.v1.
  • Python clients sending a /generate body the schema rejects now get a 400 where the dataclass path previously accepted it.
  • hidden_states values serialize through double, matching Python's tolist() output.
  • max_new_tokens: null is preserved as null on re-encode.

Howto

cd rust && cargo gen-api

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): 🚫 Run #37553797497
Latest PR Test (Extra): ❌ Run #37553797318
Latest PR Test (AMD ROCm 10): ❌ Run #37553797573

@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Sep 28, 2026
@rainj-me
rainj-me requested a review from merrymercy September 28, 2026 20:18
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 30, 2026
@rainj-me

Copy link
Copy Markdown
Collaborator Author

/rerun-group rust-server

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-group rust-server:

🚀 ubuntu-latest (1 test): ✅ View workflow run

cd test/ && python3 registered/rust/test_run_rust_tests.py

🚀 1-gpu-5090 (1 test): ✅ View workflow run

cd test/ && python3 registered/core/test_srt_endpoint.py

🚀 1-gpu-h100 (4 tests): ✅ View workflow run

cd test/ && python3 registered/vlm/test_rust_native_mm_e2e.py
cd test/ && python3 registered/vlm/test_rust_native_mm_mmmu.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_cpu.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_cuda_ipc.py

🚀 2-gpu-h100 (2 tests): ✅ View workflow run

cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_tp2_shm.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_2node_cpu.py

Comment thread proto/sglang/api/v1/generate.proto Outdated
Comment thread python/sglang/api/v1/api_types.py

@merrymercy merrymercy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Two tests, can be in follow up prs:

  1. delete most items in sglang/rust/sglang-server/src/message and use the generated rust api definition
  2. run parity checks. python io_struct.py <-> generated python types <-> generated rust types. check fields, serialization orders, round trips.

package sglang.json.v1;

import "google/protobuf/descriptor.proto";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

simplify and unify the custom option annotations.

  1. why do we need 3? MessageJson, FieldJson, OneofJson? any ways to simplify them?
  2. It seems bare_list and transparent are the same thing.

Checks:

  • check whether we can simplify them.
  • check whether they match the current things used in rust (sglang/rust/sglang-server/src/message) and python
    (io_struct.py). It can be a subset but needs to match against them.
  • check whether everything is strongly and correctly typed.

@github-actions github-actions Bot added the Multi-modal multi-modal language model label Oct 6, 2026
@rainj-me

rainj-me commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

/rerun-group rust-server

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

⚠️ Rebase Required Before Re-run

A major update has landed on main. Your PR is diverged relative to required base commit 60878c3fba50.

Re-run was not dispatched. What to do:

  • Rebase your branch onto the latest main and push again
  • Follow issue #21065 for context
  • CI-fix PRs may request the bypass-maintenance label to skip this check

@rainj-me rainj-me added the run-ci CI: run the baseline test suite on this PR label Oct 6, 2026
@rainj-me

rainj-me commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

/rerun-group rust-server

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

🚀 ubuntu-latest (1 test): ✅ View workflow run

cd test/ && python3 registered/rust/test_run_rust_tests.py

🚀 1-gpu-5090 (1 test): ✅ View workflow run

cd test/ && python3 registered/core/test_srt_endpoint.py

🚀 1-gpu-h100 (4 tests): ✅ View workflow run

cd test/ && python3 registered/vlm/test_rust_native_mm_e2e.py
cd test/ && python3 registered/vlm/test_rust_native_mm_mmmu.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_cpu.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_cuda_ipc.py

🚀 2-gpu-h100 (2 tests): ✅ View workflow run

cd test/ && python3 registered/e2e/rust_server/test_mm_transport_1node_tp2_shm.py
cd test/ && python3 registered/e2e/rust_server/test_mm_transport_2node_cpu.py

@rainj-me

rainj-me commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator Author

Two tests, can be in follow up prs:

  1. delete most items in sglang/rust/sglang-server/src/message and use the generated rust api definition
  2. run parity checks. python io_struct.py <-> generated python types <-> generated rust types. check fields, serialization orders, round trips.
  1. delete the serialization related structs that replaced by the api definition, non-serialization related structs are kept. Removed all hand-written json deserialization code
  2. add a parity check test that compare the current rust generated structs by the api definition along with python generated classes and io_struct.py (22 fields)

For followups

  1. Move all api related (de-)serialization classes in io_struct.py to schema
  2. OpenAI apis implementation within grpc
  3. Abstract tower layers for the api-server

@merrymercy
merrymercy merged commit c52dcc5 into main Oct 7, 2026
159 of 184 checks passed
@merrymercy
merrymercy deleted the rainj-me/api-spec branch October 7, 2026 08:55
nvpohanh added a commit to nvjullin/sglang that referenced this pull request Oct 7, 2026
…parity

test_parity.py::TestRequestFieldParity::test_schema_request_fields_are_a_subset_of_the_dataclass
(added on main by sgl-project#41585) requires every GenerateReqInput field to be in
the v1 API schema or listed in PYTHON_ONLY_REQUEST_FIELDS. This PR adds
GenerateReqInput.parent_session_id for --allow-subagent-keepalive, which
the HTTP contract has not adopted, so list it next to session_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@OrangeRedeng OrangeRedeng mentioned this pull request Oct 9, 2026
2 of 5 tasks
nvjullin pushed a commit to nvjullin/sglang that referenced this pull request Oct 21, 2026
…parity

test_parity.py::TestRequestFieldParity::test_schema_request_fields_are_a_subset_of_the_dataclass
(added on main by sgl-project#41585) requires every GenerateReqInput field to be in
the v1 API schema or listed in PYTHON_ONLY_REQUEST_FIELDS. This PR adds
GenerateReqInput.parent_session_id for --allow-subagent-keepalive, which
the HTTP contract has not adopted, so list it next to session_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nvjullin pushed a commit to nvjullin/sglang that referenced this pull request Oct 21, 2026
…parity

test_parity.py::TestRequestFieldParity::test_schema_request_fields_are_a_subset_of_the_dataclass
(added on main by sgl-project#41585) requires every GenerateReqInput field to be in
the v1 API schema or listed in PYTHON_ONLY_REQUEST_FIELDS. This PR adds
GenerateReqInput.parent_session_id for --allow-subagent-keepalive, which
the HTTP contract has not adopted, so list it next to session_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nvjullin pushed a commit to nvjullin/sglang that referenced this pull request Oct 21, 2026
…parity

test_parity.py::TestRequestFieldParity::test_schema_request_fields_are_a_subset_of_the_dataclass
(added on main by sgl-project#41585) requires every GenerateReqInput field to be in
the v1 API schema or listed in PYTHON_ONLY_REQUEST_FIELDS. This PR adds
GenerateReqInput.parent_session_id for --allow-subagent-keepalive, which
the HTTP contract has not adopted, so list it next to session_id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation Multi-modal multi-modal language model run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants