Initial commit to new repository
This commit is contained in:
103
src/AxCopilot/Services/Agent/SuggestActionsTool.cs
Normal file
103
src/AxCopilot/Services/Agent/SuggestActionsTool.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
|
||||
/// <summary>
|
||||
/// 작업 완료 후 후속 액션을 구조화하여 제안하는 도구.
|
||||
/// UI가 클릭 가능한 칩으로 렌더링할 수 있도록 JSON 형태로 반환합니다.
|
||||
/// </summary>
|
||||
public class SuggestActionsTool : IAgentTool
|
||||
{
|
||||
public string Name => "suggest_actions";
|
||||
|
||||
public string Description =>
|
||||
"Suggest 2-5 follow-up actions after completing a task. " +
|
||||
"Returns structured JSON that the UI renders as clickable action chips. " +
|
||||
"Each action has a label (display text), command (slash command or natural language prompt), " +
|
||||
"optional icon (Segoe MDL2 Assets code), and priority (high/medium/low).";
|
||||
|
||||
public ToolParameterSchema Parameters => new()
|
||||
{
|
||||
Properties = new()
|
||||
{
|
||||
["actions"] = new()
|
||||
{
|
||||
Type = "array",
|
||||
Description = "List of action objects. Each object: {\"label\": \"표시 텍스트\", \"command\": \"/slash 또는 자연어\", \"icon\": \"\\uE8A5\" (optional), \"priority\": \"high|medium|low\"}",
|
||||
Items = new() { Type = "object", Description = "Action object with label, command, icon, priority" },
|
||||
},
|
||||
["context"] = new()
|
||||
{
|
||||
Type = "string",
|
||||
Description = "Current task context summary (optional)",
|
||||
},
|
||||
},
|
||||
Required = ["actions"],
|
||||
};
|
||||
|
||||
public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!args.TryGetProperty("actions", out var actionsEl) || actionsEl.ValueKind != JsonValueKind.Array)
|
||||
return Task.FromResult(ToolResult.Fail("actions 배열이 필요합니다."));
|
||||
|
||||
var actions = new List<Dictionary<string, string>>();
|
||||
foreach (var item in actionsEl.EnumerateArray())
|
||||
{
|
||||
var label = item.TryGetProperty("label", out var l) ? l.GetString() ?? "" : "";
|
||||
var command = item.TryGetProperty("command", out var c) ? c.GetString() ?? "" : "";
|
||||
var icon = item.TryGetProperty("icon", out var i) ? i.GetString() ?? "" : "";
|
||||
var priority = item.TryGetProperty("priority", out var p) ? p.GetString() ?? "medium" : "medium";
|
||||
|
||||
if (string.IsNullOrWhiteSpace(label))
|
||||
return Task.FromResult(ToolResult.Fail("각 action에는 label이 필요합니다."));
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
return Task.FromResult(ToolResult.Fail("각 action에는 command가 필요합니다."));
|
||||
|
||||
// priority 유효성 검사
|
||||
var validPriorities = new[] { "high", "medium", "low" };
|
||||
if (!validPriorities.Contains(priority))
|
||||
priority = "medium";
|
||||
|
||||
var action = new Dictionary<string, string>
|
||||
{
|
||||
["label"] = label,
|
||||
["command"] = command,
|
||||
["priority"] = priority,
|
||||
};
|
||||
if (!string.IsNullOrEmpty(icon))
|
||||
action["icon"] = icon;
|
||||
|
||||
actions.Add(action);
|
||||
}
|
||||
|
||||
if (actions.Count < 1 || actions.Count > 5)
|
||||
return Task.FromResult(ToolResult.Fail("actions는 1~5개 사이여야 합니다."));
|
||||
|
||||
var contextSummary = args.TryGetProperty("context", out var ctx) ? ctx.GetString() ?? "" : "";
|
||||
|
||||
// 구조화된 JSON 응답 생성
|
||||
var result = new Dictionary<string, object>
|
||||
{
|
||||
["type"] = "suggest_actions",
|
||||
["actions"] = actions,
|
||||
};
|
||||
if (!string.IsNullOrEmpty(contextSummary))
|
||||
result["context"] = contextSummary;
|
||||
|
||||
var json = JsonSerializer.Serialize(result, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
});
|
||||
|
||||
return Task.FromResult(ToolResult.Ok(json));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Task.FromResult(ToolResult.Fail($"액션 제안 오류: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user