mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 09:58:00 +00:00
Added the exact token count where the provider reports it
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c95cc5bacc
commit
0aa3b23b81
21 files changed
+598
-7
No files matched your search
@@ -23,6 +23,17 @@ public record ChatCompletionAPIRequest(
|
||||
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public bool? ParallelToolCalls { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Asks a streamed request to end with what it cost.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Derived rather than set, so that every provider which builds one of these asks for it
|
||||
/// without having to know that it exists. A request which is not streamed carries no such
|
||||
/// line, and then the block would only be a field the provider has to ignore.
|
||||
/// </remarks>
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public ChatCompletionStreamOptions? StreamOptions => this.Stream ? ChatCompletionStreamOptions.INCLUDE_USAGE : null;
|
||||
|
||||
// Attention: The "required" modifier is not supported for [JsonExtensionData].
|
||||
[JsonExtensionData]
|
||||
|
||||
@@ -15,12 +15,30 @@ public record ChatCompletionDeltaStreamLine(string Id, string Object, uint Creat
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What the provider says the request cost, on the one line which carries it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Not a positional parameter: every provider builds an empty line through the constructor
|
||||
/// above, and a further parameter would change all of those call sites for a value none of
|
||||
/// them has. Providers send this block only when the request asked for it, and then on a final
|
||||
/// line of its own which carries no choices -- which is why the usage is read apart from the
|
||||
/// content rather than next to it.
|
||||
/// </remarks>
|
||||
public ChatCompletionUsage? Usage { get; init; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsContent() => this.Choices.Count > 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContentStreamChunk GetContent() => new(this.Choices[0].Delta.Content, []);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool ContainsUsage() => this.GetUsage().IsKnown;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TokenUsage GetUsage() => this.Usage is null ? TokenUsage.UNKNOWN : TokenUsage.OfReported(this.Usage.PromptTokens, this.Usage.CompletionTokens);
|
||||
|
||||
#region Implementation of IAnnotationStreamLine
|
||||
|
||||
//
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// What a streamed chat completion should report beyond its content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An OpenAI-compatible provider says nothing about what a streamed request cost unless it is asked
|
||||
/// to. Without this block, the stream simply ends and the only token number anybody ever sees is
|
||||
/// the one AI Studio estimated for itself.
|
||||
/// </remarks>
|
||||
/// <param name="IncludeUsage">Whether the stream should end with a line stating the token usage.</param>
|
||||
public sealed record ChatCompletionStreamOptions(bool IncludeUsage)
|
||||
{
|
||||
/// <summary>
|
||||
/// Asks for the usage line.
|
||||
/// </summary>
|
||||
public static readonly ChatCompletionStreamOptions INCLUDE_USAGE = new(true);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// ReSharper disable ClassNeverInstantiated.Global
|
||||
namespace AIStudio.Provider.OpenAI;
|
||||
|
||||
/// <summary>
|
||||
/// What an OpenAI-compatible provider reports a chat completion cost.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Every number is optional because this is somebody else's JSON: the block arrives only when the
|
||||
/// request asked for it, and the providers which follow the shape loosely leave fields out. Reading
|
||||
/// it is one thing, believing it another -- <see cref="TokenUsage.OfReported"/> decides that.
|
||||
/// </remarks>
|
||||
public sealed record ChatCompletionUsage
|
||||
{
|
||||
/// <summary>
|
||||
/// What everything sent to the model cost.
|
||||
/// </summary>
|
||||
public int? PromptTokens { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// What the model wrote in answer.
|
||||
/// </summary>
|
||||
public int? CompletionTokens { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// What the provider says both of them add up to. Read but not relied upon: it is the sum of
|
||||
/// the other two wherever a provider fills all three, and this way a provider which sends only
|
||||
/// this one is not a reason to throw the other numbers away.
|
||||
/// </summary>
|
||||
public int? TotalTokens { get; init; }
|
||||
}
|
||||
Reference in new issue
Block a user