namespace AIStudio.Provider; /// /// What a provider said one request actually cost, in tokens. /// /// /// The counterpart to what the app counts for itself: the app estimates what the next request will /// cost, while this is what the provider charged for the last one. Two different statements, and /// this one is the only exact one of the two. /// /// Nothing here says "unknown" with a zero. The default value of this type is unknown, which is the /// right answer for every provider which reports nothing, and a counted request can never cost zero /// prompt tokens because the factory below refuses to build one. /// public readonly record struct TokenUsage { /// /// The usage of a request nobody reported anything about. /// public static readonly TokenUsage UNKNOWN = new(); /// /// Whether a provider reported anything at all. When false, the numbers are meaningless. /// public bool IsKnown { get; private init; } /// /// What everything sent to the model cost: the conversation so far, its attachments, the system /// prompt, and whatever tools were offered. /// public int PromptTokens { get; private init; } /// /// What the model wrote in answer. /// public int CompletionTokens { get; private init; } /// /// What the whole exchange cost, which is what the next request carries as its history. /// public int TotalTokens => this.PromptTokens + this.CompletionTokens; /// /// States what a provider reported. /// /// /// A completion of zero tokens is a real answer: a model which was cut off before writing /// anything still cost its prompt. A prompt of zero is not, because there is no request /// without one, and a provider sending it means we read the wrong field. /// /// What the request carried. Has to be greater than zero. /// What the answer cost. Zero or more. /// The usage. public static TokenUsage Of(int promptTokens, int completionTokens) { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(promptTokens); ArgumentOutOfRangeException.ThrowIfNegative(completionTokens); return new() { IsKnown = true, PromptTokens = promptTokens, CompletionTokens = completionTokens, }; } /// /// States what a provider reported, or unknown when it reported nothing usable. /// /// /// For the reading side, where the numbers come out of somebody else's JSON: a missing field, a /// null, or a zero prompt all mean the same thing there, and none of them is worth an exception. /// /// What the request carried, as the provider stated it. /// What the answer cost, as the provider stated it. /// The usage, or UNKNOWN. public static TokenUsage OfReported(int? promptTokens, int? completionTokens) => promptTokens is > 0 ? Of(promptTokens.Value, completionTokens is > 0 ? completionTokens.Value : 0) : UNKNOWN; }