Cover the streamed reconstruction of a reply's tool calls · SciSharp/BotSharp@5373ad8 · GitHub
Skip to content

Commit 5373ad8

Browse files
marsyusmsclaude
andcommitted
Cover the streamed reconstruction of a reply's tool calls
Reading a reply's tool calls has two implementations. The non-streaming one is handed a finished list and only has to map it. The streaming one rebuilds the list from fragments, and it was the single piece of that path with nothing behind it: no test, and its only logging sits under #if DEBUG, so a Release deployment using it reports nothing at all. Reassembly has to guess because StreamingChatToolCallUpdate exposes only FunctionArgumentsUpdate, FunctionName, Kind and ToolCallId -- no public index. Which call a fragment belongs to is inferred from the tool call id, present when a call opens. Ten tests pin that inference: fragments joined per call, two calls kept apart, two calls of the SAME tool kept apart on id alone, an id repeated on every fragment not reopening a call, a name arriving after its id, a first fragment with no id, a call with no arguments, order, and an empty stream. Checked against the implementation this replaced -- first non-empty name, every argument fragment concatenated into one string -- four of the ten fail. The other six pass, which is the shape of the defect: it is invisible while a reply asks for one tool and produces one malformed argument blob the moment it asks for two. Same tool, twice, is not hypothetical. Production replies carry up to four copies of one tool name in a single reply, and the id is the only thing telling those apart, so grouping must never fall back to the name. ReconstructToolCalls becomes internal, with InternalsVisibleTo for the test assembly, rather than being exercised through a live stream. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 7ad0201 commit 5373ad8

4 files changed

Lines changed: 224 additions & 1 deletion

File tree

‎src/Plugins/BotSharp.Plugin.OpenAI/BotSharp.Plugin.OpenAI.csproj‎

Lines changed: 6 additions & 0 deletions

‎src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.Chat.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ private static List<LlmToolCall> ToLlmToolCalls(IEnumerable<ChatToolCall>? toolC
769769
/// Concatenating every fragment into a single string, as this did before, produced one
770770
/// malformed argument blob as soon as the model asked for more than one tool at a time.
771771
/// </remarks>
772-
private static List<LlmToolCall> ReconstructToolCalls(List<StreamingChatToolCallUpdate> updates)
772+
internal static List<LlmToolCall> ReconstructToolCalls(List<StreamingChatToolCallUpdate> updates)
773773
{
774774
var calls = new List<LlmToolCall>();
775775
var args = new List<StringBuilder>();

‎tests/BotSharp.Core.UnitTests/BotSharp.Core.UnitTests.csproj‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
<ProjectReference Include="..\..\src\Plugins\BotSharp.Plugin.AgentTesting\BotSharp.Plugin.AgentTesting.csproj" />
3535
<!-- For ChatEventDispatcherTests: chat events must never make their producer wait on a client. -->
3636
<ProjectReference Include="..\..\src\Plugins\BotSharp.Plugin.ChatHub\BotSharp.Plugin.ChatHub.csproj" />
37+
<!-- For StreamingToolCallReconstructionTests: the streamed tool-call reconstruction lives here. -->
38+
<ProjectReference Include="..\..\src\Plugins\BotSharp.Plugin.OpenAI\BotSharp.Plugin.OpenAI.csproj" />
3739
</ItemGroup>
3840

3941
</Project>
Lines changed: 215 additions & 0 deletions

0 commit comments

Comments
 (0)