Skip to content

Add output token replay annotations#1063

Draft
jthomson04 wants to merge 1 commit into
mainfrom
jthomson04/mocker-response-replay
Draft

Add output token replay annotations#1063
jthomson04 wants to merge 1 commit into
mainfrom
jthomson04/mocker-response-replay

Conversation

@jthomson04

@jthomson04 jthomson04 commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add optional Mooncake trace output_token_ids and request_id fields
  • derive generic output_replay_id annotations from request_id, session turn index, or trace line index
  • document the output token replay trace contract and validate output_length/token count mismatches

Validation

  • uv run --with ruff ruff format src/aiperf/dataset/loader/output_replay.py src/aiperf/dataset/loader/mooncake_trace.py src/aiperf/dataset/loader/models.py tests/unit/dataset/loader/test_mooncake_payload_mode.py
  • uv run --with ruff ruff check src/aiperf/dataset/loader/output_replay.py src/aiperf/dataset/loader/mooncake_trace.py src/aiperf/dataset/loader/models.py tests/unit/dataset/loader/test_mooncake_payload_mode.py
  • uv run pytest tests/unit/dataset/loader/test_trace.py tests/unit/dataset/loader/test_mooncake_payload_mode.py -q

Summary by CodeRabbit

  • New Features

    • Added support for "Output Token Replay" in trace-based mode, allowing traces to include output token IDs for request identification and replay validation.
    • Introduced request_id field for stable request identification.
    • AIPerf now automatically derives and attaches replay identifiers to requests using request ID, session/turn context, or physical line index.
  • Documentation

    • Updated trace replay documentation to explain the new Output Token Replay capability and replay key derivation mechanisms.

@github-actions

Copy link
Copy Markdown

Try out this PR

Quick install:

pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@2d2accb348cac368623ccb0c417e88e5c2a5c6d6

Recommended with virtual environment (using uv):

uv venv --python 3.12 && source .venv/bin/activate
uv pip install --upgrade --force-reinstall git+https://github.com/ai-dynamo/aiperf.git@2d2accb348cac368623ccb0c417e88e5c2a5c6d6

Last updated for commit: 2d2accbBrowse code

@github-actions

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds output token replay support to the trace-based dataset loader. A new output_replay.py module derives stable replay keys from request_id, session_id+turn_index, or line_index, and merges them as annotations into nvext.annotations. MooncakeTrace gains output_token_ids, request_id, and _output_replay_key fields with a post-validator. The loader computes and assigns replay keys per record, then injects annotations into turn payloads. Documentation is updated accordingly.

Changes

Output Token Replay Feature

Layer / File(s) Summary
Output replay key derivation and annotation merging
src/aiperf/dataset/loader/output_replay.py
New module defines OUTPUT_REPLAY_ID_ANNOTATION_KEY and prefix constants, effective_replay_key() with request_id/session_id/line precedence, output_replay_id_annotation() formatter, and merge_output_replay_annotation() to deep-copy a body and inject the annotation into nvext.annotations with type validation.
MooncakeTrace schema fields and validator
src/aiperf/dataset/loader/models.py
Adds PrivateAttr import, _output_replay_key private attribute, optional output_token_ids and request_id fields, and a validate_output_token_ids post-validator enforcing non-negative IDs, required output_length, and output_length == len(output_token_ids).
Loader: record iteration, replay key assignment, turn building, and reconstruct
src/aiperf/dataset/loader/mooncake_trace.py
Adds _iter_records_with_line_index iterator sourcing from inline_records or file with orjson parsing and error context. load_dataset tracks turn_index per session and assigns _output_replay_key when output_token_ids is present. _build_turn merges replay annotations into raw_payload or extra_body. _reconstruct_traces propagates _output_replay_key from originals.
Output token replay documentation
docs/benchmark-modes/trace-replay.md
Adds a section describing output_token_ids constraints, that token IDs are not forwarded to the server, and the nvext.annotations key derivation rules (request_id, session_id:turn_index, line:index). Adjusts closing comment placement.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 Hop, hop, annotate the trace!
Each token replay finds its place,
request_id leads with a trim so neat,
Then session and turn if you need to repeat,
Or line-index fallback — no key left behind.
The rabbit has keyed every token you'll find! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add output token replay annotations' directly and clearly summarizes the main objective of the pull request—introducing output token replay annotation support to the dataset loader.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/aiperf/dataset/loader/output_replay.py`:
- Around line 13-23: The pre-commit check is failing due to missing docstrings
(D103) on three public functions: effective_replay_key,
output_replay_id_annotation, and merge_output_replay_annotation. Add docstrings
to each of these functions immediately after their function definition lines,
describing what each function does, its parameters, and what it returns. The
docstrings should follow the standard Python docstring format (e.g.,
triple-quoted strings) to satisfy the ruff-baselined pre-commit requirement.

In `@tests/unit/dataset/loader/test_mooncake_payload_mode.py`:
- Around line 239-244: Add complete type annotations to the test methods. For
the test_replay_request_id_injects_output_replay_annotation method and the other
three test methods mentioned in the comment, add the return type annotation ->
None to each method signature and provide a proper type hint for the
mock_prompt_generator parameter (which should be annotated with the appropriate
fixture or Mock type). Ensure all four test methods have complete type hints for
both parameters and return types per coding guidelines.
- Around line 42-53: The pytest.raises match parameters contain regex
metacharacters (.*) that should be in raw strings to avoid RUF043 lint warnings.
In both test functions test_mooncake_trace_rejects_output_length_mismatch and
test_mooncake_trace_rejects_replay_tokens_without_output_length, add the r
prefix to the match string arguments so that the regex patterns are treated as
raw strings. Change match="output_length.*len" to match=r"output_length.*len"
and match="output_length.*required" to match=r"output_length.*required"
respectively.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b91c741e-da48-4094-9df2-27ef0cc26cda

📥 Commits

Reviewing files that changed from the base of the PR and between d72160e and 2d2accb.

📒 Files selected for processing (5)
  • docs/benchmark-modes/trace-replay.md
  • src/aiperf/dataset/loader/models.py
  • src/aiperf/dataset/loader/mooncake_trace.py
  • src/aiperf/dataset/loader/output_replay.py
  • tests/unit/dataset/loader/test_mooncake_payload_mode.py

Comment on lines +13 to +23
def effective_replay_key(
request_id: str | None,
session_id: str | None,
turn_index: int,
line_index: int,
) -> str:
if request_id is not None and request_id.strip():
return request_id.strip()
if session_id is not None and session_id.strip():
return f"{session_id.strip()}:{turn_index}"
return f"line:{line_index}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add docstrings to exported helpers to unblock pre-commit.

ruff-baselined is currently failing on D103 for effective_replay_key (Line 13), output_replay_id_annotation (Line 26), and merge_output_replay_annotation (Line 30), so this change cannot merge until those docstrings are added.

🛠️ Proposed fix
 def effective_replay_key(
     request_id: str | None,
     session_id: str | None,
     turn_index: int,
     line_index: int,
 ) -> str:
+    """Derive a stable replay key from request/session/line context.
+
+    Priority: request_id, then session_id:turn_index, then line:<line_index>.
+    """
     if request_id is not None and request_id.strip():
         return request_id.strip()
     if session_id is not None and session_id.strip():
         return f"{session_id.strip()}:{turn_index}"
     return f"line:{line_index}"
 
 
 def output_replay_id_annotation(replay_key: str) -> str:
+    """Format the replay-key annotation value for nvext.annotations."""
     return f"{OUTPUT_REPLAY_ID_ANNOTATION_PREFIX}{replay_key}"
 
 
 def merge_output_replay_annotation(
     body: dict[str, Any] | None, replay_key: str | None
 ) -> dict[str, Any] | None:
+    """Merge output replay annotation into body['nvext']['annotations'].
+
+    Returns the original body unchanged when replay_key is None.
+    """
     if replay_key is None:
         return body

Based on learnings from pipeline failures, pre-commit is failing specifically on D103 for these three public functions.

Also applies to: 26-27, 30-57

🧰 Tools
🪛 GitHub Actions: Pre-commit / 0_pre-commit.txt

[error] 13-13: check-ruff-baselined failed: ruff-baselined reported new violation [D103] in effective_replay_key — Missing docstring in public function. Hook exited with code 2.

🪛 GitHub Actions: Pre-commit / pre-commit

[error] 13-13: check-ruff-baselined failed (exit code 2). ruff violation [D103]: in effective_replay_key — Missing docstring in public function. Step: . .venv/bin/activate && python tools/ruff_baselined.py

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/aiperf/dataset/loader/output_replay.py` around lines 13 - 23, The
pre-commit check is failing due to missing docstrings (D103) on three public
functions: effective_replay_key, output_replay_id_annotation, and
merge_output_replay_annotation. Add docstrings to each of these functions
immediately after their function definition lines, describing what each function
does, its parameters, and what it returns. The docstrings should follow the
standard Python docstring format (e.g., triple-quoted strings) to satisfy the
ruff-baselined pre-commit requirement.

Source: Pipeline failures

Comment on lines +42 to +53
with pytest.raises(ValidationError, match="output_length.*len"):
MooncakeTrace(
text_input="Hello",
output_length=2,
output_token_ids=[10, 11, 12],
)


def test_mooncake_trace_rejects_replay_tokens_without_output_length() -> None:
with pytest.raises(ValidationError, match="output_length.*required"):
MooncakeTrace(text_input="Hello", output_token_ids=[10])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate the test file
find . -type f -name "test_mooncake_payload_mode.py" | head -5

Repository: ai-dynamo/aiperf

Length of output: 212


🏁 Script executed:

# If found, read the relevant lines from the file
if [ -f tests/unit/dataset/loader/test_mooncake_payload_mode.py ]; then
  sed -n '40,55p' tests/unit/dataset/loader/test_mooncake_payload_mode.py
fi

Repository: ai-dynamo/aiperf

Length of output: 718


🏁 Script executed:

# Also check the full context around those lines
if [ -f tests/unit/dataset/loader/test_mooncake_payload_mode.py ]; then
  wc -l tests/unit/dataset/loader/test_mooncake_payload_mode.py
fi

Repository: ai-dynamo/aiperf

Length of output: 214


🌐 Web query:

Ruff RUF043 rule regex pytest raises match raw string

💡 Result:

The Ruff rule RUF043 (pytest-raises-ambiguous-pattern) identifies cases where a non-raw string is passed to the match parameter of pytest.raises while containing unescaped regex metacharacters [1]. Because pytest interprets the match argument as a regular expression, using a standard string can create ambiguity about whether the pattern is intended to be a literal string or a regex [1]. To resolve this rule, you should clarify your intent by choosing one of the following approaches: 1. If you intended the string to be a regex: Use a raw string literal (e.g., match=r"pattern") to explicitly signal that regex metacharacters are intentional [1]. 2. If you intended the string to be a literal match: Use re.escape to ensure the string is treated as a literal sequence (e.g., match=re.escape("pattern")) [1]. 3. If you prefer to escape manually: Use backslashes to escape any regex metacharacters within the standard string [1]. Currently, RUF043 does not provide an automated fix [2][3]. This is intentional because Ruff cannot programmatically determine whether the developer's original intent was to use a regex or a literal string, and choosing the wrong fix would alter the test's behavior [4][3]. Therefore, developers must manually choose the approach that matches their specific testing requirement [1].

Citations:


🏁 Script executed:

# Search for all pytest.raises with match parameter in this file
rg "pytest\.raises.*match=" tests/unit/dataset/loader/test_mooncake_payload_mode.py -A 1

Repository: ai-dynamo/aiperf

Length of output: 691


Use raw regex literals in pytest.raises(match=...) assertions.

Ruff flags these patterns with RUF043 (pytest-raises-ambiguous-pattern), which can fail lint checks in CI. Both assertions at lines 42 and 49 need raw string prefixes to clarify that the regex metacharacters (.*) are intentional.

Suggested fix
-    with pytest.raises(ValidationError, match="output_length.*len"):
+    with pytest.raises(ValidationError, match=r"output_length.*len"):
         MooncakeTrace(
             text_input="Hello",
             output_length=2,
             output_token_ids=[10, 11, 12],
         )

-    with pytest.raises(ValidationError, match="output_length.*required"):
+    with pytest.raises(ValidationError, match=r"output_length.*required"):
         MooncakeTrace(text_input="Hello", output_token_ids=[10])
🧰 Tools
🪛 Ruff (0.15.18)

[warning] 42-42: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)


[warning] 51-51: Pattern passed to match= contains metacharacters but is neither escaped nor raw

(RUF043)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/dataset/loader/test_mooncake_payload_mode.py` around lines 42 -
53, The pytest.raises match parameters contain regex metacharacters (.*) that
should be in raw strings to avoid RUF043 lint warnings. In both test functions
test_mooncake_trace_rejects_output_length_mismatch and
test_mooncake_trace_rejects_replay_tokens_without_output_length, add the r
prefix to the match string arguments so that the regex patterns are treated as
raw strings. Change match="output_length.*len" to match=r"output_length.*len"
and match="output_length.*required" to match=r"output_length.*required"
respectively.

Source: Linters/SAST tools

Comment on lines +239 to +244
def test_replay_request_id_injects_output_replay_annotation(
self,
tmp_path: Path,
default_cfg: CLIConfig,
mock_prompt_generator,
):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the test file
fd test_mooncake_payload_mode.py

Repository: ai-dynamo/aiperf

Length of output: 210


🏁 Script executed:

# Get an outline of the file to see the structure
ast-grep outline tests/unit/dataset/loader/test_mooncake_payload_mode.py

Repository: ai-dynamo/aiperf

Length of output: 1477


🏁 Script executed:

# Read the specific lines mentioned in the review comment (239-244 and others)
sed -n '235,250p' tests/unit/dataset/loader/test_mooncake_payload_mode.py
sed -n '260,275p' tests/unit/dataset/loader/test_mooncake_payload_mode.py
sed -n '296,310p' tests/unit/dataset/loader/test_mooncake_payload_mode.py
sed -n '320,335p' tests/unit/dataset/loader/test_mooncake_payload_mode.py

Repository: ai-dynamo/aiperf

Length of output: 2009


🏁 Script executed:

# Check the imports at the top of the file to understand fixture types
head -50 tests/unit/dataset/loader/test_mooncake_payload_mode.py

Repository: ai-dynamo/aiperf

Length of output: 1637


Add full type annotations to test method signatures.

The four test methods are missing -> None return type annotations and have an untyped mock_prompt_generator parameter. Per coding guidelines, all functions must have complete type hints for parameters and return types.

Suggested fix
 def test_replay_request_id_injects_output_replay_annotation(
     self,
     tmp_path: Path,
     default_cfg: CLIConfig,
-    mock_prompt_generator,
-):
+    mock_prompt_generator: Mock,
+) -> None:
 def test_replay_key_uses_session_turn_index_without_request_id(
     self,
     tmp_path: Path,
     default_cfg: CLIConfig,
-    mock_prompt_generator,
-):
+    mock_prompt_generator: Mock,
+) -> None:
 def test_replay_key_uses_physical_line_index_without_request_or_session(
     self,
     tmp_path: Path,
     default_cfg: CLIConfig,
-    mock_prompt_generator,
-):
+    mock_prompt_generator: Mock,
+) -> None:
 def test_replay_annotation_is_injected_into_payload_mode_raw_payload(
     self,
     tmp_path: Path,
     default_cfg: CLIConfig,
-    mock_prompt_generator,
-):
+    mock_prompt_generator: Mock,
+) -> None:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/dataset/loader/test_mooncake_payload_mode.py` around lines 239 -
244, Add complete type annotations to the test methods. For the
test_replay_request_id_injects_output_replay_annotation method and the other
three test methods mentioned in the comment, add the return type annotation ->
None to each method signature and provide a proper type hint for the
mock_prompt_generator parameter (which should be annotated with the appropriate
fixture or Mock type). Ensure all four test methods have complete type hints for
both parameters and return types per coding guidelines.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant