fix(console): include legacy run input tokens in model usage (#7411)

This commit is contained in:
N 2026-10-25 11:01:44 +08:00 committed by GitHub
parent 0f8ba6f244
commit 481f8168d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 32 additions and 0 deletions

View File

@ -254,6 +254,8 @@ fences, including unclosed fences, in complete and streamed replies.
Optional per-model pricing must use one currency across all priced models.
DeerFlow disables Console cost estimates when currencies are mixed rather
than presenting an invalid aggregate.
For older runs without a per-model usage breakdown, Console attributes the
recorded total and input token counts to the run's model.
Administrators can open **Settings → Models** to select a provider and enter
only its **API Key** and **model ID**. Endpoints, authentication and adapters are

View File

@ -1,5 +1,10 @@
# Router contracts
Console usage attributes legacy rows without a per-model usage map to
`model_name`, including their recorded `total_input_tokens`. Accumulate those
inputs with newer usage buckets for the same model; do not infer cache hits.
Regression coverage lives in `tests/test_console_router.py`.
Shared scheduled-task update validation preserves an unchanged future `once`
instant on a live task inside `scheduler.min_once_delay_seconds`; new/changed
instants and terminal reactivation still enforce the minimum delay.

View File

@ -528,6 +528,7 @@ async def console_usage(
# Legacy rows predating token_usage_by_model: fall back to the run's model.
entry = by_model.setdefault(row.model_name, ConsoleUsageModelBreakdown())
entry.tokens += run_tokens
entry.input_tokens += row.total_input_tokens or 0
entry.runs += 1
if run_cost is not None:
entry.cost = round((entry.cost or 0.0) + run_cost, 6)

View File

@ -234,6 +234,30 @@ class TestConsoleUsage:
assert data["by_model"]["qwen"]["tokens"] == 70
# Legacy row (empty token_usage_by_model) falls back to model_name
assert data["by_model"]["gpt-x"]["tokens"] == 50
assert data["by_model"]["gpt-x"]["input_tokens"] == 50
@pytest.mark.parametrize("legacy_usage", [{}, None], ids=["empty-map", "null-map"])
@pytest.mark.parametrize("legacy_input_tokens", [0, 50])
def test_legacy_input_tokens_accumulate_with_model_usage(self, client, session_factory, legacy_usage, legacy_input_tokens):
async def _update_legacy_run() -> None:
async with session_factory() as session:
row = await session.get(RunRow, "r3")
row.model_name = "minimax-m2"
row.token_usage_by_model = legacy_usage
row.total_input_tokens = legacy_input_tokens
row.total_output_tokens = 50 - legacy_input_tokens
await session.commit()
asyncio.run(_update_legacy_run())
response = client.get("/api/console/usage", params={"days": 14})
assert response.status_code == 200
data = response.json()
model = data["by_model"]["minimax-m2"]
assert model["input_tokens"] == 1000 + legacy_input_tokens
assert model["tokens"] == 1550
assert model["runs"] == 3
assert model["cache_read_tokens"] == 500
assert sum(day["input_tokens"] for day in data["days"]) == sum(entry["input_tokens"] for entry in data["by_model"].values())
def test_window_excludes_old_rows_but_stats_include_them(self, client):
usage = client.get("/api/console/usage", params={"days": 7}).json()