Files

331 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace AxCopilot.Services.Agent;
public class JsonTool : IAgentTool
{
private record PathSegment(string Key, int Index, bool IsIndex);
public string Name => "json_tool";
public string Description => "JSON processing tool. Actions: 'validate' — check if text is valid JSON and report errors; 'format' — pretty-print or minify JSON; 'query' — extract value by dot-path (e.g. 'data.users[0].name'); 'keys' — list top-level keys; 'convert' — convert between JSON/CSV (flat arrays only).";
public ToolParameterSchema Parameters
{
get
{
ToolParameterSchema toolParameterSchema = new ToolParameterSchema();
Dictionary<string, ToolProperty> dictionary = new Dictionary<string, ToolProperty>();
ToolProperty obj = new ToolProperty
{
Type = "string",
Description = "Action to perform"
};
int num = 5;
List<string> list = new List<string>(num);
CollectionsMarshal.SetCount(list, num);
Span<string> span = CollectionsMarshal.AsSpan(list);
span[0] = "validate";
span[1] = "format";
span[2] = "query";
span[3] = "keys";
span[4] = "convert";
obj.Enum = list;
dictionary["action"] = obj;
dictionary["json"] = new ToolProperty
{
Type = "string",
Description = "JSON text to process"
};
dictionary["path"] = new ToolProperty
{
Type = "string",
Description = "Dot-path for query action (e.g. 'data.items[0].name')"
};
dictionary["minify"] = new ToolProperty
{
Type = "string",
Description = "For format action: 'true' to minify, 'false' to pretty-print (default)"
};
ToolProperty obj2 = new ToolProperty
{
Type = "string",
Description = "For convert action: target format"
};
num = 1;
List<string> list2 = new List<string>(num);
CollectionsMarshal.SetCount(list2, num);
CollectionsMarshal.AsSpan(list2)[0] = "csv";
obj2.Enum = list2;
dictionary["target_format"] = obj2;
toolParameterSchema.Properties = dictionary;
num = 2;
List<string> list3 = new List<string>(num);
CollectionsMarshal.SetCount(list3, num);
Span<string> span2 = CollectionsMarshal.AsSpan(list3);
span2[0] = "action";
span2[1] = "json";
toolParameterSchema.Required = list3;
return toolParameterSchema;
}
}
public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken))
{
string text = args.GetProperty("action").GetString() ?? "";
string json = args.GetProperty("json").GetString() ?? "";
try
{
if (1 == 0)
{
}
JsonElement value;
JsonElement value2;
JsonElement value3;
ToolResult result = text switch
{
"validate" => Validate(json),
"format" => Format(json, args.TryGetProperty("minify", out value) && value.GetString() == "true"),
"query" => Query(json, args.TryGetProperty("path", out value2) ? (value2.GetString() ?? "") : ""),
"keys" => Keys(json),
"convert" => Convert(json, args.TryGetProperty("target_format", out value3) ? (value3.GetString() ?? "csv") : "csv"),
_ => ToolResult.Fail("Unknown action: " + text),
};
if (1 == 0)
{
}
return Task.FromResult(result);
}
catch (Exception ex)
{
return Task.FromResult(ToolResult.Fail("JSON 처리 오류: " + ex.Message));
}
}
private static ToolResult Validate(string json)
{
try
{
using JsonDocument jsonDocument = JsonDocument.Parse(json);
JsonElement rootElement = jsonDocument.RootElement;
JsonValueKind valueKind = rootElement.ValueKind;
if (1 == 0)
{
}
string text = valueKind switch
{
JsonValueKind.Object => $"Object ({rootElement.EnumerateObject().Count()} keys)",
JsonValueKind.Array => $"Array ({rootElement.GetArrayLength()} items)",
_ => rootElement.ValueKind.ToString(),
};
if (1 == 0)
{
}
string text2 = text;
return ToolResult.Ok("✓ Valid JSON — " + text2);
}
catch (JsonException ex)
{
return ToolResult.Ok("✗ Invalid JSON — " + ex.Message);
}
}
private static ToolResult Format(string json, bool minify)
{
using JsonDocument jsonDocument = JsonDocument.Parse(json);
JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = !minify
};
string text = JsonSerializer.Serialize(jsonDocument.RootElement, options);
if (text.Length > 8000)
{
text = text.Substring(0, 8000) + "\n... (truncated)";
}
return ToolResult.Ok(text);
}
private static ToolResult Query(string json, string path)
{
if (string.IsNullOrEmpty(path))
{
return ToolResult.Fail("path parameter is required for query action");
}
using JsonDocument jsonDocument = JsonDocument.Parse(json);
JsonElement value = jsonDocument.RootElement;
foreach (PathSegment item in ParsePath(path))
{
if (item.IsIndex)
{
if (value.ValueKind != JsonValueKind.Array || item.Index >= value.GetArrayLength())
{
return ToolResult.Fail($"Array index [{item.Index}] out of range");
}
value = value[item.Index];
}
else
{
if (value.ValueKind != JsonValueKind.Object || !value.TryGetProperty(item.Key, out var value2))
{
return ToolResult.Fail("Key '" + item.Key + "' not found");
}
value = value2;
}
}
JsonValueKind valueKind = value.ValueKind;
if (1 == 0)
{
}
string text = valueKind switch
{
JsonValueKind.String => value.GetString() ?? "",
JsonValueKind.Number => value.GetRawText(),
JsonValueKind.True => "true",
JsonValueKind.False => "false",
JsonValueKind.Null => "null",
_ => JsonSerializer.Serialize(value, new JsonSerializerOptions
{
WriteIndented = true
}),
};
if (1 == 0)
{
}
string text2 = text;
if (text2.Length > 5000)
{
text2 = text2.Substring(0, 5000) + "\n... (truncated)";
}
return ToolResult.Ok(text2);
}
private static ToolResult Keys(string json)
{
using JsonDocument jsonDocument = JsonDocument.Parse(json);
if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object)
{
return ToolResult.Fail("Root element is not an object");
}
IEnumerable<string> values = jsonDocument.RootElement.EnumerateObject().Select(delegate(JsonProperty p)
{
JsonValueKind valueKind = p.Value.ValueKind;
if (1 == 0)
{
}
string text;
switch (valueKind)
{
case JsonValueKind.Object:
text = "object";
break;
case JsonValueKind.Array:
text = $"array[{p.Value.GetArrayLength()}]";
break;
case JsonValueKind.String:
text = "string";
break;
case JsonValueKind.Number:
text = "number";
break;
case JsonValueKind.True:
case JsonValueKind.False:
text = "boolean";
break;
default:
text = "null";
break;
}
if (1 == 0)
{
}
string text2 = text;
return " " + p.Name + ": " + text2;
});
return ToolResult.Ok($"Keys ({jsonDocument.RootElement.EnumerateObject().Count()}):\n{string.Join("\n", values)}");
}
private static ToolResult Convert(string json, string targetFormat)
{
if (targetFormat != "csv")
{
return ToolResult.Fail("Unsupported target format: " + targetFormat);
}
using JsonDocument jsonDocument = JsonDocument.Parse(json);
if (jsonDocument.RootElement.ValueKind != JsonValueKind.Array)
{
return ToolResult.Fail("JSON must be an array for CSV conversion");
}
JsonElement rootElement = jsonDocument.RootElement;
if (rootElement.GetArrayLength() == 0)
{
return ToolResult.Ok("(empty array)");
}
List<string> list = new List<string>();
foreach (JsonElement item2 in rootElement.EnumerateArray())
{
if (item2.ValueKind != JsonValueKind.Object)
{
return ToolResult.Fail("All array items must be objects for CSV conversion");
}
foreach (JsonProperty item3 in item2.EnumerateObject())
{
if (!list.Contains(item3.Name))
{
list.Add(item3.Name);
}
}
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine(string.Join(",", list.Select((string k) => "\"" + k + "\"")));
foreach (JsonElement item in rootElement.EnumerateArray())
{
JsonElement value;
IEnumerable<string> values = list.Select((string k) => (!item.TryGetProperty(k, out value)) ? "\"\"" : ((value.ValueKind == JsonValueKind.String) ? ("\"" + value.GetString()?.Replace("\"", "\"\"") + "\"") : value.GetRawText()));
stringBuilder.AppendLine(string.Join(",", values));
}
string text = stringBuilder.ToString();
if (text.Length > 8000)
{
text = text.Substring(0, 8000) + "\n... (truncated)";
}
return ToolResult.Ok(text);
}
private static List<PathSegment> ParsePath(string path)
{
List<PathSegment> list = new List<PathSegment>();
string[] array = path.Split('.');
foreach (string text in array)
{
int num = text.IndexOf('[');
if (num >= 0)
{
string text2 = text.Substring(0, num);
if (!string.IsNullOrEmpty(text2))
{
list.Add(new PathSegment(text2, 0, IsIndex: false));
}
string text3 = text;
int num2 = num + 1;
string s = text3.Substring(num2, text3.Length - num2).TrimEnd(']');
if (int.TryParse(s, out var result))
{
list.Add(new PathSegment("", result, IsIndex: true));
}
}
else
{
list.Add(new PathSegment(text, 0, IsIndex: false));
}
}
return list;
}
}