ollive-oss-assistant / shared /observability.py
VPM100's picture
Upload shared/observability.py
36ebd5a verified
Raw
History Blame Contribute Delete
2.57 kB
import sqlite3
import time
from contextlib import contextmanager
from pathlib import Path
from typing import Optional
DB_PATH = Path(__file__).parent.parent / "data" / "observability.db"
# Cost per 1K tokens (USD) — update as pricing changes
COST_PER_1K = {
"llama-3.3-70b-versatile": {"in": 0.00059, "out": 0.00079}, # Groq pricing
"llama-3.1-8b-instant": {"in": 0.00005, "out": 0.00008},
"Qwen/Qwen2.5-0.5B-Instruct": {"in": 0.0, "out": 0.0}, # free HF tier
}
def _get_conn() -> sqlite3.Connection:
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
conn = sqlite3.connect(str(DB_PATH))
conn.execute(
"""CREATE TABLE IF NOT EXISTS calls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
assistant TEXT NOT NULL,
model TEXT NOT NULL,
ts REAL NOT NULL,
latency_ms REAL NOT NULL,
in_tokens INTEGER NOT NULL,
out_tokens INTEGER NOT NULL,
est_cost_usd REAL NOT NULL
)"""
)
conn.commit()
return conn
class ObservabilityLogger:
def log_call(
self,
assistant: str,
model: str,
latency_ms: float,
in_tokens: int,
out_tokens: int,
) -> None:
rates = COST_PER_1K.get(model, {"in": 0.0, "out": 0.0})
cost = (in_tokens / 1000) * rates["in"] + (out_tokens / 1000) * rates["out"]
conn = _get_conn()
conn.execute(
"INSERT INTO calls (assistant, model, ts, latency_ms, in_tokens, out_tokens, est_cost_usd) VALUES (?,?,?,?,?,?,?)",
(assistant, model, time.time(), latency_ms, in_tokens, out_tokens, cost),
)
conn.commit()
conn.close()
def get_summary(self, assistant: Optional[str] = None) -> dict:
conn = _get_conn()
where = "WHERE assistant=?" if assistant else ""
params = (assistant,) if assistant else ()
row = conn.execute(
f"SELECT COUNT(*), AVG(latency_ms), SUM(est_cost_usd), SUM(in_tokens+out_tokens) FROM calls {where}",
params,
).fetchone()
conn.close()
return {
"calls": row[0] or 0,
"avg_latency_ms": round(row[1] or 0, 1),
"total_cost_usd": round(row[2] or 0, 6),
"total_tokens": row[3] or 0,
}
@contextmanager
def timed():
"""Context manager that yields a dict; sets 'ms' key on exit."""
result = {}
start = time.perf_counter()
try:
yield result
finally:
result["ms"] = (time.perf_counter() - start) * 1000