90 lines
2.6 KiB
C#
90 lines
2.6 KiB
C#
using System.Net.Http.Headers;
|
|
using System.Text.Json;
|
|
|
|
namespace AnchorCli.Providers;
|
|
|
|
/// <summary>
|
|
/// Generic token extractor for any OpenAI-compatible endpoint.
|
|
/// Tries common header names and JSON body parsing.
|
|
/// </summary>
|
|
internal sealed class GenericTokenExtractor : ITokenExtractor
|
|
{
|
|
public string ProviderName => "Generic";
|
|
|
|
public (int inputTokens, int outputTokens)? ExtractTokens(HttpResponseHeaders headers, string? responseBody)
|
|
{
|
|
// Try various common header names
|
|
var headerNames = new[] {
|
|
"x-total-tokens",
|
|
"x-ai-response-tokens",
|
|
"x-tokens",
|
|
"x-prompt-tokens",
|
|
"x-completion-tokens"
|
|
};
|
|
|
|
foreach (var headerName in headerNames)
|
|
{
|
|
if (headers.TryGetValues(headerName, out var values))
|
|
{
|
|
if (int.TryParse(values.FirstOrDefault(), out var tokens))
|
|
{
|
|
// Assume all tokens are output if we can't determine split
|
|
return (0, tokens);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: try parsing from response body JSON
|
|
if (!string.IsNullOrEmpty(responseBody))
|
|
{
|
|
try
|
|
{
|
|
using var doc = JsonDocument.Parse(responseBody);
|
|
var root = doc.RootElement;
|
|
|
|
// Try standard OpenAI format: usage.prompt_tokens, usage.completion_tokens
|
|
if (root.TryGetProperty("usage", out var usage))
|
|
{
|
|
var prompt = usage.TryGetProperty("prompt_tokens", out var p) ? p.GetInt32() : 0;
|
|
var completion = usage.TryGetProperty("completion_tokens", out var c) ? c.GetInt32() : 0;
|
|
|
|
if (prompt > 0 || completion > 0)
|
|
{
|
|
return (prompt, completion);
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Ignore parsing errors
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public int? ExtractLatency(HttpResponseHeaders headers)
|
|
{
|
|
// Try various common latency headers
|
|
var headerNames = new[] {
|
|
"x-response-time",
|
|
"x-response-timing",
|
|
"x-latency-ms",
|
|
"x-duration-ms"
|
|
};
|
|
|
|
foreach (var headerName in headerNames)
|
|
{
|
|
if (headers.TryGetValues(headerName, out var values))
|
|
{
|
|
if (int.TryParse(values.FirstOrDefault(), out var latency))
|
|
{
|
|
return latency;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|