mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
fix(web): address sogou search review feedback
This commit is contained in:
+38
-12
@@ -43,11 +43,12 @@ type webSearchProviderOption struct {
|
||||
}
|
||||
|
||||
type webSearchProviderConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
MaxResults int `json:"max_results"`
|
||||
BaseURL string `json:"base_url,omitempty"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
APIKeySet bool `json:"api_key_set,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
MaxResults int `json:"max_results"`
|
||||
BaseURL string `json:"base_url,omitempty"`
|
||||
APIKey string `json:"api_key,omitempty"`
|
||||
APIKeys []string `json:"api_keys,omitempty"`
|
||||
APIKeySet bool `json:"api_key_set,omitempty"`
|
||||
}
|
||||
|
||||
type webSearchConfigResponse struct {
|
||||
@@ -416,23 +417,23 @@ func (h *Handler) handleUpdateWebSearchConfig(w http.ResponseWriter, r *http.Req
|
||||
if settings, ok := req.Settings["brave"]; ok {
|
||||
cfg.Tools.Web.Brave.Enabled = settings.Enabled
|
||||
cfg.Tools.Web.Brave.MaxResults = settings.MaxResults
|
||||
if key := strings.TrimSpace(settings.APIKey); key != "" {
|
||||
cfg.Tools.Web.Brave.SetAPIKey(key)
|
||||
if keys, ok := normalizeWebSearchAPIKeys(settings.APIKeys, settings.APIKey); ok {
|
||||
cfg.Tools.Web.Brave.SetAPIKeys(keys)
|
||||
}
|
||||
}
|
||||
if settings, ok := req.Settings["tavily"]; ok {
|
||||
cfg.Tools.Web.Tavily.Enabled = settings.Enabled
|
||||
cfg.Tools.Web.Tavily.MaxResults = settings.MaxResults
|
||||
cfg.Tools.Web.Tavily.BaseURL = strings.TrimSpace(settings.BaseURL)
|
||||
if key := strings.TrimSpace(settings.APIKey); key != "" {
|
||||
cfg.Tools.Web.Tavily.SetAPIKey(key)
|
||||
if keys, ok := normalizeWebSearchAPIKeys(settings.APIKeys, settings.APIKey); ok {
|
||||
cfg.Tools.Web.Tavily.SetAPIKeys(keys)
|
||||
}
|
||||
}
|
||||
if settings, ok := req.Settings["perplexity"]; ok {
|
||||
cfg.Tools.Web.Perplexity.Enabled = settings.Enabled
|
||||
cfg.Tools.Web.Perplexity.MaxResults = settings.MaxResults
|
||||
if key := strings.TrimSpace(settings.APIKey); key != "" {
|
||||
cfg.Tools.Web.Perplexity.SetAPIKey(key)
|
||||
if keys, ok := normalizeWebSearchAPIKeys(settings.APIKeys, settings.APIKey); ok {
|
||||
cfg.Tools.Web.Perplexity.APIKeys = config.SimpleSecureStrings(keys...)
|
||||
}
|
||||
}
|
||||
if settings, ok := req.Settings["searxng"]; ok {
|
||||
@@ -479,6 +480,31 @@ func normalizeWebSearchProvider(provider string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeWebSearchAPIKeys(apiKeys []string, apiKey string) ([]string, bool) {
|
||||
if apiKeys != nil {
|
||||
keys := make([]string, 0, len(apiKeys))
|
||||
seen := make(map[string]struct{}, len(apiKeys))
|
||||
for _, key := range apiKeys {
|
||||
trimmed := strings.TrimSpace(key)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[trimmed]; ok {
|
||||
continue
|
||||
}
|
||||
seen[trimmed] = struct{}{}
|
||||
keys = append(keys, trimmed)
|
||||
}
|
||||
return keys, true
|
||||
}
|
||||
|
||||
if trimmed := strings.TrimSpace(apiKey); trimmed != "" {
|
||||
return []string{trimmed}, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func buildWebSearchConfigResponse(cfg *config.Config) webSearchConfigResponse {
|
||||
current := resolveCurrentWebSearchProvider(cfg)
|
||||
settings := map[string]webSearchProviderConfig{
|
||||
@@ -614,7 +640,7 @@ func resolveCurrentWebSearchProvider(cfg *config.Config) string {
|
||||
if selected != "" && selected != "auto" && webSearchProviderConfigured(cfg, selected) {
|
||||
return selected
|
||||
}
|
||||
for _, name := range []string{"sogou", "perplexity", "brave", "searxng", "tavily", "duckduckgo", "baidu_search", "glm_search"} {
|
||||
for _, name := range []string{"perplexity", "brave", "searxng", "tavily", "sogou", "duckduckgo", "baidu_search", "glm_search"} {
|
||||
if webSearchProviderConfigured(cfg, name) {
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -245,6 +245,15 @@ func TestHandleUpdateWebSearchConfig(t *testing.T) {
|
||||
configPath, cleanup := setupOAuthTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
cfg, err := config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error = %v", err)
|
||||
}
|
||||
cfg.Tools.Web.Brave.SetAPIKeys([]string{"brave-old-1", "brave-old-2"})
|
||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||
t.Fatalf("SaveConfig() error = %v", err)
|
||||
}
|
||||
|
||||
h := NewHandler(configPath)
|
||||
mux := http.NewServeMux()
|
||||
h.RegisterRoutes(mux)
|
||||
@@ -294,3 +303,89 @@ func TestHandleUpdateWebSearchConfig(t *testing.T) {
|
||||
t.Fatalf("brave api key not updated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleUpdateWebSearchConfig_PreservesAndReplacesMultiKeys(t *testing.T) {
|
||||
configPath, cleanup := setupOAuthTestEnv(t)
|
||||
defer cleanup()
|
||||
|
||||
cfg, err := config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error = %v", err)
|
||||
}
|
||||
cfg.Tools.Web.Brave.SetAPIKeys([]string{"brave-old-1", "brave-old-2"})
|
||||
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||
t.Fatalf("SaveConfig() error = %v", err)
|
||||
}
|
||||
|
||||
h := NewHandler(configPath)
|
||||
mux := http.NewServeMux()
|
||||
h.RegisterRoutes(mux)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/tools/web-search-config",
|
||||
bytes.NewBufferString(`{
|
||||
"provider":"auto",
|
||||
"prefer_native":true,
|
||||
"proxy":"",
|
||||
"settings":{
|
||||
"brave":{"enabled":true,"max_results":7}
|
||||
}
|
||||
}`),
|
||||
)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
mux.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
||||
}
|
||||
|
||||
updated, err := config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error = %v", err)
|
||||
}
|
||||
if got := updated.Tools.Web.Brave.APIKeys.Values(); len(got) != 2 || got[0] != "brave-old-1" || got[1] != "brave-old-2" {
|
||||
t.Fatalf("brave api keys should be preserved, got %#v", got)
|
||||
}
|
||||
|
||||
rec = httptest.NewRecorder()
|
||||
req = httptest.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/tools/web-search-config",
|
||||
bytes.NewBufferString(`{
|
||||
"provider":"auto",
|
||||
"prefer_native":true,
|
||||
"proxy":"",
|
||||
"settings":{
|
||||
"brave":{"enabled":true,"max_results":7,"api_keys":["brave-new-1","brave-new-2","brave-new-1"]}
|
||||
}
|
||||
}`),
|
||||
)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
mux.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
|
||||
}
|
||||
|
||||
updated, err = config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadConfig() error = %v", err)
|
||||
}
|
||||
if got := updated.Tools.Web.Brave.APIKeys.Values(); len(got) != 2 || got[0] != "brave-new-1" || got[1] != "brave-new-2" {
|
||||
t.Fatalf("brave api keys should be replaced by api_keys, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCurrentWebSearchProvider_PrefersConfiguredProvidersBeforeSogou(t *testing.T) {
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.Tools.Web.Provider = "auto"
|
||||
cfg.Tools.Web.Sogou.Enabled = true
|
||||
cfg.Tools.Web.Brave.Enabled = true
|
||||
cfg.Tools.Web.Brave.SetAPIKey("brave-test-key")
|
||||
|
||||
if got := resolveCurrentWebSearchProvider(cfg); got != "brave" {
|
||||
t.Fatalf("resolveCurrentWebSearchProvider() = %q, want brave", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user