Repository navigation
[fix] Coerce falsy stored metric JSON columns in DayAggregatedMetrics.from_dict - #10320
Lesereingrape wants to merge 1 commit into
Conversation
….from_dict
A metrics row is read straight from the JSON column, and that column can hold
its declared default `{}` for model_metrics, or no key at all for a row written
before per-owner metrics existed. Both shapes reached the field as a list and
made the /metrics endpoint answer 500 instead of reporting that day.
|
Real CI has not run on this PR yet - it needs one click from someone with write access. The 3 workflow runs on head The verification I do control - the Six sibling PRs from this account are in the same state (#10281, #10287, #10297, #10301, #10323, #10325); I have asked on 4 of them, so clearing the whole set is 20 clicks in total. Say the word and I will narrow it down. Disclosure: this account is being operated by an AI coding assistant on the owner's instruction, and the PR bodies say so as well. |
Summary
DayAggregatedMetrics.from_dict()handed the two JSON columns straight to pydantic with a fallback that contradicts their annotations:A stored row legitimately holds
{}formodel_metrics— that is the declared default on every adapter's JSON column (sqlite/schemas.py:130,postgres/schemas.py:131,singlestore/schemas.py:118,redis/schemas.py:46,valkey/schemas.py:46) — and a row written before per-owner metrics existed carries no such key at all. Either shape reached aListfield, soGET /metricsfor a scoped caller answered 500 for a day that simply has no per-model breakdown.Both fields now normalize with
oronto the container they declare. This is the rule the rest of the stack already applies —db/utils.py:806reads[dict(m) for m in (row.get("model_metrics") or [])], andV3_MIGRATION_GUIDE.md:499,515backfills withCASE WHEN jsonb_typeof(...) = 'array' ... ELSE '[]'::jsonb— but the per-owner read path (identify_metrics_by_owner(), which only rewritesid) never passed rows through it, so the reader is the only place left to enforce it.A value that cannot be metrics at all (e.g.
model_metrics = {"gpt-5-mini": 2}) still raises; only the falsy shapes change.Closes #10319
Type of change
Checklist
./scripts/format.shand./scripts/validate.sh)Duplicate and AI-Generated PR Check
Additional Notes
Verification
main(85b6d1d) withValidationError: Input should be a valid list [type=list_type, input_value={}, input_type=dict]andGET /metrics→500; with this change it returns200.libs/agno/tests/unit/os/routers/test_metrics_router.py: 4 failed / 39 passed with only the source change reverted (the four per-owner cases), 43 passed with it applied. No existing test changed status in the other direction.{}/None/ key-absent formodel_metrics,None/ key-absent fortoken_metrics, each across both the scoped and unscoped reads, plus one case asserting a genuinely malformedmodel_metricsstill surfaces as an error rather than being swallowed.ruff checkandruff format --checkpass on both changed files and acrossagno/os(130 files);mypyreports no issues onschemas.py.from_dictis called only atos/routers/metrics/metrics.py:152and:333, both covered by the test file above. (agno/client/os.py:2991validates already-serialized responses, so it is unaffected.) Note the widertests/unit/osdirectory is not runnable in my environment — 222 collection errors from optional extras that aren't installed (slack_sdk,telebot,a2a,fastmcp,jwt,uvicorn) — so I verified reachability by call site rather than claiming that suite green.Collision check
Searched open issues and PRs for
model_metricsandDayAggregatedMetrics(the only hits are my own #10300/#10301, which touchos/utils.py, a different file), then scanned the changed-file lists of all 777 open PRs. Exactly one other PR toucheslibs/agno/agno/os/routers/metrics/schemas.py: #6072, a cookbook/timestamp sync stale since 2026-06-06 and currentlyCONFLICTING. Its surviving change to this file was theint→datetimeshift forcreated_at/updated_atviato_utc_datetime, which is already onmain, and its diff leaves bothmodel_metrics=metrics_dict.get("model_metrics", {})and thetoken_metricsline untouched — so it does not address this bug, and this PR does not overlap its remaining content.AI assistance (per
CONTRIBUTING.md§5): the investigation, the diff and this description were produced by an AI coding agent working under the account owner's standing instruction to find and fix defects of this kind and open PRs for them; no human read the diff before it was opened. Everything quoted in the validation section was run by that agent on a local checkout and is reproducible on a fresh clone of this branch. The commit carries no AI co-author trailer.