Pull requests / #1916

#1916 OpenAI logprobs on /v1/chat/completions for the CUDA/HIP engine

open · @gt50 · 0 comentários · No GitHub

BenchmarksServer & APIAMD / HIPNVIDIA / CUDADocumentationWindows

Descrição

## Title
OpenAI logprobs on /v1/chat/completions for the CUDA/HIP engine

## Summary
`"logprobs": true` (with `"top_logprobs": 0..20`) on `/v1/chat/completions` now returns `choices[0].logprobs.content`, as OpenAI and llama-server do. Until now only the SYCL port had it (835d30b); the CUDA/HIP engine and `serve/` accepted the field and returned nothing. A client that marks values the model was unsure of (the weakest token of a value under 50%) needs it.

## What changed
- **Engine (`src/program/generate.cpp`):** the GEN key `logprobs=K` writes `LP <logprob> [id:logprob ...]` after each `T <id>`, from the verify window's head logits (`Verifier::copy_logits`), before penalties, temperature and sampling. It is the SYCL port's code, placed at both token outputs (the plain and the pipelined path). Without the key, no LP lines and nothing else changes.
- **`serve/server.py`:**
  - `sampling_keys` sends `logprobs=K` only when the request asks; an engine without LP lines ignores the key and the reply has no `logprobs`.
  - `StrataEngine.generate` reads a token's LP line before it yields the token (the engine writes both in the same flush), so the logprobs belong to the right text chunk. When nothing follows a token within 0.5 s, it is yielded without one. A line read this way is handed to the early-stop drain, so a `DONE` read after the last token is not waited for again (tested).
  - Each content / reasoning chunk carries the entries of the tokens that spell its text. `token` is the text the token completes (an incremental UTF-8 decode, as the reply's text is made): a character split across tokens is in the token that finishes it, so the tokens, joined, spell the reply. `bytes` are the token's own bytes. Tokens that make no text (`</think>` and the newlines after it) and the stop token are left out.
  - `openai_collect` merges them into `choices[0].logprobs` (`content`, `refusal`, and `reasoning_content` with thinking on).
  - `response_format` `json_schema`: with `logprobs`, the answer is returned as the model wrote it (validated as before, the same value) when its tokens spell it, including the JSON inside a ```` ```json ```` fence; otherwise the canonical JSON without `logprobs`. Without `logprobs`, the canonical JSON as before.
- **`docs/DETAILS.md`:** a "Logprobs" paragraph after the sampling one.
- **`serve/test_logprobs.py`:** the request keys, the engine line pairing (with and without LP lines, not asked, early stop), the API streamed and not (multi-byte characters, `top_logprobs`, the stop token), thinking, `json_schema` with a fenced answer, and the span helper.

## Extra Notes
- **Default path:** a request without `logprobs` sends the same GEN line, reads the same lines and returns the same reply (tested). The engine prints LP lines only when asked.
- **Measured** on a Radeon 8060S (Ryzen AI Max+ 395, Windows 11, UD-IQ4_XS, `--spec 4 --mtp`), this branch built with `tools/hip/build_windows.bat` (gfx1151, engine 0.1.42):
  - A greedy 255-token answer: the same text with and without `logprobs`, 38.5 and 38.1 tok/s; 254 entries (the stop token left out).
  - `{"rent": "$1,250", "quote": "Rent is $1,250 a month."}` from a two-field `json_schema` at temperature 0: 26 tokens that spell the content.
  - Thinking on: `content` and `reasoning_content` each spelled by their tokens. A streamed `json_object` answer and a sentence with `café`, `東京` and `€5`: spelled too.
  - The same patch on 0.1.40.2 gave the same results; that build is what I run day to day.
- **Not covered:** the Responses and Anthropic APIs, and `--parallel` batch slots (their `BT` lines get no LP lines). The legacy completions form `"logprobs": N` is taken as `top_logprobs` N.
- **Tests:** all `serve` test files pass on `main` 61b3fb5d (704 tests).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

No site

Links install, modelos, releases.