Repository navigation
fix(logs): match the in-memory call-log filter to the SQL one it re-applies - #12896
Merged
diegosouzapw merged 2 commits intoSep 10, 2026
Conversation
…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
force-pushed
the
fix/call-logs-filter-parity
branch
from
September 7, 2026 06:32
101f5e4 to
547f3b2
Compare
diegosouzapw
merged commit Sep 10, 2026
a6f2821
into
cheaper-inference:release/v3.8.51
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.
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #12873.
GET /api/usage/call-logsfilters twice:getCallLogs()builds a SQLWHERE, thenrowMatchesFilter()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 theWHEREaccepted 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.
buildCallLogFilterSql()rowMatchesFilter()beforeapiKeyapi_key_name LIKE ? OR api_key_id LIKE ?apiKeyNameonlycombocombo_name IS NOT NULL— value unusedcomboNamemodelmodel LIKE ? OR requested_model LIKE ?modelonlysearchapi_key_id,combo_step_id,combo_execution_key,requested_model,status#12873 is the
apiKeyrow.RequestLoggerV2builds each dropdown option's value asapiKeyId || apiKeyName(lines 865 and 1020), so choosing a key sends its UUID. SQL returns the rows; this pass compares the UUID toapiKeyName, 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
comborow is worse and, as far as I can find, unreported. The dashboard's Combo tab sendscombo=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 character1and 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.accountneeded nothing:mapRowalready resolves it (row.resolved_account || row.account) to the same valueRESOLVED_ACCOUNT_SQLcompares.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 anapiKeyorcombofilter.Verification
tests/unit/call-logs-row-filter.test.tsis 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:
apiKeyback to name-onlyapiKey filter matches the key id the dashboard dropdown sendscomboback to a name substring matchcombo filter is a presence flag, not a name querymodelback to the served model onlymodel filter matches the requested model, as the SQL clause doesapiKeyIddropped from the search haystacksearch covers the same columns as the SQL haystackcomboStepIddropped from the search haystacksearch covers the same columns as the SQL haystackThe 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 anapiKeyorcombofilter is on. Carrying attribution onto pending rows is a separate change.