(ctx context.Context, input map[string]any, tc *tools.ToolContext)
| 83 | } |
| 84 | |
| 85 | func (t *MemorySearchTool) Execute(ctx context.Context, input map[string]any, tc *tools.ToolContext) (any, error) { |
| 86 | query, _ := input["query"].(string) |
| 87 | |
| 88 | rawNamespace, _ := input["namespace"].(string) |
| 89 | |
| 90 | regex, _ := input["regex"].(bool) |
| 91 | |
| 92 | glob, _ := input["glob"].(string) |
| 93 | |
| 94 | maxResults := 20 |
| 95 | if v, ok := input["max_results"].(float64); ok && int(v) > 0 { |
| 96 | maxResults = int(v) |
| 97 | } |
| 98 | |
| 99 | // 组合基础命名空间 + 调用命名空间 |
| 100 | // 规则: |
| 101 | // - 如果 namespace 以 "/" 开头, 视为全局命名空间, 不叠加 baseNamespace |
| 102 | // - 否则在 baseNamespace 下叠加 |
| 103 | ns := strings.TrimSpace(rawNamespace) |
| 104 | if after, found := strings.CutPrefix(ns, "/"); found { |
| 105 | ns = after |
| 106 | } else if t.baseNamespace != "" { |
| 107 | if ns != "" { |
| 108 | ns = filepath.ToSlash(filepath.Join(t.baseNamespace, ns)) |
| 109 | } else { |
| 110 | ns = t.baseNamespace |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | matches, err := t.manager.Search(ctx, &memory.SearchOptions{ |
| 115 | Query: query, |
| 116 | Regex: regex, |
| 117 | Namespace: ns, |
| 118 | Glob: glob, |
| 119 | MaxResults: maxResults, |
| 120 | }) |
| 121 | if err != nil { |
| 122 | return map[string]any{ |
| 123 | "ok": false, |
| 124 | "error": fmt.Sprintf("memory search failed: %v", err), |
| 125 | }, nil |
| 126 | } |
| 127 | |
| 128 | items := make([]map[string]any, 0, len(matches)) |
| 129 | for _, m := range matches { |
| 130 | items = append(items, map[string]any{ |
| 131 | "path": m.Path, |
| 132 | "line_number": m.LineNumber, |
| 133 | "line": m.Line, |
| 134 | "match": m.Match, |
| 135 | }) |
| 136 | } |
| 137 | |
| 138 | return map[string]any{ |
| 139 | "ok": true, |
| 140 | "query": query, |
| 141 | "namespace": ns, |
| 142 | "regex": regex, |
nothing calls this directly
no test coverage detected