Repository report generation now captures per-run operational metrics and exposes aggregate period snapshots for operators.
Per-report metrics captured
Each generated repository Report now stores nullable metrics fields:
model_latency_msprompt_tokenscompletion_tokenstotal_tokens
Latency is measured by the reporting service (time.monotonic) and token usage
comes from the selected status model adapter when available.
Structured reporting events
Reporting runs emit structured lifecycle events:
reporting.report.startedreporting.report.completedreporting.report.failed
Completion events include model identifier, latency, and token counts.
Querying aggregate metrics for a period
Use ReportingMetricsService to compute totals and latency profile for a time
window:
import asyncio
import datetime as dt
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from ghillie.reporting.metrics_service import ReportingMetricsService
async def main() -> None:
engine = create_async_engine("postgresql+asyncpg://user:pass@host:5432/ghillie")
session_factory = async_sessionmaker(engine, expire_on_commit=False)
service = ReportingMetricsService(session_factory)
snapshot = await service.get_metrics_for_period(
period_start=dt.datetime(2026, 2, 1, tzinfo=dt.UTC),
period_end=dt.datetime(2026, 3, 1, tzinfo=dt.UTC),
)
print(snapshot.total_reports)
print(snapshot.avg_latency_ms)
print(snapshot.total_tokens)
asyncio.run(main())
For estate-scoped queries, call get_metrics_for_estate(estate_id, start, end).
Estimating reporting cost
ReportingMetricsService returns token totals. Convert those totals to
currency using pricing configured for the active model (for example, prompt and
completion token rates from the provider contract).