[Core][Frontend] Bound duplex output delivery and reject cancelled audio (#7644)
Signed-off-by: Nolen Liang <nliang@nvidia.com> Signed-off-by: bcsdhjew <nliang@nvidia.com>
This commit is contained in:
1 parent
3f20252151
commit
f781f7bb08
26 files changed
+1170
-147
No files matched your search
@@ -250,6 +250,40 @@ call; transport failures raise `DuplexConnectionError`; a session that ends
|
||||
while you are awaiting it raises `DuplexSessionClosedError`. All three derive
|
||||
from `DuplexClientError`.
|
||||
|
||||
### Output limits and slow consumers
|
||||
|
||||
Deployment settings under `duplex_session` bound each session's pending
|
||||
output: `max_pending_output_bytes_per_session` defaults to 2 MiB and
|
||||
`max_pending_output_events_per_session` to 512 events. Bytes count compact
|
||||
Realtime JSON, including base64 audio, not decoded audio. The limits apply
|
||||
to both WebSocket delivery and direct Python handles, not to replay journals
|
||||
or client playback queues.
|
||||
|
||||
Exceeding a limit fails the active response without committing its partial
|
||||
text to history, then closes only that session. If overflow occurs while a
|
||||
completion is being queued, its not-yet-queued `response.done` becomes failed
|
||||
with the same identity and output, and its response history entry is removed.
|
||||
An ending already accepted by the buffer is not replaced or followed by a
|
||||
second ending. Open a new session to continue; the closed session cannot be
|
||||
resumed. The notifications use these fields:
|
||||
|
||||
| Event | Field values |
|
||||
| --- | --- |
|
||||
| `error` | `error.type: "rate_limit_error"`, `error.code: "output_backpressure"` |
|
||||
| `response.done` (active response or not-yet-queued ending) | `response.status: "failed"`, `response.status_details: {"type": "failed", "reason": "output_backpressure"}` |
|
||||
| `session.closed` | `reason: "output_backpressure"` |
|
||||
|
||||
Queued audio for the affected response is removed. Previously queued non-audio
|
||||
events retain their order, but newly emitted ordinary events, including
|
||||
audio/text, content-part and output-item completion markers, are suppressed.
|
||||
Clients must therefore handle closure without waiting for every completion
|
||||
marker. Errors and response endings have an extra 64 KiB / eight-event reserve;
|
||||
if that also fills, either notification may be omitted. Closure has its own
|
||||
independent slot. A closure notification exceeding 4 KiB is compacted to
|
||||
`reason: "close_details_exceed_output_limit"` with empty details, preserving
|
||||
the event type and identity. One event held by the consumer is outside the
|
||||
queued budget; audio already sent still requires the client to stop playback.
|
||||
|
||||
### Measure latency
|
||||
|
||||
`EventCollector` accumulates events for assertions and metrics:
|
||||
@@ -315,6 +349,8 @@ single-consumer async iterator that ends after `session.closed` /
|
||||
`session.expired`; rejected commands come back as `ErrorEvent`s on it.
|
||||
Every event renders the wire JSON with `to_realtime()`, so anything written
|
||||
against the WebSocket protocol works unchanged on the typed stream.
|
||||
If opening fails, the abandoned handle is closed. Late engine events do not
|
||||
reopen a locally closed handle.
|
||||
|
||||
`vllm_omni.clients.inline_duplex.InlineDuplexClient` wraps a `DuplexOmni`
|
||||
behind the `DuplexClient` API, so the same application code runs in-process
|
||||
@@ -573,7 +609,7 @@ formats (`pcm16`, `pcm_s16le`, `s16le`, `pcm_f32le`, `g711_ulaw`,
|
||||
| `session.created` | 2 | Session opened. OpenAI name; adds `attachment_generation`, `resume_token` and vLLM-Omni keys inside `session` (`id` is the server-allocated session id, `epoch`, `turn_id`, `playback`, `capabilities`, ...). |
|
||||
| `session.updated` | 2 | Echo of the effective session config after every `session.update` (same extended `session` object). |
|
||||
| `session.heartbeat_ack` | 3 | Reply to `session.heartbeat`. |
|
||||
| `session.closed` | 3 | Last event on the socket, emitted once the engine released the session; `reason` ∈ `client_close`, `disconnect`, `timeout`, `transport_error`, `shutdown`, ... |
|
||||
| `session.closed` | 3 | Last event on the socket, emitted once the engine released the session; `reason` ∈ `client_close`, `disconnect`, `timeout`, `transport_error`, `shutdown`, `output_backpressure`, `close_details_exceed_output_limit`, ... |
|
||||
| `session.resumed` | 3 | Resume accepted; carries the new `attachment_generation` and rotated `resume_token`; journaled events are replayed after it. |
|
||||
| `session.replaced` | 3 | Sent to the superseded socket when another socket resumes the session. |
|
||||
| `session.expired` | 3 | Engine lease reaped (`disconnect_grace_expired`, `idle_ttl_expired`); socket closes after it. |
|
||||
|
||||
Reference in new issue
Block a user