Inference
Read-only listing of the inference lifecycle log — one row per chat or embedding call processed by the gateway. Every row carries the model, adapter, status, token counts, latency, and a request / response excerpt; this is the operator surface that the 02 — Inspect inferences how-to and the bw infer ls CLI wrap.
The router lives at packages/observability/admin_routes.py. Pydantic models: InferenceLogEntry (list view) and InferenceLogDetail (detail view, includes excerpts).
Endpoints
| Method | Path | Auth | Summary |
|---|---|---|---|
| GET | /admin/inference | Bearer | Paginated list with filters. |
| GET | /admin/inference/{log_id} | Bearer | Full InferenceLogDetail for one row. |
GET /admin/inference
Query parameters (all optional):
| Param | Type | Notes |
|---|---|---|
limit | int (1–500) | Default 50. |
offset | int (≥0) | Default 0. |
model_id | string | Exact match. |
adapter_id | string | Exact match. |
status | enum | One of succeeded, failed, cancelled, blocked. |
since | ISO-8601 | Inclusive UTC lower bound on ts. |
until | ISO-8601 | Inclusive UTC upper bound on ts. |
Filters compose with AND. Workspace is taken from the bearer principal. Rows are ordered by ts DESC.
Response shape:
{
"entries": [
{
"id": "log_01HF…",
"workspace_id": "default",
"model_id": "llama3.2:3b",
"adapter_id": null,
"status": "succeeded",
"prompt_tokens": 41,
"completion_tokens": 22,
"latency_ms": 412,
"ts": "2026-05-01T12:00:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}total is the unpaginated row count for the given filters.
curl -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
"http://localhost:8000/admin/inference?status=blocked&since=2026-04-01T00:00:00Z"GET /admin/inference/{log_id}
Returns the same row as the list, plus the prompt/response excerpts captured at log time. Returns 404 when the row does not exist or belongs to a different workspace — cross-tenant lookups MUST NOT leak existence (the router enforces this directly).
curl -H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
http://localhost:8000/admin/inference/log_01HF...Operator notes
- Workspace fence. Both routes resolve the workspace from the bearer principal; the
{log_id}path segment alone never identifies a row. See workspace fence. - Status semantics.
succeededis the happy path;blockedrows are guardrail-rejected requests (see Guardrails policy) and carry zero token counts so they contribute nothing to/admin/cost.failedcovers backend errors (engine OOM, upstream timeouts). - Cross-layer correlation. Audit rows (
/admin/audit) carry aninference_log_idwhen the audited action followed a logged call, letting incident response pivot from "who did what" to "what model did". Schema added in migration0016_cross_layer_correlation. - Excerpts. The detail view includes redacted request / response bodies; the list view does not. Excerpt size is bounded by
BIGWAVE_INFERENCE_LOG_MAX_BYTES(see the configuration reference). - Shadow rows. When shadow mode is active, a parallel row with
shadow_of = <primary_log_id>is emitted; both rows are visible here. 503causes.app.state.inference_log_repois wired in lifespan; routes return503with"InferenceLogRepository not initialised"when missing.