mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-06 11:28:58 +08:00
feat: harden jsonl runtime reliability
This commit is contained in:
103
pkg/tools/session_search.go
Normal file
103
pkg/tools/session_search.go
Normal file
@@ -0,0 +1,103 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user