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;
}