Count the tool conversation while it is in flight

This commit is contained in:
Thorsten Sommer committed 2026-09-14 18:39:52 +02:00
1 parent 3b6fd41bda
commit b8b8e3f66d
8 files changed
+96 -7

No files matched your search

@@ -114,6 +114,7 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
// The model's turn has to be recorded before its results, or the provider sees
// results for a turn it does not know about:
adapter.RecordAssistantTurn();
await context.PublishPendingToolConversationAsync(adapter);
foreach (var call in round.Calls)
{
@@ -124,6 +125,7 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
toolResultCharacterCount += invalidContent.Length;
await context.AddToolInvocationAsync(invalidTrace);
adapter.RecordToolResult(call.CallId, invalidContent, isError: true);
await context.PublishPendingToolConversationAsync(adapter);
continue;
}
@@ -135,6 +137,7 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
if (callsUnavailableInstruction is not null)
{
adapter.RecordToolResult(call.CallId, callsUnavailableInstruction);
await context.PublishPendingToolConversationAsync(adapter);
continue;
}
@@ -156,6 +159,7 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
// A blocked call counts as a failure towards the model as much as an errored
// one does: in both cases it did not get the data it asked for.
adapter.RecordToolResult(call.CallId, toolContent, trace.Status is not ToolInvocationTraceStatus.SUCCESS);
await context.PublishPendingToolConversationAsync(adapter);
}
}
finally
@@ -58,6 +58,24 @@ public sealed class ToolCallingLoopContext
await this.CurrentAssistantContent.StreamingEvent();
}
/// <summary>
/// Hands the conversation the adapter has accumulated to the assistant message.
/// </summary>
/// <remarks>
/// Called after every recording, not once per round: a round which reads five web pages is the
/// one during which the request grows the most, and a number which only moves between rounds
/// would stand still through exactly that.
/// </remarks>
/// <param name="adapter">The adapter of this run, which knows what it has recorded.</param>
public async Task PublishPendingToolConversationAsync(IToolCallingProviderAdapter adapter)
{
if (this.CurrentAssistantContent is null)
return;
this.CurrentAssistantContent.PendingToolConversation = [..adapter.RecordedRequestTexts];
await this.CurrentAssistantContent.StreamingEvent();
}
/// <summary>
/// Tells the UI that the named tools are running.
/// </summary>