Skip to content

Accept a service_tier the enum does not name - #42

Merged
krinart merged 1 commit into
spiceaifrom
viktor/open-service-tier
Oct 9, 2026
Merged

krinart merged 1 commit into
spiceaifrom
viktor/open-service-tier

Conversation

@krinart

@krinart krinart commented Oct 9, 2026

Copy link
Copy Markdown

Fixes the fork half of spiceai/spiceai#14916.

OpenAI reports the serving tier in service_tier and adds tiers on its side. Replies with "service_tier": "fast" fail to deserialize against the closed enum (unknown variant 'fast'), so a model whose load-time health check is answered on that tier never loads.

Change

  • chat::ServiceTier and responses::ServiceTier gain #[serde(untagged)] Other(String). An unnamed tier is kept verbatim and serializes back unchanged. The request types share the enums, so a caller's own unnamed tier is accepted and forwarded.
  • Hand-written ToSchema for both: anyOf: [{type: string, enum: [named tiers]}, {type: string}]. The derive ignores variant-level untagged and would render Other as {"other": string}.
  • responses::ServiceTier drops Copy (a String variant cannot be Copy). Builds clean with --all-features --all-targets.

Validation

  • cargo test --features "chat-completion-types response-types" --test ser_de --test responses_ser_de: 11 passed. Covers unnamed tier round trip, named tier to its variant, non-string refused, and the schema shape with every listed name mapping to a named variant.
  • Offline suites pass with --all-features. The 12 failures in the lib tests and boxed_future are live-API tests that api.openai.com rejects with invalid_api_key.
  • In Spice (OpenAI model fails to load when the response carries service_tier: "fast" spiceai#14916): spiced against a mock endpoint answering service_tier: "fast" loads all four models, and /v1/ready returns 200. On the current pin the fast models fail to load and /v1/ready stays 503.

OpenAI reports the tier that served a request in `service_tier` and adds
tiers on its side, so a closed enum fails the whole reply when it names a
new one (`"fast"`): a client whose first call is answered on that tier
cannot deserialize anything it gets back.

`chat::ServiceTier` and `responses::ServiceTier` gain an untagged
`Other(String)` variant that keeps an unnamed tier verbatim and
serializes it back unchanged. The request types share these enums, so a
caller's own unnamed tier is accepted and forwarded too.

The `ToSchema` derive ignores `#[serde(untagged)]` on a variant and
would describe `Other` as an object, so both enums get a hand-written
schema: any string, listing the named tiers. `responses::ServiceTier`
loses `Copy`, which a `String` variant cannot have.
@krinart krinart self-assigned this Oct 9, 2026
@krinart
krinart merged commit 2c102dc into spiceai Oct 9, 2026
51 checks passed
krinart added a commit to spiceai/spiceai that referenced this pull request Oct 9, 2026
The fork PR squash-merged to the `spiceai` branch as 2c102dc, whose tree is identical to the tested branch commit 3a2d567. Repoint the pin and the ledger at it and drop the TEMPORARY marker.
krinart added a commit that referenced this pull request Oct 9, 2026
#42 made `chat::ServiceTier` and `responses::ServiceTier` open, and the
request types share them, so a request naming any tier became valid. A
caller that validates incoming requests against these types widened its
accepted values as a side effect of a response-decoding fix.

Split the two directions. `ServiceTier` is closed again, exactly as before
#42, and stays on `CreateChatCompletionRequest` and `CreateResponse`. The
response fields (`CreateChatCompletionResponse`,
`CreateChatCompletionStreamResponse`, `Response`) use `ServiceTierResponse`,
which keeps an unnamed tier verbatim in `Other(String)`, with the
hand-written any-string schema. `chat::ServiceTierResponse` was unused; it
gains `Auto` so it covers every value the request enum can hold.
krinart added a commit to spiceai/spiceai that referenced this pull request Oct 9, 2026
…openai#44

spiceai/async-openai#42 opened `ServiceTier`, which the request types share,
so `/v1/chat/completions` and `/v1/responses` started accepting and
forwarding any `service_tier` a client sent. That widens a published request
field, which the #14916 fix did not need.

#44 (merged as dfadaed) restores the closed `ServiceTier` on requests and
moves only the response fields to the open `ServiceTierResponse`. The
Databricks stream type follows the response field. The guard now sends a
named tier and checks the server's unnamed tier comes back, and a new
`a_request_naming_an_unnamed_tier_is_refused` pins both request types to the
listed tiers, which fails on the previous pin.
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