mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-08 08:11:07 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62ff435e4e | ||
|
|
836411f4c8 | ||
|
|
970f5b3f32 |
No files matched your search
@@ -0,0 +1,303 @@
|
||||
# PraisonAI Performance Monitoring Examples
|
||||
|
||||
This directory contains comprehensive examples demonstrating various performance monitoring approaches for PraisonAI Agents without modifying existing code.
|
||||
|
||||
## 📁 Directory Structure
|
||||
|
||||
```
|
||||
monitoring/
|
||||
├── basic/ # Simple monitoring examples
|
||||
│ ├── simple_agent_monitoring.py
|
||||
│ └── task_timing_metrics.py
|
||||
├── advanced/ # Complex monitoring implementations
|
||||
│ └── comprehensive_session_monitoring.py
|
||||
├── integration/ # Integration with external systems
|
||||
│ └── monitoring_integrations.py
|
||||
├── telemetry/ # Telemetry-specific examples
|
||||
│ └── telemetry_integration.py
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
All examples are self-contained and can be run independently:
|
||||
|
||||
```bash
|
||||
# Basic agent monitoring
|
||||
python examples/monitoring/basic/simple_agent_monitoring.py
|
||||
|
||||
# Task timing and metrics
|
||||
python examples/monitoring/basic/task_timing_metrics.py
|
||||
|
||||
# Advanced session monitoring
|
||||
python examples/monitoring/advanced/comprehensive_session_monitoring.py
|
||||
|
||||
# Telemetry integration
|
||||
python examples/monitoring/telemetry/telemetry_integration.py
|
||||
|
||||
# External integrations
|
||||
python examples/monitoring/integration/monitoring_integrations.py
|
||||
```
|
||||
|
||||
## 📊 Monitoring Options Overview
|
||||
|
||||
### 1. Basic Monitoring (`basic/`)
|
||||
|
||||
**Simple Agent Monitoring** (`simple_agent_monitoring.py`)
|
||||
- ✅ Enable metrics with `track_metrics=True`
|
||||
- ✅ Access metrics via `agent.last_metrics`
|
||||
- ✅ Session-level aggregation with `MetricsCollector`
|
||||
- ✅ Basic performance reporting
|
||||
|
||||
**Task Timing & Metrics** (`task_timing_metrics.py`)
|
||||
- ✅ Manual performance metrics creation
|
||||
- ✅ Context managers for automatic timing
|
||||
- ✅ Token metrics aggregation
|
||||
- ✅ Custom timing measurements
|
||||
- ✅ Metrics export to files
|
||||
|
||||
### 2. Advanced Monitoring (`advanced/`)
|
||||
|
||||
**Comprehensive Session Monitoring** (`comprehensive_session_monitoring.py`)
|
||||
- ✅ Multi-agent session tracking
|
||||
- ✅ Real-time performance monitoring
|
||||
- ✅ Automated performance alerts
|
||||
- ✅ Live monitoring dashboard
|
||||
- ✅ Agent performance rankings
|
||||
- ✅ Comprehensive analytics and reporting
|
||||
- ✅ Export capabilities (JSON + summary)
|
||||
|
||||
### 3. Telemetry Integration (`telemetry/`)
|
||||
|
||||
**Telemetry Integration** (`telemetry_integration.py`)
|
||||
- ✅ Automatic telemetry tracking
|
||||
- ✅ PostHog integration for analytics
|
||||
- ✅ Custom event tracking
|
||||
- ✅ Environment-based configuration
|
||||
- ✅ Debug logging for development
|
||||
- ✅ Session-level data aggregation
|
||||
|
||||
### 4. External Integrations (`integration/`)
|
||||
|
||||
**Monitoring Integrations** (`monitoring_integrations.py`)
|
||||
- ✅ SQLite database logging
|
||||
- ✅ Webhook notifications with cooldowns
|
||||
- ✅ Real-time HTTP dashboard
|
||||
- ✅ Configurable performance alerts
|
||||
- ✅ API endpoints for metrics
|
||||
- ✅ Multi-system integration
|
||||
|
||||
## 🔧 Configuration Options
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```bash
|
||||
# Disable telemetry completely
|
||||
export PRAISONAI_TELEMETRY_DISABLED=true
|
||||
|
||||
# Enable PostHog integration
|
||||
export POSTHOG_API_KEY=your_posthog_key
|
||||
|
||||
# Enable debug logging
|
||||
export LOGLEVEL=DEBUG
|
||||
```
|
||||
|
||||
### Agent Configuration
|
||||
|
||||
```python
|
||||
# Basic monitoring
|
||||
agent = Agent(
|
||||
name="MyAgent",
|
||||
role="Data Analyst",
|
||||
track_metrics=True # Enable monitoring
|
||||
)
|
||||
|
||||
# Custom metrics collector
|
||||
collector = MetricsCollector()
|
||||
agent = Agent(
|
||||
name="MyAgent",
|
||||
role="Data Analyst",
|
||||
track_metrics=True,
|
||||
metrics_collector=collector # Use shared collector
|
||||
)
|
||||
```
|
||||
|
||||
### Performance Thresholds
|
||||
|
||||
```python
|
||||
# Configure custom performance alerts
|
||||
performance_thresholds = {
|
||||
'max_ttft': 2.0, # Maximum Time To First Token (seconds)
|
||||
'min_tokens_per_sec': 10.0, # Minimum tokens per second
|
||||
'max_total_time': 30.0 # Maximum total execution time (seconds)
|
||||
}
|
||||
```
|
||||
|
||||
## 📈 Metrics Available
|
||||
|
||||
### Token Metrics
|
||||
- **Input Tokens**: Tokens in the prompt/input
|
||||
- **Output Tokens**: Tokens in the generated response
|
||||
- **Total Tokens**: Combined input + output tokens
|
||||
- **Cached Tokens**: Tokens retrieved from cache
|
||||
- **Reasoning Tokens**: Tokens used for internal reasoning
|
||||
- **Audio Tokens**: Tokens for audio processing (if applicable)
|
||||
|
||||
### Performance Metrics
|
||||
- **Time To First Token (TTFT)**: Time until first token is generated
|
||||
- **Total Time**: Complete execution time
|
||||
- **Tokens Per Second (TPS)**: Generation speed
|
||||
- **Request Count**: Number of requests processed
|
||||
|
||||
### Session Metrics
|
||||
- **Session ID**: Unique session identifier
|
||||
- **Duration**: Total session duration
|
||||
- **Agent Metrics**: Per-agent token and performance statistics
|
||||
- **Model Metrics**: Per-model usage statistics
|
||||
- **Performance Rankings**: Agent performance comparisons
|
||||
|
||||
## 🎯 Use Cases & Scenarios
|
||||
|
||||
### 1. Development & Debugging
|
||||
- **Use**: `basic/simple_agent_monitoring.py`
|
||||
- **Benefits**: Quick performance insights, debug slow responses
|
||||
- **Setup**: Just add `track_metrics=True` to your agents
|
||||
|
||||
### 2. Production Monitoring
|
||||
- **Use**: `advanced/comprehensive_session_monitoring.py`
|
||||
- **Benefits**: Real-time alerts, performance tracking, trend analysis
|
||||
- **Setup**: Implement monitoring session wrapper
|
||||
|
||||
### 3. Analytics & Insights
|
||||
- **Use**: `telemetry/telemetry_integration.py`
|
||||
- **Benefits**: Long-term trends, usage patterns, optimization insights
|
||||
- **Setup**: Configure PostHog integration
|
||||
|
||||
### 4. Enterprise Integration
|
||||
- **Use**: `integration/monitoring_integrations.py`
|
||||
- **Benefits**: Database logging, webhook alerts, custom dashboards
|
||||
- **Setup**: Implement database and webhook handlers
|
||||
|
||||
## 🏆 Best Practices
|
||||
|
||||
### 1. **Choose the Right Level**
|
||||
```python
|
||||
# Development: Basic monitoring
|
||||
agent = Agent(name="DevAgent", track_metrics=True)
|
||||
|
||||
# Production: Session-level monitoring
|
||||
collector = MetricsCollector()
|
||||
agents = [
|
||||
Agent(name="Agent1", track_metrics=True, metrics_collector=collector),
|
||||
Agent(name="Agent2", track_metrics=True, metrics_collector=collector)
|
||||
]
|
||||
|
||||
# Enterprise: Full integration
|
||||
monitoring_system = IntegratedMonitoringSystem("Production")
|
||||
```
|
||||
|
||||
### 2. **Performance Thresholds**
|
||||
```python
|
||||
# Set realistic thresholds based on your use case
|
||||
thresholds = {
|
||||
'max_ttft': 1.0, # Interactive: < 1s
|
||||
'max_ttft': 3.0, # Batch processing: < 3s
|
||||
'min_tokens_per_sec': 20.0, # High-performance: > 20 TPS
|
||||
'min_tokens_per_sec': 5.0, # Standard: > 5 TPS
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Export & Analysis**
|
||||
```python
|
||||
# Regular exports for analysis
|
||||
collector.export_metrics(f"metrics_{datetime.now().strftime('%Y%m%d')}.json")
|
||||
|
||||
# Database logging for long-term storage
|
||||
db_logger = DatabaseLogger()
|
||||
db_logger.log_agent_metrics(session_id, agent_name, task, tokens, perf)
|
||||
```
|
||||
|
||||
### 4. **Alert Management**
|
||||
```python
|
||||
# Configure cooldowns to prevent alert spam
|
||||
alert_config = AlertConfig(
|
||||
name="high_latency",
|
||||
condition=lambda data: data['ttft'] > 2.0,
|
||||
cooldown_seconds=300 # 5-minute cooldown
|
||||
)
|
||||
```
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **No metrics collected**
|
||||
- ✅ Ensure `track_metrics=True` on agents
|
||||
- ✅ Check if telemetry is disabled: `PRAISONAI_TELEMETRY_DISABLED`
|
||||
|
||||
2. **PostHog integration not working**
|
||||
- ✅ Set `POSTHOG_API_KEY` environment variable
|
||||
- ✅ Check network connectivity
|
||||
- ✅ Enable debug logging: `LOGLEVEL=DEBUG`
|
||||
|
||||
3. **Database logging fails**
|
||||
- ✅ Check write permissions for database file
|
||||
- ✅ Ensure SQLite is available
|
||||
- ✅ Verify database schema initialization
|
||||
|
||||
4. **Dashboard not accessible**
|
||||
- ✅ Check if port 8080 is available
|
||||
- ✅ Verify HTTP server started successfully
|
||||
- ✅ Check firewall settings
|
||||
|
||||
### Debug Logging
|
||||
|
||||
```bash
|
||||
# Enable verbose logging
|
||||
export LOGLEVEL=DEBUG
|
||||
python your_monitoring_script.py
|
||||
|
||||
# Look for telemetry debug messages:
|
||||
# "Token usage tracked: 150 total tokens"
|
||||
# "Performance tracked: TTFT=0.250s, TPS=45.2"
|
||||
```
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
### Code Examples
|
||||
- All examples include detailed comments and documentation
|
||||
- Each example can be run independently
|
||||
- Progressive complexity from basic to advanced
|
||||
|
||||
### Integration Guides
|
||||
- **Database Integration**: SQLite schema and queries
|
||||
- **Webhook Integration**: Payload formats and error handling
|
||||
- **Dashboard Integration**: HTTP server and API endpoints
|
||||
- **Alert Configuration**: Threshold setting and cooldown management
|
||||
|
||||
### Performance Optimization
|
||||
- Monitor token usage to optimize prompts
|
||||
- Track TTFT to identify bottlenecks
|
||||
- Use TPS metrics to compare model performance
|
||||
- Analyze cache hit ratios for efficiency gains
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Found an issue or want to add more monitoring examples?
|
||||
|
||||
1. **Report Issues**: Create GitHub issues for bugs or feature requests
|
||||
2. **Add Examples**: Contribute new monitoring scenarios
|
||||
3. **Improve Documentation**: Help make examples clearer
|
||||
4. **Share Use Cases**: Document your monitoring implementations
|
||||
|
||||
---
|
||||
|
||||
## 💡 Key Takeaways
|
||||
|
||||
1. **Start Simple**: Begin with `track_metrics=True` for basic monitoring
|
||||
2. **Scale Gradually**: Add session-level monitoring as needed
|
||||
3. **Integrate Wisely**: Connect to your existing monitoring infrastructure
|
||||
4. **Monitor Continuously**: Set up alerts and regular reporting
|
||||
5. **Optimize Based on Data**: Use metrics to improve performance
|
||||
|
||||
Happy monitoring! 🚀📊
|
||||
@@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive Session Monitoring Example
|
||||
|
||||
This example demonstrates advanced monitoring capabilities including:
|
||||
- Multi-agent session monitoring
|
||||
- Real-time performance tracking
|
||||
- Detailed analytics and reporting
|
||||
- Export capabilities
|
||||
- Custom monitoring hooks
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Any, Optional
|
||||
|
||||
# Add the praisonai-agents module to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.telemetry.metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
|
||||
class AdvancedMonitoringSession:
|
||||
"""Advanced session monitoring with real-time analytics."""
|
||||
|
||||
def __init__(self, session_name: str):
|
||||
self.session_name = session_name
|
||||
self.session_start = datetime.now()
|
||||
self.metrics_collector = MetricsCollector()
|
||||
self.agents: Dict[str, Agent] = {}
|
||||
self.task_history: List[Dict[str, Any]] = []
|
||||
self.performance_thresholds = {
|
||||
'max_ttft': 2.0, # seconds
|
||||
'min_tokens_per_sec': 10.0,
|
||||
'max_total_time': 30.0
|
||||
}
|
||||
self.alerts: List[Dict[str, Any]] = []
|
||||
|
||||
print(f"🚀 Advanced Monitoring Session: {session_name}")
|
||||
print(f"📅 Started at: {self.session_start.isoformat()}")
|
||||
print("-" * 60)
|
||||
|
||||
def create_agent(self, name: str, role: str, goal: str, backstory: str) -> Agent:
|
||||
"""Create a monitored agent."""
|
||||
agent = Agent(
|
||||
name=name,
|
||||
role=role,
|
||||
goal=goal,
|
||||
backstory=backstory,
|
||||
track_metrics=True,
|
||||
metrics_collector=self.metrics_collector
|
||||
)
|
||||
|
||||
self.agents[name] = agent
|
||||
print(f"👤 Agent created: {name} ({role})")
|
||||
return agent
|
||||
|
||||
def execute_monitored_task(self, agent_name: str, task: str,
|
||||
expected_tokens: int = 100) -> Dict[str, Any]:
|
||||
"""Execute a task with comprehensive monitoring."""
|
||||
|
||||
if agent_name not in self.agents:
|
||||
raise ValueError(f"Agent {agent_name} not found")
|
||||
|
||||
agent = self.agents[agent_name]
|
||||
task_start = time.time()
|
||||
|
||||
print(f"\n🎯 Executing Task: {task[:50]}...")
|
||||
print(f"👤 Agent: {agent_name}")
|
||||
|
||||
# Create performance metrics for this task
|
||||
perf_metrics = PerformanceMetrics()
|
||||
perf_metrics.start_timing()
|
||||
|
||||
# Simulate task execution (in real usage, call agent.chat(task))
|
||||
execution_time = 0.5 + (expected_tokens / 100) * 0.3 # Simulate based on expected tokens
|
||||
time.sleep(execution_time)
|
||||
|
||||
# Simulate first token timing
|
||||
time.sleep(0.1)
|
||||
perf_metrics.mark_first_token()
|
||||
|
||||
# Complete timing
|
||||
perf_metrics.end_timing(expected_tokens)
|
||||
|
||||
# Create simulated token metrics
|
||||
token_metrics = TokenMetrics(
|
||||
input_tokens=int(expected_tokens * 0.7),
|
||||
output_tokens=int(expected_tokens * 0.3),
|
||||
total_tokens=expected_tokens,
|
||||
cached_tokens=int(expected_tokens * 0.1),
|
||||
reasoning_tokens=int(expected_tokens * 0.05)
|
||||
)
|
||||
|
||||
# Add to metrics collector
|
||||
self.metrics_collector.add_agent_metrics(
|
||||
agent_name=agent_name,
|
||||
token_metrics=token_metrics,
|
||||
performance_metrics=perf_metrics,
|
||||
model_name="gpt-4o"
|
||||
)
|
||||
|
||||
# Create task record
|
||||
task_record = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'agent': agent_name,
|
||||
'task': task,
|
||||
'execution_time': time.time() - task_start,
|
||||
'tokens': {
|
||||
'input': token_metrics.input_tokens,
|
||||
'output': token_metrics.output_tokens,
|
||||
'total': token_metrics.total_tokens,
|
||||
'cached': token_metrics.cached_tokens,
|
||||
'reasoning': token_metrics.reasoning_tokens
|
||||
},
|
||||
'performance': {
|
||||
'ttft': perf_metrics.time_to_first_token,
|
||||
'total_time': perf_metrics.total_time,
|
||||
'tokens_per_second': perf_metrics.tokens_per_second
|
||||
}
|
||||
}
|
||||
|
||||
self.task_history.append(task_record)
|
||||
|
||||
# Check performance thresholds
|
||||
self._check_performance_alerts(agent_name, perf_metrics, token_metrics)
|
||||
|
||||
# Display real-time metrics
|
||||
self._display_task_metrics(task_record)
|
||||
|
||||
return task_record
|
||||
|
||||
def _check_performance_alerts(self, agent_name: str, perf: PerformanceMetrics,
|
||||
tokens: TokenMetrics):
|
||||
"""Check for performance threshold violations."""
|
||||
alerts = []
|
||||
|
||||
if perf.time_to_first_token > self.performance_thresholds['max_ttft']:
|
||||
alerts.append(f"High TTFT: {perf.time_to_first_token:.3f}s")
|
||||
|
||||
if perf.tokens_per_second < self.performance_thresholds['min_tokens_per_sec']:
|
||||
alerts.append(f"Low TPS: {perf.tokens_per_second:.1f}")
|
||||
|
||||
if perf.total_time > self.performance_thresholds['max_total_time']:
|
||||
alerts.append(f"Long execution: {perf.total_time:.3f}s")
|
||||
|
||||
if alerts:
|
||||
alert_record = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'agent': agent_name,
|
||||
'alerts': alerts,
|
||||
'metrics': {
|
||||
'ttft': perf.time_to_first_token,
|
||||
'tps': perf.tokens_per_second,
|
||||
'total_time': perf.total_time
|
||||
}
|
||||
}
|
||||
self.alerts.append(alert_record)
|
||||
|
||||
print(f"⚠️ Performance Alert for {agent_name}:")
|
||||
for alert in alerts:
|
||||
print(f" • {alert}")
|
||||
|
||||
def _display_task_metrics(self, task_record: Dict[str, Any]):
|
||||
"""Display real-time task metrics."""
|
||||
tokens = task_record['tokens']
|
||||
perf = task_record['performance']
|
||||
|
||||
print(f"📊 Task Metrics:")
|
||||
print(f" Tokens: {tokens['total']} (↑{tokens['input']} ↓{tokens['output']})")
|
||||
print(f" Performance: TTFT={perf['ttft']:.3f}s, TPS={perf['tokens_per_second']:.1f}")
|
||||
print(f" Efficiency: {tokens['cached']} cached, {tokens['reasoning']} reasoning")
|
||||
|
||||
def get_session_analytics(self) -> Dict[str, Any]:
|
||||
"""Generate comprehensive session analytics."""
|
||||
|
||||
session_metrics = self.metrics_collector.get_session_metrics()
|
||||
current_time = datetime.now()
|
||||
session_duration = current_time - self.session_start
|
||||
|
||||
# Calculate additional analytics
|
||||
total_tasks = len(self.task_history)
|
||||
tasks_per_minute = total_tasks / (session_duration.total_seconds() / 60) if session_duration.total_seconds() > 0 else 0
|
||||
|
||||
# Agent performance rankings
|
||||
agent_rankings = []
|
||||
if session_metrics['performance']:
|
||||
for agent_name, perf in session_metrics['performance'].items():
|
||||
ranking_score = (
|
||||
(1 / max(perf['average_ttft'], 0.001)) * 0.3 +
|
||||
perf['average_tokens_per_second'] * 0.4 +
|
||||
(1 / max(perf['average_total_time'], 0.001)) * 0.3
|
||||
)
|
||||
agent_rankings.append({
|
||||
'agent': agent_name,
|
||||
'score': ranking_score,
|
||||
'requests': perf['request_count'],
|
||||
'avg_tps': perf['average_tokens_per_second']
|
||||
})
|
||||
|
||||
agent_rankings.sort(key=lambda x: x['score'], reverse=True)
|
||||
|
||||
# Token usage analysis
|
||||
token_analysis = {
|
||||
'efficiency_ratio': 0,
|
||||
'cache_hit_ratio': 0,
|
||||
'reasoning_ratio': 0
|
||||
}
|
||||
|
||||
if session_metrics['total_tokens']['total_tokens'] > 0:
|
||||
total = session_metrics['total_tokens']['total_tokens']
|
||||
token_analysis['efficiency_ratio'] = session_metrics['total_tokens']['output_tokens'] / session_metrics['total_tokens']['input_tokens'] if session_metrics['total_tokens']['input_tokens'] > 0 else 0
|
||||
token_analysis['cache_hit_ratio'] = session_metrics['total_tokens']['cached_tokens'] / total
|
||||
token_analysis['reasoning_ratio'] = session_metrics['total_tokens']['reasoning_tokens'] / total
|
||||
|
||||
return {
|
||||
'session_info': {
|
||||
'name': self.session_name,
|
||||
'start_time': self.session_start.isoformat(),
|
||||
'duration_seconds': session_duration.total_seconds(),
|
||||
'duration_formatted': str(session_duration).split('.')[0]
|
||||
},
|
||||
'task_summary': {
|
||||
'total_tasks': total_tasks,
|
||||
'tasks_per_minute': tasks_per_minute,
|
||||
'unique_agents': len(self.agents),
|
||||
'alerts_count': len(self.alerts)
|
||||
},
|
||||
'token_analysis': token_analysis,
|
||||
'agent_rankings': agent_rankings,
|
||||
'alerts': self.alerts,
|
||||
'raw_metrics': session_metrics
|
||||
}
|
||||
|
||||
def export_comprehensive_report(self, output_dir: str = "/tmp"):
|
||||
"""Export comprehensive monitoring report."""
|
||||
|
||||
analytics = self.get_session_analytics()
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
# Export detailed JSON report
|
||||
json_file = os.path.join(output_dir, f"monitoring_report_{timestamp}.json")
|
||||
with open(json_file, 'w') as f:
|
||||
json.dump(analytics, f, indent=2, default=str)
|
||||
|
||||
# Export human-readable summary
|
||||
summary_file = os.path.join(output_dir, f"monitoring_summary_{timestamp}.txt")
|
||||
with open(summary_file, 'w') as f:
|
||||
f.write(f"Session Monitoring Report: {self.session_name}\n")
|
||||
f.write("=" * 60 + "\n\n")
|
||||
|
||||
# Session info
|
||||
info = analytics['session_info']
|
||||
f.write(f"Session Duration: {info['duration_formatted']}\n")
|
||||
f.write(f"Started: {info['start_time']}\n\n")
|
||||
|
||||
# Task summary
|
||||
summary = analytics['task_summary']
|
||||
f.write(f"Tasks Executed: {summary['total_tasks']}\n")
|
||||
f.write(f"Tasks per Minute: {summary['tasks_per_minute']:.1f}\n")
|
||||
f.write(f"Agents Used: {summary['unique_agents']}\n")
|
||||
f.write(f"Performance Alerts: {summary['alerts_count']}\n\n")
|
||||
|
||||
# Token analysis
|
||||
tokens = analytics['token_analysis']
|
||||
f.write("Token Analysis:\n")
|
||||
f.write(f" Efficiency Ratio: {tokens['efficiency_ratio']:.2f}\n")
|
||||
f.write(f" Cache Hit Ratio: {tokens['cache_hit_ratio']:.2f}\n")
|
||||
f.write(f" Reasoning Ratio: {tokens['reasoning_ratio']:.2f}\n\n")
|
||||
|
||||
# Agent rankings
|
||||
f.write("Agent Performance Rankings:\n")
|
||||
for i, agent in enumerate(analytics['agent_rankings'], 1):
|
||||
f.write(f" {i}. {agent['agent']} (Score: {agent['score']:.1f}, TPS: {agent['avg_tps']:.1f})\n")
|
||||
|
||||
return json_file, summary_file
|
||||
|
||||
def display_live_dashboard(self):
|
||||
"""Display a live monitoring dashboard."""
|
||||
|
||||
analytics = self.get_session_analytics()
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print(f"📈 LIVE MONITORING DASHBOARD - {self.session_name}")
|
||||
print("=" * 80)
|
||||
|
||||
# Session overview
|
||||
info = analytics['session_info']
|
||||
summary = analytics['task_summary']
|
||||
|
||||
print(f"⏰ Session Duration: {info['duration_formatted']}")
|
||||
print(f"📋 Tasks: {summary['total_tasks']} ({summary['tasks_per_minute']:.1f}/min)")
|
||||
print(f"👥 Agents: {summary['unique_agents']}")
|
||||
print(f"⚠️ Alerts: {summary['alerts_count']}")
|
||||
|
||||
# Token metrics
|
||||
raw = analytics['raw_metrics']
|
||||
print(f"\n💰 Token Usage:")
|
||||
print(f" Total: {raw['total_tokens']['total_tokens']:,}")
|
||||
print(f" Input/Output: {raw['total_tokens']['input_tokens']:,}/{raw['total_tokens']['output_tokens']:,}")
|
||||
print(f" Cached: {raw['total_tokens']['cached_tokens']:,}")
|
||||
print(f" Reasoning: {raw['total_tokens']['reasoning_tokens']:,}")
|
||||
|
||||
# Performance by agent
|
||||
if raw['performance']:
|
||||
print(f"\n🚀 Performance by Agent:")
|
||||
for agent_name, perf in raw['performance'].items():
|
||||
print(f" {agent_name}:")
|
||||
print(f" TTFT: {perf['average_ttft']:.3f}s")
|
||||
print(f" Speed: {perf['average_tokens_per_second']:.1f} tokens/sec")
|
||||
print(f" Requests: {perf['request_count']}")
|
||||
|
||||
# Recent alerts
|
||||
if analytics['alerts']:
|
||||
print(f"\n⚠️ Recent Alerts:")
|
||||
for alert in analytics['alerts'][-3:]: # Show last 3
|
||||
print(f" {alert['agent']}: {', '.join(alert['alerts'])}")
|
||||
|
||||
print("=" * 80)
|
||||
|
||||
def main():
|
||||
"""Demonstrate comprehensive session monitoring."""
|
||||
|
||||
# Create monitoring session
|
||||
session = AdvancedMonitoringSession("Multi-Agent Analytics Session")
|
||||
|
||||
# Create multiple agents with different roles
|
||||
data_analyst = session.create_agent(
|
||||
"DataAnalyst",
|
||||
"Senior Data Analyst",
|
||||
"Analyze data and provide insights",
|
||||
"Expert in statistical analysis and data visualization"
|
||||
)
|
||||
|
||||
content_writer = session.create_agent(
|
||||
"ContentWriter",
|
||||
"Content Strategist",
|
||||
"Create engaging content",
|
||||
"Experienced writer specializing in technical content"
|
||||
)
|
||||
|
||||
researcher = session.create_agent(
|
||||
"Researcher",
|
||||
"Research Specialist",
|
||||
"Conduct thorough research",
|
||||
"PhD-level researcher with expertise in multiple domains"
|
||||
)
|
||||
|
||||
# Define complex workflow with varying token requirements
|
||||
workflow_tasks = [
|
||||
("DataAnalyst", "Analyze Q4 sales performance with statistical significance", 150),
|
||||
("ContentWriter", "Write an executive summary of the sales analysis", 200),
|
||||
("Researcher", "Research market trends affecting our sales performance", 180),
|
||||
("DataAnalyst", "Create data visualizations for the executive report", 120),
|
||||
("ContentWriter", "Draft recommendations based on analysis and research", 160),
|
||||
("Researcher", "Validate recommendations against industry best practices", 140),
|
||||
("DataAnalyst", "Perform final statistical validation of recommendations", 100),
|
||||
]
|
||||
|
||||
print(f"\n🎯 Executing {len(workflow_tasks)} tasks in monitored workflow...")
|
||||
|
||||
# Execute workflow with monitoring
|
||||
for i, (agent_name, task, expected_tokens) in enumerate(workflow_tasks, 1):
|
||||
print(f"\n--- Task {i}/{len(workflow_tasks)} ---")
|
||||
|
||||
try:
|
||||
session.execute_monitored_task(agent_name, task, expected_tokens)
|
||||
|
||||
# Show live dashboard every few tasks
|
||||
if i % 3 == 0:
|
||||
session.display_live_dashboard()
|
||||
time.sleep(1) # Brief pause for readability
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Task failed: {e}")
|
||||
|
||||
# Final dashboard and analytics
|
||||
print(f"\n🏁 Workflow Complete!")
|
||||
session.display_live_dashboard()
|
||||
|
||||
# Export comprehensive report
|
||||
print(f"\n📄 Generating comprehensive report...")
|
||||
try:
|
||||
json_file, summary_file = session.export_comprehensive_report()
|
||||
print(f"✅ Reports exported:")
|
||||
print(f" 📊 Detailed JSON: {json_file}")
|
||||
print(f" 📝 Summary: {summary_file}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Export failed: {e}")
|
||||
|
||||
# Final analytics summary
|
||||
analytics = session.get_session_analytics()
|
||||
|
||||
print(f"\n🎊 Session Complete: {session.session_name}")
|
||||
print(f"⌚ Total Duration: {analytics['session_info']['duration_formatted']}")
|
||||
print(f"📈 Productivity: {analytics['task_summary']['tasks_per_minute']:.1f} tasks/min")
|
||||
print(f"🏆 Top Performer: {analytics['agent_rankings'][0]['agent'] if analytics['agent_rankings'] else 'N/A'}")
|
||||
print(f"💰 Total Tokens: {analytics['raw_metrics']['total_tokens']['total_tokens']:,}")
|
||||
|
||||
print("\n💡 Advanced Monitoring Features Demonstrated:")
|
||||
print("• Multi-agent session tracking")
|
||||
print("• Real-time performance monitoring")
|
||||
print("• Automated performance alerts")
|
||||
print("• Comprehensive analytics and rankings")
|
||||
print("• Export capabilities (JSON + summary)")
|
||||
print("• Live monitoring dashboard")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Basic Agent Performance Monitoring Example
|
||||
|
||||
This example demonstrates the simplest way to monitor agent performance
|
||||
including token usage and timing metrics without modifying existing code.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Add the praisonai-agents module to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.telemetry.metrics import MetricsCollector
|
||||
|
||||
def main():
|
||||
"""Demonstrate basic agent performance monitoring."""
|
||||
|
||||
print("🚀 Basic Agent Performance Monitoring Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Create an agent with metrics tracking enabled
|
||||
agent = Agent(
|
||||
name="DataAnalyst",
|
||||
role="Data Analyst",
|
||||
goal="Analyze data and provide insights",
|
||||
backstory="You are an experienced data analyst who excels at finding patterns in data.",
|
||||
track_metrics=True # Enable metrics tracking
|
||||
)
|
||||
|
||||
print(f"✅ Agent created: {agent.name}")
|
||||
print(f"📊 Metrics tracking: {agent.track_metrics}")
|
||||
print(f"🗂️ Metrics collector: {type(agent.metrics_collector).__name__}")
|
||||
print()
|
||||
|
||||
# Example tasks to monitor
|
||||
tasks = [
|
||||
"What are the key benefits of data visualization?",
|
||||
"Explain the difference between correlation and causation.",
|
||||
"How would you approach analyzing customer churn data?"
|
||||
]
|
||||
|
||||
print("🔍 Running monitored tasks...")
|
||||
print("-" * 30)
|
||||
|
||||
# Execute tasks and monitor performance
|
||||
for i, task in enumerate(tasks, 1):
|
||||
print(f"\nTask {i}: {task[:50]}...")
|
||||
|
||||
# Record start time
|
||||
start_time = time.time()
|
||||
|
||||
# Execute the task (simulated - in real usage you'd call agent.chat())
|
||||
# For demo purposes, we'll simulate a response
|
||||
print("📝 Processing task... (simulated)")
|
||||
time.sleep(0.5) # Simulate processing time
|
||||
|
||||
# Get last metrics (would be populated after real agent.chat() call)
|
||||
if hasattr(agent, 'last_metrics') and agent.last_metrics:
|
||||
tokens = agent.last_metrics.get('tokens')
|
||||
performance = agent.last_metrics.get('performance')
|
||||
|
||||
if tokens:
|
||||
print(f"🪙 Tokens - Input: {tokens.input_tokens}, Output: {tokens.output_tokens}, Total: {tokens.total_tokens}")
|
||||
|
||||
if performance:
|
||||
print(f"⏱️ Performance - TTFT: {performance.time_to_first_token:.3f}s, Total: {performance.total_time:.3f}s")
|
||||
print(f"🚀 Speed: {performance.tokens_per_second:.1f} tokens/sec")
|
||||
else:
|
||||
# Simulate metrics for demo
|
||||
elapsed = time.time() - start_time
|
||||
print(f"⏱️ Elapsed time: {elapsed:.3f}s")
|
||||
print(f"📝 Task completed (simulated)")
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
|
||||
# Display session-level metrics
|
||||
if agent.metrics_collector:
|
||||
session_metrics = agent.metrics_collector.get_session_metrics()
|
||||
|
||||
print("📋 Session Summary:")
|
||||
print(f"🆔 Session ID: {session_metrics['session_id']}")
|
||||
print(f"⏰ Duration: {session_metrics['duration_seconds']:.1f} seconds")
|
||||
print(f"🪙 Total Tokens: {session_metrics['total_tokens']['total_tokens']}")
|
||||
|
||||
# Display agent-specific metrics
|
||||
if session_metrics['by_agent']:
|
||||
print("\n📊 By Agent:")
|
||||
for agent_name, metrics in session_metrics['by_agent'].items():
|
||||
print(f" {agent_name}:")
|
||||
print(f" Input tokens: {metrics['input_tokens']}")
|
||||
print(f" Output tokens: {metrics['output_tokens']}")
|
||||
print(f" Total tokens: {metrics['total_tokens']}")
|
||||
|
||||
# Display performance metrics
|
||||
if session_metrics['performance']:
|
||||
print("\n⚡ Performance Summary:")
|
||||
for agent_name, perf in session_metrics['performance'].items():
|
||||
print(f" {agent_name}:")
|
||||
print(f" Avg TTFT: {perf['average_ttft']:.3f}s")
|
||||
print(f" Avg Total Time: {perf['average_total_time']:.3f}s")
|
||||
print(f" Avg Speed: {perf['average_tokens_per_second']:.1f} tokens/sec")
|
||||
print(f" Requests: {perf['request_count']}")
|
||||
|
||||
print("\n✅ Basic monitoring example complete!")
|
||||
print("\n💡 Key Takeaways:")
|
||||
print("• Set track_metrics=True to enable monitoring")
|
||||
print("• MetricsCollector is auto-created when needed")
|
||||
print("• Access last execution metrics via agent.last_metrics")
|
||||
print("• Get session summary via metrics_collector.get_session_metrics()")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Task Performance Timing and Metrics Example
|
||||
|
||||
This example shows how to monitor individual task performance,
|
||||
measure execution times, and track detailed metrics for specific operations.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
# Add the praisonai-agents module to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.telemetry.metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
|
||||
class TaskTimer:
|
||||
"""Helper class to time task execution and collect metrics."""
|
||||
|
||||
def __init__(self, task_name: str):
|
||||
self.task_name = task_name
|
||||
self.start_time = None
|
||||
self.end_time = None
|
||||
self.performance_metrics = PerformanceMetrics()
|
||||
|
||||
def __enter__(self):
|
||||
"""Context manager entry - start timing."""
|
||||
self.start_time = time.time()
|
||||
self.performance_metrics.start_timing()
|
||||
print(f"⏱️ Starting task: {self.task_name}")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""Context manager exit - end timing."""
|
||||
self.end_time = time.time()
|
||||
elapsed = self.end_time - self.start_time
|
||||
|
||||
# Simulate token count for demo (in real usage, this comes from agent response)
|
||||
simulated_tokens = 50
|
||||
self.performance_metrics.end_timing(simulated_tokens)
|
||||
|
||||
print(f"✅ Task completed: {self.task_name}")
|
||||
print(f" Duration: {elapsed:.3f}s")
|
||||
print(f" Estimated speed: {simulated_tokens/elapsed:.1f} tokens/sec")
|
||||
return False # Don't suppress exceptions
|
||||
|
||||
def demonstrate_manual_metrics():
|
||||
"""Demonstrate manual performance metrics creation and tracking."""
|
||||
|
||||
print("🔧 Manual Performance Metrics Creation")
|
||||
print("-" * 40)
|
||||
|
||||
# Create performance metrics manually
|
||||
perf_metrics = PerformanceMetrics()
|
||||
|
||||
# Start timing
|
||||
perf_metrics.start_timing()
|
||||
print("⏱️ Started timing...")
|
||||
|
||||
# Simulate some work
|
||||
time.sleep(0.2)
|
||||
|
||||
# Mark first token (simulated)
|
||||
perf_metrics.mark_first_token()
|
||||
print("🎯 First token received (simulated)")
|
||||
|
||||
# Continue work
|
||||
time.sleep(0.3)
|
||||
|
||||
# End timing with token count
|
||||
token_count = 75
|
||||
perf_metrics.end_timing(token_count)
|
||||
|
||||
print(f"✅ Timing complete:")
|
||||
print(f" Time to First Token: {perf_metrics.time_to_first_token:.3f}s")
|
||||
print(f" Total Time: {perf_metrics.total_time:.3f}s")
|
||||
print(f" Tokens per Second: {perf_metrics.tokens_per_second:.1f}")
|
||||
|
||||
return perf_metrics
|
||||
|
||||
def demonstrate_token_metrics():
|
||||
"""Demonstrate token metrics creation and aggregation."""
|
||||
|
||||
print("\n💰 Token Metrics Demonstration")
|
||||
print("-" * 40)
|
||||
|
||||
# Create individual token metrics
|
||||
task1_tokens = TokenMetrics(
|
||||
input_tokens=100,
|
||||
output_tokens=50,
|
||||
total_tokens=150,
|
||||
cached_tokens=20,
|
||||
reasoning_tokens=10
|
||||
)
|
||||
|
||||
task2_tokens = TokenMetrics(
|
||||
input_tokens=80,
|
||||
output_tokens=60,
|
||||
total_tokens=140,
|
||||
cached_tokens=15,
|
||||
reasoning_tokens=5
|
||||
)
|
||||
|
||||
print("Task 1 Tokens:")
|
||||
print(f" Input: {task1_tokens.input_tokens}, Output: {task1_tokens.output_tokens}")
|
||||
print(f" Cached: {task1_tokens.cached_tokens}, Reasoning: {task1_tokens.reasoning_tokens}")
|
||||
|
||||
print("Task 2 Tokens:")
|
||||
print(f" Input: {task2_tokens.input_tokens}, Output: {task2_tokens.output_tokens}")
|
||||
print(f" Cached: {task2_tokens.cached_tokens}, Reasoning: {task2_tokens.reasoning_tokens}")
|
||||
|
||||
# Aggregate tokens
|
||||
total_tokens = task1_tokens + task2_tokens
|
||||
print("\nAggregated Tokens:")
|
||||
print(f" Total Input: {total_tokens.input_tokens}")
|
||||
print(f" Total Output: {total_tokens.output_tokens}")
|
||||
print(f" Total Cached: {total_tokens.cached_tokens}")
|
||||
print(f" Total Reasoning: {total_tokens.reasoning_tokens}")
|
||||
print(f" Grand Total: {total_tokens.total_tokens}")
|
||||
|
||||
return [task1_tokens, task2_tokens, total_tokens]
|
||||
|
||||
def main():
|
||||
"""Main demonstration function."""
|
||||
|
||||
print("🎯 Task Timing and Metrics Example")
|
||||
print("=" * 50)
|
||||
|
||||
# Create agent with metrics tracking
|
||||
agent = Agent(
|
||||
name="TaskMonitor",
|
||||
role="Performance Monitor",
|
||||
goal="Monitor and analyze task performance",
|
||||
backstory="You specialize in performance analysis and optimization.",
|
||||
track_metrics=True
|
||||
)
|
||||
|
||||
# Demonstrate different monitoring approaches
|
||||
tasks = [
|
||||
"Analyze quarterly sales data",
|
||||
"Generate marketing report",
|
||||
"Review customer feedback"
|
||||
]
|
||||
|
||||
print("📋 Monitoring Task Execution")
|
||||
print("-" * 30)
|
||||
|
||||
# Method 1: Using context manager for timing
|
||||
for i, task in enumerate(tasks, 1):
|
||||
with TaskTimer(f"Task {i}: {task}") as timer:
|
||||
# Simulate task execution
|
||||
time.sleep(0.1 + i * 0.1) # Variable execution time
|
||||
|
||||
# In real usage, you would call:
|
||||
# response = agent.chat(task)
|
||||
# metrics = agent.last_metrics
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
|
||||
# Method 2: Manual metrics creation
|
||||
manual_metrics = demonstrate_manual_metrics()
|
||||
|
||||
# Method 3: Token metrics demonstration
|
||||
token_metrics_list = demonstrate_token_metrics()
|
||||
|
||||
# Method 4: Using MetricsCollector for aggregation
|
||||
print("\n📊 MetricsCollector Aggregation")
|
||||
print("-" * 40)
|
||||
|
||||
collector = MetricsCollector()
|
||||
|
||||
# Add metrics to collector
|
||||
for i, tokens in enumerate(token_metrics_list[:-1], 1): # Exclude the aggregated one
|
||||
collector.add_agent_metrics(
|
||||
agent_name=f"Agent_{i}",
|
||||
token_metrics=tokens,
|
||||
performance_metrics=manual_metrics if i == 1 else None,
|
||||
model_name=f"gpt-4o-{i}"
|
||||
)
|
||||
|
||||
# Get session metrics
|
||||
session_data = collector.get_session_metrics()
|
||||
|
||||
print("Session Summary:")
|
||||
print(f" Duration: {session_data['duration_seconds']:.1f}s")
|
||||
print(f" Total Tokens: {session_data['total_tokens']['total_tokens']}")
|
||||
|
||||
print("\nBy Agent:")
|
||||
for agent_name, metrics in session_data['by_agent'].items():
|
||||
print(f" {agent_name}: {metrics['total_tokens']} total tokens")
|
||||
|
||||
print("\nBy Model:")
|
||||
for model_name, metrics in session_data['by_model'].items():
|
||||
print(f" {model_name}: {metrics['total_tokens']} total tokens")
|
||||
|
||||
# Export metrics (optional)
|
||||
export_file = "/tmp/task_metrics_example.json"
|
||||
try:
|
||||
collector.export_metrics(export_file)
|
||||
print(f"\n💾 Metrics exported to: {export_file}")
|
||||
except Exception as e:
|
||||
print(f"\n⚠️ Export failed: {e}")
|
||||
|
||||
print("\n✅ Task timing and metrics example complete!")
|
||||
print("\n💡 Key Monitoring Techniques:")
|
||||
print("• Use context managers for automatic timing")
|
||||
print("• Create PerformanceMetrics manually for custom timing")
|
||||
print("• Aggregate TokenMetrics using + operator")
|
||||
print("• Use MetricsCollector for session-level aggregation")
|
||||
print("• Export metrics to files for analysis")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,581 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Monitoring Integration Examples
|
||||
|
||||
This example demonstrates how to integrate PraisonAI monitoring with:
|
||||
- Custom dashboards
|
||||
- External monitoring systems
|
||||
- Webhooks and alerts
|
||||
- Database logging
|
||||
- API endpoints
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Any, Callable, Optional
|
||||
from dataclasses import dataclass, asdict
|
||||
import threading
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import urllib.request
|
||||
|
||||
# Add the praisonai-agents module to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.telemetry.metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
|
||||
@dataclass
|
||||
class AlertConfig:
|
||||
"""Configuration for performance alerts."""
|
||||
name: str
|
||||
condition: Callable[[Dict[str, Any]], bool]
|
||||
webhook_url: Optional[str] = None
|
||||
email_recipient: Optional[str] = None
|
||||
cooldown_seconds: int = 300 # 5 minutes
|
||||
|
||||
class DatabaseLogger:
|
||||
"""Log monitoring data to SQLite database."""
|
||||
|
||||
def __init__(self, db_path: str = "/tmp/monitoring.db"):
|
||||
self.db_path = db_path
|
||||
self._init_database()
|
||||
|
||||
def _init_database(self):
|
||||
"""Initialize database schema."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create tables
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
duration_seconds REAL,
|
||||
total_tokens INTEGER,
|
||||
total_requests INTEGER
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS agent_metrics (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT,
|
||||
agent_name TEXT,
|
||||
task_name TEXT,
|
||||
timestamp TIMESTAMP,
|
||||
input_tokens INTEGER,
|
||||
output_tokens INTEGER,
|
||||
total_tokens INTEGER,
|
||||
cached_tokens INTEGER,
|
||||
reasoning_tokens INTEGER,
|
||||
ttft REAL,
|
||||
total_time REAL,
|
||||
tokens_per_second REAL,
|
||||
model_name TEXT,
|
||||
FOREIGN KEY (session_id) REFERENCES sessions (id)
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS alerts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT,
|
||||
alert_name TEXT,
|
||||
timestamp TIMESTAMP,
|
||||
message TEXT,
|
||||
severity TEXT,
|
||||
resolved BOOLEAN DEFAULT FALSE,
|
||||
FOREIGN KEY (session_id) REFERENCES sessions (id)
|
||||
)
|
||||
''')
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print(f"🗄️ Database initialized: {self.db_path}")
|
||||
|
||||
def log_session_start(self, session_id: str):
|
||||
"""Log session start."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"INSERT OR REPLACE INTO sessions (id, start_time) VALUES (?, ?)",
|
||||
(session_id, datetime.now())
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def log_agent_metrics(self, session_id: str, agent_name: str, task_name: str,
|
||||
token_metrics: TokenMetrics, perf_metrics: PerformanceMetrics,
|
||||
model_name: str = "unknown"):
|
||||
"""Log agent execution metrics."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO agent_metrics (
|
||||
session_id, agent_name, task_name, timestamp,
|
||||
input_tokens, output_tokens, total_tokens, cached_tokens, reasoning_tokens,
|
||||
ttft, total_time, tokens_per_second, model_name
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
session_id, agent_name, task_name, datetime.now(),
|
||||
token_metrics.input_tokens, token_metrics.output_tokens, token_metrics.total_tokens,
|
||||
token_metrics.cached_tokens, token_metrics.reasoning_tokens,
|
||||
perf_metrics.time_to_first_token, perf_metrics.total_time,
|
||||
perf_metrics.tokens_per_second, model_name
|
||||
))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def log_alert(self, session_id: str, alert_name: str, message: str, severity: str = "warning"):
|
||||
"""Log performance alert."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute(
|
||||
"INSERT INTO alerts (session_id, alert_name, timestamp, message, severity) VALUES (?, ?, ?, ?, ?)",
|
||||
(session_id, alert_name, datetime.now(), message, severity)
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def update_session_end(self, session_id: str, total_tokens: int, total_requests: int):
|
||||
"""Update session end metrics."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
UPDATE sessions
|
||||
SET end_time = ?, total_tokens = ?, total_requests = ?,
|
||||
duration_seconds = (
|
||||
SELECT (julianday(?) - julianday(start_time)) * 86400
|
||||
FROM sessions
|
||||
WHERE id = ?
|
||||
)
|
||||
WHERE id = ?
|
||||
''', (datetime.now(), total_tokens, total_requests, datetime.now(), session_id, session_id))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def get_session_stats(self, session_id: str) -> Dict[str, Any]:
|
||||
"""Get comprehensive session statistics."""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Get session info
|
||||
cursor.execute("SELECT * FROM sessions WHERE id = ?", (session_id,))
|
||||
session_row = cursor.fetchone()
|
||||
|
||||
# Get agent metrics
|
||||
cursor.execute("""
|
||||
SELECT agent_name, COUNT(*) as requests,
|
||||
AVG(ttft) as avg_ttft, AVG(total_time) as avg_total_time,
|
||||
AVG(tokens_per_second) as avg_tps, SUM(total_tokens) as total_tokens
|
||||
FROM agent_metrics
|
||||
WHERE session_id = ?
|
||||
GROUP BY agent_name
|
||||
""", (session_id,))
|
||||
agent_stats = cursor.fetchall()
|
||||
|
||||
# Get alerts
|
||||
cursor.execute("SELECT * FROM alerts WHERE session_id = ?", (session_id,))
|
||||
alerts = cursor.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
return {
|
||||
'session': session_row,
|
||||
'agent_stats': agent_stats,
|
||||
'alerts': alerts
|
||||
}
|
||||
|
||||
class WebhookNotifier:
|
||||
"""Send monitoring data via webhooks."""
|
||||
|
||||
def __init__(self):
|
||||
self.webhook_urls: List[str] = []
|
||||
self.last_sent: Dict[str, float] = {}
|
||||
|
||||
def add_webhook(self, url: str):
|
||||
"""Add webhook URL."""
|
||||
self.webhook_urls.append(url)
|
||||
print(f"🔗 Webhook added: {url}")
|
||||
|
||||
def send_alert(self, alert_name: str, data: Dict[str, Any], cooldown: int = 300):
|
||||
"""Send alert via webhook with cooldown."""
|
||||
|
||||
# Check cooldown
|
||||
now = time.time()
|
||||
if alert_name in self.last_sent:
|
||||
if now - self.last_sent[alert_name] < cooldown:
|
||||
print(f"⏰ Alert {alert_name} in cooldown, skipping")
|
||||
return
|
||||
|
||||
payload = {
|
||||
'alert_name': alert_name,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'data': data,
|
||||
'severity': data.get('severity', 'warning')
|
||||
}
|
||||
|
||||
for webhook_url in self.webhook_urls:
|
||||
try:
|
||||
# Simulate webhook call (in real usage, use requests library)
|
||||
print(f"📤 Sending webhook to {webhook_url}")
|
||||
print(f" Alert: {alert_name}")
|
||||
print(f" Data: {json.dumps(data, indent=2)}")
|
||||
|
||||
# In real implementation:
|
||||
# import requests
|
||||
# response = requests.post(webhook_url, json=payload, timeout=10)
|
||||
# response.raise_for_status()
|
||||
|
||||
self.last_sent[alert_name] = now
|
||||
print(f"✅ Webhook sent successfully")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Webhook failed: {e}")
|
||||
|
||||
class MonitoringDashboardHandler(BaseHTTPRequestHandler):
|
||||
"""Simple HTTP handler for monitoring dashboard."""
|
||||
|
||||
monitoring_data = {}
|
||||
|
||||
def do_GET(self):
|
||||
"""Handle GET requests for dashboard."""
|
||||
if self.path == "/":
|
||||
self.send_dashboard()
|
||||
elif self.path == "/api/metrics":
|
||||
self.send_metrics_api()
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def send_dashboard(self):
|
||||
"""Send HTML dashboard."""
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>PraisonAI Monitoring Dashboard</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.metric-box { border: 1px solid #ccc; padding: 15px; margin: 10px; border-radius: 5px; }
|
||||
.alert { background-color: #ffebee; border-color: #f44336; }
|
||||
.success { background-color: #e8f5e8; border-color: #4caf50; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #f2f2f2; }
|
||||
</style>
|
||||
<script>
|
||||
function refreshData() {
|
||||
fetch('/api/metrics')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
document.getElementById('metrics-data').innerHTML =
|
||||
'<pre>' + JSON.stringify(data, null, 2) + '</pre>';
|
||||
});
|
||||
}
|
||||
setInterval(refreshData, 5000); // Refresh every 5 seconds
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🔍 PraisonAI Monitoring Dashboard</h1>
|
||||
<div class="metric-box success">
|
||||
<h2>Real-time Metrics</h2>
|
||||
<div id="metrics-data">Loading...</div>
|
||||
</div>
|
||||
<script>refreshData();</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write(html.encode())
|
||||
|
||||
def send_metrics_api(self):
|
||||
"""Send metrics as JSON API."""
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.end_headers()
|
||||
|
||||
response_data = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'monitoring_data': self.monitoring_data
|
||||
}
|
||||
|
||||
self.wfile.write(json.dumps(response_data, indent=2, default=str).encode())
|
||||
|
||||
def log_message(self, format, *args):
|
||||
"""Suppress default logging."""
|
||||
pass
|
||||
|
||||
class IntegratedMonitoringSystem:
|
||||
"""Comprehensive monitoring system with multiple integrations."""
|
||||
|
||||
def __init__(self, session_name: str):
|
||||
self.session_name = session_name
|
||||
self.session_id = f"session_{int(time.time())}_{id(self)}"
|
||||
self.metrics_collector = MetricsCollector()
|
||||
|
||||
# Initialize integrations
|
||||
self.db_logger = DatabaseLogger()
|
||||
self.webhook_notifier = WebhookNotifier()
|
||||
self.alerts: List[AlertConfig] = []
|
||||
|
||||
# Start session
|
||||
self.db_logger.log_session_start(self.session_id)
|
||||
|
||||
# Configure default alerts
|
||||
self._setup_default_alerts()
|
||||
|
||||
print(f"🚀 Integrated Monitoring System: {session_name}")
|
||||
print(f"🆔 Session ID: {self.session_id}")
|
||||
|
||||
def _setup_default_alerts(self):
|
||||
"""Setup default performance alerts."""
|
||||
|
||||
# High TTFT Alert
|
||||
self.add_alert(AlertConfig(
|
||||
name="high_ttft",
|
||||
condition=lambda data: data.get('ttft', 0) > 2.0,
|
||||
cooldown_seconds=300
|
||||
))
|
||||
|
||||
# Low TPS Alert
|
||||
self.add_alert(AlertConfig(
|
||||
name="low_tps",
|
||||
condition=lambda data: data.get('tokens_per_second', 0) < 10.0,
|
||||
cooldown_seconds=300
|
||||
))
|
||||
|
||||
# High Token Usage Alert
|
||||
self.add_alert(AlertConfig(
|
||||
name="high_token_usage",
|
||||
condition=lambda data: data.get('total_tokens', 0) > 500,
|
||||
cooldown_seconds=600
|
||||
))
|
||||
|
||||
def add_alert(self, alert_config: AlertConfig):
|
||||
"""Add performance alert configuration."""
|
||||
self.alerts.append(alert_config)
|
||||
print(f"⚠️ Alert configured: {alert_config.name}")
|
||||
|
||||
def add_webhook(self, url: str):
|
||||
"""Add webhook for notifications."""
|
||||
self.webhook_notifier.add_webhook(url)
|
||||
|
||||
def execute_monitored_task(self, agent: Agent, task: str, expected_tokens: int = 100):
|
||||
"""Execute task with comprehensive monitoring."""
|
||||
|
||||
print(f"\n🎯 Task: {task[:50]}...")
|
||||
|
||||
# Create performance metrics
|
||||
perf_metrics = PerformanceMetrics()
|
||||
perf_metrics.start_timing()
|
||||
|
||||
# Simulate execution
|
||||
time.sleep(0.2)
|
||||
perf_metrics.mark_first_token()
|
||||
time.sleep(0.3)
|
||||
|
||||
# Create token metrics (normally from agent response)
|
||||
token_metrics = TokenMetrics(
|
||||
input_tokens=int(expected_tokens * 0.7),
|
||||
output_tokens=int(expected_tokens * 0.3),
|
||||
total_tokens=expected_tokens,
|
||||
cached_tokens=int(expected_tokens * 0.1),
|
||||
reasoning_tokens=int(expected_tokens * 0.05)
|
||||
)
|
||||
|
||||
perf_metrics.end_timing(token_metrics.output_tokens)
|
||||
|
||||
# Add to metrics collector
|
||||
self.metrics_collector.add_agent_metrics(
|
||||
agent_name=agent.name,
|
||||
token_metrics=token_metrics,
|
||||
performance_metrics=perf_metrics,
|
||||
model_name="gpt-4o"
|
||||
)
|
||||
|
||||
# Log to database
|
||||
self.db_logger.log_agent_metrics(
|
||||
self.session_id, agent.name, task, token_metrics, perf_metrics, "gpt-4o"
|
||||
)
|
||||
|
||||
# Check alerts
|
||||
alert_data = {
|
||||
'agent': agent.name,
|
||||
'task': task,
|
||||
'ttft': perf_metrics.time_to_first_token,
|
||||
'total_time': perf_metrics.total_time,
|
||||
'tokens_per_second': perf_metrics.tokens_per_second,
|
||||
'total_tokens': token_metrics.total_tokens,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
|
||||
self._check_alerts(alert_data)
|
||||
|
||||
# Update dashboard data
|
||||
MonitoringDashboardHandler.monitoring_data[agent.name] = {
|
||||
'last_task': task,
|
||||
'last_update': datetime.now().isoformat(),
|
||||
'metrics': alert_data
|
||||
}
|
||||
|
||||
print(f"✅ Task completed: {perf_metrics.tokens_per_second:.1f} TPS")
|
||||
|
||||
return token_metrics, perf_metrics
|
||||
|
||||
def _check_alerts(self, data: Dict[str, Any]):
|
||||
"""Check if any alerts should be triggered."""
|
||||
|
||||
for alert in self.alerts:
|
||||
if alert.condition(data):
|
||||
message = f"Alert triggered: {alert.name} for agent {data.get('agent', 'unknown')}"
|
||||
|
||||
# Log alert to database
|
||||
self.db_logger.log_alert(
|
||||
self.session_id, alert.name, message, "warning"
|
||||
)
|
||||
|
||||
# Send webhook notification
|
||||
self.webhook_notifier.send_alert(
|
||||
alert.name, data, alert.cooldown_seconds
|
||||
)
|
||||
|
||||
print(f"🚨 ALERT: {message}")
|
||||
|
||||
def start_dashboard_server(self, port: int = 8080):
|
||||
"""Start HTTP dashboard server in background."""
|
||||
|
||||
def run_server():
|
||||
try:
|
||||
server = HTTPServer(('localhost', port), MonitoringDashboardHandler)
|
||||
print(f"🌐 Dashboard server started: http://localhost:{port}")
|
||||
server.serve_forever()
|
||||
except Exception as e:
|
||||
print(f"❌ Dashboard server failed: {e}")
|
||||
|
||||
# Start server in background thread
|
||||
server_thread = threading.Thread(target=run_server, daemon=True)
|
||||
server_thread.start()
|
||||
|
||||
def finalize_session(self):
|
||||
"""Finalize monitoring session."""
|
||||
|
||||
session_metrics = self.metrics_collector.get_session_metrics()
|
||||
|
||||
# Update database with final metrics
|
||||
self.db_logger.update_session_end(
|
||||
self.session_id,
|
||||
session_metrics['total_tokens']['total_tokens'],
|
||||
len([task for tasks in session_metrics['by_agent'].values() for task in [tasks]])
|
||||
)
|
||||
|
||||
# Get final statistics
|
||||
stats = self.db_logger.get_session_stats(self.session_id)
|
||||
|
||||
print(f"\n📊 Session Finalized: {self.session_name}")
|
||||
print(f" Total Tokens: {session_metrics['total_tokens']['total_tokens']:,}")
|
||||
print(f" Duration: {session_metrics['duration_seconds']:.1f}s")
|
||||
print(f" Alerts Generated: {len(stats['alerts'])}")
|
||||
|
||||
return stats
|
||||
|
||||
def main():
|
||||
"""Demonstrate monitoring integrations."""
|
||||
|
||||
print("🔗 Monitoring Integration Examples")
|
||||
print("=" * 60)
|
||||
|
||||
# Create integrated monitoring system
|
||||
monitor = IntegratedMonitoringSystem("Integration Demo Session")
|
||||
|
||||
# Add webhook (replace with real URL for testing)
|
||||
monitor.add_webhook("https://hooks.slack.com/services/YOUR/WEBHOOK/URL")
|
||||
|
||||
# Start dashboard server
|
||||
monitor.start_dashboard_server(8080)
|
||||
|
||||
# Create agents
|
||||
agents = [
|
||||
Agent(
|
||||
name="Analyst",
|
||||
role="Data Analyst",
|
||||
goal="Analyze complex datasets",
|
||||
backstory="Expert in statistical analysis",
|
||||
track_metrics=True,
|
||||
metrics_collector=monitor.metrics_collector
|
||||
),
|
||||
Agent(
|
||||
name="Writer",
|
||||
role="Content Writer",
|
||||
goal="Create comprehensive reports",
|
||||
backstory="Experienced technical writer",
|
||||
track_metrics=True,
|
||||
metrics_collector=monitor.metrics_collector
|
||||
)
|
||||
]
|
||||
|
||||
# Execute monitored workflow
|
||||
tasks = [
|
||||
("Analyst", "Analyze quarterly sales data with trends", 200),
|
||||
("Writer", "Write executive summary of sales analysis", 300),
|
||||
("Analyst", "Create statistical models for forecasting", 250),
|
||||
("Writer", "Document methodology and findings", 180),
|
||||
("Analyst", "Perform sensitivity analysis on models", 150),
|
||||
]
|
||||
|
||||
print(f"\n🎯 Executing {len(tasks)} monitored tasks...")
|
||||
|
||||
for i, (agent_name, task, expected_tokens) in enumerate(tasks, 1):
|
||||
agent = next((a for a in agents if a.name == agent_name), None)
|
||||
if agent:
|
||||
print(f"\n--- Task {i}/{len(tasks)} ---")
|
||||
monitor.execute_monitored_task(agent, task, expected_tokens)
|
||||
|
||||
# Brief pause between tasks
|
||||
time.sleep(0.5)
|
||||
|
||||
# Finalize session
|
||||
final_stats = monitor.finalize_session()
|
||||
|
||||
print(f"\n📋 Integration Summary:")
|
||||
print(f"• Database logging: {monitor.db_logger.db_path}")
|
||||
print(f"• Webhook notifications: {len(monitor.webhook_notifier.webhook_urls)} configured")
|
||||
print(f"• Performance alerts: {len(monitor.alerts)} configured")
|
||||
print(f"• Dashboard server: http://localhost:8080")
|
||||
|
||||
print(f"\n💡 Integration Capabilities Demonstrated:")
|
||||
print(f"• SQLite database logging for persistence")
|
||||
print(f"• Webhook notifications with cooldowns")
|
||||
print(f"• Real-time dashboard with API endpoints")
|
||||
print(f"• Configurable performance alerts")
|
||||
print(f"• Multi-agent session tracking")
|
||||
print(f"• Comprehensive statistics and reporting")
|
||||
|
||||
print(f"\n🌐 Visit http://localhost:8080 to see the live dashboard!")
|
||||
print(f" (Dashboard will run in background - press Ctrl+C to exit)")
|
||||
|
||||
# Keep the dashboard running for a while
|
||||
try:
|
||||
time.sleep(30) # Run for 30 seconds for demo
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n👋 Dashboard stopped.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,362 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Telemetry Integration Example
|
||||
|
||||
This example demonstrates how to integrate with the telemetry system
|
||||
for automatic metrics tracking, PostHog integration, and custom telemetry events.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Any
|
||||
|
||||
# Add the praisonai-agents module to the Python path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents.agent import Agent
|
||||
from praisonaiagents.telemetry.metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
from praisonaiagents.telemetry import get_telemetry
|
||||
|
||||
# Configure logging to see telemetry debug messages
|
||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
class TelemetryMonitor:
|
||||
"""Enhanced monitoring with telemetry integration."""
|
||||
|
||||
def __init__(self, enable_posthog: bool = False):
|
||||
self.telemetry = get_telemetry()
|
||||
self.enable_posthog = enable_posthog
|
||||
self.custom_events: List[Dict[str, Any]] = []
|
||||
|
||||
print(f"📡 Telemetry Monitor Initialized")
|
||||
print(f" Telemetry Enabled: {self.telemetry.enabled}")
|
||||
print(f" Session ID: {self.telemetry.session_id}")
|
||||
print(f" PostHog Integration: {enable_posthog}")
|
||||
|
||||
# Configure PostHog if requested (requires POSTHOG_API_KEY env var)
|
||||
if enable_posthog:
|
||||
self._setup_posthog()
|
||||
|
||||
def _setup_posthog(self):
|
||||
"""Setup PostHog integration for analytics."""
|
||||
posthog_key = os.getenv('POSTHOG_API_KEY')
|
||||
if posthog_key:
|
||||
print(f"🔧 PostHog configured with API key")
|
||||
else:
|
||||
print(f"⚠️ PostHog API key not found in environment")
|
||||
print(f" Set POSTHOG_API_KEY to enable PostHog integration")
|
||||
|
||||
def track_custom_event(self, event_name: str, properties: Dict[str, Any]):
|
||||
"""Track custom telemetry events."""
|
||||
|
||||
# Add timestamp and session info
|
||||
enriched_properties = {
|
||||
**properties,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'session_id': self.telemetry.session_id
|
||||
}
|
||||
|
||||
# Store for local tracking
|
||||
event_record = {
|
||||
'event': event_name,
|
||||
'properties': enriched_properties,
|
||||
'tracked_at': time.time()
|
||||
}
|
||||
self.custom_events.append(event_record)
|
||||
|
||||
print(f"📊 Custom Event: {event_name}")
|
||||
for key, value in properties.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
def demonstrate_manual_telemetry(self):
|
||||
"""Demonstrate manual telemetry tracking."""
|
||||
|
||||
print(f"\n🔍 Manual Telemetry Tracking")
|
||||
print("-" * 40)
|
||||
|
||||
# Track feature usage
|
||||
self.telemetry.track_feature_usage("manual_monitoring", {"demo": True})
|
||||
|
||||
# Create and track token metrics
|
||||
token_metrics = TokenMetrics(
|
||||
input_tokens=120,
|
||||
output_tokens=80,
|
||||
total_tokens=200,
|
||||
cached_tokens=30,
|
||||
reasoning_tokens=15,
|
||||
audio_tokens=5
|
||||
)
|
||||
|
||||
print(f"📤 Tracking token metrics...")
|
||||
self.telemetry.track_tokens(token_metrics)
|
||||
|
||||
# Create and track performance metrics
|
||||
perf_metrics = PerformanceMetrics()
|
||||
perf_metrics.start_timing()
|
||||
time.sleep(0.3) # Simulate work
|
||||
perf_metrics.mark_first_token()
|
||||
time.sleep(0.2) # Simulate more work
|
||||
perf_metrics.end_timing(80) # 80 tokens generated
|
||||
|
||||
print(f"📈 Tracking performance metrics...")
|
||||
self.telemetry.track_performance(perf_metrics)
|
||||
|
||||
# Track custom application events
|
||||
self.track_custom_event("workflow_started", {
|
||||
"workflow_type": "data_analysis",
|
||||
"expected_duration": 300,
|
||||
"complexity": "high"
|
||||
})
|
||||
|
||||
return token_metrics, perf_metrics
|
||||
|
||||
def demonstrate_agent_telemetry(self):
|
||||
"""Demonstrate automatic agent telemetry integration."""
|
||||
|
||||
print(f"\n🤖 Agent Telemetry Integration")
|
||||
print("-" * 40)
|
||||
|
||||
# Create agent with metrics tracking (automatic telemetry)
|
||||
agent = Agent(
|
||||
name="TelemetryAgent",
|
||||
role="Data Processor",
|
||||
goal="Process data with telemetry tracking",
|
||||
backstory="Specialized in monitored data processing workflows",
|
||||
track_metrics=True
|
||||
)
|
||||
|
||||
print(f"✅ Agent created with telemetry integration")
|
||||
|
||||
# Simulate agent tasks with telemetry
|
||||
tasks = [
|
||||
("Process customer data", 150),
|
||||
("Generate insights report", 200),
|
||||
("Create visualization", 100)
|
||||
]
|
||||
|
||||
for i, (task, expected_tokens) in enumerate(tasks, 1):
|
||||
print(f"\n🎯 Task {i}: {task}")
|
||||
|
||||
# Track task start
|
||||
self.track_custom_event("task_started", {
|
||||
"task_name": task,
|
||||
"agent": agent.name,
|
||||
"expected_tokens": expected_tokens
|
||||
})
|
||||
|
||||
# Simulate task execution with metrics
|
||||
start_time = time.time()
|
||||
|
||||
# Create simulated metrics (in real usage, these come from agent.chat())
|
||||
token_metrics = TokenMetrics(
|
||||
input_tokens=int(expected_tokens * 0.6),
|
||||
output_tokens=int(expected_tokens * 0.4),
|
||||
total_tokens=expected_tokens,
|
||||
cached_tokens=int(expected_tokens * 0.1)
|
||||
)
|
||||
|
||||
perf_metrics = PerformanceMetrics()
|
||||
perf_metrics.start_timing()
|
||||
time.sleep(0.2) # Simulate processing
|
||||
perf_metrics.mark_first_token()
|
||||
time.sleep(0.3) # Simulate generation
|
||||
perf_metrics.end_timing(token_metrics.output_tokens)
|
||||
|
||||
# Manually add to agent's metrics collector (normally automatic)
|
||||
if agent.metrics_collector:
|
||||
agent.metrics_collector.add_agent_metrics(
|
||||
agent_name=agent.name,
|
||||
token_metrics=token_metrics,
|
||||
performance_metrics=perf_metrics,
|
||||
model_name="gpt-4o"
|
||||
)
|
||||
|
||||
# Track via telemetry (normally automatic in agent.chat())
|
||||
self.telemetry.track_tokens(token_metrics)
|
||||
self.telemetry.track_performance(perf_metrics)
|
||||
|
||||
# Track task completion
|
||||
execution_time = time.time() - start_time
|
||||
self.track_custom_event("task_completed", {
|
||||
"task_name": task,
|
||||
"agent": agent.name,
|
||||
"execution_time": execution_time,
|
||||
"tokens_used": token_metrics.total_tokens,
|
||||
"tokens_per_second": perf_metrics.tokens_per_second
|
||||
})
|
||||
|
||||
print(f" ✅ Completed in {execution_time:.2f}s")
|
||||
print(f" 📊 {token_metrics.total_tokens} tokens, {perf_metrics.tokens_per_second:.1f} TPS")
|
||||
|
||||
return agent
|
||||
|
||||
def analyze_telemetry_data(self):
|
||||
"""Analyze collected telemetry data."""
|
||||
|
||||
print(f"\n📊 Telemetry Data Analysis")
|
||||
print("-" * 40)
|
||||
|
||||
# Get telemetry metrics
|
||||
telemetry_metrics = self.telemetry.get_metrics()
|
||||
|
||||
print(f"Telemetry Status:")
|
||||
print(f" Enabled: {telemetry_metrics['enabled']}")
|
||||
print(f" Session ID: {telemetry_metrics.get('session_id', 'N/A')}")
|
||||
|
||||
if telemetry_metrics['enabled']:
|
||||
metrics_data = telemetry_metrics.get('metrics', {})
|
||||
print(f" Collected Metrics: {len(metrics_data)} entries")
|
||||
|
||||
# Show environment info
|
||||
env_info = telemetry_metrics.get('environment', {})
|
||||
print(f"\nEnvironment:")
|
||||
for key, value in env_info.items():
|
||||
print(f" {key}: {value}")
|
||||
|
||||
# Analyze custom events
|
||||
print(f"\nCustom Events Analysis:")
|
||||
print(f" Total Events: {len(self.custom_events)}")
|
||||
|
||||
# Group events by type
|
||||
event_types = {}
|
||||
for event in self.custom_events:
|
||||
event_name = event['event']
|
||||
event_types[event_name] = event_types.get(event_name, 0) + 1
|
||||
|
||||
for event_type, count in event_types.items():
|
||||
print(f" {event_type}: {count} events")
|
||||
|
||||
# Show recent events
|
||||
if self.custom_events:
|
||||
print(f"\nRecent Events:")
|
||||
for event in self.custom_events[-3:]: # Last 3 events
|
||||
print(f" • {event['event']}: {list(event['properties'].keys())}")
|
||||
|
||||
return telemetry_metrics
|
||||
|
||||
def export_telemetry_report(self, output_file: str = "/tmp/telemetry_report.json"):
|
||||
"""Export comprehensive telemetry report."""
|
||||
|
||||
import json
|
||||
|
||||
# Collect all telemetry data
|
||||
report_data = {
|
||||
'session_info': {
|
||||
'session_id': self.telemetry.session_id,
|
||||
'generated_at': datetime.now().isoformat(),
|
||||
'telemetry_enabled': self.telemetry.enabled
|
||||
},
|
||||
'telemetry_metrics': self.telemetry.get_metrics(),
|
||||
'custom_events': self.custom_events,
|
||||
'event_summary': {
|
||||
'total_events': len(self.custom_events),
|
||||
'event_types': {}
|
||||
}
|
||||
}
|
||||
|
||||
# Summarize event types
|
||||
for event in self.custom_events:
|
||||
event_name = event['event']
|
||||
if event_name not in report_data['event_summary']['event_types']:
|
||||
report_data['event_summary']['event_types'][event_name] = {
|
||||
'count': 0,
|
||||
'first_seen': event['tracked_at'],
|
||||
'last_seen': event['tracked_at']
|
||||
}
|
||||
|
||||
summary = report_data['event_summary']['event_types'][event_name]
|
||||
summary['count'] += 1
|
||||
summary['last_seen'] = max(summary['last_seen'], event['tracked_at'])
|
||||
|
||||
# Export to file
|
||||
try:
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(report_data, f, indent=2, default=str)
|
||||
print(f"📄 Telemetry report exported to: {output_file}")
|
||||
return output_file
|
||||
except Exception as e:
|
||||
print(f"❌ Export failed: {e}")
|
||||
return None
|
||||
|
||||
def demonstrate_environment_setup():
|
||||
"""Demonstrate telemetry environment configuration."""
|
||||
|
||||
print(f"🔧 Telemetry Environment Configuration")
|
||||
print("-" * 50)
|
||||
|
||||
# Show current environment variables relevant to telemetry
|
||||
relevant_env_vars = [
|
||||
'PRAISONAI_TELEMETRY_DISABLED',
|
||||
'POSTHOG_API_KEY',
|
||||
'LOGLEVEL'
|
||||
]
|
||||
|
||||
print(f"Environment Variables:")
|
||||
for var in relevant_env_vars:
|
||||
value = os.getenv(var, 'Not set')
|
||||
print(f" {var}: {value}")
|
||||
|
||||
print(f"\n💡 Configuration Options:")
|
||||
print(f"• Set PRAISONAI_TELEMETRY_DISABLED=true to disable telemetry")
|
||||
print(f"• Set POSTHOG_API_KEY=<your_key> for PostHog integration")
|
||||
print(f"• Set LOGLEVEL=DEBUG to see detailed telemetry logs")
|
||||
print(f"• Use track_metrics=True on agents for automatic tracking")
|
||||
|
||||
def main():
|
||||
"""Main demonstration function."""
|
||||
|
||||
print(f"📡 Telemetry Integration Example")
|
||||
print("=" * 60)
|
||||
|
||||
# Setup environment demonstration
|
||||
demonstrate_environment_setup()
|
||||
|
||||
# Create telemetry monitor
|
||||
monitor = TelemetryMonitor(enable_posthog=False) # Set to True if you have PostHog key
|
||||
|
||||
# Demonstrate manual telemetry
|
||||
token_metrics, perf_metrics = monitor.demonstrate_manual_telemetry()
|
||||
|
||||
# Demonstrate agent integration
|
||||
agent = monitor.demonstrate_agent_telemetry()
|
||||
|
||||
# Analyze collected data
|
||||
telemetry_data = monitor.analyze_telemetry_data()
|
||||
|
||||
# Show agent session metrics
|
||||
if agent.metrics_collector:
|
||||
print(f"\n🤖 Agent Session Metrics:")
|
||||
session_metrics = agent.metrics_collector.get_session_metrics()
|
||||
print(f" Total Tokens: {session_metrics['total_tokens']['total_tokens']}")
|
||||
print(f" Duration: {session_metrics['duration_seconds']:.1f}s")
|
||||
|
||||
if session_metrics['performance']:
|
||||
for agent_name, perf in session_metrics['performance'].items():
|
||||
print(f" {agent_name} Performance:")
|
||||
print(f" Avg TTFT: {perf['average_ttft']:.3f}s")
|
||||
print(f" Avg TPS: {perf['average_tokens_per_second']:.1f}")
|
||||
print(f" Requests: {perf['request_count']}")
|
||||
|
||||
# Export comprehensive report
|
||||
print(f"\n📄 Exporting Telemetry Report...")
|
||||
report_file = monitor.export_telemetry_report()
|
||||
|
||||
# Flush telemetry data
|
||||
print(f"\n🚀 Flushing telemetry data...")
|
||||
monitor.telemetry.flush()
|
||||
|
||||
print(f"\n✅ Telemetry Integration Example Complete!")
|
||||
print(f"\n💡 Key Telemetry Features:")
|
||||
print(f"• Automatic metrics tracking with track_metrics=True")
|
||||
print(f"• Manual telemetry via track_tokens() and track_performance()")
|
||||
print(f"• Custom event tracking for application-specific metrics")
|
||||
print(f"• PostHog integration for analytics (with API key)")
|
||||
print(f"• Environment-based configuration")
|
||||
print(f"• Debug logging for development and troubleshooting")
|
||||
print(f"• Session-level data aggregation and export")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -218,7 +218,9 @@ class Agent:
|
||||
max_guardrail_retries: int = 3,
|
||||
handoffs: Optional[List[Union['Agent', 'Handoff']]] = None,
|
||||
base_url: Optional[str] = None,
|
||||
api_key: Optional[str] = None
|
||||
api_key: Optional[str] = None,
|
||||
track_metrics: bool = False,
|
||||
metrics_collector: Optional['MetricsCollector'] = None
|
||||
):
|
||||
"""Initialize an Agent instance.
|
||||
|
||||
@@ -309,6 +311,11 @@ class Agent:
|
||||
If provided, automatically creates a custom LLM instance. Defaults to None.
|
||||
api_key (Optional[str], optional): API key for LLM provider. If not provided,
|
||||
falls back to environment variables. Defaults to None.
|
||||
track_metrics (bool, optional): Enable detailed metrics tracking including token usage,
|
||||
performance metrics (TTFT), and session-level aggregation. Defaults to False.
|
||||
metrics_collector (Optional[MetricsCollector], optional): Custom MetricsCollector instance
|
||||
for session-level metric aggregation. If None and track_metrics is True, a new
|
||||
collector will be created automatically. Defaults to None.
|
||||
|
||||
Raises:
|
||||
ValueError: If all of name, role, goal, backstory, and instructions are None.
|
||||
@@ -507,6 +514,16 @@ Your Goal: {self.goal}
|
||||
if knowledge:
|
||||
for source in knowledge:
|
||||
self._process_knowledge(source)
|
||||
|
||||
# Initialize metrics tracking
|
||||
self.track_metrics = track_metrics
|
||||
self.metrics_collector = metrics_collector
|
||||
self.last_metrics = {} # Store last execution metrics
|
||||
|
||||
if self.track_metrics and self.metrics_collector is None:
|
||||
# Create a new MetricsCollector if none provided
|
||||
from ..telemetry.metrics import MetricsCollector
|
||||
self.metrics_collector = MetricsCollector()
|
||||
|
||||
@property
|
||||
def _openai_client(self):
|
||||
@@ -1182,6 +1199,48 @@ Your Goal: {self.goal}"""
|
||||
max_iterations=10
|
||||
)
|
||||
|
||||
# Extract metrics if tracking is enabled
|
||||
if self.track_metrics and final_response and hasattr(final_response, 'usage'):
|
||||
try:
|
||||
from ..telemetry.metrics import TokenMetrics
|
||||
from ..telemetry import get_telemetry
|
||||
|
||||
# Extract token metrics from the response
|
||||
token_metrics = TokenMetrics.from_completion_usage(final_response.usage)
|
||||
|
||||
# Track performance metrics if available
|
||||
perf_metrics = None
|
||||
if hasattr(self, '_current_performance_metrics'):
|
||||
perf_metrics = self._current_performance_metrics
|
||||
# Calculate tokens per second
|
||||
if token_metrics.output_tokens > 0 and perf_metrics.total_time > 0:
|
||||
perf_metrics.tokens_per_second = token_metrics.output_tokens / perf_metrics.total_time
|
||||
|
||||
# Store last metrics for user access
|
||||
self.last_metrics = {
|
||||
'tokens': token_metrics,
|
||||
'performance': perf_metrics
|
||||
}
|
||||
|
||||
# Add to metrics collector if available
|
||||
if self.metrics_collector:
|
||||
self.metrics_collector.add_agent_metrics(
|
||||
agent_name=self.name,
|
||||
token_metrics=token_metrics,
|
||||
performance_metrics=perf_metrics,
|
||||
model_name=self.llm
|
||||
)
|
||||
|
||||
# Send to telemetry system
|
||||
telemetry = get_telemetry()
|
||||
telemetry.track_tokens(token_metrics)
|
||||
if perf_metrics:
|
||||
telemetry.track_performance(perf_metrics)
|
||||
|
||||
except Exception as metrics_error:
|
||||
# Don't fail the main response if metrics collection fails
|
||||
logging.debug(f"Failed to collect metrics: {metrics_error}")
|
||||
|
||||
return final_response
|
||||
|
||||
except Exception as e:
|
||||
@@ -1249,6 +1308,13 @@ Your Goal: {self.goal}"""
|
||||
# Reset the final display flag for each new conversation
|
||||
self._final_display_shown = False
|
||||
|
||||
# Initialize metrics tracking for this request
|
||||
performance_metrics = None
|
||||
if self.track_metrics:
|
||||
from ..telemetry.metrics import PerformanceMetrics
|
||||
performance_metrics = PerformanceMetrics()
|
||||
performance_metrics.start_timing()
|
||||
|
||||
# Log all parameter values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
param_info = {
|
||||
@@ -1419,7 +1485,19 @@ Your Goal: {self.goal}"""
|
||||
agent_tools=agent_tools
|
||||
)
|
||||
|
||||
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None, reasoning_steps=reasoning_steps, stream=stream, task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
# Set performance metrics for access in _chat_completion
|
||||
if performance_metrics:
|
||||
self._current_performance_metrics = performance_metrics
|
||||
|
||||
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None, reasoning_steps=reasoning_steps, stream=self.stream, task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
|
||||
# End timing for performance metrics
|
||||
if performance_metrics:
|
||||
token_count = 0
|
||||
if response and hasattr(response, 'usage') and hasattr(response.usage, 'completion_tokens'):
|
||||
token_count = response.usage.completion_tokens or 0
|
||||
performance_metrics.end_timing(token_count)
|
||||
|
||||
if not response:
|
||||
# Rollback chat history on response failure
|
||||
self.chat_history = self.chat_history[:chat_history_length]
|
||||
|
||||
@@ -30,6 +30,7 @@ if TYPE_CHECKING:
|
||||
|
||||
# Import the classes for real (not just type checking)
|
||||
from .telemetry import MinimalTelemetry, TelemetryCollector
|
||||
from .metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
|
||||
# Import performance monitoring tools
|
||||
try:
|
||||
@@ -58,6 +59,9 @@ __all__ = [
|
||||
'force_shutdown_telemetry',
|
||||
'MinimalTelemetry',
|
||||
'TelemetryCollector', # For backward compatibility
|
||||
'TokenMetrics',
|
||||
'PerformanceMetrics',
|
||||
'MetricsCollector',
|
||||
]
|
||||
|
||||
# Add performance monitoring to __all__ if available
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
Advanced metrics tracking for PraisonAI Agents.
|
||||
|
||||
This module provides comprehensive token and performance tracking
|
||||
with session-level aggregation and export capabilities.
|
||||
"""
|
||||
|
||||
import time
|
||||
import json
|
||||
from dataclasses import dataclass, asdict
|
||||
from typing import Dict, Any, Optional, List, Union
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
@dataclass
|
||||
class TokenMetrics:
|
||||
"""Comprehensive token tracking for all token types."""
|
||||
input_tokens: int = 0
|
||||
output_tokens: int = 0
|
||||
total_tokens: int = 0
|
||||
|
||||
# Special tokens
|
||||
audio_tokens: int = 0
|
||||
input_audio_tokens: int = 0
|
||||
output_audio_tokens: int = 0
|
||||
cached_tokens: int = 0
|
||||
cache_write_tokens: int = 0
|
||||
reasoning_tokens: int = 0
|
||||
|
||||
def __add__(self, other: 'TokenMetrics') -> 'TokenMetrics':
|
||||
"""Enable metric aggregation."""
|
||||
return TokenMetrics(
|
||||
input_tokens=self.input_tokens + other.input_tokens,
|
||||
output_tokens=self.output_tokens + other.output_tokens,
|
||||
total_tokens=self.total_tokens + other.total_tokens,
|
||||
audio_tokens=self.audio_tokens + other.audio_tokens,
|
||||
input_audio_tokens=self.input_audio_tokens + other.input_audio_tokens,
|
||||
output_audio_tokens=self.output_audio_tokens + other.output_audio_tokens,
|
||||
cached_tokens=self.cached_tokens + other.cached_tokens,
|
||||
cache_write_tokens=self.cache_write_tokens + other.cache_write_tokens,
|
||||
reasoning_tokens=self.reasoning_tokens + other.reasoning_tokens,
|
||||
)
|
||||
|
||||
def update_totals(self):
|
||||
"""Update total_tokens based on input and output tokens."""
|
||||
self.total_tokens = self.input_tokens + self.output_tokens
|
||||
|
||||
@classmethod
|
||||
def from_completion_usage(cls, usage: Any) -> 'TokenMetrics':
|
||||
"""Create TokenMetrics from OpenAI CompletionUsage object."""
|
||||
metrics = cls()
|
||||
|
||||
if hasattr(usage, 'prompt_tokens'):
|
||||
metrics.input_tokens = usage.prompt_tokens or 0
|
||||
if hasattr(usage, 'completion_tokens'):
|
||||
metrics.output_tokens = usage.completion_tokens or 0
|
||||
if hasattr(usage, 'total_tokens'):
|
||||
metrics.total_tokens = usage.total_tokens or 0
|
||||
|
||||
# Handle audio tokens if present
|
||||
if hasattr(usage, 'prompt_tokens_details'):
|
||||
details = usage.prompt_tokens_details
|
||||
if hasattr(details, 'audio_tokens'):
|
||||
metrics.input_audio_tokens = details.audio_tokens or 0
|
||||
metrics.audio_tokens += metrics.input_audio_tokens
|
||||
if hasattr(details, 'cached_tokens'):
|
||||
metrics.cached_tokens = details.cached_tokens or 0
|
||||
|
||||
if hasattr(usage, 'completion_tokens_details'):
|
||||
details = usage.completion_tokens_details
|
||||
if hasattr(details, 'audio_tokens'):
|
||||
metrics.output_audio_tokens = details.audio_tokens or 0
|
||||
metrics.audio_tokens += metrics.output_audio_tokens
|
||||
if hasattr(details, 'reasoning_tokens'):
|
||||
metrics.reasoning_tokens = details.reasoning_tokens or 0
|
||||
|
||||
# Update total if not provided
|
||||
if metrics.total_tokens == 0:
|
||||
metrics.update_totals()
|
||||
|
||||
return metrics
|
||||
|
||||
@dataclass
|
||||
class PerformanceMetrics:
|
||||
"""Performance tracking including TTFT and response times."""
|
||||
time_to_first_token: float = 0.0 # Time to first token in seconds
|
||||
total_time: float = 0.0 # Total generation time in seconds
|
||||
tokens_per_second: float = 0.0 # Tokens generated per second
|
||||
start_time: Optional[float] = None
|
||||
first_token_time: Optional[float] = None
|
||||
end_time: Optional[float] = None
|
||||
|
||||
def start_timing(self):
|
||||
"""Start timing for this request."""
|
||||
self.start_time = time.time()
|
||||
|
||||
def mark_first_token(self):
|
||||
"""Mark when first token was received."""
|
||||
if self.start_time:
|
||||
self.first_token_time = time.time()
|
||||
self.time_to_first_token = self.first_token_time - self.start_time
|
||||
|
||||
def end_timing(self, token_count: int = 0):
|
||||
"""End timing and calculate final metrics."""
|
||||
if self.start_time:
|
||||
self.end_time = time.time()
|
||||
self.total_time = self.end_time - self.start_time
|
||||
|
||||
# Calculate tokens per second if we have token count
|
||||
if token_count > 0 and self.total_time > 0:
|
||||
self.tokens_per_second = token_count / self.total_time
|
||||
|
||||
class MetricsCollector:
|
||||
"""Session-level metric aggregation and export."""
|
||||
|
||||
def __init__(self):
|
||||
self.session_id = f"session_{int(time.time())}_{id(self)}"
|
||||
self.start_time = datetime.now()
|
||||
self.agent_metrics: Dict[str, TokenMetrics] = {}
|
||||
self.agent_performance: Dict[str, List[PerformanceMetrics]] = {}
|
||||
self.model_metrics: Dict[str, TokenMetrics] = {}
|
||||
self.total_metrics = TokenMetrics()
|
||||
|
||||
def add_agent_metrics(self, agent_name: str, token_metrics: TokenMetrics,
|
||||
performance_metrics: Optional[PerformanceMetrics] = None,
|
||||
model_name: Optional[str] = None):
|
||||
"""Add metrics for a specific agent."""
|
||||
# Aggregate by agent
|
||||
if agent_name not in self.agent_metrics:
|
||||
self.agent_metrics[agent_name] = TokenMetrics()
|
||||
self.agent_metrics[agent_name] += token_metrics
|
||||
|
||||
# Track performance metrics
|
||||
if performance_metrics:
|
||||
if agent_name not in self.agent_performance:
|
||||
self.agent_performance[agent_name] = []
|
||||
self.agent_performance[agent_name].append(performance_metrics)
|
||||
|
||||
# Aggregate by model
|
||||
if model_name:
|
||||
if model_name not in self.model_metrics:
|
||||
self.model_metrics[model_name] = TokenMetrics()
|
||||
self.model_metrics[model_name] += token_metrics
|
||||
|
||||
# Update total
|
||||
self.total_metrics += token_metrics
|
||||
|
||||
def get_session_metrics(self) -> Dict[str, Any]:
|
||||
"""Get aggregated session metrics."""
|
||||
# Calculate average performance metrics
|
||||
avg_performance = {}
|
||||
for agent_name, perf_list in self.agent_performance.items():
|
||||
if perf_list:
|
||||
avg_ttft = sum(p.time_to_first_token for p in perf_list) / len(perf_list)
|
||||
avg_total_time = sum(p.total_time for p in perf_list) / len(perf_list)
|
||||
avg_tps = sum(p.tokens_per_second for p in perf_list if p.tokens_per_second > 0)
|
||||
if avg_tps > 0:
|
||||
avg_tps = avg_tps / len([p for p in perf_list if p.tokens_per_second > 0])
|
||||
|
||||
avg_performance[agent_name] = {
|
||||
"average_ttft": avg_ttft,
|
||||
"average_total_time": avg_total_time,
|
||||
"average_tokens_per_second": avg_tps,
|
||||
"request_count": len(perf_list)
|
||||
}
|
||||
|
||||
return {
|
||||
"session_id": self.session_id,
|
||||
"start_time": self.start_time.isoformat(),
|
||||
"duration_seconds": (datetime.now() - self.start_time).total_seconds(),
|
||||
"total_tokens": asdict(self.total_metrics),
|
||||
"by_agent": {name: asdict(metrics) for name, metrics in self.agent_metrics.items()},
|
||||
"by_model": {name: asdict(metrics) for name, metrics in self.model_metrics.items()},
|
||||
"performance": avg_performance
|
||||
}
|
||||
|
||||
def export_metrics(self, file_path: Union[str, Path], format: str = "json"):
|
||||
"""Export metrics to file."""
|
||||
metrics = self.get_session_metrics()
|
||||
|
||||
file_path = Path(file_path)
|
||||
|
||||
if format.lower() == "json":
|
||||
with open(file_path, 'w') as f:
|
||||
json.dump(metrics, f, indent=2, default=str)
|
||||
else:
|
||||
raise ValueError(f"Unsupported export format: {format}")
|
||||
|
||||
def reset(self):
|
||||
"""Reset all metrics for a new session."""
|
||||
self.session_id = f"session_{int(time.time())}_{id(self)}"
|
||||
self.start_time = datetime.now()
|
||||
self.agent_metrics.clear()
|
||||
self.agent_performance.clear()
|
||||
self.model_metrics.clear()
|
||||
self.total_metrics = TokenMetrics()
|
||||
@@ -303,6 +303,59 @@ class MinimalTelemetry:
|
||||
# Track which features are being used
|
||||
self.logger.debug(f"Feature usage tracked: {feature_name}")
|
||||
|
||||
def track_tokens(self, metrics: 'TokenMetrics'):
|
||||
"""
|
||||
Track token usage metrics.
|
||||
|
||||
Args:
|
||||
metrics: TokenMetrics instance with token counts
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
# Send detailed token metrics to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='tokens_used',
|
||||
properties={
|
||||
'total_tokens': metrics.total_tokens,
|
||||
'input_tokens': metrics.input_tokens,
|
||||
'output_tokens': metrics.output_tokens,
|
||||
'cached_tokens': metrics.cached_tokens,
|
||||
'reasoning_tokens': metrics.reasoning_tokens,
|
||||
'audio_tokens': metrics.audio_tokens,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
self.logger.debug(f"Token usage tracked: {metrics.total_tokens} total tokens")
|
||||
|
||||
def track_performance(self, metrics: 'PerformanceMetrics'):
|
||||
"""
|
||||
Track performance metrics including TTFT.
|
||||
|
||||
Args:
|
||||
metrics: PerformanceMetrics instance with timing data
|
||||
"""
|
||||
if not self.enabled:
|
||||
return
|
||||
|
||||
# Send performance metrics to PostHog
|
||||
if self._posthog:
|
||||
self._posthog.capture(
|
||||
distinct_id=self.session_id,
|
||||
event='performance_metrics',
|
||||
properties={
|
||||
'ttft': metrics.time_to_first_token,
|
||||
'total_time': metrics.total_time,
|
||||
'tokens_per_second': metrics.tokens_per_second,
|
||||
'session_id': self.session_id
|
||||
}
|
||||
)
|
||||
|
||||
self.logger.debug(f"Performance tracked: TTFT={metrics.time_to_first_token:.3f}s, TPS={metrics.tokens_per_second:.1f}")
|
||||
|
||||
def get_metrics(self) -> Dict[str, Any]:
|
||||
"""
|
||||
Get current metrics summary.
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for the comprehensive monitoring system implementation.
|
||||
|
||||
Tests all the features requested in issue #970:
|
||||
1. TokenMetrics - Granular token tracking
|
||||
2. PerformanceMetrics - TTFT and response time tracking
|
||||
3. MetricsCollector - Session-level aggregation
|
||||
4. Agent integration - metrics tracking parameters
|
||||
5. Enhanced telemetry - token and performance tracking
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import tempfile
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
# Set environment variable to disable telemetry for testing
|
||||
os.environ['PRAISONAI_TELEMETRY_DISABLED'] = 'true'
|
||||
|
||||
# Import the classes to test
|
||||
from praisonaiagents.telemetry.metrics import TokenMetrics, PerformanceMetrics, MetricsCollector
|
||||
from praisonaiagents.telemetry import get_telemetry
|
||||
from praisonaiagents.agent import Agent
|
||||
|
||||
# Mock CompletionUsage for testing
|
||||
class MockCompletionUsage:
|
||||
def __init__(self):
|
||||
self.prompt_tokens = 100
|
||||
self.completion_tokens = 50
|
||||
self.total_tokens = 150
|
||||
self.prompt_tokens_details = MockPromptTokensDetails()
|
||||
self.completion_tokens_details = MockCompletionTokensDetails()
|
||||
|
||||
class MockPromptTokensDetails:
|
||||
def __init__(self):
|
||||
self.audio_tokens = 10
|
||||
self.cached_tokens = 20
|
||||
|
||||
class MockCompletionTokensDetails:
|
||||
def __init__(self):
|
||||
self.audio_tokens = 5
|
||||
self.reasoning_tokens = 15
|
||||
|
||||
def test_token_metrics():
|
||||
"""Test TokenMetrics functionality."""
|
||||
print("🧪 Testing TokenMetrics...")
|
||||
|
||||
# Test basic creation
|
||||
metrics1 = TokenMetrics(input_tokens=100, output_tokens=50, audio_tokens=10)
|
||||
metrics1.update_totals()
|
||||
assert metrics1.total_tokens == 150, f"Expected 150, got {metrics1.total_tokens}"
|
||||
|
||||
# Test aggregation
|
||||
metrics2 = TokenMetrics(input_tokens=200, output_tokens=75, cached_tokens=30)
|
||||
combined = metrics1 + metrics2
|
||||
assert combined.input_tokens == 300, f"Expected 300, got {combined.input_tokens}"
|
||||
assert combined.output_tokens == 125, f"Expected 125, got {combined.output_tokens}"
|
||||
assert combined.cached_tokens == 30, f"Expected 30, got {combined.cached_tokens}"
|
||||
|
||||
# Test from_completion_usage
|
||||
mock_usage = MockCompletionUsage()
|
||||
metrics3 = TokenMetrics.from_completion_usage(mock_usage)
|
||||
assert metrics3.input_tokens == 100, f"Expected 100, got {metrics3.input_tokens}"
|
||||
assert metrics3.output_tokens == 50, f"Expected 50, got {metrics3.output_tokens}"
|
||||
assert metrics3.cached_tokens == 20, f"Expected 20, got {metrics3.cached_tokens}"
|
||||
assert metrics3.reasoning_tokens == 15, f"Expected 15, got {metrics3.reasoning_tokens}"
|
||||
assert metrics3.audio_tokens == 15, f"Expected 15, got {metrics3.audio_tokens}" # 10 + 5
|
||||
|
||||
print("✅ TokenMetrics tests passed!")
|
||||
|
||||
def test_performance_metrics():
|
||||
"""Test PerformanceMetrics functionality."""
|
||||
print("🧪 Testing PerformanceMetrics...")
|
||||
|
||||
perf = PerformanceMetrics()
|
||||
|
||||
# Test timing
|
||||
perf.start_timing()
|
||||
time.sleep(0.1) # Simulate some processing
|
||||
perf.mark_first_token()
|
||||
time.sleep(0.05) # Simulate additional processing
|
||||
perf.end_timing(100) # 100 tokens generated
|
||||
|
||||
assert perf.time_to_first_token > 0.09, f"TTFT too low: {perf.time_to_first_token}"
|
||||
assert perf.total_time > 0.14, f"Total time too low: {perf.total_time}"
|
||||
assert perf.tokens_per_second > 0, f"TPS should be > 0: {perf.tokens_per_second}"
|
||||
|
||||
print(f"✅ PerformanceMetrics tests passed! TTFT: {perf.time_to_first_token:.3f}s, TPS: {perf.tokens_per_second:.1f}")
|
||||
|
||||
def test_metrics_collector():
|
||||
"""Test MetricsCollector functionality."""
|
||||
print("🧪 Testing MetricsCollector...")
|
||||
|
||||
collector = MetricsCollector()
|
||||
|
||||
# Add metrics for different agents
|
||||
metrics1 = TokenMetrics(input_tokens=100, output_tokens=50, total_tokens=150)
|
||||
perf1 = PerformanceMetrics()
|
||||
perf1.time_to_first_token = 0.5
|
||||
perf1.total_time = 2.0
|
||||
perf1.tokens_per_second = 25.0
|
||||
|
||||
collector.add_agent_metrics("Agent1", metrics1, perf1, "gpt-4o")
|
||||
|
||||
# Add more metrics for same agent
|
||||
metrics2 = TokenMetrics(input_tokens=200, output_tokens=100, total_tokens=300)
|
||||
collector.add_agent_metrics("Agent1", metrics2, model_name="gpt-4o")
|
||||
|
||||
# Add metrics for different agent
|
||||
metrics3 = TokenMetrics(input_tokens=50, output_tokens=25, total_tokens=75)
|
||||
collector.add_agent_metrics("Agent2", metrics3, model_name="claude-3")
|
||||
|
||||
# Test session metrics
|
||||
session_metrics = collector.get_session_metrics()
|
||||
|
||||
assert "Agent1" in session_metrics["by_agent"], "Agent1 not found in session metrics"
|
||||
assert "Agent2" in session_metrics["by_agent"], "Agent2 not found in session metrics"
|
||||
assert session_metrics["by_agent"]["Agent1"]["input_tokens"] == 300, "Agent1 input tokens incorrect"
|
||||
assert session_metrics["total_tokens"]["total_tokens"] == 525, "Total tokens incorrect"
|
||||
|
||||
# Test export functionality
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
||||
temp_path = f.name
|
||||
|
||||
try:
|
||||
collector.export_metrics(temp_path)
|
||||
|
||||
# Verify exported data
|
||||
with open(temp_path, 'r') as f:
|
||||
exported_data = json.load(f)
|
||||
|
||||
assert "session_id" in exported_data, "Session ID not in exported data"
|
||||
assert "by_agent" in exported_data, "by_agent not in exported data"
|
||||
assert exported_data["total_tokens"]["total_tokens"] == 525, "Exported total tokens incorrect"
|
||||
|
||||
finally:
|
||||
os.unlink(temp_path)
|
||||
|
||||
print("✅ MetricsCollector tests passed!")
|
||||
|
||||
def test_agent_integration():
|
||||
"""Test Agent integration with metrics tracking."""
|
||||
print("🧪 Testing Agent metrics integration...")
|
||||
|
||||
# Test Agent creation with metrics tracking
|
||||
collector = MetricsCollector()
|
||||
agent = Agent(
|
||||
name="TestAgent",
|
||||
instructions="You are a test agent",
|
||||
track_metrics=True,
|
||||
metrics_collector=collector
|
||||
)
|
||||
|
||||
assert agent.track_metrics == True, "track_metrics not set correctly"
|
||||
assert agent.metrics_collector == collector, "metrics_collector not set correctly"
|
||||
assert hasattr(agent, 'last_metrics'), "last_metrics attribute missing"
|
||||
|
||||
# Test Agent with auto-created collector
|
||||
agent2 = Agent(
|
||||
name="TestAgent2",
|
||||
instructions="You are another test agent",
|
||||
track_metrics=True
|
||||
)
|
||||
|
||||
assert agent2.track_metrics == True, "track_metrics not set correctly"
|
||||
assert agent2.metrics_collector is not None, "MetricsCollector not auto-created"
|
||||
|
||||
# Test Agent without metrics tracking (default)
|
||||
agent3 = Agent(name="TestAgent3", instructions="You are a normal agent")
|
||||
assert agent3.track_metrics == False, "track_metrics should default to False"
|
||||
|
||||
print("✅ Agent integration tests passed!")
|
||||
|
||||
def test_enhanced_telemetry():
|
||||
"""Test enhanced telemetry functionality."""
|
||||
print("🧪 Testing enhanced telemetry...")
|
||||
|
||||
# Get telemetry instance
|
||||
telemetry = get_telemetry()
|
||||
|
||||
# Test token tracking
|
||||
token_metrics = TokenMetrics(
|
||||
input_tokens=100,
|
||||
output_tokens=50,
|
||||
total_tokens=150,
|
||||
cached_tokens=20,
|
||||
reasoning_tokens=10,
|
||||
audio_tokens=5
|
||||
)
|
||||
|
||||
# This should not raise any exceptions
|
||||
telemetry.track_tokens(token_metrics)
|
||||
|
||||
# Test performance tracking
|
||||
perf_metrics = PerformanceMetrics()
|
||||
perf_metrics.time_to_first_token = 0.5
|
||||
perf_metrics.total_time = 2.0
|
||||
perf_metrics.tokens_per_second = 25.0
|
||||
|
||||
# This should not raise any exceptions
|
||||
telemetry.track_performance(perf_metrics)
|
||||
|
||||
print("✅ Enhanced telemetry tests passed!")
|
||||
|
||||
def test_backward_compatibility():
|
||||
"""Test that existing functionality still works."""
|
||||
print("🧪 Testing backward compatibility...")
|
||||
|
||||
# Test Agent creation without metrics (should work as before)
|
||||
agent = Agent(name="CompatibilityAgent", instructions="Test compatibility")
|
||||
|
||||
assert hasattr(agent, 'name'), "Basic agent attributes missing"
|
||||
assert agent.name == "CompatibilityAgent", "Agent name not set correctly"
|
||||
assert agent.track_metrics == False, "Default metrics tracking should be False"
|
||||
|
||||
# Test telemetry basic functions still work
|
||||
telemetry = get_telemetry()
|
||||
telemetry.track_agent_execution("test_agent", success=True)
|
||||
telemetry.track_task_completion("test_task", success=True)
|
||||
telemetry.track_tool_usage("test_tool", success=True)
|
||||
telemetry.track_error("test_error")
|
||||
telemetry.track_feature_usage("test_feature")
|
||||
|
||||
print("✅ Backward compatibility tests passed!")
|
||||
|
||||
def main():
|
||||
"""Run all tests."""
|
||||
print("🚀 Starting comprehensive monitoring system tests...")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
test_token_metrics()
|
||||
test_performance_metrics()
|
||||
test_metrics_collector()
|
||||
test_agent_integration()
|
||||
test_enhanced_telemetry()
|
||||
test_backward_compatibility()
|
||||
|
||||
print("=" * 60)
|
||||
print("🎉 All tests passed! Monitoring system implementation is working correctly.")
|
||||
print()
|
||||
print("📊 Features implemented:")
|
||||
print(" ✅ TokenMetrics - Granular token tracking with aggregation")
|
||||
print(" ✅ PerformanceMetrics - TTFT and response time measurement")
|
||||
print(" ✅ MetricsCollector - Session-level aggregation and export")
|
||||
print(" ✅ Agent Integration - Optional track_metrics and metrics_collector")
|
||||
print(" ✅ Enhanced Telemetry - Token and performance tracking methods")
|
||||
print(" ✅ Backward Compatibility - All existing functionality preserved")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Test failed: {e}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user