mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-22 06:37:29 +08:00
104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/YspCoder/clawgo/pkg/session"
|
|
)
|
|
|
|
type SessionSearchTool struct {
|
|
manager *session.SessionManager
|
|
}
|
|
|
|
func NewSessionSearchTool(manager *session.SessionManager) *SessionSearchTool {
|
|
return &SessionSearchTool{manager: manager}
|
|
}
|
|
|
|
func (t *SessionSearchTool) Name() string { return "session_search" }
|
|
|
|
func (t *SessionSearchTool) Description() string {
|
|
return "Search past session history across JSONL session logs. Use when the user refers to previous conversations, past decisions, or earlier project work."
|
|
}
|
|
|
|
func (t *SessionSearchTool) Parameters() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"query": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Keywords or short phrase to search across past sessions.",
|
|
},
|
|
"limit": map[string]interface{}{
|
|
"type": "integer",
|
|
"description": "Maximum number of matching sessions to return.",
|
|
"default": 5,
|
|
},
|
|
"kinds": map[string]interface{}{
|
|
"type": "array",
|
|
"description": "Optional session kinds filter, e.g. main, cron, subagent, hook.",
|
|
"items": map[string]interface{}{"type": "string"},
|
|
},
|
|
"exclude_current": map[string]interface{}{
|
|
"type": "boolean",
|
|
"description": "Exclude the current session from results when session_key is provided.",
|
|
"default": true,
|
|
},
|
|
"session_key": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional current session key for exclude_current behavior.",
|
|
},
|
|
},
|
|
"required": []string{"query"},
|
|
}
|
|
}
|
|
|
|
func (t *SessionSearchTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
|
|
_ = ctx
|
|
if t == nil || t.manager == nil {
|
|
return "", fmt.Errorf("session manager not configured")
|
|
}
|
|
query := MapStringArg(args, "query")
|
|
if query == "" {
|
|
return "", fmt.Errorf("query is required")
|
|
}
|
|
limit := MapIntArg(args, "limit", 5)
|
|
kinds := MapStringListArg(args, "kinds")
|
|
excludeCurrent, excludeSet := MapBoolArg(args, "exclude_current")
|
|
excludeKey := ""
|
|
if excludeSet && excludeCurrent {
|
|
excludeKey = MapStringArg(args, "session_key")
|
|
}
|
|
|
|
results := t.manager.Search(query, kinds, excludeKey, limit)
|
|
if len(results) == 0 {
|
|
return fmt.Sprintf("No past sessions matched %q.", query), nil
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.WriteString(fmt.Sprintf("Session search results for %q:\n\n", query))
|
|
for _, item := range results {
|
|
sb.WriteString(fmt.Sprintf("- %s kind=%s updated=%s score=%d\n", item.Key, item.Kind, item.UpdatedAt.Format("2006-01-02 15:04:05"), item.Score))
|
|
if summary := strings.TrimSpace(item.Summary); summary != "" {
|
|
sb.WriteString(" summary: " + singleLine(summary, 220) + "\n")
|
|
}
|
|
for _, snippet := range item.Snippets {
|
|
line := singleLine(snippet.Content, 220)
|
|
sb.WriteString(fmt.Sprintf(" [#%d][%s] %s\n", snippet.Seq, snippet.Role, line))
|
|
}
|
|
sb.WriteString("\n")
|
|
}
|
|
return strings.TrimSpace(sb.String()), nil
|
|
}
|
|
|
|
func (t *SessionSearchTool) ParallelSafe() bool { return true }
|
|
|
|
func singleLine(s string, max int) string {
|
|
s = strings.TrimSpace(strings.ReplaceAll(s, "\n", " "))
|
|
if max > 0 && len(s) > max {
|
|
return s[:max] + "..."
|
|
}
|
|
return s
|
|
}
|