fix(antigravity-cli): attribute turns whose blob omits responseModel - #1051
Conversation
Antigravity CLI leaves `chatModel.junhoyeo#19` (responseModel) out of some generations — continuation and tool turns, which also carry a large `cacheRead` and a tiny output — while still writing the `junhoyeo#21` display label. Those rows fell back to the literal `unknown`, which `inferred_provider_from_model` cannot place, so they surfaced as `antigravity/unknown`. One unpriceable row aborts the whole submission, so eight such rows across three conversations blocked every submit on this machine. The rows are not information-poor: sibling rows in the same database carry the machine id next to the identical `junhoyeo#21` label. Index the conversation before parsing it, then resolve a missing `junhoyeo#19` through that index — by display label when the row has one, or through the conversation's sole model when it has neither field. A label that identifies no model, or two, resolves to nothing and the row still reports `unknown` rather than borrowing an id the evidence contradicts. The recovered value is always a `junhoyeo#19` machine id read from the same file, so display labels stay out of pricing keys — they get renamed (`Gemini 3 Flash` → `Gemini 3.5 Flash (High)`) and could be localized.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
All reported issues were addressed across 1 file
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
… went unidentified `SessionModels::sole_model` served rows carrying neither `junhoyeo#19` nor `junhoyeo#21` whenever the file named exactly one model. That counted named ids, not models: a conversation where one row carries a display label no row ever identifies ran at least two models while naming only one, and an unlabelled row there could be either. It inherited the single named id and billed a model switch under the wrong model. Gate the fallback on every label in the file resolving to a machine id, so those rows stay `unknown` — the same rule the label path already followed, now applied to the unlabelled one. Also mark `junhoyeo#21` optional in the field table. It was present on every row observed so far, but the parser has never required it and the `sole_model` path exists precisely for rows that carry neither field.
|
Both valid, fixed in 73d5937. The second one is a real hole and it was my own rule applied inconsistently. On the first: |
|
Looks good! |
|
Shipped in v4.11.0 — https://github.com/junhoyeo/tokscale/releases/tag/v4.11.0 @haunchen @channprj thanks. Recovering the model from the Worth calling out how you handled the review: the P1 about a conversation containing a model switch billing under the wrong model was a real hole, and One gap that came out of triaging #1035 afterwards: this covers the CLI parser only. Also relevant now: post-#1053 an unattributable row is excluded with a warning instead of aborting the submission, so a regression here would be quiet rather than loud. If tokscale has been useful to you, a ⭐ on the repo helps other people find it. |
Problem
tokscale submitaborted on every run against my local data:Eight rows out of 634 token-bearing generations, spread over three of 222 conversations, blocked the entire submission.
validate_priced_messagesrejects the whole batch when any token-bearing message has neither an authoritative cost nor a pricing hit, so a single unattributable row is enough.Root cause
parse_gen_metadatareads the model fromchatModel.#19(responseModel) and falls back to the literal stringunknown.inferred_provider_from_modelcannot placeunknown, so the provider falls back toantigravityand the row becomes the unpriceableantigravity/unknown.Antigravity CLI does not always write
#19. Decoding everygen_metadatablob in my conversations directory with the module's own wire-format reader shows the field missing on some continuation and tool turns — the ones that also carry a largecacheReadand a tiny output. Those rows drop a whole group of chatModel fields (1, 2, 8, 10, 16, 19), not#19alone.The rows are not information-poor, though.
#21— the model display label — survives on every one of them, and sibling rows in the same database carry the machine id next to the identical label:Fix
Index the conversation before parsing it, then resolve a missing
#19through that index:#19;#19seen elsewhere in the same file next to the row's#21display label;#19, when the row carries no label at all and every row that has one agrees;unknown, as before.A label that identifies no model, or two different ones, resolves to nothing and the row still reports
unknownrather than borrowing an id the evidence contradicts. Case 3 is the same idea asantigravity.rs's existingsession_modelfallback, which the CLI parser never had.Display labels stay out of pricing. The value handed back is always a
#19machine id read from that same file;#21is only ever a join key between rows of one conversation. This keeps the rule the alias table already states — labels get renamed (Gemini 3 Flash→Gemini 3.5 Flash (High)) and could be localized, so they must not become pricing keys. The data shows why a label-to-id table would be the wrong shape here anyway:Gemini 3.5 Flash (High)is the label for the wire stringgemini-3-flash-a, and the two track each other only through the alias table.Indexing the file up front rather than carrying the previous row's model forward also makes recovery independent of row order, and keeps it correct across a mid-conversation model switch — a label that never appeared next to a
#19resolves to nothing instead of inheriting whatever ran last.Testing
Six unit tests cover recovery by label, recovery when the gap is the first row, alias resolution of a recovered id, a label the file never identified, a label claimed by two models, and the no-label single-model and multi-model cases. They build minimal proto fixtures with the existing
enc_varint/enc_lenhelpers — no real databases are checked in.cargo fmt --all -- --checkis clean, andcargo test --workspace --all-features --no-fail-fastmatches the pre-change baseline on every target (tokscale-core --libgoes from 1476 to 1482 for the new tests). My Windows checkout has some pre-existing failures unrelated to this change — thetui::cachelegacy-path test, the timezone group incli_tests, and one Clippyunneeded returnin a#[cfg(target_os = "windows")]block inclients.rs— all of which reproduce identically on an unmodified tree at 4.10.0.End to end against real local data: with the three affected conversations in place, an unmodified build reproduces
antigravity/unknown (x8); with this change,tokscale --no-spinner submit --dry-run -c antigravity-clisucceeds, and so does a full-history dry run across every client.Relation to #1035 and #1044
This removes one concrete cause of unpriced rows rather than changing what submit does when it meets one. #1035 lists two other unpriced ids that this does not touch, and the broader "don't abort the whole batch" request there and in #1021 is blocked on #1044 — so this is orthogonal to both and closes neither.
Summary by cubic
Fixes unpriced rows in
antigravity-cliby recovering missing model IDs, sotokscale submitno longer aborts onantigravity/unknown. Adds a safety guard so the “single-model” fallback is used only when every label in the file is identified.chatModel.#19via its#21display label from the same file; if a row has no label, uses the file’s sole#19only when every label resolves; otherwise returnsunknown.#21is optional and rows with neither field are handled.unknownrather than guessing.Written for commit 73d5937. Summary will update on new commits.