Quick Start
How It Works
Profilers live in aContextVar — each async task or thread sees its own instance when you call set_profiler().
| API | Purpose |
|---|---|
_ProfilerImpl(max_records=...) | Bounded buffer per profiler instance |
set_profiler(profiler) | Install profiler in current context |
get_profiler() | Read context profiler (creates default if unset) |
profiler.block("name") | Time a code block |
get_statistics() | P50, P95, P99 latency summaries |
Profiler class delegates to get_profiler() — existing Profiler.block() calls still work.
Statistics
get_statistics() returns percentile timing data for all recorded operations.
| Key | Description |
|---|---|
p50 | Median latency (ms) |
p95 | 95th percentile latency (ms) |
p99 | 99th percentile latency (ms) |
mean | Average latency (ms) |
std_dev | Standard deviation (ms) |
min | Minimum latency (ms) |
max | Maximum latency (ms) |
Data Retrieval
Retrieve raw records for custom analysis: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 withcProfile or tracemalloc:
- cProfile
- Memory Tracking
- Memory Snapshot
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:Report Exporters
- JSON
- HTML
- File Export
| Method | Returns | Description |
|---|---|---|
export_json() | str | JSON summary of all profiling data |
export_html() | str | Minimal HTML report — title, total time, slowest-op table |
export_to_file(filepath, format="json") | None | Write JSON or HTML to disk |
export_flamegraph(filepath) | None | Write placeholder SVG for external renderer |
Which API Should I Use?
| Goal | Method |
|---|---|
| Latency p50 / p95 / p99 | get_statistics() |
| Human-readable console report | report() / get_summary() |
| Machine-readable data | export_json() |
| Web dashboard | export_html() / export_to_file(path, "html") |
| CPU call-graph analysis | cprofile("name") context manager |
| Streaming TTFT data | streaming_async("name") or streaming("name") |
| Memory footprint | memory("name") or memory_snapshot() |
Import Forms
- Recommended (per-agent isolation)
- Compat (legacy flat call surface)
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
| Parameter | Type | Default | Description |
|---|---|---|---|
max_records | int | 10000 | Buffer size before rotation |
PRAISONAI_PROFILE_MAX | env | 10000 | Global default buffer cap |
PRAISONAI_PROFILE | env | "" | Set to 1 / true / yes to auto-enable profiling |
Profiler SDK Reference
Auto-generated API reference for all Profiler methods
Best Practices
Create one profiler per agent
Create one profiler per agent
Separate
_ProfilerImpl instances prevent mixed timings in concurrent runs. Use set_profiler() inside each agent’s async task.Size buffers for workload
Size buffers for workload
Use larger
max_records for high-frequency agents; smaller for quick tasks. The default 10,000 covers most workloads.Use descriptive block names
Use descriptive block names
profiler.block("llm_call") and profiler.block("tool_execution") produce readable reports. Avoid generic names like "step1".Prefer get_profiler over global Profiler
Prefer get_profiler over global Profiler
New code should call
get_profiler() for context-aware isolation. The Profiler compat alias is for existing codebases only.Export after the run completes
Export after the run completes
Call
export_json() or export_html() after all agent tasks finish to capture the full session in one report.Related
Observability Overview
Traces, metrics, and logging
Profiling
Broader performance profiling options

