Skip to content

feat(trace): replay Dynamo agent request traces - #3

Closed
ishandhanani wants to merge 13 commits into
ajcasagrande:ajc/agentxfrom
ishandhanani:idhanani/agentx-dynamo-trajectories
Closed

ishandhanani wants to merge 13 commits into
ajcasagrande:ajc/agentxfrom
ishandhanani:idhanani/agentx-dynamo-trajectories

Conversation

@ishandhanani

@ishandhanani ishandhanani commented Jun 23, 2026 •

Copy link
Copy Markdown

AIPerf now converts canonical dynamo.request.trace.v1 captures into replayable Weka traces and preserves session lineage on the wire. Existing deployments keep the deprecated nvext.session_control path by default, while AIPERF_DYNAMO_SESSION_TRANSPORT=headers enables the new Dynamo session headers for migration testing.

How This Was Implemented

  • Add aiperf synthesize dynamo-trace to preserve timing, token/cache shape, and direct parent-child topology.
  • Keep --use-dynamo-conv-aware-routing and --use-dynamo-session-control as aliases for enabling session identity.
  • Select nvext or headers with AIPERF_DYNAMO_SESSION_TRANSPORT; nvext remains the temporary default and emits a deprecation warning.
  • Schedule Weka child conversations only through their parent branch, avoiding duplicate dispatch in fixed-schedule replay.
  • Reject malformed traces and unsupported nested subagent trees at conversion time.
Walkthrough
  • synthesize.py: convert request-trace JSONL into one Weka trace per root session.
  • inference_client.py: emit either nvext.session_control or X-Dynamo-Session-ID / X-Dynamo-Parent-Session-ID.
  • environment.py: expose the transport selector with nvext as the default.
  • fixed_schedule.py: leave child dispatch to the branch orchestrator.
  • Tests and generated docs cover both transports, raw-payload compatibility, topology, and trace validation.

Dependencies

Validation

  • Focused unit and integration suite: 199 passed, 2 deselected.
  • Ruff passed for every changed Python file; git diff --check passed.
  • Dynamo mocker run in header mode: 30/30 HTTP 200 responses, 9 sessions, 0 session crossings, 3 workers used.

Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
@github-actions

github-actions Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Try out this PR

Quick install:

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

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@18da1715635681940a4a7dd676d263f8b493a777

Last updated for commit: 18da171 • Browse code

Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
@ishandhanani ishandhanani changed the title refactor(dynamo): replace session control with trajectory headers feat(trace): replay Dynamo agent request traces Jun 23, 2026
@github-actions github-actions Bot added feat and removed refactor labels Jun 23, 2026
@ishandhanani
ishandhanani marked this pull request as ready for review June 23, 2026 12:01
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>

@ajcasagrande ajcasagrande left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed idhanani/agentx-dynamo-trajectories @ e99a179d9 vs ajc/agentx. Strong, clean refactor — moving Dynamo conversation-aware routing from body-injected nvext.session_control to X-Dynamo-Trajectory-ID / X-Dynamo-Parent-Trajectory-ID headers. No correctness defects found. Validated against source and reproduced end-to-end with the in-repo mock server.

What's working well

  • Dead-symbol removal is complete — no dangling references to the removed flags/helpers/detector anywhere in src/, docs/, or tests/.
  • get_endpoint_headers() returns a fresh dict per call, so the .update(...) in inference_client.py can't leak headers across requests.
  • synthesize dynamo-trace output validates against the real WekaTrace schema and round-trips through the loader; subagent inner-request t is emitted in absolute root-trace coordinates, exactly as weka_trace.py::_subagent_request_absolute_t expects.
  • Verified E2E against the mock server: every turn carries a stable trajectory header with zero nvext in the body; fixed-schedule Weka replay dispatches children through branch orchestration only (no double-dispatch), with correct parent lineage. 155 unit tests + the DAG integration test pass on this branch.

Main thing to resolve: merge ordering (not a code defect)

This removes the legacy open/bind/close lifecycle entirely and depends on ai-dynamo/dynamo#10214. Deployments without #10214 lose conversation-aware routing with no fallback, and --use-dynamo-session-control / --use-legacy-dynamo-session-control / --dynamo-session-timeout-seconds now hard-error (Unknown option). Please gate the merge on #10214 being in the deployment baseline and add a release note for the removed flags. (Already acknowledged in the PR description — restating as the key pre-merge action.)

Suggested fix order

  1. Merge gate on #10214 + release note for removed flags.
  2. Harden converter output validation (inline).
  3. Decide skip-vs-abort policy for replay-less records (inline).
  4. is_root is False style nit (inline).
  5. Optional: integration coverage for fixed-schedule DAG replay and the PAYLOAD_BYTES + routing path (both currently unit-only; verified manually in this review).

Full living review + runtime receipts kept locally under artifacts/.

Comment thread src/aiperf/cli_commands/synthesize.py Outdated
root_trajectory_id: Optional root lineage to select instead of converting all roots.
"""
traces = _dynamo_traces_to_weka(input_file, root_trajectory_id)
if output.exists() and any(output.iterdir()):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Low — output validation ordering / non-directory error. The output check runs after the full conversion, so pointing --output at a non-empty dir wastes the whole conversion. More importantly, output.iterdir() raises a raw NotADirectoryError when --output is an existing file (confirmed at runtime: NotADirectoryError: [Errno 20] Not a directory).

Consider validating existence / empty / is-a-directory before calling _dynamo_traces_to_weka(...), and give the file case a clean message.

return None
request = event.get("request")
replay = request.get("replay") if isinstance(request, dict) else None
if not isinstance(request, dict) or not isinstance(replay, dict):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Low–Medium — abort vs skip on replay-less records. A request_end with no agent_context.trajectory_id is silently skipped (returns None), but one that has a trajectory_id yet lacks a replay block aborts the entire conversion (confirmed: ValueError: Line N: agent request is missing replay metadata).

If real dynamo.request.trace.v1 captures can contain trajectory-tagged but non-replayable request_end events (e.g. tool/non-LLM requests), a single such record fails the whole file with no skip option — consider skipping them like the trajectory-less case. If the schema guarantees every trajectory-tagged request_end carries replay, this is fine as-is (matches the PR's 'reject incomplete' stance).


# Validate and build schedule
for conv in self._conversation_source.dataset_metadata.conversations:
if conv.is_root is False:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Trivial nit. ConversationMetadata.is_root is a strict bool (default=True), so if not conv.is_root: is equivalent and reads more naturally than is False. The identity check would only matter for a bool | None field. Optional — current code is correct.

Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
Signed-off-by: Ishan Dhanani <ishandhanani@gmail.com>
@weireweire

Copy link
Copy Markdown

could we finish this feature and add to infx repo? need this to update dynamo

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants