Files
ollama/app/tools/browser_websearch.go
Daniel Hiltgen d3b4b9970a app: add code for macOS and Windows apps under 'app' (#12933)
* app: add code for macOS and Windows apps under 'app'

* app: add readme

* app: windows and linux only for now

* ci: fix ui CI validation

---------

Co-authored-by: jmorganca <jmorganca@gmail.com>
2025-11-04 11:40:17 -08:00

144 lines
4.0 KiB
Go

//go:build windows || darwin
package tools
import (
"context"
"encoding/json"
"fmt"
"strconv"
"time"
)
// WebSearchContent represents the content of a search result
type WebSearchContent struct {
Snippet string `json:"snippet"`
FullText string `json:"full_text"`
}
// WebSearchMetadata represents metadata for a search result
type WebSearchMetadata struct {
PublishedDate *time.Time `json:"published_date,omitempty"`
}
// WebSearchResult represents a single search result
type WebSearchResult struct {
Title string `json:"title"`
URL string `json:"url"`
Content WebSearchContent `json:"content"`
Metadata WebSearchMetadata `json:"metadata"`
}
// WebSearchResponse represents the complete response from the websearch API
type WebSearchResponse struct {
Results map[string][]WebSearchResult `json:"results"`
}
// BrowserWebSearch tool for searching the web using ollama.com search API
type BrowserWebSearch struct{}
func (w *BrowserWebSearch) Name() string {
return "gpt_oss_web_search"
}
func (w *BrowserWebSearch) Description() string {
return "Search the web for real-time information using ollama.com search API."
}
func (w *BrowserWebSearch) Prompt() string {
return `Use the gpt_oss_web_search tool to search the web.
1. Come up with a list of search queries to get comprehensive information (typically 2-3 related queries work well)
2. Use the gpt_oss_web_search tool with multiple queries to get results organized by query
3. Use the search results to provide current up to date, accurate information
Today's date is ` + time.Now().Format("January 2, 2006") + `
Add "` + time.Now().Format("January 2, 2006") + `" for news queries and ` + strconv.Itoa(time.Now().Year()+1) + ` for other queries that need current information.`
}
func (w *BrowserWebSearch) Schema() map[string]any {
schemaBytes := []byte(`{
"type": "object",
"properties": {
"queries": {
"type": "array",
"items": {
"type": "string"
},
"description": "List of search queries to look up"
},
"max_results": {
"type": "integer",
"description": "Maximum number of results to return per query (default: 2) up to 5",
"default": 2
}
},
"required": ["queries"]
}`)
var schema map[string]any
if err := json.Unmarshal(schemaBytes, &schema); err != nil {
return nil
}
return schema
}
func (w *BrowserWebSearch) Execute(ctx context.Context, args map[string]any) (any, error) {
queriesRaw, ok := args["queries"].([]any)
if !ok {
return nil, fmt.Errorf("queries parameter is required and must be an array of strings")
}
queries := make([]string, 0, len(queriesRaw))
for _, q := range queriesRaw {
if query, ok := q.(string); ok {
queries = append(queries, query)
}
}
if len(queries) == 0 {
return nil, fmt.Errorf("at least one query is required")
}
maxResults := 5
if mr, ok := args["max_results"].(int); ok {
maxResults = mr
}
return w.performWebSearch(ctx, queries, maxResults)
}
// performWebSearch handles the actual HTTP request to ollama.com search API
func (w *BrowserWebSearch) performWebSearch(ctx context.Context, queries []string, maxResults int) (*WebSearchResponse, error) {
response := &WebSearchResponse{Results: make(map[string][]WebSearchResult, len(queries))}
for _, query := range queries {
searchResp, err := performWebSearch(ctx, query, maxResults)
if err != nil {
return nil, fmt.Errorf("web_search failed for %q: %w", query, err)
}
converted := make([]WebSearchResult, 0, len(searchResp.Results))
for _, item := range searchResp.Results {
converted = append(converted, WebSearchResult{
Title: item.Title,
URL: item.URL,
Content: WebSearchContent{
Snippet: truncateString(item.Content, 400),
FullText: item.Content,
},
Metadata: WebSearchMetadata{},
})
}
response.Results[query] = converted
}
return response, nil
}
func truncateString(input string, limit int) string {
if limit <= 0 || len(input) <= limit {
return input
}
return input[:limit]
}