…upt JSON (mini-router#165)
`_ledger_append` built each cost-ledger line by hand with an f-string, so a
provider or model id containing a double quote, a backslash, or a control
character emitted a line that is not valid JSON. `costing.ledger_cost_report`
skips unparseable lines silently, so every call for such a model vanished from
`cost_usd`, `cost_calls`, and the per-model breakdown — silent under-reporting
of API spend, with no error and no warning, and a corrupted JSONL file for any
other consumer.
Before (model id `weird"model`):
{"provider":"openrouter","m":"weird"model","p":1000,"c":2000} <- invalid
Serialize the record with `json.dumps(..., ensure_ascii=False)` instead, so the
writer and the reader agree on escaping. Non-ASCII ids stay human-readable.
Adds tests/test_cost_ledger_json.py: quote / backslash / tab / newline / unicode
ids each round-trip as valid JSON with the id preserved, the provider is escaped
too, no call is dropped from the cost report totals, tricky ids survive in the
per-model breakdown, a newline no longer splits one record into two lines, and
the unset-env path stays a no-op.
Fixes mini-router#165
Fixes #165
Problem
_ledger_appendinsrc/trinity/llm/openai_compatible_pool.pybuilt each cost-ledger line by hand with an f-string:modelisroute.model_id, taken verbatim fromconfigs/models*.yaml. If an id contains a double quote, a backslash, or a control character, the emitted line is not valid JSON:costing.ledger_cost_reportdoestry: json.loads(line) except JSONDecodeError: continue, so those lines are silently skipped — their tokens never reachcost_usd,cost_calls, or the per-model breakdown. That is silent under-reporting of API spend, with no error and no warning, and it corrupts the JSONL file for any other consumer.Reproduced before the fix: appending 3 records where 2 have metacharacter ids gives
cost_calls == 1.Fix
Serialize the record with
json.dumps(..., ensure_ascii=False)instead of hand-formatting, so the writer and reader agree on escaping.ensure_ascii=Falsekeeps non-ASCII ids human-readable in the ledger.After the fix the same 5 records all parse, and
cost_calls == 5with exact token totals and ids preserved (weird"model,back\slash,ctrl\tchar,uniécode/model).Tests
Adds
tests/test_cost_ledger_json.py(14 cases, pure stdlib — no network/GPU/torch):cost_calls/cost_prompt_tokens/cost_completion_tokensTRINITY_COST_LEDGERpath stays a no-opFull root suite: 260 passed;
ruffclean.