Observed since 21 Oct, 11:24 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ai-monitor
Ultra-lightweight AI monitoring - pure Python, no compilation required! Docker-friendly.
A comprehensive, zero-configuration monitoring solution for AI agents that requires no source code changes. Simply import and use decorators or context managers.
One command installs everything:
pip install ai-monitor
That's it! No cloning, no source code changes, no complex setup. Your AI monitoring is ready to use immediately.
from ai_monitor import ultra_simple_setup
# Add this ONE line to enable comprehensive monitoring
ultra_simple_setup()
# Your existing AI code works unchanged!
import openai
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
)
# 🎉 Automatically monitored: latency, tokens, quality, traces
from ai_monitor import setup_with_traceloop
# Enable enterprise-grade tracing
setup_with_traceloop("my_app_name")
# Your AI code - now with distributed tracing!
from flask import Flask
from ai_monitor import flask_plug_and_play
app = Flask(__name__)
# One line to monitor entire Flask app
flask_plug_and_play(app)
# All your routes are now monitored automatically!
After pip install ai-monitor, you get:
http://localhost:8000/metricsfrom ai_monitor import monitor_llm_call, monitor_agent, monitor_tool_use
# Monitor LLM calls automatically
@monitor_llm_call(model="gpt-4")
def call_openai(prompt):
# Your existing LLM code - no changes needed!
response = openai.ChatCompletion.create(...)
return response
# Monitor agent sessions
@monitor_agent(name="my_assistant")
def run_agent():
# Your agent code
return agent_result
# Monitor tool usage
@monitor_tool_use(tool_name="web_search")
def search_web(query):
# Your tool code
return search_results
from ai_monitor import monitor_llm_call, monitor_agent_session
# Monitor individual LLM calls
with monitor_llm_call("gpt-4", "Hello world") as monitor:
response = call_llm("Hello world")
monitor.record_response(
response="Hello! How can I help?",
input_tokens=2,
output_tokens=5,
cost=0.001
)
# Monitor complete agent sessions
with monitor_agent_session("customer_service") as session:
# Record LLM calls within session
session.record_llm_call("gpt-4", "Help request", "I can help with that", 10, 15, 0.002)
# Record tool usage
session.record_tool_use("knowledge_base", "search query", "results", success=True, duration=0.5)
from ai_monitor import get_monitor
# Get the default monitor instance
monitor = get_monitor()
# Record data manually
call_id = monitor.record_llm_call(
model="gpt-3.5-turbo",
prompt="What is AI?",
response="AI is artificial intelligence...",
input_tokens=4,
output_tokens=20,
latency=1.2,
cost=0.001
)
# Get monitoring data
stats = monitor.get_summary_stats()
metrics = monitor.get_metrics()
recent_calls = monitor.get_llm_calls(limit=10)
client.chat.completions.create() ✅client.completions.create() ✅client.embeddings.create() ✅client.images.generate() (DALL-E) ✅client.audio.transcriptions.create() (Whisper) ✅client.audio.translations.create() (Whisper) ✅client.moderations.create() ✅client.models.list(), file operations, fine-tuning ✅from ai_monitor import init_monitoring, MonitoringConfig
config = MonitoringConfig(
# Exporters
enable_prometheus=True,
enable_jaeger=True,
enable_logging=True,
# Prometheus
prometheus_port=8000,
# Features
detect_hallucination=True,
detect_drift=True,
track_costs=True,
# Sampling
trace_sampling_rate=1.0
)
monitor = init_monitoring(config)
from ai_monitor.detectors import HallucinationDetector
detector = HallucinationDetector()
result = detector.detect(
prompt="What is the capital of France?",
response="I think it might be Paris, but I'm not entirely sure...",
context="France is a country in Europe."
)
print(f"Is hallucination: {result.is_hallucination}")
print(f"Confidence: {result.confidence_score}")
print(f"Reasons: {result.reasons}")
from ai_monitor.detectors import DriftDetector
drift_detector = DriftDetector()
# Update with each LLM call
drift_detector.update(
latency=1.2,
input_tokens=10,
output_tokens=50,
cost=0.002,
response="AI response text"
)
# Check for drift
drift_results = drift_detector.detect_drift()
for drift in drift_results:
if drift.has_drift:
print(f"Drift detected in {drift.drift_type}")
print(f"Baseline: {drift.baseline_value}, Current: {drift.current_value}")
ai_monitor_llm_calls_total{model="gpt-4"} 150
ai_monitor_llm_latency_seconds_bucket{model="gpt-4",le="1.0"} 120
ai_monitor_tokens_total{model="gpt-4",type="input"} 5000
ai_monitor_tokens_total{model="gpt-4",type="output"} 15000
ai_monitor_cost_total{model="gpt-4"} 2.50
from ai_monitor import get_monitor
monitor = get_monitor()
stats = monitor.get_summary_stats()
print(f"Total LLM calls: {stats['total_calls']}")
print(f"Total tokens: {stats['total_tokens']}")
print(f"Total cost: ${stats['total_cost']:.4f}")
print(f"Average latency: {stats['average_latency']:.2f}s")
print(f"Tokens per second: {stats['tokens_per_second']:.1f}")
from ai_monitor import monitor_llm_call
import openai
@monitor_llm_call() # Automatically detects OpenAI format
def chat_with_gpt(messages):
response = openai.ChatCompletion.create(
model="gpt-4",
messages=messages
)
return response
# Usage - monitoring happens automatically!
result = chat_with_gpt([{"role": "user", "content": "Hello"}])
@monitor_llm_call(model="claude-3")
def chat_with_claude(prompt):
response = anthropic.completions.create(
model="claude-3-sonnet-20240229",
prompt=prompt,
max_tokens_to_sample=100
)
return response
@monitor_agent(name="research_assistant")
def research_agent(query):
with monitor_agent_session("research_session") as session:
# Search step
search_results = search_web(query)
session.record_tool_use("web_search", query, search_results, True, 2.1)
# Analysis step
analysis = analyze_with_llm(search_results)
session.record_llm_call("gpt-4", f"Analyze: {search_results}", analysis, 100, 200, 0.01)
# Summary step
summary = summarize_findings(analysis)
session.record_llm_call("gpt-3.5-turbo", f"Summarize: {analysis}", summary, 200, 50, 0.002)
return summary
localhost:8000/metricsgroups:
- name: ai_monitor_alerts
rules:
- alert: HighLatency
expr: ai_monitor_llm_latency_seconds > 5
labels:
severity: warning
annotations:
summary: "High LLM latency detected"
- alert: CostThreshold
expr: increase(ai_monitor_cost_total[1h]) > 10
labels:
severity: critical
annotations:
summary: "Hourly cost exceeds $10"
from ai_monitor import configure_exporters, get_monitor
monitor = get_monitor()
configure_exporters(monitor, [
{'type': 'console'},
{'type': 'json_file', 'file_prefix': 'my_app_monitor'}
])
from ai_monitor.utils import PerformanceProfiler
profiler = PerformanceProfiler()
with profiler.profile_operation("llm_inference"):
result = call_llm("prompt")
summary = profiler.get_performance_summary()
MIT License - feel free to use in your projects!
Ready to monitor your AI agents? Just pip install the dependencies and add a single decorator! 🚀
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
numpyEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | numpy>=1.20.0 | runtime |
requestsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | requests>=2.25.0 | runtime |
httpxEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | httpx>=0.24.0 | runtime |
tiktokenEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | tiktoken>=0.5.0 | runtime |
psutilEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | psutil>=5.9.0 | runtime |
prometheus-clientEnvironment{
"extras": "",
"marker": "extra == \"prometheus\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | prometheus-client>=0.15.0; extra == "prometheus" | runtime |
opentelemetry-apiEnvironment{
"extras": "",
"marker": "extra == \"tracing\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-api>=1.22.0; extra == "tracing" | runtime |
opentelemetry-sdkEnvironment{
"extras": "",
"marker": "extra == \"tracing\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-sdk>=1.22.0; extra == "tracing" | runtime |
opentelemetry-exporter-jaegerEnvironment{
"extras": "",
"marker": "extra == \"tracing\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-exporter-jaeger>=1.21.0; extra == "tracing" | runtime |
opentelemetry-apiEnvironment{
"extras": "",
"marker": "extra == \"jaeger\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-api>=1.15.0; extra == "jaeger" | runtime |
opentelemetry-sdkEnvironment{
"extras": "",
"marker": "extra == \"jaeger\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-sdk>=1.15.0; extra == "jaeger" | runtime |
opentelemetry-exporter-jaegerEnvironment{
"extras": "",
"marker": "extra == \"jaeger\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-exporter-jaeger>=1.15.0; extra == "jaeger" | runtime |
flaskEnvironment{
"extras": "",
"marker": "extra == \"dashboard\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flask>=2.0.0; extra == "dashboard" | runtime |
flask-corsEnvironment{
"extras": "",
"marker": "extra == \"dashboard\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flask-cors>=3.0.0; extra == "dashboard" | runtime |
sentence-transformersEnvironment{
"extras": "",
"marker": "extra == \"semantic\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | sentence-transformers>=2.0.0; extra == "semantic" | runtime |
psutilEnvironment{
"extras": "",
"marker": "extra == \"system\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | psutil>=5.9.0; extra == "system" | runtime |
prometheus-clientEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | prometheus-client>=0.15.0; extra == "all" | runtime |
opentelemetry-apiEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-api>=1.22.0; extra == "all" | runtime |
opentelemetry-sdkEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-sdk>=1.22.0; extra == "all" | runtime |
opentelemetry-exporter-jaegerEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | opentelemetry-exporter-jaeger>=1.21.0; extra == "all" | runtime |
psutilEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | psutil>=5.9.0; extra == "all" | runtime |
flaskEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flask>=2.0.0; extra == "all" | runtime |
flask-corsEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flask-cors>=3.0.0; extra == "all" | runtime |
sentence-transformersEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | sentence-transformers>=2.0.0; extra == "all" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest>=7.0.0; extra == "dev" | runtime |
pytest-covEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | pytest-cov>=4.0.0; extra == "dev" | runtime |
blackEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | black; extra == "dev" | runtime |
flake8Environment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | flake8; extra == "dev" | runtime |
mypyEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.8",
"evaluation": "NOT_EVALUATED"
} | mypy; extra == "dev" | runtime |
bdist_wheel · 94.6 KB · Python py3
sdist · 97.49 KB · Python source
Observed since 21 Oct, 11:24 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:51ed5c01405ed4e40dd3b5d1907a8e1d8adb7ea2561b304cf62745a828360a85