using AxCopilot.Models; using AxCopilot.Services.Agent; using FluentAssertions; using Xunit; namespace AxCopilot.Tests.Services; public class AgentLoopLlmRequestPreparationServiceTests { [Fact] public void Prepare_ShouldInjectToolReminderOnFirstForcedCall() { var queryMessages = new List { new() { Role = "user", Content = "inspect the repository" } }; var result = AgentLoopLlmRequestPreparationService.Prepare( queryMessages, totalToolCalls: 0, forceInitialToolCallEnabled: true, injectPreCallToolReminder: true, noToolCallLoopRetry: 0); result.ForceInitialToolCall.Should().BeTrue(); result.InjectedToolReminder.Should().BeTrue(); result.SendMessages.Should().HaveCount(2); result.SendMessages.Last().Content.Should().Contain("[TOOL_REQUIRED]"); } [Fact] public void Prepare_ShouldSkipReminderWhenRetryLoopIsAlreadyActive() { var queryMessages = new List { new() { Role = "user", Content = "inspect the repository" } }; var result = AgentLoopLlmRequestPreparationService.Prepare( queryMessages, totalToolCalls: 0, forceInitialToolCallEnabled: true, injectPreCallToolReminder: true, noToolCallLoopRetry: 1); result.ForceInitialToolCall.Should().BeTrue(); result.InjectedToolReminder.Should().BeFalse(); result.SendMessages.Should().HaveCount(1); } [Fact] public void Prepare_ShouldAppendSupplementalMessagesBeforeReminder() { var queryMessages = new List { new() { Role = "user", Content = "fix the latest build failure" } }; var supplemental = new ChatMessage { Role = "system", MetaKind = "code_working_set", Content = "[code-working-set]\n- Active diagnostic: MC4005 - Themes/ControlStyles.xaml" }; var result = AgentLoopLlmRequestPreparationService.Prepare( queryMessages, totalToolCalls: 0, forceInitialToolCallEnabled: true, injectPreCallToolReminder: true, noToolCallLoopRetry: 0, supplementalMessages: [supplemental]); result.SupplementalMessageCount.Should().Be(1); result.SendMessages[1].MetaKind.Should().Be("code_working_set"); result.SendMessages.Last().Content.Should().Contain("[TOOL_REQUIRED]"); } [Fact] public void Prepare_ShouldNormalizeBrokenHistoricalToolTraceBeforeSend() { var queryMessages = new List { new() { Role = "assistant", Content = """{"_tool_use_blocks":[{"type":"text","text":"Inspecting source"},{"type":"tool_use","id":"call-missing","name":"file_read","input":{"path":"App.xaml"}}]}""" }, new() { Role = "user", Content = "fix the latest build failure" } }; var result = AgentLoopLlmRequestPreparationService.Prepare( queryMessages, totalToolCalls: 1, forceInitialToolCallEnabled: true, injectPreCallToolReminder: true, noToolCallLoopRetry: 0); result.FlattenedStructuredAssistantCount.Should().Be(1); result.ConvertedOrphanToolResultCount.Should().Be(0); result.SendMessages[0].Content.Should().Be("Inspecting source\n[previous tool call] file_read"); queryMessages[0].Content.Should().StartWith("{\"_tool_use_blocks\""); } }