Repository navigation
feat(agent): record per-request timings with --request-events-jsonl - #41
Merged
Merged
Conversation
batchbench-agent reported only aggregate latency percentiles, so a replayed request could not be compared with the latency production recorded for it. --request-events-jsonl writes one line per request, success or failure, when the run ends: when the first attempt started on the run clock, time to the first and last streamed chunks carrying content, reasoning or tool calls, end-to-end latency, time per output token, usage token counts including cached prompt tokens, attempts, status, error and HTTP status. Records are collected per worker only when the option is set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDPqeFzGgF3vkuiyimwMxX
There was a problem hiding this comment.
1 issue found across 4 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="README.md">
<violation number="1" location="README.md:139">
P2: The timing definition should say that `content` and `reasoning_content` must be non-empty and that `tool_calls` must contain a delta; otherwise readers can count the initial empty stream chunk as the first token. Match the documentation to `delta_carries_output`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| | `trajectory_id`, `agent_id` | The trajectory the request belongs to. | | ||
| | `invocation` | The request's 1-based position in its trajectory. | | ||
| | `sent_at_ms` | Start of the first attempt, from the start of the run (the clock `--agent-events-jsonl` uses). | | ||
| | `ttft_ms` | From `sent_at_ms` to the first streamed chunk whose delta carries `content`, `reasoning_content` or `tool_calls`. Null for non-streamed requests and when no such chunk arrived. | |
There was a problem hiding this comment.
P2: The timing definition should say that content and reasoning_content must be non-empty and that tool_calls must contain a delta; otherwise readers can count the initial empty stream chunk as the first token. Match the documentation to delta_carries_output.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At README.md, line 139:
<comment>The timing definition should say that `content` and `reasoning_content` must be non-empty and that `tool_calls` must contain a delta; otherwise readers can count the initial empty stream chunk as the first token. Match the documentation to `delta_carries_output`.</comment>
<file context>
@@ -128,6 +128,28 @@ perfect prefix caching and the end-to-end session latency. `--results-csv` persi
+| `trajectory_id`, `agent_id` | The trajectory the request belongs to. |
+| `invocation` | The request's 1-based position in its trajectory. |
+| `sent_at_ms` | Start of the first attempt, from the start of the run (the clock `--agent-events-jsonl` uses). |
+| `ttft_ms` | From `sent_at_ms` to the first streamed chunk whose delta carries `content`, `reasoning_content` or `tool_calls`. Null for non-streamed requests and when no such chunk arrived. |
+| `last_token_ms` | From `sent_at_ms` to the last such chunk. |
+| `e2e_ms` | From `sent_at_ms` until the request succeeded or its last attempt failed. |
</file context>
Suggested change
| | `ttft_ms` | From `sent_at_ms` to the first streamed chunk whose delta carries `content`, `reasoning_content` or `tool_calls`. Null for non-streamed requests and when no such chunk arrived. | | |
| | `ttft_ms` | From `sent_at_ms` to the first streamed chunk whose delta has non-empty `content` or `reasoning_content`, or a non-empty `tool_calls` array. Null for non-streamed requests and when no such chunk arrived. | |
Contributor
Author
|
Virtual Fergus: reviewed, merging.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
batchbench-agentreports only aggregate latency percentiles and per-trajectory lifecycle events, so a replayed request cannot be compared with the latency production recorded for the same source request.--request-events-jsonl <PATH>writes one JSON line per request (one per manifest request, or per synthetic invocation), success or failure, when the run ends.Record
{"trajectory_id":"t-1","agent_id":0,"invocation":1,"sent_at_ms":0.27,"ttft_ms":62.72,"last_token_ms":133.24,"e2e_ms":134.13,"tpot_ms":23.51,"prompt_tokens":8,"completion_tokens":4,"cached_tokens":6,"attempts":1,"status":"ok","error":null,"http_status":200} {"trajectory_id":"t-1","agent_id":0,"invocation":3,"sent_at_ms":251.24,"ttft_ms":null,"last_token_ms":null,"e2e_ms":3.93,"tpot_ms":null,"prompt_tokens":null,"completion_tokens":null,"cached_tokens":null,"attempts":1,"status":"error","error":"request failed (503 Service Unavailable) busy","http_status":503}sent_at_ms: start of the first attempt, on the run clock that--agent-events-jsonluses (closed loop: after the initial prompts are prepared; open loop: the admission clock).ttft_ms/last_token_ms: from the first attempt's start to the first and last streamed chunks whose delta carries non-emptycontent,reasoning_contentortool_calls. The usage-only final chunk does not count. Null for non-streamed requests or when no such chunk arrived. For a retried request, the chunks come from the attempt that succeeded, but the times are still measured from the first attempt's start, which is the latency the client saw.e2e_ms: the existing per-request latency (first attempt start to success, or to the last attempt's failure).tpot_ms = (last_token_ms - ttft_ms) / (completion_tokens - 1)whencompletion_tokens >= 2, else null.cached_tokens:usage.prompt_tokens_details.cached_tokens, null when absent.http_status: status of the last attempt's response, null when no response arrived (connect error, timeout before headers).A trajectory still stops at its first failed request; that request now also gets a record.
Changes
StreamedCompletion::absorbtakes the arrival time of the network chunk a payload came in and keeps the first and last arrival times of chunks that carry output. Nothing else in the streaming path changes.single_attemptis split intosend_requestandread_responseso it can return the response status alongside the result.request_with_retriesreturns aRequestOutcome(first-attempt start, latency, attempts, last status, result).Instant(closed-loopstart, open-loopshared.start) is passed to the workers. Each worker collectsAgentRequestRecords likefailures, the accumulator merges them, and the report sorts them by agent and invocation. They are collected only whenAgentLoopConfig::with_request_records(true)is set, which the CLI does when the option is given.agent_cliwrites the file with a sharedwrite_jsonlhelper (now also used by--agent-events-jsonl), and the results CSV gets arequest_events_jsonlcolumn next toagent_events_jsonl.Testing
cargo test --manifest-path rust/Cargo.toml: 74 passed (4 new). The new tests cover:tool_callsdelta, and a usage-only final chunk.cargo clippy --all-targets: clean.maturin develop, thenpython -m unittest discover -s tests): 19 passed, including the exported-manifest dry run throughbatchbench-agent. The CI smoke tests (Python API and CLI) also pass.--results-csv) produced the records shown above.🤖 Generated with Claude Code
https://claude.ai/code/session_01VDPqeFzGgF3vkuiyimwMxX