Repository navigation
The sgalng api specs - #41585
The sgalng api specs#41585
Conversation
07c17e3 to
9a5ef85
Compare
|
/rerun-group rust-server |
|
Results for 🚀 🚀 🚀 🚀 |
merrymercy
left a comment
There was a problem hiding this comment.
Two tests, can be in follow up prs:
- delete most items in sglang/rust/sglang-server/src/message and use the generated rust api definition
- 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"; | ||
|
|
There was a problem hiding this comment.
simplify and unify the custom option annotations.
- why do we need 3? MessageJson, FieldJson, OneofJson? any ways to simplify them?
- 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.
d1824e3 to
73cd657
Compare
|
/rerun-group rust-server |
|
73cd657 to
235aa9c
Compare
|
/rerun-group rust-server |
|
🚀 🚀 🚀 🚀 |
For followups
|
bbe6f6f to
5f65014
Compare
…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>
…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>
…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>
…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>
Motivation
The Rust server and the Python server each hand-wrote their own
/generaterequest 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, andmeta_infoshapes that only matched by convention.This PR makes
proto/sglang/api/v1the 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/GenerateMetaInfowith typed logprob entries and finish reasons,GenerateStreamItem, and theSglangServicewithGenerate,HealthCheck,GetModelInfo, andGetServerInfo.proto/sglang/json/v1/options.protois the JSON-fidelity vocabulary: theMessageShape,OneofShape,NullPolicy, andUnknownFieldsenums 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 emitsrust/sglang-api-types/src/generated/*(prost, tonic, and schema-driven serde forapi.v1; prost and tonic forruntime.v1) andpython/sglang/api/v1/api_types.py(frozen msgspec structs withfrom_json_value/to_json_valuecodecs that mirror the Rust serde byte for byte). All output is checked in.rust/sglang-api-typesreplaces the oldproto/crate (sglang-grpc-types);sglang-grpcnow depends on it. prost and tonic move to 0.14.Rust server
message/wire.rsis the one bridge from the generated request to the internal types, andinto_requestsfans out the generatedGenerateRequest. The hand-writtenGenerateBody,SamplingParamsInput,processor_extensions, and the Qwen packing round trip are gone. Wire token ids areint64.api_serveris reorganized ascore(the transport-neutral handle, events, errors, and theCoreOutputtoapi.v1frame builder),http, andgrpc. The gRPC adapter now servesapi.v1over the same fan-out and admission path as HTTP; theruntime.v1conversion layer and its 26 stubs are deleted. Batch items fail independently as in-stream error items on both transports.GenerateResponseserialized through serde. The hand-writtenmeta_infoJSON and theOutputAccumulatorcache 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/ServerInfomoved tomessage/info.rs;CoreError::http_statusandCoreError::stream_errormap errors for both transports.apply_absent_defaults) after the preferred-sampling fill, so an unsetmax_new_tokensis 128, not unbounded.Python server
/generatevalidates the parsed body against the generatedGenerateRequestbefore 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.rsandtest/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.jsonwithtests/json_parity.rsandtest/registered/unit/api/v1/test_parity.py: 17 shared fixtures that both generators must decode to the same canonical bytes, plus field-set parity againstGenerateReqInputandSamplingParams, dataclass normalization of schema bodies, Pythonmeta_infodecoding to the schema subset, and key-order pins.Three contract bugs the parity tests found are fixed in the same change:
null_is_nonefields now round-trip an explicit null;preferred_sampling_paramsisraw_jsonto match the HTTP shape; raw passthroughs keep insertion order regardless of which crate linksserde_json.Behavior changes to note
sglang.api.v1, notsglang.runtime.v1./generatebody the schema rejects now get a 400 where the dataclass path previously accepted it.hidden_statesvalues serialize throughdouble, matching Python'stolist()output.max_new_tokens: nullis preserved as null on re-encode.Howto
Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): 🚫 Run #37553797497
Latest PR Test (Extra): ❌ Run #37553797318
Latest PR Test (AMD ROCm 10): ❌ Run #37553797573