Commit Graph

5 Commits

Author SHA1 Message Date
sususu
580df36423 feat(usage): propagate session and parent session hierarchy to usage reporting queue
- Reuse coresession.ExtractSessionInfo across HTTP headers and request payloads to unify canonical session prefix namespaces with the scheduler.
- Extract hierarchical session identities in two phases: initial extraction from request headers on entry, and authoritative deep extraction once request payloads and metadata are available.
- Support Claude Code multi-level subagents (X-Claude-Code-Agent-Id, metadata.agent_id) and Codex thread fork lineages.
- Propagate SessionID and ParentSessionID across ClientRequestMetadata, UsageReporter, and coreusage.Record without root_session_id.
- Include session_id and parent_session_id in queuedUsageDetail for Home LPushUsage forwarding and Redis consumption with self-loop guards.
- Add comprehensive test coverage for canonical headers, body extraction, ghost parent elimination, and self-referential loop guards.
2026-09-06 10:45:32 +08:00
sususu98
ca601db05d feat: observe upstream provider quota signals (#5211)
Codex and Claude already emit credential-level quota watermarks on ordinary
responses. CPA used to drop them. Keep the latest watermark in memory and
return it from the management auth-file API.

Hard rule: this is observation only. It must not change scheduling, cooldown
selection, or auth-file persistence.

Snapshot, not accumulation
- QuotaState now has ObservedAt and a bounded Signals map. MarkResult fills
  them from the response headers already recorded on the request.
- Signals is the current response, not a union of earlier ones. Retry-After
  and "limit reached" only appear on the response that produced them; merging
  across responses would keep an expired value forever.
- A response with no quota header (transport failure, 5xx, unrelated endpoint)
  leaves the previous snapshot in place.
- ObservedAt is the time of the current snapshot. It advances even when the
  values did not change, so a consumer can tell a fresh reading from a stale
  one.
- When two model states merge, keep the newer snapshot. Do not union keys
  captured at different times.

What is observed, and what is not
- One predicate, ProviderSupportsQuotaObservation, decides the provider set.
- Keep Codex and Claude. Drop Kimi, xAI/Grok, Antigravity, and the Gemini
  family (gemini/vertex/aistudio): their ordinary headers are not a reliable
  credential-level remaining quota.
- Count-tokens reuses the credential but is not generation traffic.
  ExecuteCount sets SkipQuotaObservation so those headers cannot replace the
  last generation snapshot. Cooldown and success/failure accounting still run.

Cooldown must not overwrite the last snapshot
- Observation writes only ObservedAt and Signals.
- Cooldown writes only Exceeded, Reason, NextRecoverAt, and BackoffLevel,
  through applyCooldownFields. Never assign a fresh QuotaState{...} over a
  live value: that would zero the snapshot on 429, Cloudflare, credential-
  scope sibling updates, and cooldown clears.
- If a credential-quota cooldown is still active, MarkResult still observes
  an already-present model state. It does not create scheduler state just to
  record a watermark.
- .cds files persist cooldownFieldsOf(Quota) only. Restore keeps the newer
  ObservedAt, so reloading cooldown cannot clobber a newer in-memory snapshot.
- cooldownQuotaEqual still ignores observation fields, so a watermark change
  cannot by itself persist cooldown or move the scheduler.
- The management payload omits every cooldown field, so it cannot be mistaken
  for scheduler state or wired back into scheduling.
- Manual ResetQuota still clears the full QuotaState.

Codex websocket events
- Codex WS reports quota as codex.rate_limits frames, not HTTP headers.
  ParseCodexQuotaEventHeaders turns one event into the same bounded header
  shape, and MergeResponseHeaders folds it into the request-scoped holder.
  additional_rate_limits is accepted as an object (websocket) or an array
  (/wham/usage).
- Parse only through AppendCodexAPIWebsocketResponse. The shared
  AppendAPIWebsocketResponse is also used by xAI, and xAI error frames really
  do carry x-ratelimit-* headers. Parsing every frame as Codex quota would
  forge Codex headers into another provider's request log.
- Also capture code_review_rate_limits.
- A malformed active-limit name drops only that one header, not the window
  watermarks parsed from the same event.
- The type discriminator scans a bounded frame prefix, not every byte of
  every frame.
- HTTP namespaces an extra limit by short name (x-codex-bengalfox-*); WS
  namespaces it by limit name (GPT-5.3-Codex-Spark). The two paths cannot
  emit the same header names. The X-Codex-Additional- prefix marks the WS
  origin, and snapshot replacement keeps the two spellings from piling up.

Hardening
- Reject observed values with control characters. These strings reach the
  plain-text request log, and Limit-Name is upstream-controlled, so CR/LF
  could forge a header line.
- When the header cap is hit, keep plan/credits/primary ahead of
  additional-limit namespaces, then sort names so truncation is deterministic.
- QuotaState.Clone deep-copies Signals and is used by Auth.Clone and
  ModelState.Clone.
- Token stores still serialize credential metadata only, so observation adds
  no auth-file writes.
2026-08-24 17:15:37 +08:00
Luis Pater
c9417c8ae9 feat(logging): add client request metadata extraction and propagation
- Added `ClientRequestMetadata` struct to capture client IP, `X-Forwarded-For` headers, and user agent.
- Implemented metadata extraction in HTTP handlers and propagation through context.
- Updated Redis queue plugin to include client request metadata in payloads.
- Added tests to validate metadata extraction and inclusion in request contexts.
2026-07-28 16:34:15 +08:00
Luis Pater
ad98c9549a feat(runtime): track upstream response headers in logging and usage reporting
- Added APIs to store, retrieve, and clone upstream response headers in context for detailed logging.
- Updated `RecordAPIResponseMetadata`, `RecordAPIWebsocketHandshake`, and related methods to capture response headers.
- Extended `UsageReporter` to include response headers in published usage records.
- Enhanced payload tests to validate response headers' integrity and persistence.
- Refactored `usage.Record` to support optional `ResponseHeaders` field.
2026-05-19 01:29:23 +08:00
Luis Pater
4035abc0cd refactor(logging): replace gin-specific context handling with generic context-based request metadata utilities
- Introduced reusable utilities in `requestmeta` to manage endpoint and response status in request contexts.
- Refactored plugins and handlers to use context-based metadata, removing direct dependency on `gin`.
- Updated tests to validate new context utilities and replaced `gin`-based context handling.

Fixed: #3166
2026-04-30 23:36:07 +08:00