chopratejas--headroom
0ef5fcb1c5
Security / Dependency audit (pip-audit) (push) Has been cancelled
Security / CodeQL (javascript-typescript) (push) Has been cancelled
Security / CodeQL (python) (push) Has been cancelled
Security / Secret scan (gitleaks) (push) Has been cancelled
rust / test (ubuntu) (push) Has been cancelled
rust / simulator e2e (macos-latest) (push) Has been cancelled
rust / simulator e2e (ubuntu-latest) (push) Has been cancelled
rust / simulator e2e (windows-latest) (push) Has been cancelled
rust / wheels (aarch64-apple-darwin) (push) Has been cancelled
rust / wheels (x86_64-unknown-linux-gnu) (push) Has been cancelled
rust / wheels (x86_64-apple-darwin) (push) Has been cancelled
rust / audit (push) Has been cancelled
rust / parity (nightly, allowed to fail during Phase 0) (push) Has been cancelled
CI / commitlint (push) Has been skipped
Dev Containers / validate (.devcontainer/devcontainer.json, default) (push) Failing after 0s
Dev Containers / validate (.devcontainer/memory-stack/devcontainer.json, memory-stack) (push) Failing after 0s
Dev Containers / validate-worktree (push) Failing after 0s
CI / changes (push) Failing after 4s
Deploy Documentation / validate (push) Has been skipped
Deploy Documentation / deploy (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, claude) (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, codex) (push) Failing after 1s
Install Native E2E / install-native (ubuntu-latest) (push) Failing after 1s
OpenCode Plugin / typecheck + build + test (push) Failing after 1s
Init Native E2E / init-native (ubuntu-latest, copilot) (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
Wrap E2E / docker-wrap-e2e (push) Failing after 1s
Wrap Native E2E / wrap-native (ubuntu-latest) (push) Failing after 1s
Init E2E / docker-init-e2e (push) Failing after 4s
Merge Conflicts / merge-conflicts (push) Failing after 4s
CI / lint (push) Has been cancelled
CI / build-wheel (push) Has been cancelled
CI / build-wheel-windows (push) Has been cancelled
CI / prefetch-model (push) Has been cancelled
CI / test-dashboard-ui (push) Has been cancelled
CI / test (1) (push) Has been cancelled
CI / test (2) (push) Has been cancelled
CI / test (3) (push) Has been cancelled
CI / test (4) (push) Has been cancelled
CI / test-extras (push) Has been cancelled
CI / test-agno (push) Has been cancelled
CI / build (push) Has been cancelled
CI / workflow-validation (push) Has been cancelled
CI / docker-native-e2e (push) Has been cancelled
CI / windows-native-wrapper (push) Has been cancelled
CI / macos-native-wrapper (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-manifest (map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-nonroot name:code-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim name:code-slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-code-slim-nonroot name:code-slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-nonroot name:nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim name:slim]) (push) Has been cancelled
Docker / docker-build (map[name:amd64 platform:linux/amd64 runs_on:ubuntu-24.04], map[bake_target:runtime-slim-nonroot name:slim-nonroot]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime name:]) (push) Has been cancelled
Docker / docker-build (map[name:arm64 platform:linux/arm64 runs_on:ubuntu-24.04-arm], map[bake_target:runtime-code name:code]) (push) Has been cancelled
Docker / promote-latest (push) Has been cancelled
Init Native E2E / init-native (macos-latest, claude) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, codex) (push) Has been cancelled
Init Native E2E / init-native (macos-latest, copilot) (push) Has been cancelled
Install Native E2E / install-native (macos-latest) (push) Has been cancelled
Wrap Native E2E / wrap-native (macos-latest) (push) Has been cancelled
221 行
7.6 KiB
Python
221 行
7.6 KiB
Python
"""JSONL file storage implementation for Headroom SDK."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from collections.abc import Iterator
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from ..config import RequestMetrics
|
|
from ..utils import format_timestamp, parse_timestamp
|
|
from .base import Storage
|
|
|
|
|
|
class JSONLStorage(Storage):
|
|
"""JSONL file-based metrics storage."""
|
|
|
|
def __init__(self, file_path: str):
|
|
"""
|
|
Initialize JSONL storage.
|
|
|
|
Args:
|
|
file_path: Path to JSONL file.
|
|
"""
|
|
self.file_path = file_path
|
|
self._ensure_file_exists()
|
|
|
|
def _ensure_file_exists(self) -> None:
|
|
"""Create file and parent directories if they don't exist."""
|
|
path = Path(self.file_path)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
if not path.exists():
|
|
path.touch()
|
|
|
|
def _metrics_to_dict(self, metrics: RequestMetrics) -> dict[str, Any]:
|
|
"""Convert RequestMetrics to serializable dict."""
|
|
return {
|
|
"id": metrics.request_id,
|
|
"timestamp": format_timestamp(metrics.timestamp),
|
|
"model": metrics.model,
|
|
"stream": metrics.stream,
|
|
"mode": metrics.mode,
|
|
"tokens_input_before": metrics.tokens_input_before,
|
|
"tokens_input_after": metrics.tokens_input_after,
|
|
"tokens_output": metrics.tokens_output,
|
|
"block_breakdown": metrics.block_breakdown,
|
|
"waste_signals": metrics.waste_signals,
|
|
"stable_prefix_hash": metrics.stable_prefix_hash,
|
|
"cache_alignment_score": metrics.cache_alignment_score,
|
|
"cached_tokens": metrics.cached_tokens,
|
|
"transforms_applied": metrics.transforms_applied,
|
|
"tool_units_dropped": metrics.tool_units_dropped,
|
|
"turns_dropped": metrics.turns_dropped,
|
|
"messages_hash": metrics.messages_hash,
|
|
"error": metrics.error,
|
|
}
|
|
|
|
def _dict_to_metrics(self, data: dict[str, Any]) -> RequestMetrics:
|
|
"""Convert dict to RequestMetrics."""
|
|
return RequestMetrics(
|
|
request_id=data["id"],
|
|
timestamp=parse_timestamp(data["timestamp"]),
|
|
model=data["model"],
|
|
stream=data["stream"],
|
|
mode=data["mode"],
|
|
tokens_input_before=data["tokens_input_before"],
|
|
tokens_input_after=data["tokens_input_after"],
|
|
tokens_output=data.get("tokens_output"),
|
|
block_breakdown=data.get("block_breakdown", {}),
|
|
waste_signals=data.get("waste_signals", {}),
|
|
stable_prefix_hash=data.get("stable_prefix_hash", ""),
|
|
cache_alignment_score=data.get("cache_alignment_score", 0.0),
|
|
cached_tokens=data.get("cached_tokens"),
|
|
transforms_applied=data.get("transforms_applied", []),
|
|
tool_units_dropped=data.get("tool_units_dropped", 0),
|
|
turns_dropped=data.get("turns_dropped", 0),
|
|
messages_hash=data.get("messages_hash", ""),
|
|
error=data.get("error"),
|
|
)
|
|
|
|
def save(self, metrics: RequestMetrics) -> None:
|
|
"""Save request metrics."""
|
|
data = self._metrics_to_dict(metrics)
|
|
with open(self.file_path, "a") as f:
|
|
f.write(json.dumps(data) + "\n")
|
|
|
|
def get(self, request_id: str) -> RequestMetrics | None:
|
|
"""Get metrics by request ID."""
|
|
for metrics in self.iter_all():
|
|
if metrics.request_id == request_id:
|
|
return metrics
|
|
return None
|
|
|
|
def query(
|
|
self,
|
|
start_time: datetime | None = None,
|
|
end_time: datetime | None = None,
|
|
model: str | None = None,
|
|
mode: str | None = None,
|
|
limit: int = 100,
|
|
offset: int = 0,
|
|
) -> list[RequestMetrics]:
|
|
"""Query metrics with filters."""
|
|
results: list[RequestMetrics] = []
|
|
skipped = 0
|
|
|
|
for metrics in self.iter_all():
|
|
# Apply filters
|
|
if start_time is not None and metrics.timestamp < start_time:
|
|
continue
|
|
if end_time is not None and metrics.timestamp > end_time:
|
|
continue
|
|
if model is not None and metrics.model != model:
|
|
continue
|
|
if mode is not None and metrics.mode != mode:
|
|
continue
|
|
|
|
# Handle offset
|
|
if skipped < offset:
|
|
skipped += 1
|
|
continue
|
|
|
|
results.append(metrics)
|
|
|
|
# Handle limit
|
|
if len(results) >= limit:
|
|
break
|
|
|
|
# Sort by timestamp descending
|
|
results.sort(key=lambda m: m.timestamp, reverse=True)
|
|
return results
|
|
|
|
def count(
|
|
self,
|
|
start_time: datetime | None = None,
|
|
end_time: datetime | None = None,
|
|
model: str | None = None,
|
|
mode: str | None = None,
|
|
) -> int:
|
|
"""Count metrics matching filters."""
|
|
count = 0
|
|
|
|
for metrics in self.iter_all():
|
|
if start_time is not None and metrics.timestamp < start_time:
|
|
continue
|
|
if end_time is not None and metrics.timestamp > end_time:
|
|
continue
|
|
if model is not None and metrics.model != model:
|
|
continue
|
|
if mode is not None and metrics.mode != mode:
|
|
continue
|
|
count += 1
|
|
|
|
return count
|
|
|
|
def iter_all(self) -> Iterator[RequestMetrics]:
|
|
"""Iterate over all stored metrics."""
|
|
if not Path(self.file_path).exists():
|
|
return
|
|
|
|
with open(self.file_path) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
data = json.loads(line)
|
|
yield self._dict_to_metrics(data)
|
|
except json.JSONDecodeError:
|
|
# Skip malformed lines
|
|
continue
|
|
|
|
def get_summary_stats(
|
|
self,
|
|
start_time: datetime | None = None,
|
|
end_time: datetime | None = None,
|
|
) -> dict[str, Any]:
|
|
"""Get summary statistics."""
|
|
total_requests = 0
|
|
total_tokens_before = 0
|
|
total_tokens_after = 0
|
|
total_cache_alignment = 0.0
|
|
audit_count = 0
|
|
optimize_count = 0
|
|
|
|
for metrics in self.iter_all():
|
|
if start_time is not None and metrics.timestamp < start_time:
|
|
continue
|
|
if end_time is not None and metrics.timestamp > end_time:
|
|
continue
|
|
|
|
total_requests += 1
|
|
total_tokens_before += metrics.tokens_input_before
|
|
total_tokens_after += metrics.tokens_input_after
|
|
total_cache_alignment += metrics.cache_alignment_score
|
|
|
|
if metrics.mode == "audit":
|
|
audit_count += 1
|
|
elif metrics.mode == "optimize":
|
|
optimize_count += 1
|
|
|
|
total_tokens_saved = total_tokens_before - total_tokens_after
|
|
avg_tokens_saved = total_tokens_saved / total_requests if total_requests > 0 else 0
|
|
avg_cache_alignment = total_cache_alignment / total_requests if total_requests > 0 else 0
|
|
|
|
return {
|
|
"total_requests": total_requests,
|
|
"total_tokens_before": total_tokens_before,
|
|
"total_tokens_after": total_tokens_after,
|
|
"total_tokens_saved": total_tokens_saved,
|
|
"avg_tokens_saved": avg_tokens_saved,
|
|
"avg_cache_alignment": avg_cache_alignment,
|
|
"audit_count": audit_count,
|
|
"optimize_count": optimize_count,
|
|
}
|
|
|
|
def close(self) -> None:
|
|
"""No-op for file storage."""
|
|
pass
|