Skip to main content
Profile agent execution with per-agent isolation, latency percentiles, memory snapshots, and HTML/JSON reports.
The user runs the agent; the profiler captures latency, streaming, and memory for that turn.

Quick Start

1

Simple Usage

Profile a single agent turn and print P95 latency:
2

With Configuration

Isolate profilers across concurrent agents:

How It Works

Profilers live in a ContextVar — each async task or thread sees its own instance when you call set_profiler(). The legacy Profiler class delegates to get_profiler() — existing Profiler.block() calls still work.

Statistics

get_statistics() returns percentile timing data for all recorded operations.
Filter by category:

Data Retrieval

Retrieve raw records for custom analysis:
Store custom line-profile output:

Async Streaming

Profile streaming LLM responses with the async context manager:
Use record_streaming(name, ttft_ms, total_ms, chunk_count=0, total_tokens=0) to manually record a streaming operation without using the context manager.

cProfile & Memory

Wrap heavy blocks with cProfile or tracemalloc:
Use record_memory(name, current_kb, peak_kb) to store a manual memory snapshot:

Flamegraph

Export call-graph data for use with external renderers such as speedscope or flamegraph.pl:
export_flamegraph writes a placeholder SVG containing node counts (nodes=<count>). It is not a rendered flamegraph. Load the returned data from get_flamegraph_data() into an external renderer like speedscope for a visual flamechart.

Report Exporters


Which API Should I Use?


Import Forms

Singleton behaviour change (PR #2546): ProfilerCompat (exposed as Profiler) is now a singleton — repeated Profiler() calls return the same instance. Code that relied on creating separate Profiler() instances for isolation must switch to _ProfilerImpl + set_profiler() instead.

Configuration Options

Profiler SDK Reference

Auto-generated API reference for all Profiler methods

Best Practices

Separate _ProfilerImpl instances prevent mixed timings in concurrent runs. Use set_profiler() inside each agent’s async task.
Use larger max_records for high-frequency agents; smaller for quick tasks. The default 10,000 covers most workloads.
profiler.block("llm_call") and profiler.block("tool_execution") produce readable reports. Avoid generic names like "step1".
New code should call get_profiler() for context-aware isolation. The Profiler compat alias is for existing codebases only.
Call export_json() or export_html() after all agent tasks finish to capture the full session in one report.

Observability Overview

Traces, metrics, and logging

Profiling

Broader performance profiling options