- AxAgentExecutionEngine에서 시스템 프롬프트 중복을 제거하고 structured tool_use/tool_result 전사본을 conversation.Messages로 동기화해 다음 턴과 저장 이력에서도 코드 작업 컨텍스트가 유지되도록 수정 - AgentQueryContextBuilder와 ContextCondenser에 post-compact tool snippet 복원, recent window 확대, tool result 보존 강화 로직을 추가해 장기 코드 실행 중 빌드/파일 근거 손실을 줄임 - MaxContextTokens=0 Auto 모드를 AppSettings, SettingsService 마이그레이션, 설정 UI, 오버레이 UI, 컨텍스트 사용량 표시, LLM 요청 본문에 연결하고 Auto 모드에서는 provider output cap 강제 주입을 제거 - 관련 회귀 테스트와 문서 README/DEVELOPMENT/CODE_CONTEXT_RELIABILITY_PLAN을 갱신하고 깨진 진단 문자열 기대값을 영어 기준으로 정리 검증: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_context_reliability_followup\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup\\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AxAgentExecutionEngineTests|AgentQueryContextBuilderTests|ContextCondenserTests|SettingsServiceTests|AgentLoopDiagnosticsFormatterTests" -p:OutputPath=bin\\verify_context_reliability_followup_tests\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup_tests\\ - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentLoopQueryAssemblyServiceTests|AgentLoopPreLlmStageServiceTests|AgentLoopLlmRequestPreparationServiceTests|AgentMessageInvariantHelperTests|CodeTaskWorkingSetServiceTests|AgentLoopE2ETests" -p:OutputPath=bin\\verify_context_reliability_followup_tests2\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_followup_tests2\\ - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_context_reliability_final\\ -p:IntermediateOutputPath=obj\\verify_context_reliability_final\\
139 lines
4.9 KiB
C#
139 lines
4.9 KiB
C#
using AxCopilot.Models;
|
|
using AxCopilot.Services.Agent;
|
|
using FluentAssertions;
|
|
using Xunit;
|
|
|
|
namespace AxCopilot.Tests.Services;
|
|
|
|
public class AgentQueryContextBuilderTests
|
|
{
|
|
[Fact]
|
|
public void Build_ShouldPopulateMissingToolResultPreviewBeforeCreatingQueryView()
|
|
{
|
|
var longContent = new string('Z', 1800);
|
|
var sourceMessages = new List<ChatMessage>
|
|
{
|
|
new()
|
|
{
|
|
MsgId = "tool-source-1",
|
|
Role = "user",
|
|
Content = $$"""{"type":"tool_result","tool_use_id":"call-view","tool_name":"file_read","content":"{{longContent}}"}""",
|
|
QueryPreviewContent = """{"type":"tool_result","tool_use_id":"call-view","tool_name":"file_read","content":"preview-view"}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tool-source-2",
|
|
Role = "user",
|
|
Content = $$"""{"type":"tool_result","tool_use_id":"call-view","tool_name":"file_read","content":"{{longContent}}"}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tail-1",
|
|
Role = "assistant",
|
|
Content = "recent tail"
|
|
}
|
|
};
|
|
|
|
var result = AgentQueryContextBuilder.Build(sourceMessages);
|
|
|
|
sourceMessages[1].QueryPreviewContent.Should().Be(sourceMessages[0].QueryPreviewContent);
|
|
result.Messages[1].QueryPreviewContent.Should().Be(sourceMessages[0].QueryPreviewContent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ShouldSynthesizeToolResultPreview_WhenNoStoredPreviewExists()
|
|
{
|
|
var longContent = string.Join(' ', Enumerable.Repeat("long tool output", 120));
|
|
var sourceMessages = new List<ChatMessage>
|
|
{
|
|
new()
|
|
{
|
|
MsgId = "tool-source-1",
|
|
Role = "user",
|
|
Content = $$"""{"type":"tool_result","tool_use_id":"call-synth-view","tool_name":"file_read","content":"{{longContent}}"}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tail-1",
|
|
Role = "assistant",
|
|
Content = "recent tail"
|
|
}
|
|
};
|
|
|
|
var result = AgentQueryContextBuilder.Build(sourceMessages);
|
|
|
|
sourceMessages[0].QueryPreviewContent.Should().NotBeNullOrWhiteSpace();
|
|
sourceMessages[0].QueryPreviewContent.Should().Contain("call-synth-view");
|
|
result.Messages.Should().Contain(message =>
|
|
message.QueryPreviewContent != null &&
|
|
message.QueryPreviewContent.Contains("call-synth-view", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ShouldExposeCodeProfileMetadata()
|
|
{
|
|
var sourceMessages = new List<ChatMessage>
|
|
{
|
|
new()
|
|
{
|
|
MsgId = "tool-source-1",
|
|
Role = "user",
|
|
Content = """{"type":"tool_result","tool_use_id":"call-code","tool_name":"build_run","content":"short"}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tail-1",
|
|
Role = "assistant",
|
|
Content = "recent tail"
|
|
}
|
|
};
|
|
|
|
var result = AgentQueryContextBuilder.Build(
|
|
sourceMessages,
|
|
AgentQueryContextBuilder.AgentQueryContextBuildOptions.CreateCodeDefault());
|
|
|
|
result.ProfileName.Should().Be("code");
|
|
result.ProtectedRecentNonSystemMessages.Should().BeGreaterThan(8);
|
|
result.ToolResultAggregateBudgetChars.Should().BeGreaterThan(AgentToolResultBudget.DefaultAggregateBudgetChars);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ShouldRestoreRecentCompactedToolSnippetsIntoPostCompactContextMessage()
|
|
{
|
|
var sourceMessages = new List<ChatMessage>
|
|
{
|
|
new()
|
|
{
|
|
MsgId = "tool-call-1",
|
|
Role = "assistant",
|
|
Content = """{"_tool_use_blocks":[{"type":"tool_use","id":"call-restore","name":"file_read","input":{"path":"MainWindow.xaml"}}]}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tool-result-1",
|
|
Role = "user",
|
|
Content = """{"type":"tool_result","tool_use_id":"call-restore","tool_name":"file_read","content":"<Window Title=\"Preview\" />"}"""
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "boundary-1",
|
|
Role = "system",
|
|
MetaKind = "microcompact_boundary",
|
|
Content = "[previous conversation summary]"
|
|
},
|
|
new()
|
|
{
|
|
MsgId = "tail-1",
|
|
Role = "assistant",
|
|
Content = "recent tail"
|
|
}
|
|
};
|
|
|
|
var result = AgentQueryContextBuilder.Build(sourceMessages);
|
|
var postCompactMessage = result.Messages.Single(message => message.MetaKind == "post_compact_context");
|
|
|
|
postCompactMessage.Content.Should().Contain("recent compacted tool trace:");
|
|
postCompactMessage.Content.Should().Contain("file_read result:");
|
|
}
|
|
}
|