GuidesMetrics catalogue

Metrics catalogue

Everything you can chart in Neens comes from one semantic catalogue: a registry of measures (the things you can compute — trace counts, error rate, latency percentiles, spend, eval pass rate, …) and dimensions (the columns you can slice them by — time, org, project, agent, model, …). Widgets, digest tiles, and overview stats all resolve through it, so the same number means the same thing everywhere.

At a glance

ModelMeasure × Dimension: a query is a registered measure, optionally grouped by dimensions valid for it
Key API routesGET /measures/catalogue (the full menu), GET /measures/fields (your agent’s dynamic metadata dimensions)
SafetyOnly valid measure × dimension combinations can be requested — never arbitrary SQL; filter values are always parameterized
ScopeEvery query is constrained to the caller’s accessible agents
Used byDashboard widgets, the platform persona dashboards, the daily digest email tiles, and the Overview / Insights fleet stats

Naming: the measure keyed sessions is labeled Traces (individual agent runs), and conversations is labeled Sessions (traces rolled up by conversation) — matching how the rest of the app names them. See Core concepts.

Measures

Twenty measures ship, organized into the seven categories the widget gallery uses (Volume · Quality · Latency · Errors · Cost · Topics · Business). The headline set:

  • Volume — sessions (Traces), conversations (Sessions), span_count (Spans)
  • Quality — eval_pass_rate, avg_score, score_count, cluster_count
  • Latency — avg_latency, p50_latency, p99_latency
  • Errors — error_rate
  • Cost — spend_usd, tokens_in, tokens_out, avg_tokens_per_trace
  • Topics — topic_size
  • Business — containment_rate, resolution_time_p50, resolution_time_p90, cost_per_case
Full measure reference — key, label, unit, and what each computes
KeyLabelUnitGrainWhat it computes
sessionsTracescounttracesNumber of individual traces (agent runs)
conversationsSessionscounttracesDistinct conversations — traces rolled up by conversation id
span_countSpanscountspansNumber of spans (steps) recorded
error_rateError rateratiotracesShare of traces whose status is error
avg_latencyAverage latencymstracesMean end-to-end trace duration
p50_latencyp50 latencymstracesMedian trace duration
p99_latencyp99 latencymstraces99th-percentile trace duration
spend_usdSpendusdmodel calls (spans)LLM cost — tokens × the model’s price from the price table, computed at query time (not a stored column). Unpriced models contribute $0 and the result is labelled partial
tokens_inInput tokenscountmodel calls (spans)Sum of prompt tokens sent to models
tokens_outOutput tokenscountmodel calls (spans)Sum of completion tokens generated
avg_tokens_per_traceAvg tokens / tracecounttracesAverage total tokens (in + out) per trace
eval_pass_rateEval pass rateratioscoresShare of scores at or above their threshold
avg_scoreAverage scorescore (0–1)scoresMean score value
score_countTotal scorescountscoresNumber of scores recorded
cluster_countFailure clusterscountclustersNumber of failure clusters found
topic_sizeTopic sizecounttopic assignmentsTraces assigned to each topic
containment_rateContainment rateratiocasesShare of decided cases the agent handled with no escalation — see Business KPIs
resolution_time_p50Resolution time (p50)mscasesMedian time to resolve a case, from your measured outcomes
resolution_time_p90Resolution time (p90)mscasesThe slow tail — the 10% of cases that take longest to resolve
cost_per_caseCost per caseusdcasesLLM spend divided by the cases handled in the range

Notes:

  • Grain matters for slicing. Trace-grain measures slice by trace attributes (agent, status, your metadata); span-sourced cost measures slice by model; score-grain measures slice by judge, metric, and — because the model and agent behind each score are recorded on the score itself — by model, agent, and model_source; case-grain measures slice by the attributes of a case’s first trace.
  • spend_usd, tokens_in, and tokens_out share one source and one price table, so tokens and dollars always stay mutually consistent. A model with no price contributes tokens but no dollars, and the spend result carries a partial flag naming the unpriced models — see Cost & model pricing.
  • topic_size has no timestamp, so the time dimension and time-range filters don’t apply to it.
  • The four Business measures are computed per case (conversation_id, falling back to the trace id), and containment_rate / resolution_time_* need the agent to have declared what an escalation and a resolution mean. They render with the provenance (measured vs inferred) and the coverage behind the number — see Business KPIs.

Dimensions

A dimension only applies to measures whose grain carries it — the builder (and the API) enforce this, so you can’t ask for “spend by judge” or “error rate by metric”.

  • time and the scope dimensions org / project apply to (almost) everything — time buckets by hour or day.
  • Trace-grain slices: agent, status, source, conversation, plus the derived bins latency_bucket and token_bucket.
  • model has two homes. On the span-sourced cost measures (spend_usd, tokens_in, tokens_out) it’s spend and token volume per model; those three also slice by agent, so spend by agent — which agent is burning the budget — is a first-class breakdown. On the score-grain measures (eval_pass_rate, avg_score, score_count) it’s quality per model — the model that produced the answer each score graded. See Model comparison.
  • Case-grain slices (the Business measures): agent, status, source, conversation — read off the case’s first trace — plus queue and outcome_meta:… from the matched outcome’s metadata.
  • Score-grain slices: judge, score_source, score_label, target_type, model, agent, and model_source (Model attribution — how the score’s model was determined), plus metric_key (pass rate only) and the derived score_bucket distribution.
  • span_kind / span_status slice span_count; topic slices topic_size.
Full dimension reference — key, label, and which measures it applies to
KeyLabelApplies to
timeTimeEvery measure except topic_size (bucketed by hour or day)
orgOrgAll measures
projectProjectAll measures
agentAgentTrace-grain: sessions, conversations, error_rate, avg_latency, p50_latency, p99_latency, avg_tokens_per_trace; the cost measures spend_usd, tokens_in, tokens_out; the score measures eval_pass_rate, avg_score, score_count; and every Business measure
modelModelspend_usd, tokens_in, tokens_out (the model that ran) and eval_pass_rate, avg_score, score_count (the model that produced the graded answer)
model_sourceModel attributioneval_pass_rate, avg_score, score_count — how the score’s model was determined (preprod_run / span / session_uniform / mixed / unknown)
statusStatussessions, error_rate, avg_latency, p50_latency, p99_latency, and the Business measures
sourceSourcesessions, error_rate, avg_latency, p50_latency, p99_latency, and the Business measures (ingest source: otlp / openinference / raw)
conversationConversationTrace-grain measures and the Business measures
queueQueuecontainment_rate, resolution_time_p50, resolution_time_p90 — the queue field of the matched outcome’s metadata
outcome_meta:…your outcome metadata keycontainment_rate, resolution_time_p50, resolution_time_p90 — any other key in the outcome’s metadata
latency_bucketLatency rangeTrace-grain measures (derived bins of duration; group-by only)
token_bucketToken rangeTrace-grain measures (derived bins of in + out tokens; group-by only)
metric_keyMetriceval_pass_rate
judgeJudgeeval_pass_rate, avg_score, score_count
score_sourceScore sourceeval_pass_rate, avg_score, score_count
score_labelScore labeleval_pass_rate, avg_score, score_count
target_typeTarget typeeval_pass_rate, avg_score, score_count (session vs span scores)
score_bucketScore rangescore_count (derived bins of the score value; group-by only)
span_kindSpan kindspan_count
span_statusSpan statusspan_count
topicTopictopic_size (grouped by topic name)
meta:…your metadata keyTrace-grain measures — see below

Derived-bin dimensions (latency_bucket, token_bucket, score_bucket) can only be used as group-bys, not as filters. Every other dimension works as both a group-by and a filter.

Dynamic metadata dimensions (meta: keys)

Any top-level scalar key you send in a trace’s metadata becomes a sliceable dimension of the form meta: + the key — for example, traces tagged with a region field can be grouped or filtered by meta:region on any trace-grain measure. There’s nothing to register:

  • GET /measures/fields discovers the metadata keys present in your accessible traces (optionally for one agent with ?projectId=), and the widget builder offers them automatically.
  • Keys must look like identifiers (letters, digits, _, ., -; up to 64 characters) — anything else is rejected.
  • Metadata dimensions apply to trace-grain measures only (the metadata lives on the trace).

Where measures are used

SurfaceHow
Custom dashboardsEvery widget is a measure + dimensions + filters; the builder’s menu is this catalogue (GET /measures/catalogue), including ~45 ready-made presets across the seven categories
Platform persona dashboardsThe Executive digest, Product quality board, Finance cost explorer, and Org scorecard are built entirely from catalogue measures — nothing bespoke
Digest emailThe fleet-health tiles (Traces, Error rate, Eval pass rate, Spend) resolve through the catalogue, so the email can never drift from the in-app numbers
Overview & InsightsThe workspace Overview KPIs/trends/segments and the Insights fleet line resolve through the same layer

How it works

A request names a measure, dimensions, filters, and a time range. The catalogue validates the combination and emits a neutral query description; a resolver turns that into SQL for whichever backend stores your traces, always constrained to the agents you can access. Two guarantees fall out of this design:

  • No invention: an unknown measure, an inapplicable dimension, or a filter on a derived bucket is rejected with a clear error — nothing is silently approximated.
  • No injection: filter values are bound as parameters, and metadata keys are strictly validated, so no widget or API call can smuggle SQL.
  • Dashboards — build widgets from these measures.
  • Business KPIs — the case-grain measures (containment, resolution time, cost per case), and how to declare what they mean in your data.
  • Custom measures — add measures of your own to this catalogue; they behave like the ones above everywhere.
  • Cost & model pricing — where the prices behind spend_usd come from, and how to set your own.
  • Model comparison — slicing the quality measures by model and by agent × model, and what the Mixed / Unknown buckets mean.
  • Insights — detectors that watch some of these signals for you.
  • Core concepts — traces vs sessions, scores, and thresholds.