ds4-winhip / docs /ds4-api-surface-matrix.md
staccs's picture
ds4-winhip: Windows/HIP 7.2 build tree, launcher, supervisor, bench + notes (part 5)
bb74494 verified
|
Raw History Blame Contribute Delete
15.4 kB
# DS4 API surface matrix
This guide describes the Rust host's HTTP behavior. Configuration and resolved
plans belong to the [serving contract](serving-contract.md); qualification and
workload limits belong to the [release ledgers](README.md#release-ledgers).
The original route oracle came from the v0.5.6/v0.6.0 API-promotion arc.
Wire contracts are shared across the [supported families](../README.md#supported-model-families).
Native state and serving-lane support remain family-specific.
Tokenizer, prompt/tool syntax, and stop-token handling are dispatched by the
loaded model family without changing the endpoint schemas.
DS4 serves four wire surfaces. "Surface" means a distinct wire contract
(object types, stream events, ID scheme, finish mapping, error envelope),
not an API vendor. OpenAI Chat and legacy Completions are different
surfaces: they share an endpoint family but stream different objects.
| Surface | Endpoint | Buffered object | Stream objects |
|---|---|---|---|
| OpenAI Chat | `POST /v1/chat/completions` | `chat.completion` | `chat.completion.chunk` deltas, `[DONE]` |
| OpenAI Completion | `POST /v1/completions` | `text_completion` | `text_completion` chunks with `choices[].text`, `[DONE]` |
| Anthropic Messages | `POST /v1/messages` | `type:"message"` | `message_start` .. `content_block_*` .. `message_delta`, `message_stop` |
| OpenAI Responses | `POST /v1/responses` | `object:"response"` | `response.created` .. item/delta events with `sequence_number`, `response.completed` |
## Compatibility boundary
The project attempts to preserve these public contracts through `v0.x`:
- core `ds4-*` CLI flags and `DS4_*` environment variables;
- HTTP request/response behavior and observable sampling semantics;
- KVC files, EXT_TOOL_MAP, KTM trailers, recurrent payloads, and image replay
keys across C-save/Rust-load and Rust-save/C-load;
- explicit distributed byte codecs.
The following are internal and may change during `v0.x`: crate APIs, Rust
types, file layout, scheduler internals, and the native bridge ABI. Native and
Rust objects must be built from the same commit. Distributed transport has no
wire version, encryption, or authentication; use matching binaries on a
trusted network.
## Serving lanes and current routing
Three lanes serve generation requests:
- **serial** (`generate_job`): one request at a time on the session graph.
Full feature set, including live tool continuation and corrective tool
recovery.
- **continuous** (`generate_continuous_jobs`): the batched engine with
per-row sampling, streaming, stops, and tools. The Rust scheduler operates
over the configured/native-fitted N-bank width, including width one, and
refills idle rows from the live queue during an epoch.
- **static** (`generate_batch_jobs`): coalesced buffered greedy batches;
the computed routing-needs mask must be zero.
The shared `route_decide` function selects a lane from each request's computed
needs. The routing table is:
| Surface | serial | continuous | static |
|---|---|---|---|
| OpenAI Chat | fallback | yes (prompt fits one bank) | buffered + greedy + non-thinking + no stops/tools |
| OpenAI Completion | fallback | yes (no tools/echo) | same conditions as Chat |
| Anthropic Messages | buffered tools, prefill-only zero, serial-owned live frontiers, fallback | streaming tools and bank-owned output-only continuations | needs-free buffered |
| OpenAI Responses | buffered tools, live reasoning state, serial-owned live frontiers, fallback | same as Anthropic | needs-free buffered |
Buffered tool generation stays serial because its model-visible corrective
retry has no row-local batched equivalent. Streaming tool turns publish
bank-owned continuation records. Output-only follow-ups reclaim the bank only
when its generation and frontier still match; eviction respects the record's
grace and pin windows.
`DS4_SERVER_CONT_ANTHROPIC` and `DS4_SERVER_CONT_RESPONSES` control stateless
continuous routing. `DS4_SERVER_CONT_TOOLS_ANTHROPIC` and
`DS4_SERVER_CONT_TOOLS_RESPONSES` control tool routing, and take effect only
while the corresponding stateless switch is on.
Within OpenAI, still serial: non-streaming `return_token_ids` chat,
completion-kind requests with `return_token_ids`, and completion-kind
requests carrying tools.
GLM 5.3 text and image requests use the serial lane. Inline PNG/JPEG inputs
have message shapes in Chat Completions, Responses and Anthropic Messages;
legacy Completions has no image form. GLM live evidence covers text/image
Chat with the exact Q2 main GGUF and vision sidecar. Responses and Anthropic
image shapes have model-free parser coverage, not equivalent GLM live gates.
See the [artifact and API scope](ds4-dfm-model-families.md#glm-53-flash-release-scope).
Inkling Small uses serial CUDA serving with text output. Chat, Responses and
Messages accept PNG/JPEG images; only Chat accepts 16 kHz PCM/float WAV audio.
Legacy Completions accepts text only. The optional eight-layer MTP sidecar
preserves the wire contracts and uses speculative decoding for unforced greedy
tokens. See [MQ85GB validation and limits](inkling-small.md).
## Inline images
Qwen and GLM image content is accepted in the existing API-native shapes:
```text
Chat: {"type":"image_url","image_url":{"url":"data:image/jpeg;base64,..."}}
Responses: {"type":"input_image","image_url":"data:image/jpeg;base64,..."}
Anthropic: {"type":"image","source":{"type":"base64","media_type":"image/jpeg","data":"..."}}
```
## Output-budget (`max_tokens`) semantics today
All four parsers reject negative decode budgets with an endpoint-native
error naming the supplied field. Numeric values above `i32::MAX` are capped;
fractional values are truncated, so this is not strict integer validation.
The shared `request_decode_budget` helper distinguishes:
- **omitted** β€” the server default (`--tokens`, default 393216);
- **explicit zero** β€” zero requested decode tokens;
- **positive** β€” the requested budget.
The serial lane supports zero-decode prefill. Anthropic explicitly adds the
prefill-only routing need for a zero budget, preserving its cache-prewarm
contract (`stop_reason: "max_tokens"`, empty content). Other surfaces can
route zero-budget requests to a batched lane, whose native engine floors
`max_new` at one to retire the admission. Those requests may decode one token.
Do not rely on route-independent zero-decode behavior outside Anthropic.
The Anthropic parser requires `messages` but does not require
`max_tokens` (upstream requires it); an omitted value gets the server
default like every other surface.
## Trust domain
<a id="trust-domain-inc-5"></a>
The server is one trust domain. The continuation registry and exact-DSML tool
memory are global, with no authentication or tenant namespace. Registry records
are keyed by `(protocol, call_id)` for each Anthropic/Responses tool-call turn.
A tool-call ID grants access to its conversation: an output-only continuation
resumes the owning serial session or batch bank. Queued continuations can evict
other clients' serial work or prevent bank eviction until the bounded grace/pin
deadline. IDs are unguessable but appear in responses. Serve mutually untrusted
clients through an authenticating proxy or separate servers; see the
[compatibility boundary](#compatibility-boundary).
## Explicitly unsupported
- **Schema-constrained output** (v0.6.3): `response_format` with type
`json_object`/`json_schema` (OpenAI Chat and Completion),
`text.format` (Responses), and `output_format` / `output_config.format`
(Anthropic) are refused at parse time with HTTP 400 and a typed
message naming the mode, in the endpoint's native envelope
(ds4-on-spark#10). `{"type":"text"}`, `null`, omitted, and a typeless
object are accepted unchanged; the string spelling of a schema mode
is refused too. Decode is never schema-constrained.
- **Responses durable references**: non-null `previous_response_id` or
`conversation` values are rejected at parse time with
"not supported; replay full input instead". DS4 serves a stateless
Responses subset; clients replay full history. Literal `null` is
accepted and ignored.
- **`Idempotency-Key`**: the HTTP reader parses only `Content-Length`,
`Transfer-Encoding` (chunked request bodies, v0.6.3;
`DS4_SERVER_CHUNKED=0` restores the Content-Length-only reader), and
`Accept`; the header is accepted and discarded, so a retry is a new
generation with new IDs.
- **`/v1/batch`** is not implemented by the Rust host (404); the retained C
bulk path does not make it a production Rust endpoint.
- **`return_token_ids`** is a DS4 extension on OpenAI Chat and Completion,
with the serial-routing restrictions described above; it is not a shared
four-surface feature.
## Historical defects and retained fixtures
1. **FIXED (Inc 0b): continuous legacy-Completion streaming emitted chat
deltas.** `cont_on_token` projected every streaming row through the
chat delta machine, so a `text_completion` client received
`chat.completion.chunk` objects. Completion rows now stream the
serial oracle's plain `text_completion` chunks
(`cont_stream_emit_plain`); the Inc 0a negative fixture is inverted
(`test_cont_completion_stream_matches_serial_oracle`) and
`speed-bench/completion_stream_gate.sh` holds the live schema +
cont-engagement line.
2. **FIXED (Rust host RC.3): engine failures after SSE starts now terminate
with the surface-native error event.** Responses preserve their live
`sequence_number`, Anthropic emits `event: error` with its error envelope,
and OpenAI streams emit an error object instead of a misleading `length`
finish.
3. **FIXED (Inc 2b): error envelopes are endpoint-native.** Anthropic
buffered errors carry the documented `{"type":"error","error":{...}}`
envelope with a status-mapped type (400/409 `invalid_request_error`,
404 `not_found_error`, 429 `rate_limit_error`, 500 `api_error`,
503 `overloaded_error` β€” retryable in the native SDKs); Responses
stream errors are protocol `data:` events (`{"type":"error",...}`)
whose spliced `sequence_number` continues a live machine's counter.
OpenAI chat/completions and Responses buffered errors keep the OpenAI
envelope β€” it is the native family shape there. The flip happened
inside `wire_http_error`/`wire_stream_error` (Inc 1c made the surface
explicit at every call site, including the two 409 continuation-state
refusals). Negative decode budgets now reject at parse with the
client's own field name (`max_tokens` / `max_completion_tokens` /
`max_output_tokens`); explicit ZERO stays supported on every surface
with the lane-dependent behavior described under output budgets above.
4. **FIXED (Inc 0b): admission accounting dropped decode-growth
commitments** once a bank's prefill landed (the old `outstanding`
charge covered pending prompt targets only). Every continuous
admission now holds a lifetime credit β€” its full normalized target
`min(prompt + decode budget, seq_cap)` β€” from install until the row
ends, and both admission verdicts (comp-cache budget and live
memory floor) charge the page UNION of all live credits plus the
candidate. The union matters: per-layer bank strides are narrower
than VMM pages are wide, so neighbor banks share edge pages and the
true union of k full banks is far below `k x virtual/bank` β€” summing
per-bank rounded projections would silently shrink live width. The
verdict total (resident + projected credits) is timing-independent:
the promise holds no matter how much of a row's growth has faulted
in. Gate: `speed-bench/admission_credit_gate.sh` (achieved width at
the default budget, pinned-budget hard-promise reject with serial
fallback, credit release on row death and on mid-prefill abort).
5. **FIXED (Inc 0b): the v0.5.5 budget-cut honesty fix (#13) was
serial-only.** On the continuous lane β€” where chat+tools actually
routes β€” an unrepairable `max_tokens` cut inside a tool call
reported `finish="error"` (and a repairable one still silently
completes to `tool_calls`, matching serial's repair tier). The cont
lane now mirrors serial: the finish stays the honest `length`, the
partial call returns as assistant content, and the same
"tool call cut by token budget" marker is logged β€” so
`finish_reason_gate.sh`'s engagement oracle finally fires on the
lane it actually exercises. Found by the Inc 0a baseline battery
(deterministic fail, reproduced byte-identical on the tip-parent
binary β€” the gate had never actually proven the cont lane). The
full failure-cause taxonomy (stranding, aborts) remains Inc 2
typed-outcome work.
## Recorded quirks (current behavior, not upstream-shaped)
- **Anthropic response IDs**: the host mints a shared `chatcmpl-N` /
`cmpl-N` job ID for every surface, and `anthropic_final_response` /
the Anthropic stream use it directly β€” so Anthropic clients see
`"id":"chatcmpl-N"` instead of an upstream-shaped `msg_*` ID.
Responses is unaffected (`responses_final_response` and
`responses_stream_init` mint their own `resp_*`/`rs_*`/`msg_*` IDs).
This is the current compatibility behavior.
## Route observation metrics
`GET /metrics` exposes `ds4_route_requests_total{surface=...,lane=...}`
(fixed cardinality: 4 surfaces x 3 lanes, all cells always emitted);
`GET /v1/stats` mirrors it as the `routes` section. One increment per job
at the moment a lane takes it β€” a failed batched attempt that falls back
to serial increments both lanes. These counters are observation only;
they exist so route promotion can prove engagement (an eligible request
actually moved lanes) instead of inferring it.
## Retained C oracle fixtures (`ds4_test --server`)
These fixtures replay deterministic token/text tapes through the C projectors,
validated by protocol event validators (event order, one open/close per
block/item, contiguous Responses `sequence_number`, UTF-8 hold-back):
- `test_tape_openai_chat_stream_projection` (thinking + UTF-8-split tapes)
- `test_tape_openai_completion_stream_projection` (the oracle for defect 1)
- `test_tape_anthropic_stream_projection`
- `test_tape_responses_stream_projection`
- `test_tape_buffered_final_responses` (all four buffered objects +
finish mapping, including Anthropic `length -> max_tokens`)
- `test_cont_completion_stream_matches_serial_oracle` (the inverted
Inc 0a negative fixture for defect 1 β€” cont and serial now share the
legacy-Completion stream shape)
- `test_route_decisions_record_current_dispatch`
- `test_idempotency_key_header_is_ignored`
- `test_responses_durable_references_rejected_at_parse`
- `test_error_envelopes_native_shapes` (defect 3, now asserting the
native shapes + the negative-budget parse rejections)
Existing per-feature streaming tests (`test_openai_tool_stream_*`,
`test_anthropic_tool_stream_*`, `test_responses_*`) cover tool-call
projection per surface and remain part of the oracle. Current Rust checks
include `make test-server-parity` and the `ds4-server` crate tests; see
[CONTRIBUTING.md](../CONTRIBUTING.md).
Live-sampled output is never a byte oracle: continuous temp-0 emissions
jitter run-to-run, so live end-to-end gates assert schema, event automata,
route engagement, and semantic equivalence β€” not byte identity.