Files
Cyrene/backend/tool-engine/internal/tools/json_ops.go
T
AskaEth 78e3f450c2 feat: Round 5 - Memory Service, Tool Engine, Call Records, Thinking Logs
- Fix: Session history flash (race condition + WS guard)
- Fix: Chat background overlay + sidebar transparency
- Fix: IoT device control (Chinese action names, status field)
- Feat: Independent memory-service (port 8091, 13 endpoints)
- Feat: Independent tool-engine service (port 8092, 13 tools)
- Feat: Tool call logs with paginated DevTools panel
- Feat: Thinking log records with DevTools panel
- Feat: Future development roadmap document
- Chore: Updated .gitignore, go.work, DevTools config
- Chore: 5-service health check, project review docs
2026-05-18 20:05:14 +08:00

188 lines
5.2 KiB
Go

package tools
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"github.com/yourname/cyrene-ai/tool-engine/internal/model"
)
// JSONTool provides JSON parsing, querying, and validation for the LLM.
type JSONTool struct{}
// NewJSONTool creates a JSON processing tool.
func NewJSONTool() *JSONTool {
return &JSONTool{}
}
// Definition returns the tool definition for LLM function calling.
func (t *JSONTool) Definition() model.ToolDefinition {
return model.ToolDefinition{
Name: "json_ops",
Description: "JSON处理工具。解析JSON字符串并格式化输出、用简单路径查询JSON字段、验证JSON是否合法。",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{
"type": "string",
"enum": []string{"parse", "query", "validate"},
"description": "操作类型。parse: 解析JSON并格式化输出;query: 用路径查询JSON中的值(如\"users.0.name\"表示取users数组第0个元素的name字段);validate: 验证JSON字符串是否合法",
},
"json_string": map[string]interface{}{
"type": "string",
"description": "JSON字符串",
},
"path": map[string]interface{}{
"type": "string",
"description": "查询路径(query操作时使用)。支持点分隔和数组索引,如 \"users.0.name\"、\"data.list.2.title\"",
},
},
"required": []string{"action", "json_string"},
},
}
}
// Execute performs JSON operations.
func (t *JSONTool) Execute(ctx context.Context, arguments map[string]interface{}) (*model.ToolResult, error) {
action, ok := arguments["action"].(string)
if !ok || action == "" {
return &model.ToolResult{ID: "", Error: "缺少 action 参数"}, nil
}
jsonStr, ok := arguments["json_string"].(string)
if !ok || jsonStr == "" {
return &model.ToolResult{ID: "", Error: "缺少 json_string 参数"}, nil
}
switch action {
case "parse":
return t.handleParse(jsonStr)
case "query":
path, _ := arguments["path"].(string)
return t.handleQuery(jsonStr, path)
case "validate":
return t.handleValidate(jsonStr)
default:
return &model.ToolResult{
ID: "",
Error: fmt.Sprintf("未知操作: %s,支持: parse, query, validate", action),
}, nil
}
}
func (t *JSONTool) handleParse(jsonStr string) (*model.ToolResult, error) {
var data interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON解析失败: %v", err)}, nil
}
pretty, err := json.MarshalIndent(data, "", " ")
if err != nil {
return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON格式化失败: %v", err)}, nil
}
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("解析成功\n格式化输出:\n%s", string(pretty)),
}, nil
}
func (t *JSONTool) handleQuery(jsonStr, path string) (*model.ToolResult, error) {
if path == "" {
return &model.ToolResult{ID: "", Error: "query 操作需要 path 参数"}, nil
}
var data interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return &model.ToolResult{ID: "", Error: fmt.Sprintf("JSON解析失败: %v", err)}, nil
}
value, err := queryPath(data, path)
if err != nil {
return &model.ToolResult{ID: "", Error: err.Error()}, nil
}
pretty, err := json.MarshalIndent(value, "", " ")
if err != nil {
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("路径: %s\n值: %v", path, value),
}, nil
}
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("路径: %s\n值:\n%s", path, string(pretty)),
}, nil
}
func (t *JSONTool) handleValidate(jsonStr string) (*model.ToolResult, error) {
var data interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
errStr := err.Error()
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("❌ JSON不合法\n错误: %s", errStr),
}, nil
}
typeName := "object"
switch data.(type) {
case []interface{}:
typeName = "array"
case string:
typeName = "string"
case float64:
typeName = "number"
case bool:
typeName = "boolean"
case nil:
typeName = "null"
}
size := len(jsonStr)
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("✅ JSON合法\n类型: %s\n大小: %d bytes", typeName, size),
}, nil
}
func queryPath(data interface{}, path string) (interface{}, error) {
path = strings.TrimPrefix(path, "$.")
if path == "" || path == "$" {
return data, nil
}
parts := strings.Split(path, ".")
current := data
for _, part := range parts {
switch v := current.(type) {
case map[string]interface{}:
var ok bool
current, ok = v[part]
if !ok {
return nil, fmt.Errorf("路径 '%s' 中字段 '%s' 不存在", path, part)
}
case []interface{}:
idx, err := strconv.Atoi(part)
if err != nil {
return nil, fmt.Errorf("路径 '%s' 中 '%s' 不是有效的数组索引", path, part)
}
if idx < 0 || idx >= len(v) {
return nil, fmt.Errorf("路径 '%s' 中索引 %d 越界(数组长度 %d)", path, idx, len(v))
}
current = v[idx]
default:
return nil, fmt.Errorf("路径 '%s' 中无法继续导航:'%s' 不是对象或数组", path, part)
}
}
return current, nil
}