Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/DETAILS.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ The server listens on `http://127.0.0.1:8080` (`--port` in setup, or edit the ru
| API | Endpoint |
| --- | --- |
| OpenAI Chat Completions (stream and non-stream, tools) | `POST /v1/chat/completions` |
| OpenAI text completions (stream and non-stream, raw prompt) | `POST /v1/completions` |
| Anthropic Messages (stream and non-stream, tools) | `POST /v1/messages` |
| OpenAI Responses (stream and non-stream, tools; stateless, [below](#the-responses-api-and-codex-cli)) | `POST /v1/responses` |
| Model list and health | `GET /v1/models`, `GET /models`, `GET /health` |
Expand Down Expand Up @@ -549,6 +550,15 @@ applies at the next start. Network, key, MCP and program keys are not editable t
`--parallel`, only a `max_tokens` 1 request gets them: it is the one a batch slot decodes through the single-token
path, which writes an LP line. A longer reply there is a 400, since the slot's `BT` lines carry none -- entries for
a prefix only, which a client cannot tell from the model's own answer.
- **logprobs on `/v1/completions` (vLLM's shape).** `"logprobs": N` (0..20, or `true` with `top_logprobs`) adds
`choices[0].logprobs` with `tokens`, `token_logprobs`, `top_logprobs` (one `{token: logprob}` per token) and
`text_offset`. Every generated token is listed, the stop token too, so a one-token read whose most likely token is
the stop still has its distribution. `"allowed_token_ids"` lists those ids with their full-vocabulary logprobs, not
renormalised (unlike chat), so a caller can tell how much of the mass its labels hold;
`"return_tokens_as_token_ids": true` keys every token as `token_id:<id>`. The prompt may also be a list of token
ids. This is what a decision client reads from `vllm serve`: a typed choice from one next-token distribution
(AnyJev's `StrataBackend`). The rules are the chat ones (no cap of its own on `max_tokens`; with `--parallel`, only
`max_tokens` 1), and one of its own: logprobs are not streamed on this route, `stream: true` with them is a 400.
- `STRATA_SPEC_PROB=1` with the default `--spec-min-p` gate: the gate tests the token the draft head drew from its distribution
q. A draft that fails the gate still counts as a proposal. The verifier keeps q's mass on the tokens that pass the gate
(not renormalised; call it mu), accepts an offered token D with probability min(1, p(D)/mu(D)), and draws the replacement
Expand Down
12 changes: 12 additions & 0 deletions serve/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ def render(self, messages: list[dict], tools: list[dict] | None = None, add_gene
return self.template.render(messages=messages, tools=tools, add_generation_prompt=add_generation_prompt,
**kwargs)

def gen_prompt_tail(self, enable_thinking: bool) -> str:
"""What add_generation_prompt adds to this template: the generation prompt's tail.

The raw /v1/completions prompt is passed through untouched; this is what a chat render would have
appended when the client asks thinking off. The probe message keeps the render the template needs;
both renders get the same kwargs, so the difference is the generation prompt alone.
"""
probe = [{"role": "user", "content": "x"}]
base = self.render(probe, add_generation_prompt=False, enable_thinking=enable_thinking)
full = self.render(probe, add_generation_prompt=True, enable_thinking=enable_thinking)
return full[len(base):]

def _detect_caps(self) -> dict[str, bool]:
"""llama.cpp's capability names, checked at load time against this template and Strata's tool-call format.
These are rendering hints, not a guarantee that the model will follow a request."""
Expand Down
Loading
Loading