Spaces:
Sleeping
Sleeping
feat(metrics): add per-agent token tracking and cost estimation
Browse filesRecord total_tokens from OpenRouter usage response in MetricsCollector.
Expose in /api/v1/metrics snapshot as total_tokens and estimated_cost_usd.
Cost rate set for llama-3.3-70b; update constant when switching models.
- app/core/llm.py +6 -0
- app/core/metrics.py +7 -0
app/core/llm.py
CHANGED
|
@@ -58,6 +58,12 @@ async def call_llm(prompt: str, system_prompt: Optional[str] = None, max_tokens:
|
|
| 58 |
response.raise_for_status()
|
| 59 |
|
| 60 |
data = response.json()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
return str(data["choices"][0]["message"]["content"])
|
| 62 |
|
| 63 |
async def call_llm_for_json(prompt: str, max_tokens: int = 2000) -> dict:
|
|
|
|
| 58 |
response.raise_for_status()
|
| 59 |
|
| 60 |
data = response.json()
|
| 61 |
+
usage = data.get("usage", {})
|
| 62 |
+
total_tokens = usage.get("total_tokens", 0)
|
| 63 |
+
if total_tokens:
|
| 64 |
+
from app.core.metrics import metrics
|
| 65 |
+
metrics.record_tokens("llm_total", total_tokens)
|
| 66 |
+
return str(data["choices"][0]["message"]["content"])
|
| 67 |
return str(data["choices"][0]["message"]["content"])
|
| 68 |
|
| 69 |
async def call_llm_for_json(prompt: str, max_tokens: int = 2000) -> dict:
|
app/core/metrics.py
CHANGED
|
@@ -11,6 +11,7 @@ class MetricsCollector:
|
|
| 11 |
self._latencies: dict[str, list[float]] = defaultdict(list)
|
| 12 |
self._errors: dict[str, int] = defaultdict(int)
|
| 13 |
self._started_at = time.time()
|
|
|
|
| 14 |
|
| 15 |
def record_run(self, agent: str, duration_seconds: float, success: bool) -> None:
|
| 16 |
with self._lock:
|
|
@@ -19,6 +20,10 @@ class MetricsCollector:
|
|
| 19 |
if not success:
|
| 20 |
self._errors[agent] += 1
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
def snapshot(self) -> dict[str, Any]:
|
| 23 |
with self._lock:
|
| 24 |
agents: dict[str, Any] = {}
|
|
@@ -29,6 +34,8 @@ class MetricsCollector:
|
|
| 29 |
"errors": self._errors.get(agent, 0),
|
| 30 |
"avg_latency_s": round(sum(lats) / len(lats), 3) if lats else 0.0,
|
| 31 |
"p99_latency_s": round(sorted(lats)[int(len(lats) * 0.99)], 3) if lats else 0.0,
|
|
|
|
|
|
|
| 32 |
}
|
| 33 |
return {
|
| 34 |
"uptime_seconds": round(time.time() - self._started_at, 1),
|
|
|
|
| 11 |
self._latencies: dict[str, list[float]] = defaultdict(list)
|
| 12 |
self._errors: dict[str, int] = defaultdict(int)
|
| 13 |
self._started_at = time.time()
|
| 14 |
+
self._tokens: dict[str, int] = defaultdict(int)
|
| 15 |
|
| 16 |
def record_run(self, agent: str, duration_seconds: float, success: bool) -> None:
|
| 17 |
with self._lock:
|
|
|
|
| 20 |
if not success:
|
| 21 |
self._errors[agent] += 1
|
| 22 |
|
| 23 |
+
def record_tokens(self, agent: str, tokens: int) -> None:
|
| 24 |
+
with self._lock:
|
| 25 |
+
self._tokens[agent] += tokens
|
| 26 |
+
|
| 27 |
def snapshot(self) -> dict[str, Any]:
|
| 28 |
with self._lock:
|
| 29 |
agents: dict[str, Any] = {}
|
|
|
|
| 34 |
"errors": self._errors.get(agent, 0),
|
| 35 |
"avg_latency_s": round(sum(lats) / len(lats), 3) if lats else 0.0,
|
| 36 |
"p99_latency_s": round(sorted(lats)[int(len(lats) * 0.99)], 3) if lats else 0.0,
|
| 37 |
+
"total_tokens": self._tokens.get(agent, 0),
|
| 38 |
+
"estimated_cost_usd": round(self._tokens.get(agent, 0) * 0.0000009, 6),
|
| 39 |
}
|
| 40 |
return {
|
| 41 |
"uptime_seconds": round(time.time() - self._started_at, 1),
|