mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 18:16:09 +00:00
* feat(tools): add Serply as a web_search provider Serply is a SERP API returning Google web results on a tenant-supplied key. It joins the existing chain the same way Exa, Tavily and Brave do: keyed from config_secrets, skipped silently when no key is stored, and selectable through the provider parameter for cross-engine corroboration. Freshness maps onto Google's tbs recency filter, which Serply forwards upstream. Only the pd/pw/pm/py shortcuts are sent: upstream ignores the equivalent cdr date range and answers with unfiltered results, so a range leaves tbs unset rather than naming a filter the results do not honour. The settings form gained a fifth sortable provider, so the locked DuckDuckGo card now derives its position number instead of hardcoding it, and a stored provider_order is reconciled against the full provider list rather than against Parallel alone. * test(tools): use a locally named round trip stub in the serply test The serply cancellation test borrowed parallelRoundTripFunc from web_search_parallel_test.go. Define serplyRoundTripFunc alongside the test that uses it so the file does not depend on another provider's test helper.
104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
package tools
|
|
|
|
import (
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// buildProviderByName returns the SearchProvider for a known name.
|
|
// Returns nil for unknown names. DDG ignores apiKey (not required).
|
|
// maxResults <= 0 falls back to defaultSearchCount.
|
|
func buildProviderByName(name, apiKey string, maxResults int) SearchProvider {
|
|
if maxResults <= 0 {
|
|
maxResults = defaultSearchCount
|
|
}
|
|
switch name {
|
|
case searchProviderExa:
|
|
return newExaSearchProvider(apiKey, maxResults)
|
|
case searchProviderTavily:
|
|
return newTavilySearchProvider(apiKey, maxResults)
|
|
case searchProviderBrave:
|
|
return newBraveSearchProvider(apiKey, maxResults)
|
|
case searchProviderParallel:
|
|
return newParallelSearchProvider(maxResults)
|
|
case searchProviderSerply:
|
|
return newSerplySearchProvider(apiKey, maxResults)
|
|
case searchProviderDuckDuckGo:
|
|
return newDuckDuckGoSearchProvider(maxResults)
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// NormalizeWebSearchProviderOrder normalizes user-specified provider order.
|
|
// Explicit providers appear first in their specified order, remaining known
|
|
// providers are appended (DuckDuckGo always last as free fallback).
|
|
func NormalizeWebSearchProviderOrder(order []string) []string {
|
|
result := make([]string, 0, len(defaultSearchProviderOrder))
|
|
seen := make(map[string]bool, len(defaultSearchProviderOrder))
|
|
|
|
for _, raw := range order {
|
|
id := strings.ToLower(strings.TrimSpace(raw))
|
|
if id == searchProviderDuckDuckGo || id == "" {
|
|
continue // DDG always last
|
|
}
|
|
if !isKnownSearchProvider(id) || seen[id] {
|
|
continue
|
|
}
|
|
result = append(result, id)
|
|
seen[id] = true
|
|
}
|
|
// Append remaining known providers not yet listed (except DDG).
|
|
for _, id := range defaultSearchProviderOrder {
|
|
if id == searchProviderDuckDuckGo {
|
|
continue
|
|
}
|
|
if !seen[id] {
|
|
result = append(result, id)
|
|
}
|
|
}
|
|
return append(result, searchProviderDuckDuckGo)
|
|
}
|
|
|
|
func isKnownSearchProvider(id string) bool {
|
|
return id == searchProviderParallel || slices.Contains(defaultSearchProviderOrder, id)
|
|
}
|
|
|
|
// --- Shared provider helpers ---
|
|
|
|
func truncateStr(s string, max int) string {
|
|
if len(s) <= max {
|
|
return s
|
|
}
|
|
return s[:max] + "..."
|
|
}
|
|
|
|
func coalesceSearchText(values ...string) string {
|
|
for _, value := range values {
|
|
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
|
return trimmed
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func clampProviderResultCount(requested, providerMax int) int {
|
|
if requested <= 0 {
|
|
requested = defaultSearchCount
|
|
}
|
|
if providerMax > 0 && requested > providerMax {
|
|
return providerMax
|
|
}
|
|
return requested
|
|
}
|
|
|
|
func normalizeProviderMaxResults(value int) int {
|
|
if value <= 0 {
|
|
return defaultSearchCount
|
|
}
|
|
if value > maxSearchCount {
|
|
return maxSearchCount
|
|
}
|
|
return value
|
|
}
|