-
Notifications
You must be signed in to change notification settings - Fork 4
OTel Trace Replay
Opal can replay real agentic traffic captured as OpenTelemetry gen_ai traces. Each session's turns are re-submitted to the simulated cluster at their recorded wall-clock timing, so you can characterize KV-cache behaviour, latency, and throughput against production-shaped workloads.
OTel replay is its own workload type (otel), separate from the legacy trace workload. Pick the right one:
| Your trace file looks like… | Use type |
|---|---|
Flat JSONL rows: {"timestamp", "input_length", "output_length", "hash_ids"}
|
trace (see Running Workloads) |
OTel sessions/spans: {"trace_id", "spans": [...]} or pre-tokenized {"trace_id", "turns": [...]}
|
otel (this page) |
They can share the
.jsonlextension, so the distinction is the content, not the filename. Setting the wrongtypeproduces confusing errors (e.g.AssertionError: 0 != Nonewhen an OTel file is fed to the legacytraceworkload).
The pieces of OTel replay live in a few well-known places. A checkout that can run replay looks like:
opal-sim/
├── configs/
│ └── defaults_otel.json # example config with an `otel` workload stage
├── traces/
│ └── synthetic_otel_traces.jsonl # example OTel trace to replay
├── tools/agentic/
│ ├── synthetic-trace-generator.py # generates example OTel traces
│ └── tokenizer.py # pre-tokenizes a raw trace (see below)
trace_file paths in a config are resolved relative to the repo root, which is why the examples read traces/….
The repo ships a working example so you don't have to assemble one:
-
Config:
configs/defaults_otel.json— includes anotelstage pointed at the synthetic trace below. -
Trace:
traces/synthetic_otel_traces.jsonl— a synthetic multi-session OTel trace (produced bytools/agentic/synthetic-trace-generator.py).
Run it directly:
python opal/main.py -c configs/defaults_otel.jsonUse defaults_otel.json as the starting point for your own runs — copy it, then swap trace_file/tokenizer for your trace. The parameters below explain each field it sets.
Add a stage with "type": "otel" under workload.stages. The two required params are trace_file and (for raw traces) tokenizer:
"workload": {
"stages": [
{
"type": "otel",
"workload_params": {
"trace_file": "traces/exagentic_v2_swebench.jsonl",
"tokenizer": "meta-llama/Llama-3.1-8B-Instruct",
"pretokenized": false,
"total_requests": 10,
"multiplier_to_sec": 1,
"inter_turn_multiplier": 1,
"max_concurrent_sessions": 2
}
}
]
}| Change | From (trace) |
To (otel) |
|---|---|---|
| Stage type | "type": "trace" |
"type": "otel" |
| Tokenizer | not used |
"tokenizer" required unless pretokenized: true
|
| Pre-tokenized flag | n/a |
"pretokenized" selects the raw vs. tokenized loader |
chunk_size |
used for hash expansion | remove it — OTel replay carries real token ids, no expansion |
| Session concurrency | n/a |
"max_concurrent_sessions" controls parallelism |
| Parameter | Required | Default | Description |
|---|---|---|---|
trace_file |
yes | — | Path to the OTel .json/.jsonl trace. |
tokenizer |
yes, if not pre-tokenized | — | HuggingFace tokenizer name (e.g. meta-llama/Llama-3.1-8B-Instruct). Raw traces are tokenized on the fly, once per turn per run. |
pretokenized |
no | false |
true for traces already tokenized by tools/agentic/tokenizer.py (they carry token ids; no tokenizer is loaded and tokenizer is ignored). |
total_requests |
no |
-1 (all) |
Multi-session: cap on the number of sessions started. Single-session: cap on the number of spans replayed. |
multiplier_to_sec |
no | 1 |
Scales trace timestamps → simulation seconds (also scales session arrival spacing). e.g. 0.001 if timestamps are in ms; <1 to fast-forward. |
inter_turn_multiplier |
no | 1 |
Multi-session only. Scales only the gap between a turn finishing and the next turn's wall start. 0 replays a session's turns back-to-back. |
max_concurrent_sessions |
no | 10 |
Multi-session only. Max sessions replaying at once (FIFO semaphore). -1 starts all sessions concurrently. |
Single- vs. multi-session mode is detected automatically from the trace. In multi-session mode, turns within a session are replayed strictly in order — each turn is submitted only after the previous one completes — while up to max_concurrent_sessions sessions run in parallel.
Raw traces are re-tokenized every run, which dominates startup for large traces. Tokenize once instead:
python tools/agentic/tokenizer.py traces/exagentic_v2_swebench.jsonl \
--tokenizer meta-llama/Llama-3.1-8B-InstructThis writes traces/exagentic_v2_swebench-tokenized.jsonl. Point trace_file at it and set pretokenized: true (the tokenizer param is then unused). Same simulation, far faster startup:
{
"type": "otel",
"workload_params": {
"trace_file": "traces/exagentic_v2_swebench-tokenized.jsonl",
"pretokenized": true,
"max_concurrent_sessions": 2
}
}Point main.py at a config whose workload stage is the otel stage above. The shipped configs/defaults_otel.json already is one:
python opal/main.py -c configs/defaults_otel.json-
opal/workloads/otel_tokenizer.pymust be present (provides theOtelTokenizerused to load and tokenize OTel traces). - The
transformersandjinja2packages must be installed (they are listed inpyproject.toml;uv pip install -e .pulls them in).