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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .cursor/rules/python.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Feature branches use `<username>/feature-name` format, forked from `main`. One P
- Decorators: `@on_init`, `@on_start`, `@on_stop`, `@on_message`, `@on_command`, `@background_task`, `@on_pull_message`, `@on_request`.
- Communication: `publish()` for broadcast, `@on_message` to subscribe, `send_command_and_wait_for_response()` for sync.
- `AIPerfLifecycleMixin` for standalone components: `CREATED` -> `INITIALIZING` -> `INITIALIZED` -> `STARTING` -> `RUNNING` -> `STOPPING` -> `STOPPED`; `FAILED` terminal.
- `dag_jsonl` input type: conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md` for abstractions and authoring.
- `dag_jsonl` input type: legacy conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md`. For new agentic workloads use Graph IR — see `docs/benchmark-modes/agentic.md`.
- Validator gate convention: unsupported constructs raise `NotImplementedError` with a leading `"<loc>: <reason>"` prefix where `<loc>` identifies the conversation/turn (e.g. `"conversation 'foo' turn 3: ..."`). New validators must follow this shape.
- Per-turn payload contract: `extra_body` / `max_tokens` / `model` are dispatch-turn only; `raw_tools` is the lone field that walks history (system-prompt-like). Dataset rows author `extra`, not `extra_body`. See `docs/dev/patterns.md` "Per-turn dataset `extra`".

Expand Down
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Feature branches use `<username>/feature-name` format, forked from `main`. One P
- Decorators: `@on_init`, `@on_start`, `@on_stop`, `@on_message`, `@on_command`, `@background_task`, `@on_pull_message`, `@on_request`.
- Communication: `publish()` for broadcast, `@on_message` to subscribe, `send_command_and_wait_for_response()` for sync.
- `AIPerfLifecycleMixin` for standalone components: `CREATED` -> `INITIALIZING` -> `INITIALIZED` -> `STARTING` -> `RUNNING` -> `STOPPING` -> `STOPPED`; `FAILED` terminal.
- `dag_jsonl` input type: conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md` for abstractions and authoring.
- `dag_jsonl` input type: legacy conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md`. For new agentic workloads use Graph IR — see `docs/benchmark-modes/agentic.md`.
- Validator gate convention: unsupported constructs raise `NotImplementedError` with a leading `"<loc>: <reason>"` prefix where `<loc>` identifies the conversation/turn (e.g. `"conversation 'foo' turn 3: ..."`). New validators must follow this shape.
- Per-turn payload contract: `extra_body` / `max_tokens` / `model` are dispatch-turn only; `raw_tools` is the lone field that walks history (system-prompt-like). Dataset rows author `extra`, not `extra_body`. See `docs/dev/patterns.md` "Per-turn dataset `extra`".

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/run-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ jobs:
- name: Run unit tests (Windows)
if: runner.os == 'Windows'
run: |
uv run pytest tests/unit -n auto --tb=short -m 'not performance and not stress and not slow'
uv run pytest tests/unit -n auto --timeout 600 --timeout-method thread --tb=short -m 'not performance and not stress and not slow'
- name: Run zmq real-transport tests (Windows)
# Real-socket libzmq tests (no looptime). ``!cancelled()`` lets them run
# even if the unit step failed, matching the POSIX ``make test-ci``
Expand All @@ -131,7 +131,7 @@ jobs:
# finishing after a manual stop.
if: ${{ !cancelled() && runner.os == 'Windows' }}
run: |
uv run pytest tests/component_integration -n auto --tb=short -v -m 'not performance and not stress and not slow'
uv run pytest tests/component_integration -n auto --timeout 600 --timeout-method thread --tb=short -v -m 'not performance and not stress and not slow'
- name: Upload results to Codecov
if: matrix.os == 'linux' && matrix.python-version == '3.12'
uses: codecov/codecov-action@v5
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Feature branches use `<username>/feature-name` format, forked from `main`. One P
- Decorators: `@on_init`, `@on_start`, `@on_stop`, `@on_message`, `@on_command`, `@background_task`, `@on_pull_message`, `@on_request`.
- Communication: `publish()` for broadcast, `@on_message` to subscribe, `send_command_and_wait_for_response()` for sync.
- `AIPerfLifecycleMixin` for standalone components: `CREATED` -> `INITIALIZING` -> `INITIALIZED` -> `STARTING` -> `RUNNING` -> `STOPPING` -> `STOPPED`; `FAILED` terminal.
- `dag_jsonl` input type: conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md` for abstractions and authoring.
- `dag_jsonl` input type: legacy conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md`. For new agentic workloads use Graph IR — see `docs/benchmark-modes/agentic.md`.
- Validator gate convention: unsupported constructs raise `NotImplementedError` with a leading `"<loc>: <reason>"` prefix where `<loc>` identifies the conversation/turn (e.g. `"conversation 'foo' turn 3: ..."`). New validators must follow this shape.
- Per-turn payload contract: `extra_body` / `max_tokens` / `model` are dispatch-turn only; `raw_tools` is the lone field that walks history (system-prompt-like). Dataset rows author `extra`, not `extra_body`. See `docs/dev/patterns.md` "Per-turn dataset `extra`".

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Feature branches use `<username>/feature-name` format, forked from `main`. One P
- Decorators: `@on_init`, `@on_start`, `@on_stop`, `@on_message`, `@on_command`, `@background_task`, `@on_pull_message`, `@on_request`.
- Communication: `publish()` for broadcast, `@on_message` to subscribe, `send_command_and_wait_for_response()` for sync.
- `AIPerfLifecycleMixin` for standalone components: `CREATED` -> `INITIALIZING` -> `INITIALIZED` -> `STARTING` -> `RUNNING` -> `STOPPING` -> `STOPPED`; `FAILED` terminal.
- `dag_jsonl` input type: conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md` for abstractions and authoring.
- `dag_jsonl` input type: legacy conversation DAG benchmarks (fork + spawn modes). See `docs/benchmark-modes/dag.md`. For new agentic workloads use Graph IR — see `docs/benchmark-modes/agentic.md`.
- Validator gate convention: unsupported constructs raise `NotImplementedError` with a leading `"<loc>: <reason>"` prefix where `<loc>` identifies the conversation/turn (e.g. `"conversation 'foo' turn 3: ..."`). New validators must follow this shape.
- Per-turn payload contract: `extra_body` / `max_tokens` / `model` are dispatch-turn only; `raw_tools` is the lone field that walks history (system-prompt-like). Dataset rows author `extra`, not `extra_body`. See `docs/dev/patterns.md` "Per-turn dataset `extra`".

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,13 @@ test-ci: #? run the tests using pytest-xdist for CI.
@printf "$(bold)$(blue)Running unit and component integration tests (CI mode)...$(reset)\n"
@# Run unit tests first with coverage
@printf "$(bold)$(blue)Running unit tests...$(reset)\n"
@$(activate_venv) && pytest tests/unit -n auto --cov=src/aiperf --cov-branch --cov-report= -m 'not performance and not stress and not slow' --tb=short $(args) || exit_code=$$?; \
@$(activate_venv) && pytest tests/unit -n auto --timeout 600 --timeout-method thread --cov=src/aiperf --cov-branch --cov-report= -m 'not performance and not stress and not slow' --tb=short $(args) || exit_code=$$?; \
# Run real-socket zmq transport tests (real time + real sockets, no looptime) regardless of unit result \
printf "$(bold)$(blue)Running zmq real-transport tests...$(reset)\n"; \
$(activate_venv) && pytest tests/zmq --cov=src/aiperf --cov-branch --cov-append --cov-report= -m 'not performance and not stress and not slow' --no-looptime --tb=short $(args) || exit_code=$$((exit_code + $$?)); \
# Run component integration tests with coverage append regardless of unit test result \
printf "$(bold)$(blue)Running component integration tests...$(reset)\n"; \
$(activate_venv) && MALLOC_ARENA_MAX=2 pytest tests/component_integration -n auto --cov=src/aiperf --cov-branch --cov-append --cov-report=html --cov-report=xml --cov-report=term -m 'not performance and not stress and not slow' -v --tb=short $(args) || exit_code=$$((exit_code + $$?)); \
$(activate_venv) && MALLOC_ARENA_MAX=2 pytest tests/component_integration -n auto --timeout 600 --timeout-method thread --cov=src/aiperf --cov-branch --cov-append --cov-report=html --cov-report=xml --cov-report=term -m 'not performance and not stress and not slow' -v --tb=short $(args) || exit_code=$$((exit_code + $$?)); \
if [[ $$exit_code -eq 0 ]]; then \
printf "$(bold)$(green)AIPerf unit and component integration tests (CI mode) passed!$(reset)\n"; \
else \
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Log File: /home/user/Code/aiperf/artifacts/granite4:350m-openai-chat-concurrency

- Scalable multiprocess architecture with 10 services communicating via ZMQ
- 3 UI modes: `dashboard` (real-time TUI), `simple` (progress bars), `none` (headless)
- Multiple benchmarking modes: concurrency, request-rate, [request-rate with max concurrency](docs/tutorials/request-rate-concurrency.md), [trace replay](docs/benchmark-modes/trace-replay.md)
- Multiple benchmarking modes: concurrency, request-rate, [request-rate with max concurrency](docs/tutorials/request-rate-concurrency.md), [trace replay](docs/benchmark-modes/trace-replay.md), [agentic workloads](docs/benchmark-modes/agentic.md)
- Extensible plugin system for endpoints, datasets, transports, and metrics
- [Public dataset support](docs/benchmark-datasets.md) including ShareGPT and custom formats

Expand Down Expand Up @@ -153,6 +153,7 @@ Log File: /home/user/Code/aiperf/artifacts/granite4:350m-openai-chat-concurrency

### Workloads and Data
- [Trace Benchmarking](docs/benchmark-modes/trace-replay.md) - Deterministic workload replay
- [Agentic Workloads](docs/benchmark-modes/agentic.md) - Benchmark multi-step agent workflows: replay recorded agent traces (Dynamo, Weka) or author them in AIPerf's Graph IR
- [Bailian Traces](docs/tutorials/bailian-trace.md) - Bailian production trace replay
- [Baseten Traces](docs/tutorials/baseten-trace.md) - Baseten Parquet production trace replay
- [BurstGPT Traces](docs/tutorials/burst-gpt-trace.md) - BurstGPT real-world bursty traffic trace replay
Expand Down
4 changes: 3 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ The Dataset Manager handles all aspects of input data management during benchmar
- Parsing and validating input data to ensure it matches the expected format
- Writing dataset to memory-mapped files, enabling workers to access data directly without message passing
- Supporting custom dataset types, such as MoonCake traces, for advanced benchmarking scenarios
- For graph IR workloads, broadcasting a graph-typed `DatasetConfiguredNotification` — `DatasetMetadata.graph` (the trace universe plus per-node prefix-cache map) and `GraphSegmentClientMetadata` (the unified segment store and structural sidecar locations) — instead of conversation entries and a conversation memory-mapped store; the store build itself is owned by `GraphStoreBuilder` (`src/aiperf/dataset/graph/store_build.py`), and the schedule plane and workers read those exact broadcast paths (nothing re-parses the workload)
- Managing the lifecycle of datasets, including initialization, iteration, and cleanup

### Timing Manager

The Timing Manager controls and coordinates the timing of requests during benchmarking runs through a credit-based system.

**Key Responsibilities:**
- Scheduling when each request should be sent based on the selected timing mode (fixed schedule, request-rate, or user-centric rate)
- Scheduling when each request should be sent based on the selected timing mode (fixed schedule, request-rate, user-centric rate, or graph IR for agentic workloads)
- Managing precise timing to accurately reproduce real-world or synthetic load patterns
- Supporting advanced timing scenarios, such as replaying traces with specific inter-arrival times or simulating bursty traffic
- Ensuring that requests are dispatched to workers at the correct intervals for reliable measurement
Expand Down Expand Up @@ -183,6 +184,7 @@ The Timing Manager uses a **credit-based flow control system** to control when r
- **Fixed schedule mode**: Replays conversation traces at precise timestamps from dataset metadata
- **Request-rate mode**: Issues credits at a specific rate with configurable arrival patterns (constant, Poisson, gamma, concurrency burst)
- **User-centric rate mode**: Each session acts as a separate user with calculated gaps between turns
- **Graph IR mode**: Runs a dataflow executor per agentic-workload trace, issuing a node's credit once its dependencies complete (selected automatically for graph workloads)

**Flow Control Benefits:**
- Prevents overwhelming the inference server
Expand Down
4 changes: 3 additions & 1 deletion docs/benchmark-datasets.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ This document describes datasets that AIPerf can use to generate stimulus. Addit

## Exgentic Agent Trace Replay

The Exgentic loaders stream recorded agent sessions directly from Hugging Face. `exgentic` is pinned to v1 revision `70036b93a04e61b0ea2706a68b962f4f26774587`; `exgentic_v2` is independently pinned to v2 revision `4b8ad4ab198438e5a170f9171c19c6a2cf7c1814`. Each replays successful, positive-token chat call snapshots. Recorded messages, system instructions, tool definitions, output-token limits, request controls, and call start times are preserved. Tools are not executed, and live responses are not added to later requests. Every request carries the source session as `x-dynamo-session-id` for Dynamo agentic tracing while AIPerf retains its own request correlation ID.
The Exgentic loaders stream recorded agent sessions directly from Hugging Face. `exgentic` is pinned to v1 revision `70036b93a04e61b0ea2706a68b962f4f26774587`; `exgentic_v2` is independently pinned to v2 revision `4b8ad4ab198438e5a170f9171c19c6a2cf7c1814`. Each replays successful, positive-token chat call snapshots. Recorded messages, system instructions, tool definitions, output-token limits, request controls, and call start times are preserved. Tools are not executed, and live responses are not added to later requests. The loaders no longer stamp a session header themselves; AIPerf keeps a stable per-conversation correlation ID, and pairing the run with `--session-routing dynamo_headers` sends `X-Dynamo-Session-ID` for Dynamo session affinity and agentic tracing.

This changes fixed-schedule affinity grouping. `--session-routing dynamo_headers` keys affinity on the live per-conversation correlation ID, and in `--fixed-schedule` replay the loader explodes one recorded agent session into many single-turn span-conversations — so each span now gets its own key and routes independently, and Dynamo prefix-affinity no longer groups a recorded session's spans onto one worker. The removed stamp instead put the shared recorded session ID on every span, grouping them (but that recorded ID also collided across dataset-recycled replays, binding unrelated live conversations to a single router entry). The two behaviors serve different fidelity goals; restoring source-session grouping without the recycling collision would need a dataset-provided affinity key (possible future work).

Provide a finite materialization bound through `--num-conversations`, `--num-dataset-entries`, or `--request-count`. `--benchmark-duration` limits request issuance, not dataset setup.

Expand Down
Loading
Loading