mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-04 18:09:40 +00:00
Stream the answers again whenever tools are in play (#986)
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Verify (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Sync Flatpak repo (push) Has been cancelled
Build and Release / Collect Flatpak artifacts (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Verify (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Sync Flatpak repo (push) Has been cancelled
Build and Release / Collect Flatpak artifacts (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
1 parent
459165f1be
commit
c95cc5bacc
37 files changed
+2301
-329
No files matched your search
@@ -18,7 +18,17 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
|
||||
{
|
||||
private const string NO_ANSWER_AFTER_TOOL_CALL = "The model completed the tool call but did not return a final answer.";
|
||||
private const string NO_ANSWER_AFTER_LIMIT = "The model did not return a final answer after completing the available tool calls.";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// What separates the text of one round from the text of the next one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A model may write before it calls a tool and again after the result came back. Without a
|
||||
/// separator, the last word of one round and the first of the next would run into each other,
|
||||
/// since each round is a text of its own rather than a continuation.
|
||||
/// </remarks>
|
||||
private const string ROUND_TEXT_SEPARATOR = "\n\n";
|
||||
|
||||
/// <inheritdoc />
|
||||
public async IAsyncEnumerable<ContentStreamChunk> RunAsync(
|
||||
IToolCallingProviderAdapter adapter,
|
||||
@@ -28,6 +38,7 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
|
||||
var toolCallCount = 0;
|
||||
var toolResultCharacterCount = 0L;
|
||||
var toolSources = new List<Source>();
|
||||
var hasStreamedTextBefore = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -38,13 +49,52 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
|
||||
var finalResponseInstruction = ToolSelectionRules.GetToolCallsUnavailableInstruction(toolCallCount, toolResultCharacterCount);
|
||||
var finalResponseRequired = finalResponseInstruction is not null;
|
||||
|
||||
var round = await adapter.ExecuteRoundAsync(finalResponseInstruction, !finalResponseRequired, token);
|
||||
ToolCallingRound? round = null;
|
||||
var roundStreamedText = false;
|
||||
|
||||
//
|
||||
// The model's words go out while the round is still running. That includes what it
|
||||
// writes before a tool call -- "let me look that up" -- which used to be dropped on
|
||||
// the floor because only the round's outcome was ever shown.
|
||||
//
|
||||
await foreach (var streamEvent in adapter.ExecuteRoundAsync(finalResponseInstruction, !finalResponseRequired, token))
|
||||
{
|
||||
if (streamEvent.Kind is ToolCallingStreamEventKind.ROUND_COMPLETED)
|
||||
{
|
||||
round = streamEvent.Round;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (streamEvent.Delta is null)
|
||||
continue;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(streamEvent.Delta.Content))
|
||||
{
|
||||
//
|
||||
// The separator goes out once the new round actually has something to say:
|
||||
// otherwise it would trail a round which only called a tool.
|
||||
//
|
||||
if (!roundStreamedText && hasStreamedTextBefore)
|
||||
yield return new ContentStreamChunk(ROUND_TEXT_SEPARATOR, []);
|
||||
|
||||
roundStreamedText = true;
|
||||
hasStreamedTextBefore = true;
|
||||
}
|
||||
|
||||
yield return streamEvent.Delta;
|
||||
}
|
||||
|
||||
//
|
||||
// No outcome means the round failed: the request errored out, or the stream ended
|
||||
// mid-sentence. Either way the adapter has already reported it.
|
||||
//
|
||||
if (round is null)
|
||||
{
|
||||
await context.ResetToolRuntimeStatusAsync();
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
var roundAnswered = roundStreamedText || !string.IsNullOrWhiteSpace(round.TextOutput);
|
||||
toolSources.MergeSources(round.Sources);
|
||||
|
||||
//
|
||||
@@ -65,8 +115,14 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
|
||||
if (finalResponseRequired)
|
||||
{
|
||||
await context.ResetToolRuntimeStatusAsync();
|
||||
|
||||
//
|
||||
// The answer itself is out already, so what is left to hand over are the sources
|
||||
// the tools contributed. An empty chunk is how sources travel on their own; the
|
||||
// streaming paths of the providers attach their annotations the same way.
|
||||
//
|
||||
yield return new ContentStreamChunk(
|
||||
string.IsNullOrWhiteSpace(round.TextOutput) ? NO_ANSWER_AFTER_LIMIT : round.TextOutput,
|
||||
roundAnswered ? string.Empty : NO_ANSWER_AFTER_LIMIT,
|
||||
[..toolSources]);
|
||||
|
||||
yield break;
|
||||
@@ -75,9 +131,9 @@ public sealed class ToolCallingLoop(ILogger<ToolCallingLoop> logger) : IToolCall
|
||||
if (round.Calls.Count is 0)
|
||||
{
|
||||
await context.ResetToolRuntimeStatusAsync();
|
||||
if (!string.IsNullOrWhiteSpace(round.TextOutput))
|
||||
if (roundAnswered)
|
||||
{
|
||||
yield return new ContentStreamChunk(round.TextOutput, [..toolSources]);
|
||||
yield return new ContentStreamChunk(string.Empty, [..toolSources]);
|
||||
yield break;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user