mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-18 20:30:51 +08:00
fix(executor): ensure usage reporting for upstream responses lacking usage data
Add `ensurePublished` to guarantee request counting even when usage fields (e.g., tokens) are absent in OpenAI-compatible executor responses, particularly for streaming paths.
This commit is contained in:
@@ -116,6 +116,8 @@ func (e *OpenAICompatExecutor) Execute(ctx context.Context, auth *cliproxyauth.A
|
|||||||
}
|
}
|
||||||
appendAPIResponseChunk(ctx, e.cfg, body)
|
appendAPIResponseChunk(ctx, e.cfg, body)
|
||||||
reporter.publish(ctx, parseOpenAIUsage(body))
|
reporter.publish(ctx, parseOpenAIUsage(body))
|
||||||
|
// Ensure we at least record the request even if upstream doesn't return usage
|
||||||
|
reporter.ensurePublished(ctx)
|
||||||
// Translate response back to source format when needed
|
// Translate response back to source format when needed
|
||||||
var param any
|
var param any
|
||||||
out := sdktranslator.TranslateNonStream(ctx, to, from, req.Model, bytes.Clone(opts.OriginalRequest), translated, body, ¶m)
|
out := sdktranslator.TranslateNonStream(ctx, to, from, req.Model, bytes.Clone(opts.OriginalRequest), translated, body, ¶m)
|
||||||
@@ -225,6 +227,8 @@ func (e *OpenAICompatExecutor) ExecuteStream(ctx context.Context, auth *cliproxy
|
|||||||
reporter.publishFailure(ctx)
|
reporter.publishFailure(ctx)
|
||||||
out <- cliproxyexecutor.StreamChunk{Err: errScan}
|
out <- cliproxyexecutor.StreamChunk{Err: errScan}
|
||||||
}
|
}
|
||||||
|
// Ensure we record the request if no usage chunk was ever seen
|
||||||
|
reporter.ensurePublished(ctx)
|
||||||
}()
|
}()
|
||||||
return stream, nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,28 @@ func (r *usageReporter) publishWithOutcome(ctx context.Context, detail usage.Det
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensurePublished guarantees that a usage record is emitted exactly once.
|
||||||
|
// It is safe to call multiple times; only the first call wins due to once.Do.
|
||||||
|
// This is used to ensure request counting even when upstream responses do not
|
||||||
|
// include any usage fields (tokens), especially for streaming paths.
|
||||||
|
func (r *usageReporter) ensurePublished(ctx context.Context) {
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.once.Do(func() {
|
||||||
|
usage.PublishRecord(ctx, usage.Record{
|
||||||
|
Provider: r.provider,
|
||||||
|
Model: r.model,
|
||||||
|
Source: r.source,
|
||||||
|
APIKey: r.apiKey,
|
||||||
|
AuthID: r.authID,
|
||||||
|
RequestedAt: r.requestedAt,
|
||||||
|
Failed: false,
|
||||||
|
Detail: usage.Detail{},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func apiKeyFromContext(ctx context.Context) string {
|
func apiKeyFromContext(ctx context.Context) string {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
Reference in New Issue
Block a user