fix(stream) + feat(api): emit Usage before Done; expose model capabilities in /v1/models#99
Conversation
When the upstream sends finish_reason and usage in the same SSE chunk,
parse_openai_chunk was pushing Done before Usage. This caused
format_deltas to emit usage={0,0,0} in the chunk before [DONE],
and the OpenAI SDK stops at that first [DONE] — the real token
counts in the subsequent Usage chunk were never seen by clients.
Fix: reorder so Usage delta precedes Done. Includes regression test.
The OpenAI-compatible /v1/models endpoint now includes max_context_length and max_output_tokens for each model, enriched from models.dev.json capabilities source. This allows downstream clients (Hermes, etc.) to auto-detect the correct context window from the proxy, rather than relying on static config.
|
fetch nyro model context is virtual model, it include 1 to many providers real model, theri context length difference, so context length should not in /v1/models api. |
Fair point. Instead of dropping the field entirely, what about reporting the minimum context_window across all routes for that virtual model? Deterministic, safe for every route, and still useful to clients. |
|
good idea. |
Appreciate the gesture. Unfortunately I don't have WeChat. Happy to connect on X if you prefer. Link on my GitHub bio. Also very happy with your router. Great job ! |
Two independent fixes:
1. Stream: emit Usage delta before Done
parse_openai_chunkcurrently pushesDonebeforeUsage. This meansformat_deltasemitsusage={0,0,0}in the chunk before[DONE], and the OpenAI SDK stops at that first[DONE]— the real token counts in the subsequentUsagechunk are never seen by the client.Fix: reorder so
Usagedelta precedesDone. Includes a regression test.2. API: expose model capabilities in /v1/models
The
/v1/modelsendpoint currently returns bare{id, object, created, owned_by}. This addsmax_context_lengthandmax_output_tokensby pre-fetchingModelCapabilitiesfrom the admin for each route's target model.{ "id": "deepseek-v3", "object": "model", "owned_by": "Nyro", "max_context_length": 131072, "max_output_tokens": 8192 }Files:
stream.rs(+64, -5, 1 test),handler.rs(+41, -9)Tests:
cargo test --workspace --exclude nyro-desktop --exclude nyro-server— 153 passed, 0 failed