?? ?? tool_result preview ??? ???? golden workbook ??? ??

?? ??
- ??, ??, ?? ?? tool_result preview? ??? ??? ???? replacement state? ?? ??? ? ??? ??? ?? ??? ??
- XLSX ?? ?? ??? ?? ?? ?? workbook golden ??? ??? summary/dashboard/detail ??? ????? ??

?? ????
- AgentMessageInvariantHelper? synthetic tool_result preview ?? ??? ??? QueryPreviewContent? ?? ?? ?? ??? tool_use_id, tool_name, ??? content/output/error ?? preview? ????? ??
- BuildToolResultPreviewMap? ?? preview? ?? ???? ?? ?? synthetic preview? ?? ??? ??
- AgentMessageInvariantHelperTests? ??? preview? ?? long tool_result? synthetic preview? ???? ??? explicit preview ?? ?? ??? ?? ??
- AgentQueryContextBuilderTests? synthetic preview? query view ?? ? ?? ???? ???? ????? ??
- ExcelSkillGoldenWorkbookTests? ??? summary/dashboard/detail, formula, data validation, conditional formatting? ??? ?? ?? workbook? Needs work: none ? Repair guide: none? ????? ??
- README.md? docs/DEVELOPMENT.md? 2026-04-15 09:36 (KST) ?? ?? ??? ?? ??? ??

?? ??
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_preview_golden_finish\\ -p:IntermediateOutputPath=obj\\verify_preview_golden_finish\\ : ?? 0 / ?? 0
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentMessageInvariantHelperTests|AgentQueryContextBuilderTests|AgentQueuedCommandProjectorTests|ExcelSkillGoldenWorkbookTests|ExcelSkillDashboardSummaryTests|PptxSkillGoldenDeckTests" -p:OutputPath=bin\\verify_preview_golden_finish_tests\\ -p:IntermediateOutputPath=obj\\verify_preview_golden_finish_tests\\ : ?? 10
This commit is contained in:
2026-04-15 09:11:56 +09:00
parent ff29a83039
commit 8530ec956a
6 changed files with 299 additions and 1 deletions

View File

@@ -0,0 +1,58 @@
using AxCopilot.Models;
using AxCopilot.Services.Agent;
using FluentAssertions;
using Xunit;
namespace AxCopilot.Tests.Services;
public class AgentMessageInvariantHelperTests
{
[Fact]
public void PopulateMissingToolResultPreviews_ShouldSynthesizePreview_WhenNoStoredPreviewExists()
{
var longContent = string.Join(' ', Enumerable.Repeat("Detailed output row for recovery.", 40));
var messages = new List<ChatMessage>
{
new()
{
MsgId = "tool-result-1",
Role = "user",
Content = $$"""{"type":"tool_result","tool_use_id":"call-synth","tool_name":"file_read","content":"{{longContent}}"}"""
}
};
var changed = AgentMessageInvariantHelper.PopulateMissingToolResultPreviews(messages);
changed.Should().BeTrue();
messages[0].QueryPreviewContent.Should().NotBeNullOrWhiteSpace();
messages[0].QueryPreviewContent.Should().Contain("call-synth");
messages[0].QueryPreviewContent.Should().Contain("...");
messages[0].QueryPreviewContent.Should().NotContain(longContent);
}
[Fact]
public void BuildToolResultPreviewMap_ShouldPreferExplicitPreview_WhenAvailable()
{
var explicitPreview = """{"type":"tool_result","tool_use_id":"call-explicit","tool_name":"file_read","content":"preview"}""";
var messages = new List<ChatMessage>
{
new()
{
MsgId = "tool-result-1",
Role = "user",
Content = """{"type":"tool_result","tool_use_id":"call-explicit","tool_name":"file_read","content":"long output content"}""",
QueryPreviewContent = explicitPreview
},
new()
{
MsgId = "tool-result-2",
Role = "user",
Content = """{"type":"tool_result","tool_use_id":"call-explicit","tool_name":"file_read","content":"another long output content"}"""
}
};
var map = AgentMessageInvariantHelper.BuildToolResultPreviewMap(messages);
map["call-explicit"].Should().Be(explicitPreview);
}
}

View File

@@ -39,4 +39,33 @@ public class AgentQueryContextBuilderTests
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));
}
}

View File

@@ -0,0 +1,94 @@
using System.IO;
using System.Text.Json;
using AxCopilot.Services.Agent;
using FluentAssertions;
using Xunit;
namespace AxCopilot.Tests.Services;
public class ExcelSkillGoldenWorkbookTests
{
[Fact]
public async Task ExecuteAsync_WithRichOperatingWorkbook_ShouldReturnStableQualitySummary()
{
var workDir = Path.Combine(Path.GetTempPath(), "ax-xlsx-golden-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(workDir);
try
{
var tool = new ExcelSkill();
var context = new AgentContext
{
WorkFolder = workDir,
Permission = "Auto",
OperationMode = "external",
};
var args = JsonDocument.Parse(
"""
{
"path": "operating-golden.xlsx",
"summary_sheet": {
"name": "Summary",
"dashboard_sheet_name": "Dashboard",
"title": "Operating Review",
"decision_summary": [
{ "label": "Decision Ask", "value": "Approve phase-2 staffing", "owner": "COO" }
],
"scorecards": [
{ "label": "Revenue", "value": "128", "status": "On Track", "note": "Above plan" }
],
"dashboard_tiles": [
{ "label": "Funding Status", "value": "Ready", "status": "Green", "note": "Decision aligned" }
],
"trend_series": [
{ "label": "Revenue", "current": "128", "target": "125", "delta": "+3", "status": "Green" }
],
"variance_series": [
{ "label": "Opex", "actual": "84", "target": "82", "variance": "+2", "status": "Watch" }
],
"sheet_summaries": [
{ "sheet": "Revenue", "status": "Green", "summary": "Bookings remain above plan", "owner": "Sales Ops" },
{ "sheet": "Cost", "status": "Watch", "summary": "Contractor spend needs control", "owner": "Finance" }
],
"highlights": ["Revenue remains above plan"],
"actions": ["Approve phase-2 staffing"]
},
"sheets": [
{
"name": "Revenue",
"headers": ["Metric", "Value"],
"rows": [["Revenue", 128], ["Growth", "=B2/100"], ["Target Gap", "=B2-125"]],
"conditional_formats": [{ "range": "B2:B3", "type": "data_bar", "color": "2563EB" }]
},
{
"name": "Cost",
"headers": ["Metric", "Value"],
"rows": [["Opex", 84], ["Target", 82], ["Variance", "=B2-B3"]],
"data_validations": [{ "range": "A2:A3", "type": "list", "formula1": "\"Opex,Target\"", "allow_blank": false }]
}
]
}
""").RootElement;
var result = await tool.ExecuteAsync(args, context, CancellationToken.None);
result.Success.Should().BeTrue();
result.Output.Should().Contain("Quality score");
result.Output.Should().Contain("Needs work: none");
result.Output.Should().Contain("Repair guide: none");
File.Exists(Path.Combine(workDir, "operating-golden.xlsx")).Should().BeTrue();
}
finally
{
try
{
if (Directory.Exists(workDir))
Directory.Delete(workDir, true);
}
catch
{
}
}
}
}