Repository navigation
Conversation
Summary: - Add collectors.extraction with shared helpers for timestamps, latency, TTFT, tokens, cache, cost, and status/error fields. - Add UsageRequestNormalizer that maps LiteLLM spend-log rows (fixture and native SpendLogs shapes) into usage_requests without a session, skips rows lacking request_id/call_id with diagnostics, and preserves optional benchmark join fields in redacted metadata. - Add ProxyKeyAttributionResolver that resolves spend-log key references to proxy_keys by litellm_key_id, ephemeral virtual-key mapping, or alias; raw key references are never persisted. - Add LiteLLMUsageCollector that fetches /spend/logs for all keys (summarize=false), filters to the window, links known benchmark sessions, and writes idempotently via SQLUsageRequestRepository. - Add UsageReconciliationReport with mapped/partial/skipped, attribution, session-link, duplicate, and missing-field counts. - Document the collector flow and metadata redaction boundary. Rationale: - COE-373: usage ingestion must work for all keys, not only benchmark sessions, while keeping the session-bound benchmark collector unchanged. - ADR-002 forbids persisting raw virtual key values; unattributed keys are reported by alias or sha256 fingerprint. Tests: - pytest tests/unit/test_usage_normalizer.py tests/unit/test_collectors.py - make lint format-check type-check test (701 passed) Co-Authored-By: bot_apk <apk@cognition.ai>
Live /spend/logs dry run showed cache_key is the placeholder 'Cache OFF' on every row when caching is disabled, adding noise without join value. Co-Authored-By: bot_apk <apk@cognition.ai>
Open
7 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.
Summary
Adds a sessionless LiteLLM usage collection path (COE-373). All-key
/spend/logsrows are normalized intousage_requests, attributed to theproxy_keysregistry when possible, optionally linked to benchmark sessions, and reported through a reconciliation report. The existing session-boundLiteLLMCollector/RequestNormalizerpath is unchanged.Changes
collectors/extraction.py: shared pure helpers for timestamps, latency (seconds→ms,request_duration_ms,endTime-startTime), TTFT (seconds→ms or derived fromcompletion_start_time/completionStartTime), tokens, cache (incl. string"True",additional_usage_values), cost, and status/error (incl.metadata.error_information).UsageRequestNormalizer(normalize_requests.py): maps fixture and native LiteLLM SpendLogs rows toUsageRequestORM rows; stable ID isrequest_id→call_id; rows without either are skipped withmissing_stable_request_id; rows missing best-effort fields are accepted aspartialwith LiteLLM source field names; optional join fields (benchmark_session_id,experiment_id,variant_id,task_card_id,harness_profile,trace_id) are read frommetadata.spend_logs_metadata,metadata.requester_metadata,metadata, andrequest_tags, supporting both credential-servicebenchmark_*tags and fixture short names. Metadata is allowlisted +RedactionFilter-ed; content fields and raw key references are never persisted.collectors/key_attribution.py:ProxyKeyAttributionResolverresolves by registrylitellm_key_id, optional ephemeral virtual-key→alias mapping (ADR-002), then reported alias; cached lookups; unresolved keys logged/reported only by alias orsha256:fingerprint.LiteLLMUsageCollector(litellm_collector.py):GET /spend/logs?start_date&end_date&summarize=falsewith no session ID, half-open window filtering, known-session FK linking (viaSessionRepository.exists), dry-run, idempotent writes throughSQLUsageRequestRepository.create_many, categorized errors (auth_error,http_error,connection_error,invalid_response,repository_error).UsageReconciliationReport: mapped/partial/skipped/out-of-window/attributed/unattributed/session-linked/written/duplicate counts, missing-field counts, skip reasons, unattributed keys, per-row diagnostics (capped at 100);to_dict()andto_markdown().docs/data-model-and-observability.md; metadata redaction boundary indocs/security-and-operations.md.Evidence
Test output
Verification
call_idfallback rows assert key alias, model, provider, tokens, cost, latency, TTFT, errors, and timestamps.request_id/call_idrows are skipped with diagnostics; rows with no session metadata are accepted; rows with session metadata preserve join fields; UUID sessions that exist are linked via FK, unknown/non-UUID ones stay in metadata only.messages/response/proxy_server_request, arbitrary metadata, secret-like values, and rawapi_keynever appear in persisted rows; error messages are redacted./spend/logs(httpxMockTransport) tests cover query params (summarize=false, date span, nosession_id), SQLite idempotent re-collection, dry-run, window filtering, wrapped payloads, and error categories.missing_stable_request_id,endTime,spend,proxy_key_id) and absence of raw key values.Checklist
make test)make format-check)make lint type-check)AGENTS.mdupdated (if repo knowledge changed) — not neededType of change
Breaking changes
None. Session-bound benchmark collection APIs are unchanged; new symbols are additive exports from
collectors.Related issues
Additional notes
RequestNormalizerwas not refactored ontocollectors.extractionto avoid changing benchmark-path behavior in this PR; the helpers follow the same documented unit conventions.