mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-12 23:53:48 +00:00
Fixed reasoning tokens inflating the exact token count
This commit is contained in:
12 files changed
+172
-104
No files matched your search
+45
-16
@@ -13,7 +13,7 @@ namespace AIStudio.Tests.Chat;
|
||||
/// exactly the state the report was taken in, and the report counts again.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class ChatThreadReportedUsageTests
|
||||
public sealed class ChatThreadReportedHistoryTests
|
||||
{
|
||||
private static readonly DateTimeOffset START = new(2026, 9, 23, 10, 0, 0, TimeSpan.Zero);
|
||||
|
||||
@@ -26,12 +26,39 @@ public sealed class ChatThreadReportedUsageTests
|
||||
{
|
||||
var thread = Thread(Question(1), Answer(2, promptTokens: 1200, blockCount: 2));
|
||||
|
||||
var usage = thread.ReportedUsageFor(MODEL);
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(usage.IsKnown, Is.True);
|
||||
Assert.That(usage.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
|
||||
//
|
||||
// The answer comes along as text rather than as the provider's number for it, which
|
||||
// would include the reasoning the next request never carries:
|
||||
//
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnAnswerWithoutTextAddsNothingToThePrompt()
|
||||
{
|
||||
//
|
||||
// An answer which consists of reasoning only. It may be kept to be read, but without any
|
||||
// text it is never sent, so what the provider counted is all the next request carries of
|
||||
// the conversation so far.
|
||||
//
|
||||
var reasoningOnly = new ContentText { Text = string.Empty };
|
||||
reasoningOnly.RecordReportedUsage(TokenUsage.Of(1200), MODEL.Id, 2);
|
||||
var thread = Thread(Question(1), Block(ChatRole.AI, reasoningOnly, 2));
|
||||
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,7 +72,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
//
|
||||
var thread = Thread(Question(1), Answer(2, promptTokens: 1200, blockCount: 2), Question(3));
|
||||
|
||||
Assert.That(thread.ReportedUsageFor(MODEL).IsKnown, Is.False);
|
||||
Assert.That(thread.ReportedHistoryFor(MODEL).IsKnown, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -54,7 +81,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
var streaming = Block(ChatRole.AI, new ContentText { Text = "Half an ans", IsStreaming = true }, 4);
|
||||
var thread = Thread(Question(1), Answer(2, promptTokens: 1200, blockCount: 2), Question(3), streaming);
|
||||
|
||||
Assert.That(thread.ReportedUsageFor(MODEL).IsKnown, Is.False);
|
||||
Assert.That(thread.ReportedHistoryFor(MODEL).IsKnown, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -67,7 +94,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
var withoutReport = Block(ChatRole.AI, new ContentText { Text = "Second answer" }, 4);
|
||||
var thread = Thread(Question(1), Answer(2, promptTokens: 1200, blockCount: 2), Question(3), withoutReport);
|
||||
|
||||
Assert.That(thread.ReportedUsageFor(MODEL).IsKnown, Is.False);
|
||||
Assert.That(thread.ReportedHistoryFor(MODEL).IsKnown, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -82,7 +109,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
|
||||
thread.Remove(firstQuestion.Content!);
|
||||
|
||||
Assert.That(thread.ReportedUsageFor(MODEL).IsKnown, Is.False);
|
||||
Assert.That(thread.ReportedHistoryFor(MODEL).IsKnown, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,7 +117,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
{
|
||||
var thread = Thread(Question(1), Answer(2, promptTokens: 1200, blockCount: 2));
|
||||
|
||||
Assert.That(thread.ReportedUsageFor(OTHER_MODEL).IsKnown, Is.False);
|
||||
Assert.That(thread.ReportedHistoryFor(OTHER_MODEL).IsKnown, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -107,11 +134,12 @@ public sealed class ChatThreadReportedUsageTests
|
||||
thread.Remove(lastQuestion.Content!);
|
||||
thread.Remove(lastAnswer.Content!);
|
||||
|
||||
var usage = thread.ReportedUsageFor(MODEL);
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(usage.IsKnown, Is.True, "The first answer is the last block again, with exactly the blocks it was reported for.");
|
||||
Assert.That(usage.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.IsKnown, Is.True, "The first answer is the last block again, with exactly the blocks it was reported for.");
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,11 +151,12 @@ public sealed class ChatThreadReportedUsageTests
|
||||
|
||||
thread.RollBackTo(firstAnswer.Content!);
|
||||
|
||||
var usage = thread.ReportedUsageFor(MODEL);
|
||||
var history = thread.ReportedHistoryFor(MODEL);
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(usage.IsKnown, Is.True);
|
||||
Assert.That(usage.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.IsKnown, Is.True);
|
||||
Assert.That(history.PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(history.LastAnswer, Is.EqualTo("Answer 2"));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -141,7 +170,7 @@ public sealed class ChatThreadReportedUsageTests
|
||||
private static ContentBlock Answer(int minute, int promptTokens, int blockCount)
|
||||
{
|
||||
var answer = new ContentText { Text = $"Answer {minute}" };
|
||||
answer.RecordReportedUsage(TokenUsage.Of(promptTokens, 100), MODEL.Id, blockCount);
|
||||
answer.RecordReportedUsage(TokenUsage.Of(promptTokens), MODEL.Id, blockCount);
|
||||
return Block(ChatRole.AI, answer, minute);
|
||||
}
|
||||
|
||||
@@ -43,8 +43,6 @@ public sealed class ChatCompletionUsageTests
|
||||
{
|
||||
Assert.That(line!.GetUsage().IsKnown, Is.True);
|
||||
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(1200));
|
||||
Assert.That(line.GetUsage().CompletionTokens, Is.EqualTo(345));
|
||||
Assert.That(line.GetUsage().TotalTokens, Is.EqualTo(1545));
|
||||
|
||||
//
|
||||
// The line which carries the usage carries no answer, which is why it has to be read
|
||||
@@ -109,9 +107,10 @@ public sealed class ChatCompletionUsageTests
|
||||
/// The line a real LM Studio server sends, taken off the wire.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It carries fields the shape above does not name -- the reasoning share of the completion,
|
||||
/// among them -- and a server which sends more than we read must not stop us from reading what
|
||||
/// we came for.
|
||||
/// It carries far more than we read, and it shows why the prompt is all we take: 102 of the 116
|
||||
/// completion tokens are the model's reasoning, which the next request never carries. Counting
|
||||
/// the completion would have put the history at 133 tokens, when what travels on is the prompt
|
||||
/// and an answer of a handful of tokens.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void ARealServerLineIsRead()
|
||||
@@ -124,22 +123,7 @@ public sealed class ChatCompletionUsageTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(line!.GetUsage().IsKnown, Is.True);
|
||||
Assert.That(line.GetUsage().TotalTokens, Is.EqualTo(133));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An answer cut off before it wrote anything still cost its prompt.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AnAnswerOfNoTokensIsStillACost()
|
||||
{
|
||||
var usage = TokenUsage.OfReported(900, 0);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(usage.IsKnown, Is.True);
|
||||
Assert.That(usage.TotalTokens, Is.EqualTo(900));
|
||||
Assert.That(line.GetUsage().PromptTokens, Is.EqualTo(17));
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user