Skip to content

[Frontend][Benchmark] Add duplex performance metrics for OmniInteract and Omni-DuplexEval benchmark - #7242

Merged
linyueqian merged 7 commits into
vllm-project:mainfrom
ZacheryAU:duplex_metrics
Sep 14, 2026
Merged

linyueqian merged 7 commits into
vllm-project:mainfrom
ZacheryAU:duplex_metrics

Conversation

@ZacheryAU

@ZacheryAU ZacheryAU commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Improve full-duplex performance metrics and surface them through vllm bench serve --omni --dataset-name omniinteract and vllm bench omni-duplex-eval --omni for native Realtime duplex sessions. Draw on ideas from #6042.

What changed

  • Record native model-turn request execution start on the server and use it as the origin for per-response TTFT / TTFP.
  • Emit per-response and session-level duplex metrics into OmniInteract bench results, including stream-window metrics and top-level mean_duplex_stream_* aggregates.

Introduced metrics and definitions

Each request/session payload includes measurement_origin (or stream_measurement_origin) describing the start/end of the corresponding value.

Session means (duplex_session_metrics)

Metric Definition
ttft_ms / ttfp_ms / rtf / tpot_ms Stats across audio responses in the session (mean_tpot_ms only when positive TPOT samples exist).
audio_turn_count Number of audio responses included in the means.

Session stream window (stream_* and mean_duplex_stream_*)

Measured over the full OmniInteract input-stream window (not an average of per-response TTF*):

Metric Definition
stream_ttft_ms Input stream start → first non-empty text delta in the session.
stream_ttfp_ms Input stream start → first audio packet in the session.
stream_rtf (stream start → last audio receive) / total emitted audio duration, including concurrent realtime input.
stream_audio_generation_ms / stream_audio_duration_ms Wall time covering generation vs total emitted audio duration for that window.
duplex_stream_ttft_ms / duplex_stream_ttfp_ms / duplex_stream_rtf Bench-level stats of the per-session stream metrics across completed cases.

Notes

Test Plan

vLLM Version: 0.29.0

vLLM-Omni Commit: current commit on top of e540dfb

  • run vllm bench serve --omni --dataset-name omniinteract --save-result --result-dir ~/ --result-filename omniinteract-metrics.json as well as vllm bench omni-duplex-eval --omni generate and see duplex metrics in output json files
  • run python -m pytest -m 'core_model and cpu' tests/benchmarks/ tests/clients/test_duplex_client.py tests/e2e/online_serving/test_minicpmo_realtime_duplex_drivers.py tests/engine/test_output_processor.py tests/entrypoints/openai_api/test_duplex_handler.py tests/e2e/features/fullduplex/test_omni_duplex_eval_cli.py -q
  • run pytest -s -v tests/e2e/online_serving/test_minicpmo_4_5_duplex.py -m 'advanced_model and cuda' --run-level 'advanced_model'

Test Result

duplex metrics in OmniInteract's json file

Show more
  "duplex_session_metrics": [
    {
      "session_id": "omniinteract:1q1a:bench-5eb902d6-0:868711023927840",
      "audio_turn_count": 13,
      "ttft_ms": {
        "count": 13,
        "mean": 547.801,
        "p50": 492.138,
        "p99": 968.235
      },
      "ttfp_ms": {
        "count": 13,
        "mean": 547.801,
        "p50": 492.138,
        "p99": 968.235
      },
      "rtf": {
        "count": 13,
        "mean": 0.976753,
        "p50": 0.96645,
        "p99": 1.427951
      },
      "tpot_ms": {
        "count": 13,
        "mean": 11.462,
        "p50": 10.976,
        "p99": 14.241
      },
      "stream_ttft_ms": 2692.873,
      "stream_ttfp_ms": 2692.866,
      "stream_rtf": 2.001713,
      "stream_audio_generation_ms": 151809.897,
      "stream_audio_duration_ms": 75840,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    },
    {
      "session_id": "omniinteract:1q1a_math:bench-5eb902d6-1:868867447377069",
      "audio_turn_count": 14,
      "ttft_ms": {
        "count": 14,
        "mean": 574.871,
        "p50": 562.294,
        "p99": 991.494
      },
      "ttfp_ms": {
        "count": 14,
        "mean": 574.871,
        "p50": 562.294,
        "p99": 991.494
      },
      "rtf": {
        "count": 14,
        "mean": 0.905495,
        "p50": 0.92149,
        "p99": 1.692973
      },
      "tpot_ms": {
        "count": 13,
        "mean": 12.85,
        "p50": 11.9,
        "p99": 20.734
      },
      "stream_ttft_ms": 3069.068,
      "stream_ttfp_ms": 3069.051,
      "stream_rtf": 3.038951,
      "stream_audio_generation_ms": 279461.891,
      "stream_audio_duration_ms": 91960,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    },
    {
      "session_id": "omniinteract:1q1a:bench-5eb902d6-2:869172431146001",
      "audio_turn_count": 14,
      "ttft_ms": {
        "count": 14,
        "mean": 390.68,
        "p50": 341.632,
        "p99": 811.194
      },
      "ttfp_ms": {
        "count": 14,
        "mean": 390.68,
        "p50": 341.632,
        "p99": 811.194
      },
      "rtf": {
        "count": 14,
        "mean": 0.87263,
        "p50": 0.869336,
        "p99": 1.36989
      },
      "tpot_ms": {
        "count": 14,
        "mean": 11.052,
        "p50": 10.742,
        "p99": 13.812
      },
      "stream_ttft_ms": 2612.752,
      "stream_ttfp_ms": 2612.745,
      "stream_rtf": 3.708184,
      "stream_audio_generation_ms": 161528.484,
      "stream_audio_duration_ms": 43560,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    }
  ],
  "duplex_stream_ttft_ms": {
    "count": 3,
    "mean": 2791.564,
    "p50": 2692.873,
    "p99": 3069.068
  },
  "duplex_stream_ttfp_ms": {
    "count": 3,
    "mean": 2791.554,
    "p50": 2692.866,
    "p99": 3069.051
  },
  "duplex_stream_rtf": {
    "count": 3,
    "mean": 2.916283,
    "p50": 3.038951,
    "p99": 3.708184
  }

duplex metrics in Omni-DuplexEval's json file

Show more
  "duplex_session_metrics": [
    {
      "sample_id": "560",
      "split": "RTD_OCR",
      "session_id": "560",
      "audio_turn_count": 6,
      "ttft_ms": {
        "count": 6,
        "mean": 534.093,
        "p50": 409.494,
        "p99": 924.225
      },
      "ttfp_ms": {
        "count": 6,
        "mean": 534.093,
        "p50": 409.494,
        "p99": 924.225
      },
      "rtf": {
        "count": 6,
        "mean": 0.739869,
        "p50": 0.702124,
        "p99": 1.188374
      },
      "tpot_ms": {
        "count": 5,
        "mean": 12.8,
        "p50": 10.235,
        "p99": 22.204
      },
      "stream_ttft_ms": 4433.793,
      "stream_ttfp_ms": 4433.705,
      "stream_rtf": 2.17538,
      "stream_audio_generation_ms": 22710.967,
      "stream_audio_duration_ms": 10440.0,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    },
    {
      "sample_id": "561",
      "split": "RTD_OCR",
      "session_id": "561",
      "audio_turn_count": 2,
      "ttft_ms": {
        "count": 2,
        "mean": 909.971,
        "p50": 699.576,
        "p99": 1120.367
      },
      "ttfp_ms": {
        "count": 2,
        "mean": 909.971,
        "p50": 699.576,
        "p99": 1120.367
      },
      "rtf": {
        "count": 2,
        "mean": 1.022577,
        "p50": 0.92334,
        "p99": 1.121814
      },
      "tpot_ms": {
        "count": 1,
        "mean": 9.498,
        "p50": 9.498,
        "p99": 9.498
      },
      "stream_ttft_ms": 2579.53,
      "stream_ttfp_ms": 2579.511,
      "stream_rtf": 1.224425,
      "stream_audio_generation_ms": 23508.958,
      "stream_audio_duration_ms": 19200.0,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    },
    {
      "sample_id": "562",
      "split": "RTD_OCR",
      "session_id": "562",
      "audio_turn_count": 4,
      "ttft_ms": {
        "count": 4,
        "mean": 223.012,
        "p50": 55.557,
        "p99": 419.301
      },
      "ttfp_ms": {
        "count": 4,
        "mean": 223.012,
        "p50": 55.557,
        "p99": 419.301
      },
      "rtf": {
        "count": 4,
        "mean": 0.899004,
        "p50": 0.825088,
        "p99": 1.185069
      },
      "tpot_ms": {
        "count": 4,
        "mean": 10.934,
        "p50": 10.654,
        "p99": 12.08
      },
      "stream_ttft_ms": 2767.268,
      "stream_ttfp_ms": 2767.243,
      "stream_rtf": 1.146916,
      "stream_audio_generation_ms": 40233.798,
      "stream_audio_duration_ms": 35080.0,
      "stream_measurement_origin": {
        "ttft": "input stream start to first non-empty text delta",
        "ttfp": "input stream start to first audio packet",
        "rtf": "input stream start-to-last-audio receive time divided by total emitted audio duration; includes concurrent realtime input"
      }
    }
  ],
  "duplex_stream_ttft_ms": {
    "count": 3,
    "mean": 3260.197,
    "p50": 2767.268,
    "p99": 4433.793
  },
  "duplex_stream_ttfp_ms": {
    "count": 3,
    "mean": 3260.153,
    "p50": 2767.243,
    "p99": 4433.705
  },
  "duplex_stream_rtf": {
    "count": 3,
    "mean": 1.515574,
    "p50": 1.224425,
    "p99": 2.17538
  }

pytest results

'core_model and cpu': 669 passed, 18 warnings in 134.32s
'advanced_model and cuda': 3 passed, 1 deselected, 15 warnings in 706.16s

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@vllm-omni-review-bot

Copy link
Copy Markdown

This PR appears to belong to: docs/design/module/benchmarking.md.

Module owners: @alex-jw-brooks @Bounty-hunter

@ZacheryAU, please review your own changes and leave a short self-review comment describing what you checked. PRs without author self-review may not be assigned a reviewer.

Please take a look when you have a chance. If you would like an automated review, mention @vllm-omni-review-bot in a comment.

@hsliuustc0106 hsliuustc0106 added high priority high priority issue, needs to be done asap frontend code related to entrypoint benchmark/profiler/metrics/logger Codes related to benchmarks, profiler, metrics and logger system omni code related to omni models labels Sep 8, 2026
@ZacheryAU ZacheryAU changed the title [Frontend][Benchmark] Add duplex performance metrics for OmniInteract benchmark [Frontend][Benchmark] Add duplex performance metrics for OmniInteract and Omni-DuplexEval benchmark Sep 10, 2026

@ZacheryAU ZacheryAU left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self review

async def _run() -> bool:
nonlocal runtime_closed
try:
session.mark_model_turn_request_started(append_turn_id, time.monotonic())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per-response ttf* start

epoch=session.epoch,
)
response_request_metrics = session.mark_response_first_outputs(
observed_at_s=time.monotonic(),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pre-response ttf* end

and bool(event["delta"])
and first_text_received_at_s is None
):
first_text_received_at_s = received_at_s

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global ttft end

delta = event.get("delta") or event.get("audio")
if not isinstance(delta, str) or not delta:
continue
audio_received_at_s.append(received_at_s)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global ttfp end

session_from = 0 # the collector holds only this session's events
pcm = _ensure_final_commit_tail(pcm, client.events.events)
playback = _Playback()
stream_start = time.monotonic()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global_ttf* start

await client.configure(model, ref_audio=_ref_audio(ref_audio), instructions="Streaming Omni Conversation.")
ack_task = asyncio.create_task(_ack_playback(client))
try:
stream_start = time.monotonic()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global ttf* start

Signed-off-by: ZacheryAU <zachery.au@gmail.com>
Co-authored-by: Ruirui Yang | Rein <73573651+R2-Y@users.noreply.github.com>
Signed-off-by: ZacheryAU <zachery.au@gmail.com>
Signed-off-by: ZacheryAU <zachery.au@gmail.com>
Signed-off-by: ZacheryAU <zachery.au@gmail.com>
Signed-off-by: ZacheryAU <zachery.au@gmail.com>

@amy-why-3459 amy-why-3459 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed commit 67e8db9 across all 18 changed files and the related call paths. I found two P2 issues, detailed inline, and recommend addressing them before merging. Both were reproduced with isolated minimal examples. All 40 duplex client tests passed under isolated loading; the full server-side suite and real-model end-to-end evaluation were not run.

session_metrics = [result.session_metrics for result in results if result.session_metrics]
path = Path(response_root) / DUPLEX_METRICS_FILENAME
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve existing performance metrics when resuming generation

Without --overwrite, generate_sample() returns a GenerateSampleResult with empty metrics for an already-generated sample, but this function unconditionally overwrites duplex_metrics.json using only the returned metrics. Running the same command twice therefore preserves the generated responses while erasing their performance metrics; a partial resume also drops metrics for previously completed samples. In a minimal reproduction, a non-empty report became {"duplex_request_metrics": [], "duplex_session_metrics": []} after skipping the existing sample and writing the report again. Please persist/reload per-sample metrics or merge existing records by (split, sample_id), and add regression coverage for both all-skipped and partially resumed runs.

Comment thread vllm_omni/outputs/output_processor.py Outdated
record["vllm_itl_ms"] = sum(itls_ms) / float(len(itls_ms))
record["vllm_tpot_ms"] = _mean_time_per_output_token_ms(native_stats)
record["vllm_tpot_ms"] = (
record["vllm_itl_ms"] if record["vllm_itls_ms"] else _mean_time_per_output_token_ms(native_stats)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Account for multiple tokens per engine output when computing TPOT

The ITL list above appends one interval per engine output, while new_token_ids can contain multiple tokens. Using the arithmetic mean of those intervals as TPOT is therefore only correct when each output contributes exactly one token. In an isolated reproduction with an existing first token followed 30 ms later by an output containing three new tokens, the previous token-count-based calculation gives 10 ms/token, while this code reports 30 ms/token. The changed finished-request path also avoids replacing this value whenever ITLs exist. Please calculate TPOT from elapsed time and the corresponding token count within continuous generation segments, excluding gaps between segments, and add a multi-token-output regression test. This change is in the shared output processor, so its effect extends beyond the duplex benchmark.

Signed-off-by: ZacheryAU <zachery.au@gmail.com>
shihongzhi added a commit to shihongzhi/vllm-omni that referenced this pull request Sep 12, 2026
…async-chunk path

Implements item 8 of vllm-project#7389. Behind VLLM_OMNI_DUPLEX_FRAME_TIMING (off by
default; VLLM_OMNI_DUPLEX_FRAME_TIMING_LOG_EVERY throttles per-event lines,
emit one greppable DUPLEX_FRAME_TIMING log line per frame/chunk at five
points of the duplex tick journey:

- append (session runner): 80 ms frame reserved; inter-append jitter and
  input drift vs the tick budget
- connector_put / connector_get (chunk transfer adapter): connector hop
  wrap times; same-process put->get handoff age, cross-process joins via
  key + monotonic t_ns stamps
- stage1_decode (PersonaPlex Code2Wav): streaming Mimi decode time per
  request with the new-frame count
- audio_emit (runtime bridge): audio delta projected; emit jitter and
  output drift vs the tick budget

All stamps use time.monotonic_ns() — the same clock family as the
client-side duplex timeline proposed in vllm-project#7242/vllm-project#7025 — and the tick period
comes from the backend capabilities rather than a hard-coded constant.
No control-flow, payload, or ordering changes; every hook is a no-op when
the flag is unset.
EOF
)
shihongzhi added a commit to shihongzhi/vllm-omni that referenced this pull request Sep 12, 2026
…async-chunk path

Implements item 8 of vllm-project#7389. Behind VLLM_OMNI_DUPLEX_FRAME_TIMING (off by
default; VLLM_OMNI_DUPLEX_FRAME_TIMING_LOG_EVERY throttles per-event lines,
emit one greppable DUPLEX_FRAME_TIMING log line per frame/chunk at five
points of the duplex tick journey:

- append (session runner): 80 ms frame reserved; inter-append jitter and
  input drift vs the tick budget
- connector_put / connector_get (chunk transfer adapter): connector hop
  wrap times; same-process put->get handoff age, cross-process joins via
  key + monotonic t_ns stamps
- stage1_decode (PersonaPlex Code2Wav): streaming Mimi decode time per
  request with the new-frame count
- audio_emit (runtime bridge): audio delta projected; emit jitter and
  output drift vs the tick budget

All stamps use time.monotonic_ns() — the same clock family as the
client-side duplex timeline proposed in vllm-project#7242/vllm-project#7025 — and the tick period
comes from the backend capabilities rather than a hard-coded constant.
No control-flow, payload, or ordering changes; every hook is a no-op when
the flag is unset.
EOF
)
shihongzhi added a commit to shihongzhi/vllm-omni that referenced this pull request Sep 12, 2026
…async-chunk path

Implements item 8 of vllm-project#7389. Behind VLLM_OMNI_DUPLEX_FRAME_TIMING (off by
default; VLLM_OMNI_DUPLEX_FRAME_TIMING_LOG_EVERY throttles per-event lines),
emit one greppable DUPLEX_FRAME_TIMING log line per frame/chunk at five
points of the duplex tick journey:

- append (session runner): 80 ms frame reserved; inter-append jitter and
  input drift vs the tick budget
- connector_put / connector_get (chunk transfer adapter): connector hop
  wrap times; same-process put->get handoff age, cross-process joins via
  key + monotonic t_ns stamps
- stage1_decode (PersonaPlex Code2Wav): streaming Mimi decode time per
  request with the new-frame count
- audio_emit (runtime bridge): audio delta projected; emit jitter and
  output drift vs the tick budget

All stamps use time.monotonic_ns() - the same clock family as the
client-side duplex timeline proposed in vllm-project#7242/vllm-project#7025 - and the tick period
comes from the backend capabilities rather than a hard-coded constant.
No control-flow, payload, or ordering changes; every hook is a no-op when
the flag is unset.

Signed-off-by: shihongzhi <shi65881583@gmail.com>
…o stream_*

Signed-off-by: ZacheryAU <zachery.au@gmail.com>
@ZacheryAU

Copy link
Copy Markdown
Contributor Author

Reviewed commit 67e8db9 across all 18 changed files and the related call paths. I found two P2 issues, detailed inline, and recommend addressing them before merging. Both were reproduced with isolated minimal examples. All 40 duplex client tests passed under isolated loading; the full server-side suite and real-model end-to-end evaluation were not run.

Solved in 8beca59, @amy-why-3459 PTAL

@linyueqian linyueqian added the ready label to trigger buildkite CI label Sep 14, 2026

@linyueqian linyueqian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Most of this is benchmark and client code, but the change in outputs/output_processor.py is the part that affects serving, so that is where I spent the review.

Making TPOT token-weighted is a real correctness fix rather than a presentation change. The previous code recomputed vllm_tpot_ms from the whole-request mean on every update, which is only equivalent to the per-step view when every decode step emits exactly one token. Duplex and chunked paths do not, so a step that emitted several tokens was being averaged as though it emitted one, and the reported TPOT drifted from what the engine actually did. _accumulate_segment_tpot weighting each step by its token count while leaving ITL at one sample per step is the right split: they answer different questions and should not be derived from each other.

The precedence between the three writers is consistent, which is the part most likely to go wrong. The whole-request mean is only used while no per-step samples exist, both in _update_stats_from_output and again at finish, so once weighted data is available nothing overwrites it with the coarser number. I did go looking for a bug here: elif not record["vllm_itls_ms"] indexes directly while the finish path uses .get() on the same key, which is the shape of a KeyError waiting for the first request that takes the fallback branch. It is safe, because _native_text_metric_record seeds vllm_itls_ms to an empty list when the record is created. Worth knowing that the direct index depends on that seeding.

Stripping the _tpot_elapsed_ms and _tpot_intervals accumulators in pop_native_text_metrics keeps the internal weighting state out of the reported payload, so consumers see only the derived value.

On the benchmark side, the thing worth calling out is _STREAM_MEASUREMENT_ORIGIN spelling out what each number measures, including that stream RTF "includes concurrent realtime input". Duplex numbers are easy to quote without that caveat and then compare against a non-duplex baseline that never had it.

CI: buildkite/vllm-omni green at this head. The informational lanes carry their standing reds, covered by ci_approval_override_head.

Validation: static review of the twenty-one-file author diff from the GitHub file list, the three TPOT write paths and their precedence, the record seeding in _native_text_metric_record, and the RTF derivation guards. Fork head, so the branch was not executed.

@linyueqian
linyueqian merged commit f062124 into vllm-project:main Sep 14, 2026
6 of 9 checks passed
shihongzhi added a commit to shihongzhi/vllm-omni that referenced this pull request Sep 15, 2026
…ith vllm-project#7242/vllm-project#7025

Review follow-up for vllm-project#7453: the five site hooks now call dedicated
log_*_event helpers in metrics/duplex_frame_timing.py, so gating, cadence
state, and field building live in one durable module. The serving-side
call sites (session runner, runtime bridge) are single calls, ready to
move to the new session owner when the framework rework (vllm-project#7413) deletes
their files.

Name/clock alignment so the numbers line up with vllm-project#7242 / vllm-project#7025 once they
land: period_ms -> tick_period_ms, duration_ms -> audio_duration_ms,
handoff_ms -> chunk_age_ms, and a shared DEFAULT_TICK_PERIOD_MS (80 ms)
replaces the disagreeing per-site fallbacks (1000 vs 80). All stamps stay
time.monotonic_ns() - the same CLOCK_MONOTONIC origin as vllm-project#7242 stream_start
/ *_at_s instants; the mapping is documented in the module docstring.

Signed-off-by: shihongzhi <shi65881583@gmail.com>
chickeyton added a commit to chickeyton/vllm-omni that referenced this pull request Sep 20, 2026
…st in vllm-project#7413

vllm-project#7413 rebuilt the duplex serving stack around engine-resident sessions and
dropped the per-response request timing that vllm-project#7242 had added for the
MiniCPM-o Omni-DuplexEval / OmniInteract benchmarks: the engine no longer
emitted ``response_request_metrics``, so ``DuplexClient`` and the benchmark
summaries silently fell back to the client's receive clock, and the TPOT
weighting regressed to counting a one-token segment as one interval.

Port the timing into the engine session: the model-turn request start is
recorded when the model channel submits the append, the first text / audio
of the response fix TTFT / TTFP, and the numbers ride response.created
(under response.metadata.duplex_event) and every speak / audio delta
(under metadata.vllm_omni), exactly where the client already reads them.
Restore the interval-weighted TPOT mean.

Re-home the MiniCPM-o plugin policy unit tests deleted with
tests/engine/duplex/test_duplex_runtime.py, make the duplex e2e gate
assert the server-side anchor, fix the pre-existing mypy errors in the
two touched test files, and drop three doc passages that still describe
the session-per-request chat adapter removed before vllm-project#7413 merged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: chickeyton <ngton2014@gmail.com>
mlaneuville pushed a commit to mlaneuville/vllm-omni that referenced this pull request Sep 22, 2026
… and Omni-DuplexEval benchmark (vllm-project#7242)

Signed-off-by: Matthieu Laneuville <matthieu.laneuville@surf.nl>
khairulkabir1661 pushed a commit to khairulkabir1661/vllm-omni that referenced this pull request Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmark/profiler/metrics/logger Codes related to benchmarks, profiler, metrics and logger system frontend code related to entrypoint high priority high priority issue, needs to be done asap omni code related to omni models ready label to trigger buildkite CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants