Skip to content
Draft
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
64 changes: 64 additions & 0 deletions .github/workflows/test-realtime-ws.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Validate realtime WebSocket service

on:
pull_request:
paths:
- "funasr/bin/realtime_ws.py"
- "funasr/utils/postprocess_hotwords.py"
- "examples/industrial_data_pretraining/fun_asr_nano/serve_realtime_ws.py"
- "tests/test_realtime*.py"
- "examples/industrial_data_pretraining/fun_asr_nano/realtime_ws_benchmark.py"
- ".github/workflows/test-realtime-ws.yml"
push:
branches: [main]
paths:
- "funasr/bin/realtime_ws.py"
- "funasr/utils/postprocess_hotwords.py"
- "examples/industrial_data_pretraining/fun_asr_nano/serve_realtime_ws.py"
- "tests/test_realtime*.py"
- "examples/industrial_data_pretraining/fun_asr_nano/realtime_ws_benchmark.py"
- ".github/workflows/test-realtime-ws.yml"

permissions:
contents: read

jobs:
transport:
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
include:
- python: "3.10"
websockets: "10.4"
- python: "3.12"
websockets: "17.1"
env:
OMP_NUM_THREADS: "1"
MKL_NUM_THREADS: "1"
CUDA_VISIBLE_DEVICES: ""
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
cache: pip
- name: Install CPU test dependencies
run: |
python -m pip install torch==2.10.0 torchaudio==2.10.0 --index-url https://download.pytorch.org/whl/cpu
python -m pip install -e . pytest einops pytorch-wpe "websockets==${{ matrix.websockets }}"
- name: Check sessions, bounded ingress and real loopback transport
run: |
python -m pytest -q \
tests/test_realtime_ws_service.py \
tests/test_realtime_ws_benchmark.py \
tests/test_realtime_ws_receive_pressure.py \
tests/test_realtime_receive_configuration.py \
--junitxml=realtime-ws-results.xml
- uses: actions/upload-artifact@v4
if: always()
with:
name: realtime-ws-${{ matrix.python }}-${{ matrix.websockets }}
path: realtime-ws-results.xml
if-no-files-found: error
23 changes: 23 additions & 0 deletions docs/benchmark/realtime_ws_benchmark.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,29 @@ limit. In particular, long speech segments create synchronized, expensive
final decodes that are not representative of every meeting or voice-agent
workload.

## Receive Pressure Acceptance

The bounded receive FIFO and deferred provisional decodes must be evaluated
separately from acoustic throughput. Record both receive limits, actual
`websockets` version, frame size and send interval, and whether the load is paced
or a burst. With `--log-decode-profile`, retain the receive high-water observations
(`peak_messages`, `peak_bytes`, `skipped_partials`) and engine profile together.

Acceptance covers: Ping/Pong during a finite decode stall; explicit close1013
when either receive limit is exceeded; no silent PCM loss/reordering; ordered
START, hotword/language changes, COMMIT and STOP; full completed-segment inputs;
and reader/worker cleanup after disconnect or cancellation. A disconnected or
overloaded session is a failed session, not a completed final. The queue limits
do not bound the whole process: protocol and session buffers remain additional.

Fewer partials mean different work and potentially different context/fallback
observations. Compare first text, preview freshness, complete transcripts and
final latency alongside request counts and encoded seconds; a lower wall time
alone is not a win. Keep low-concurrency interactive cases in the matrix, then
run the reporter's L20 paced47-second/16-client workload and another GPU before
claiming a performance fix. Synthetic blocked-decoder tests establish transport
behavior only; they do not demonstrate GPU capacity or resolve issue#3528.

## Report Template

When publishing a realtime WebSocket benchmark or issue report, include:
Expand Down
29 changes: 26 additions & 3 deletions docs/vllm_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,34 @@ CUDA_VISIBLE_DEVICES=0 python examples/industrial_data_pretraining/fun_asr_nano/
Set a positive `--ws-ping-timeout` only after measuring the worst-case decode
and queue delay for the production traffic shape; keep it above that delay and
coordinate it with the gateway idle-timeout policy. The `websockets` library's
`max_queue` setting bounds receive buffering for incoming messages; it doesn't
change ping/pong timeout semantics, so increasing it doesn't fix keepalive
timeouts. Set `--ws-ping-interval 0` only when an external gateway already owns
receive high-water mark (`max_queue`) can pause socket reads when its queue fills;
this also delays processing Ping frames even if inference runs off the event loop.
Increasing that mark only moves the overload threshold. It isn't a throughput
fix. Set `--ws-ping-interval 0` only when an external gateway already owns
keepalive/reconnect policy.

The source server receives messages independently of session inference, with a
bounded application FIFO: `--ws-receive-max-messages 128` and
`--ws-receive-max-bytes 16777216`. Both limits must be positive. They count queued
messages and payload bytes (UTF-8 bytes for text commands), not total process
memory: protocol buffering, an in-flight message and session audio add to it.
An overflow closes the connection with **1013**, not a successful final result.
Do not automatically replay a partially processed session without an
application-level recovery policy.

Audio and commands remain ordered. When the next queued message is more audio,
an otherwise-due provisional decode is deferred until that backlog is consumed;
audio itself isn't dropped or combined. VAD completed-segment decoding and
`COMMIT`/`STOP` final decoding still process their full input. Preview cadence and
context/fallback observations may change under load, so identical transcript
text or improved hardware throughput isn't guaranteed. Use
`--log-decode-profile` to record `peak_messages`, `peak_bytes` and
`skipped_partials` alongside the existing engine profile.

These source changes are not in the published `funasr==1.4.15` package. Transport
tests with synthetic decoding do not replace L20 or production-traffic
acceptance; see [the benchmark contract](benchmark/realtime_ws_benchmark.md).

For long-session debugging, especially with `--enable-spk`, enable periodic
session-state logs:

Expand Down
21 changes: 19 additions & 2 deletions docs/vllm_guide_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -683,11 +683,28 @@ CUDA_VISIBLE_DEVICES=0 python examples/industrial_data_pretraining/fun_asr_nano/

只有在按生产流量测得最坏推理和排队延迟后,才设置正数
`--ws-ping-timeout`;该值应高于实测延迟,并与网关 idle timeout 策略配合。
`websockets` 库的 `max_queue` 设置只限制入站消息的接收缓冲,不会改变
ping/pong 的超时语义,因此增大它不能解决 keepalive timeout。只有外部网关
`websockets` 库的接收高水位 `max_queue` 在队列满时可能暂停 socket 读取;
即使推理已经移出事件循环,后续 Ping 帧也会因此延迟处理。
增大高水位只是推后过载触发点,不是吞吐修复。只有外部网关
已经统一负责 keepalive / reconnect 策略时,才设置 `--ws-ping-interval 0`
关闭服务端 ping。

源码服务将消息接收与会话推理解耦,应用层 FIFO 默认限制为
`--ws-receive-max-messages 128` 和 `--ws-receive-max-bytes 16777216`,
两项都必须为正数。限制分别统计排队消息数和载荷字节数(文本命令按 UTF-8
字节计),并非进程总内存上限;协议缓冲、正在处理的消息与会话音频还会占用内存。
超限时连接以 **1013** 关闭,不会作为成功的 final 返回。没有应用层恢复策略时,
不要自动重放可能已经部分处理的会话。

音频与命令保持原顺序。当队列中的下一条消息仍是音频时,会推迟一次本已到期的
临时预览解码,先消费积压音频;不会丢弃或合并音频帧。VAD 完整段解码和
`COMMIT`/`STOP` 最终解码仍处理完整输入。负载下的预览频率、上下文与回退观测可能
变化,因此不保证转写文本逐字一致或硬件吞吐提升。使用 `--log-decode-profile`
可同时记录 `peak_messages`、`peak_bytes`、`skipped_partials` 与原有 engine profile。

以上源码改动尚未进入已发布的 `funasr==1.4.15`。合成解码器的传输测试不能代替
L20 或生产流量验收,详见[压测契约](benchmark/realtime_ws_benchmark.md)。

长会话排障,尤其是启用 `--enable-spk` 时,可以打开周期性 session 状态日志:

```bash
Expand Down
21 changes: 19 additions & 2 deletions docs/vllm_guide_zh_v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -686,11 +686,28 @@ CUDA_VISIBLE_DEVICES=0 python examples/industrial_data_pretraining/fun_asr_nano/

只有在按生产流量测得最坏推理和排队延迟后,才设置正数
`--ws-ping-timeout`;该值应高于实测延迟,并与网关 idle timeout 策略配合。
`websockets` 库的 `max_queue` 设置只限制入站消息的接收缓冲,不会改变
ping/pong 的超时语义,因此增大它不能解决 keepalive timeout。只有外部网关
`websockets` 库的接收高水位 `max_queue` 在队列满时可能暂停 socket 读取;
即使推理已经移出事件循环,后续 Ping 帧也会因此延迟处理。
增大高水位只是推后过载触发点,不是吞吐修复。只有外部网关
已经统一负责 keepalive / reconnect 策略时,才设置 `--ws-ping-interval 0`
关闭服务端 ping。

源码服务将消息接收与会话推理解耦,应用层 FIFO 默认限制为
`--ws-receive-max-messages 128` 和 `--ws-receive-max-bytes 16777216`,
两项都必须为正数。限制分别统计排队消息数和载荷字节数(文本命令按 UTF-8
字节计),并非进程总内存上限;协议缓冲、正在处理的消息与会话音频还会占用内存。
超限时连接以 **1013** 关闭,不会作为成功的 final 返回。没有应用层恢复策略时,
不要自动重放可能已经部分处理的会话。

音频与命令保持原顺序。当队列中的下一条消息仍是音频时,会推迟一次本已到期的
临时预览解码,先消费积压音频;不会丢弃或合并音频帧。VAD 完整段解码和
`COMMIT`/`STOP` 最终解码仍处理完整输入。负载下的预览频率、上下文与回退观测可能
变化,因此不保证转写文本逐字一致或硬件吞吐提升。使用 `--log-decode-profile`
可同时记录 `peak_messages`、`peak_bytes`、`skipped_partials` 与原有 engine profile。

以上源码改动尚未进入已发布的 `funasr==1.4.15`。合成解码器的传输测试不能代替
L20 或生产流量验收,详见[压测契约](benchmark/realtime_ws_benchmark.md)。

长会话排障,尤其是启用 `--enable-spk` 时,可以打开周期性 session 状态日志:

```bash
Expand Down
Loading
Loading