Skip to content

[Bug]: local_runtime_token_usage keeps model NULL for historical rows, so downstream tooling cannot price them #540

Open

Description

@Luckycat133

Product or interface

Desktop app (the local-runtime usage ledger that third-party tooling reads).

Version

Desktop app 3.1.6 (build from 2026-10-23). The ledger lives in
~/.minimax/v2/sqlite/runtime-state.sqlite, table local_runtime_token_usage.

Platform

macOS (Apple Silicon).

OS version and architecture

macOS 27.0.1, arm64.

Issue area

Runtime / local-runtime — token usage ledger (local_runtime_token_usage).

Steps to reproduce

  1. Use MCode on a build that predates the model being written, then upgrade to a
    build that writes it (the fix is visible in the current build: rows written
    after 2026-10-24 01:06 local carry model = 'minimax/MiniMax-M3.1-Flash').

  2. Inspect the ledger:

    sqlite3 ~/.minimax/v2/sqlite/runtime-state.sqlite \
      "SELECT model, COUNT(*), SUM(input_tokens+output_tokens)
         FROM local_runtime_token_usage GROUP BY model;"

    On my machine:

    |                              | 2928 | 30859470
    | minimax/MiniMax-M3.1-Flash  |  105 |   5845270
    

    Every row written before the fix has model IS NULL; only rows written after
    it carry a model.

Expected and actual behavior

Expected: the ledger records the model for every usage row, so downstream
tooling can attribute usage to a model.

Actual: historical rows keep model IS NULL forever. Third-party usage
trackers that read this ledger (for example vibe-usage, which reads
local_runtime_token_usage and prices each bucket by its model string) show
those rows as model unknown and therefore cost $0.00, even though the model
is unambiguous.

Why it matters

The model is recoverable from data the runtime already keeps: the per-session
history files under ~/.minimax/v2/sessions/<history_relative_dir>/messages.jsonl
carry message.model and turn_id for every assistant turn. Joining those to
the ledger rows by session_id + turn_id recovers the model for 100% of the
rows I could check (2 621 / 2 621 exact matches by turn_id, the remainder
recovered from the session's dominant model).

Proposal

  1. One-off backfill. A migration that fills model for rows where it is
    NULL, reading the model from the session history files (the runtime owns
    those files, so this does not widen any privacy boundary). A reference
    implementation is available — happy to open a PR if this is welcome, but I
    understand the repository currently only accepts contributions from
    collaborators, so an issue is the right route.
  2. Or: surface it. When the ledger has rows with a NULL model, report the
    count somewhere observable (a status/diagnostics command or a log line),
    so tooling and users can tell "no model recorded" apart from "zero usage".
  3. Or: both.

Reference implementation

The recovery I used (Python 3, read-only on the ledger, writes only the model
column of rows where it is NULL):

# 1. (session_id, turn_id) -> model, from the session history files
for session_id, rel in sqlite3.connect(DB).execute(
        "SELECT session_id, history_relative_dir FROM local_runtime_sessions "
        "WHERE history_relative_dir IS NOT NULL"):
    for line in open(f"~/.minimax/v2/sessions/{rel}/messages.jsonl"):
        obj = json.loads(line)
        msg = obj.get("message") or {}
        if msg.get("model"):
            turn2model[(session_id, obj.get("turn_id"))] = msg["model"]

# 2. fill the NULL rows
UPDATE local_runtime_token_usage SET model = ? WHERE id = ?   # turn_id match
#    (fallback: the session's dominant model, for rows whose turn is gone)

Related

  • Third-party tracker side: vibe-cafe/vibe-usage#127 adds mcode session
    records; the missing model on historical rows is the remaining reason those
    rows still price at $0.00.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions