mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-07 16:48:45 +08:00
Fixes #5148: with aggregator upstreams (e.g. newapi) that end SSE streams without a proper terminal event, every such request was silently missing from usage logs and billing. Root cause (tracked via the nested audit issue): the low-level Anthropic SSE readers already return the partially collected usage together with the stream error (missing terminal event, read error, interval timeout), but Forward converted every such result to (nil, err) and the handler returned before submitting RecordUsage. Changes: - Add partialStreamUsageResult: on stream errors, wrap observed usage into a ForwardResult and return it alongside the error, for both the regular Anthropic path and the API-key passthrough path. Invariants: UpstreamFailoverError always keeps result=nil (failover retries are billed as the successful attempt, never twice), and zero observed usage returns no partial result (no phantom zero-usage records). - Messages handler: hoist the usage submission block into a closure shared by the success path and the new partial-result error path. - Usage record worker pool: distinguish pool-stopped drops (dropped_stopped) from operator-configured drop/sample overflow drops; billing tasks now fall back to inline synchronous execution only during the shutdown window, while explicit drop/sample overflow semantics are preserved. Image usage keeps its mandatory fallback for both drop kinds via the new mode.Dropped() helper. Tests: Forward-level regressions for missing-terminal / read-error / no-usage / failover-invariant on both paths, plus handler-level stopped-pool sync fallback and drop-policy preservation tests.