summaryrefslogtreecommitdiff
path: root/agent/format.go
blob: 01ecb079035279a933608c90c5904cc67b565b9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package agent

import (
	"encoding/json"
	"fmt"
	"strings"
)

// FormatSearchResults takes raw JSON from websearch and returns a concise summary.
func FormatSearchResults(rawJSON []byte) (string, error) {
	// Try to unmarshal as generic slice of maps
	var results []map[string]interface{}
	if err := json.Unmarshal(rawJSON, &results); err != nil {
		// If that fails, try as a single map (maybe wrapper object)
		var wrapper map[string]interface{}
		if err2 := json.Unmarshal(rawJSON, &wrapper); err2 == nil {
			// Look for a "results" or "data" field
			if data, ok := wrapper["results"].([]interface{}); ok {
				// Convert to slice of maps
				for _, item := range data {
					if m, ok := item.(map[string]interface{}); ok {
						results = append(results, m)
					}
				}
			} else if data, ok := wrapper["data"].([]interface{}); ok {
				for _, item := range data {
					if m, ok := item.(map[string]interface{}); ok {
						results = append(results, m)
					}
				}
			} else {
				// No slice found, treat wrapper as single result
				results = []map[string]interface{}{wrapper}
			}
		} else {
			return "", fmt.Errorf("failed to unmarshal search results: %v (also %v)", err, err2)
		}
	}

	if len(results) == 0 {
		return "No search results found.", nil
	}

	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("Found %d results:\n", len(results)))
	for i, r := range results {
		// Extract common fields
		title := getString(r, "title", "Title", "name", "heading")
		snippet := getString(r, "snippet", "description", "content", "body", "text", "summary")
		url := getString(r, "url", "link", "uri", "source")

		sb.WriteString(fmt.Sprintf("%d. ", i+1))
		if title != "" {
			sb.WriteString(fmt.Sprintf("**%s**", title))
		} else {
			sb.WriteString("(No title)")
		}
		if snippet != "" {
			// Truncate snippet to reasonable length
			if len(snippet) > 200 {
				snippet = snippet[:200] + "..."
			}
			sb.WriteString(fmt.Sprintf(" — %s", snippet))
		}
		if url != "" {
			sb.WriteString(fmt.Sprintf(" (%s)", url))
		}
		sb.WriteString("\n")
	}
	return sb.String(), nil
}

// FormatWebPageContent takes raw JSON from read_url and returns a concise summary.
func FormatWebPageContent(rawJSON []byte) (string, error) {
	// Try to unmarshal as generic map
	var data map[string]interface{}
	if err := json.Unmarshal(rawJSON, &data); err != nil {
		// If that fails, try as string directly
		var content string
		if err2 := json.Unmarshal(rawJSON, &content); err2 == nil {
			return truncateText(content, 500), nil
		}
		// Both failed, return first error
		return "", fmt.Errorf("failed to unmarshal web page content: %v", err)
	}

	// Look for common content fields
	content := getString(data, "content", "text", "body", "article", "html", "markdown", "data")
	if content == "" {
		// If no content field, marshal the whole thing as a short string
		summary := fmt.Sprintf("%v", data)
		return truncateText(summary, 300), nil
	}
	return truncateText(content, 500), nil
}

// Helper to get a string value from a map, trying multiple keys.
func getString(m map[string]interface{}, keys ...string) string {
	for _, k := range keys {
		if val, ok := m[k]; ok {
			switch v := val.(type) {
			case string:
				return v
			case fmt.Stringer:
				return v.String()
			default:
				return fmt.Sprintf("%v", v)
			}
		}
	}
	return ""
}

// Helper to truncate text and add ellipsis.
func truncateText(s string, maxLen int) string {
	if len(s) <= maxLen {
		return s
	}
	return s[:maxLen] + "..."
}