Skip to content

fix(logs): match the in-memory call-log filter to the SQL one it re-applies - #12896

Merged
diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.51from
ntdatt812:fix/call-logs-filter-parity
Sep 10, 2026
Merged

diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.51from
ntdatt812:fix/call-logs-filter-parity

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

Closes #12873.

GET /api/usage/call-logs filters twice: getCallLogs() builds a SQL WHERE, then rowMatchesFilter() re-applies the same predicates to the merged list so the in-memory rows (active and recently-completed) do not bypass the filters. The second pass is meant to be idempotent for persisted rows — they only got here because the WHERE accepted them.

It is not. Four clauses are narrower than their SQL counterparts, so they delete rows the query got right, and the endpoint returns a plain array with nothing saying anything was dropped.

filter buildCallLogFilterSql() rowMatchesFilter() before
apiKey api_key_name LIKE ? OR api_key_id LIKE ? apiKeyName only
combo combo_name IS NOT NULL — value unused substring match of the value against comboName
model model LIKE ? OR requested_model LIKE ? model only
search 14 columns incl. api_key_id, combo_step_id, combo_execution_key, requested_model, status 9 fields, missing those

#12873 is the apiKey row. RequestLoggerV2 builds each dropdown option's value as apiKeyId || apiKeyName (lines 865 and 1020), so choosing a key sends its UUID. SQL returns the rows; this pass compares the UUID to apiKeyName, matches nothing, and the grid is empty for a key with thousands of calls. Typing the name works, which is what makes it look like a dropdown bug.

The combo row is worse and, as far as I can find, unreported. The dashboard's Combo tab sends combo=1 (RequestLoggerV2.tsx:251) — a flag, which is exactly how the SQL clause reads it. matchesSearch(comboName, "1") reads it as a name query, so that tab showed only combo rows whose name happens to contain the character 1 and hid every other one.

All four fields exist on persisted rows (callLogs.ts:372-395), so the fix is to widen each clause to its SQL counterpart rather than to plumb anything new. account needed nothing: mapRow already resolves it (row.resolved_account || row.account) to the same value RESOLVED_ACCOUNT_SQL compares.

Widening must not turn "no attribution" into "matches everything": in-memory rows carry apiKeyId: null, apiKeyName: null, comboName: null, and a test pins that they are still excluded by an apiKey or combo filter.

Verification

tests/unit/call-logs-row-filter.test.ts is 9 pass / 0 fail on the same loader CI uses.

Each new assertion was checked by reverting the clause it covers; every mutation fails exactly one test and leaves the other eight passing:

reverted fails
apiKey back to name-only apiKey filter matches the key id the dashboard dropdown sends
combo back to a name substring match combo filter is a presence flag, not a name query
model back to the served model only model filter matches the requested model, as the SQL clause does
apiKeyId dropped from the search haystack search covers the same columns as the SQL haystack
comboStepId dropped from the search haystack search covers the same columns as the SQL haystack

The docstring now states the contract the clauses owe the SQL layer, since the defect is not in any one of them but in the two predicates being written and maintained separately.

Not changed here: an in-flight request genuinely has no key or combo attribution in buildCallLogListRows(), so it stays hidden while an apiKey or combo filter is on. Carrying attribution onto pending rows is a separate change.

…pplies

/api/usage/call-logs filters twice: getCallLogs() builds a WHERE, then
rowMatchesFilter() re-applies the same predicates over the merged list so the
in-memory rows do not bypass them. That second pass is only meant to be
idempotent for persisted rows -- they are here because the WHERE accepted them.

Four clauses were narrower than their SQL counterparts and so deleted rows the
query got right, with nothing in the response saying so:

- apiKey matched apiKeyName only, while SQL matches api_key_name OR api_key_id.
  The dashboard dropdown sends `apiKeyId || apiKeyName`, so selecting a key sent
  its UUID and emptied the grid (cheaper-inference#12873). Typing the name still worked.
- combo substring-matched its value against comboName, while the SQL clause is
  `combo_name IS NOT NULL` and ignores the value. The Combo tab sends combo=1,
  so it showed only combos whose name contains a "1".
- model matched the served model only, not requested_model.
- the search haystack lacked api_key_id, requested_model, combo_step_id,
  combo_execution_key and status, all of which the SQL search covers.

Every field is already on the persisted rows, so no new plumbing. In-memory rows
still carry no key or combo attribution and stay excluded by those filters; a
test pins that widening did not turn "unattributed" into "matches everything".
@ntdatt812
ntdatt812 force-pushed the fix/call-logs-filter-parity branch from 101f5e4 to 547f3b2 Compare September 7, 2026 06:32
@diegosouzapw
diegosouzapw merged commit a6f2821 into cheaper-inference:release/v3.8.51 Sep 10, 2026
8 of 16 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…pplies (cheaper-inference#12896)

Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings.

Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(backend): Dashboard logs API-key filter returns 0 rows when selecting a key from dropdown (UUID passed, name matched)

2 participants