Use this file to discover all available pages before exploring further.
PraisonAI includes a powerful profiling module for measuring and analyzing agent performance. Profile function execution, API calls, streaming latency, memory usage, and more.
from praisonai.profiler import Profiler, profile# Enable profilingProfiler.enable()# Profile a function@profiledef my_agent_task(): # Your agent code here pass# Profile a block of codewith Profiler.block("agent_initialization"): agent = Agent(instructions="You are helpful")# Get reportProfiler.report()
PraisonAI’s profiler uses bounded ring buffers to prevent memory leaks in long-running production workloads. Each profiling buffer (_timings, _imports, _flow, _api_calls, _streaming, _memory, _cprofile_stats) is implemented as deque(maxlen=PRAISONAI_PROFILE_MAX).Key benefits:
Memory-safe: Fixed maximum memory usage regardless of runtime
Production-ready: No unbounded growth in long-running agents
Configurable: Adjust PRAISONAI_PROFILE_MAX based on your RAM budget
Ring buffer: Only keeps the most recent N records per buffer
Enable profiling globally and configure buffer size:
export PRAISONAI_PROFILE=1export PRAISONAI_PROFILE_MAX=10000 # Set max records per buffer
Variable
Type
Default
Description
PRAISONAI_PROFILE
int
0
Enable (1) or disable (0) profiling
PRAISONAI_PROFILE_MAX
int (≥ 1)
10000
Max records kept per buffer (_timings, _imports, _flow, _api_calls, _streaming, _memory, _cprofile_stats). When full, oldest records are dropped. Invalid values fall back to 10000.
from praisonai.profiler import Profilerwith Profiler.block("data_processing"): # Code to profile process_data()with Profiler.block("model_inference", category="inference"): result = model.predict(data)
Measure Time To First Token (TTFT) and streaming performance:
from praisonai.profiler import Profiler, StreamingTracker# Using context managerwith Profiler.streaming("chat_completion") as tracker: for chunk in stream: if tracker._first_token_time is None: tracker.first_token() # Mark TTFT tracker.chunk() process(chunk)# Manual trackingtracker = StreamingTracker("my_stream")tracker.start()for i, chunk in enumerate(response_stream): if i == 0: tracker.first_token() tracker.chunk()tracker.end(total_tokens=150)# Get streaming recordsstreams = Profiler.get_streaming_records()for s in streams: print(f"TTFT: {s.ttft_ms:.2f}ms, Total: {s.total_ms:.2f}ms, Chunks: {s.chunk_count}")
from praisonai.profiler import Profiler# Profile memory for a blockwith Profiler.memory("agent_creation"): agent = Agent(instructions="...", tools=[...])# Get memory recordsmemories = Profiler.get_memory_records()for m in memories: print(f"{m.name}: current={m.current_kb:.1f}KB, peak={m.peak_kb:.1f}KB")# Take a snapshotsnapshot = Profiler.memory_snapshot()print(f"Current: {snapshot['current_kb']:.1f}KB")print(f"Peak: {snapshot['peak_kb']:.1f}KB")
from praisonai.profiler import Profiler# Get overall statisticsstats = Profiler.get_statistics()print(f"P50 (Median): {stats['p50']:.2f}ms")print(f"P95: {stats['p95']:.2f}ms")print(f"P99: {stats['p99']:.2f}ms")print(f"Mean: {stats['mean']:.2f}ms")print(f"Std Dev: {stats['std_dev']:.2f}ms")# Get statistics for specific categoryapi_stats = Profiler.get_statistics(category="api")llm_stats = Profiler.get_statistics(category="llm_call")
from praisonai.profiler import Profiler, profile_detailed# Using decorator@profile_detaileddef heavy_computation(): return sum(i * i for i in range(100000))# Using context managerwith Profiler.cprofile("agent_run") as stats: result = agent.run()# Get cProfile statscprofile_data = Profiler.get_cprofile_stats()for entry in cprofile_data: print(f"Operation: {entry['name']}") print(entry['stats'])
from praisonai.profiler import profile_lines@profile_linesdef detailed_function(): a = expensive_operation_1() # Line timing b = expensive_operation_2() # Line timing return a + b# Get line profile dataline_data = Profiler.get_line_profile_data()
Install line_profiler for full functionality: pip install line_profiler
from praisonai.profiler import profile_imports, time_import# Profile imports in a blockwith profile_imports() as profiler: import pandas import numpy from praisonaiagents import Agent# Get slowest importsslowest = profiler.get_slowest(n=5)for imp in slowest: print(f"{imp.module}: {imp.duration_ms:.2f}ms")# Quick single import timingduration = time_import("torch")print(f"torch import: {duration:.2f}ms")
When profiling is disabled, there is zero performance overhead:
from praisonai.profiler import Profiler, profileProfiler.disable() # Profiling off@profiledef fast_function(): return 1 + 1# No overhead - decorator is a no-op when disabledfor _ in range(1000000): fast_function() # Full speed
Each record uses roughly 100-200 bytes. Default 10k records ≈ 1-2 MB per buffer (7 buffers total = ~7-14 MB). For memory-constrained environments, use smaller values.
# For high-memory production serversexport PRAISONAI_PROFILE_MAX=100000# For memory-constrained containers export PRAISONAI_PROFILE_MAX=1000# For development (default)export PRAISONAI_PROFILE_MAX=10000
Disable profiling by default in production
Only enable profiling when debugging performance issues. Production agents should avoid the overhead.
import osfrom praisonai.profiler import Profiler# Only enable in debug/staging environmentsif os.environ.get("DEBUG") == "true" or os.environ.get("ENVIRONMENT") == "staging": Profiler.enable()
Export periodically for long-running processes
Ring buffers only retain recent records. Export data periodically to avoid losing historical performance data.
import timeimport threadingfrom praisonai.profiler import Profilerdef periodic_export(): """Export profiling data every hour.""" while True: time.sleep(3600) # 1 hour if Profiler.is_enabled(): filename = f"profile_{int(time.time())}.json" Profiler.export_to_file(filename, format="json") print(f"Exported profiling data to {filename}")# Start background export threadexport_thread = threading.Thread(target=periodic_export, daemon=True)export_thread.start()
Monitor buffer overflow in production
Check if you’re hitting buffer limits and adjust PRAISONAI_PROFILE_MAX accordingly.
from praisonai.profiler import Profiler# Check buffer usagetiming_records = len(Profiler.get_timings())api_records = len(Profiler.get_api_calls())max_buffer = os.environ.get("PRAISONAI_PROFILE_MAX", "10000")print(f"Buffer usage: {timing_records}/{max_buffer} timing records")print(f"API calls: {api_records}/{max_buffer} records")# If close to max, consider increasing buffer size or exporting more frequentlyif timing_records > int(max_buffer) * 0.8: print("Warning: Timing buffer nearly full, consider increasing PRAISONAI_PROFILE_MAX")