mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
fix(spend-tracking): take each unresolved key's newest spend row and drop docstrings
This commit is contained in:
parent
8c878f3686
commit
cf0488316e
4 changed files with 16 additions and 40 deletions
|
|
@ -462,9 +462,7 @@ async def get_api_key_metadata(
|
|||
|
||||
This ensures that key_alias and team_id are preserved in historical activity logs
|
||||
even after a key is deleted or regenerated. Also recovers aliases for api_key
|
||||
values that were double-hashed by the v1.99 spend-log provenance gate, and, when
|
||||
spend_logs_window is given, for keys never written to either token table (CLI
|
||||
session tokens) from the spend-log rows those requests wrote in that window.
|
||||
values that were double-hashed by the v1.99 spend-log provenance gate.
|
||||
"""
|
||||
key_records: Sequence[PrismaVerificationToken] = await VerificationTokenRepository(prisma_client).table.find_many(
|
||||
where={"token": {"in": list(api_keys)}}
|
||||
|
|
|
|||
|
|
@ -29,16 +29,21 @@ ORDER BY token, deleted_at DESC
|
|||
"""
|
||||
|
||||
_SPEND_LOG_ALIAS_SQL: Final = """
|
||||
SELECT DISTINCT ON (api_key)
|
||||
api_key AS digest,
|
||||
metadata->>'user_api_key_alias' AS key_alias,
|
||||
COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id,
|
||||
COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id
|
||||
FROM "LiteLLM_SpendLogs"
|
||||
WHERE api_key = ANY($1::text[])
|
||||
AND "startTime" >= $2::timestamp
|
||||
AND "startTime" < $3::timestamp
|
||||
ORDER BY api_key, (metadata->>'user_api_key_alias') IS NULL, "startTime" DESC
|
||||
SELECT newest.digest, newest.key_alias, newest.team_id, newest.user_id
|
||||
FROM unnest($1::text[]) AS missing(digest)
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
api_key AS digest,
|
||||
metadata->>'user_api_key_alias' AS key_alias,
|
||||
COALESCE(NULLIF(team_id, ''), metadata->>'user_api_key_team_id') AS team_id,
|
||||
COALESCE(NULLIF("user", ''), metadata->>'user_api_key_user_id') AS user_id
|
||||
FROM "LiteLLM_SpendLogs"
|
||||
WHERE api_key = missing.digest
|
||||
AND "startTime" >= $2::timestamp
|
||||
AND "startTime" < $3::timestamp
|
||||
ORDER BY "startTime" DESC
|
||||
LIMIT 1
|
||||
) AS newest
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -152,14 +157,6 @@ async def recover_double_hashed_key_metadata(
|
|||
prisma_client: PrismaClient,
|
||||
missing_keys: AbstractSet[str],
|
||||
) -> Mapping[str, KeyMetadataDict]:
|
||||
"""
|
||||
Recover key_alias/team_id/user_id for DailyUserSpend.api_key values that
|
||||
were double-hashed by the v1.99 spend-log provenance gate.
|
||||
|
||||
Those rows store hash(VerificationToken.token) instead of the token, so the
|
||||
exact join misses. Postgres hashes the token column itself, one pass over
|
||||
active keys and one over deleted keys, so no key row crosses the wire.
|
||||
"""
|
||||
sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key))
|
||||
if not sha_missing:
|
||||
return _EMPTY_KEY_METADATA
|
||||
|
|
@ -187,15 +184,6 @@ async def recover_key_metadata_from_spend_logs(
|
|||
missing_keys: AbstractSet[str],
|
||||
window: tuple[datetime, datetime],
|
||||
) -> Mapping[str, KeyMetadataDict]:
|
||||
"""
|
||||
Recover key_alias/team_id/user_id for hashed api_key values absent from both
|
||||
verification-token tables, e.g. in-memory CLI session tokens that never get a
|
||||
token row. Their owner is written to LiteLLM_SpendLogs metadata at request
|
||||
time under the same hashed api_key, so it is the only surviving source. Only
|
||||
sha256 digests are looked up, matching the reverse-hash recovery gate, since
|
||||
every current api_key value in spend logs is a token hash. The [start, end)
|
||||
bound keeps the lookup on the startTime index instead of scanning the table.
|
||||
"""
|
||||
sha_missing: Final = frozenset(key for key in missing_keys if is_valid_sha256_hash(key))
|
||||
if not sha_missing:
|
||||
return _EMPTY_KEY_METADATA
|
||||
|
|
|
|||
|
|
@ -2109,11 +2109,6 @@ async def test_get_daily_activity_aggregated_with_entity_breakdown():
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_api_key_metadata_resolves_session_key_via_spend_log_window():
|
||||
"""
|
||||
A CLI session token has no verification-token row, so the active/deleted lookups
|
||||
and reverse-hash all miss. Given a spend-log window, its alias and owner are
|
||||
recovered from the spend-log metadata and its email is filled from the user table.
|
||||
"""
|
||||
from litellm.proxy.utils import hash_token
|
||||
|
||||
session_digest = hash_token("cli-session-user-42")
|
||||
|
|
|
|||
|
|
@ -233,11 +233,6 @@ async def test_fill_missing_api_key_aliases_skips_named_keys_that_have_no_email(
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_recover_key_metadata_from_spend_logs_resolves_session_token_from_metadata():
|
||||
"""
|
||||
CLI session tokens never get a verification-token row, so both the exact join and
|
||||
the reverse-hash lookup miss them. Their owner survives only in the spend-log
|
||||
metadata written at request time, keyed by the same hashed api_key.
|
||||
"""
|
||||
session_digest = hash_token("cli-session-repro-user-6852")
|
||||
window = (datetime(2026, 9, 7), datetime(2026, 9, 10))
|
||||
mock_prisma = MagicMock()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue