2222
2323SEARCH_PROVIDER_FIRECRAWL = "firecrawl"
2424SEARCH_PROVIDER_EXA = "exa"
25+ SEARCH_PROVIDER_TAVILY = "tavily"
26+ SEARCH_PROVIDER_PERPLEXITY = "perplexity"
27+ SEARCH_PROVIDER_BING = "bing"
2528DEFAULT_FIRECRAWL_BASE_URL = "https://api.firecrawl.dev"
2629DEFAULT_EXA_BASE_URL = "https://api.exa.ai"
30+ DEFAULT_TAVILY_BASE_URL = "https://api.tavily.com"
31+ DEFAULT_PERPLEXITY_BASE_URL = "https://api.perplexity.ai"
32+ DEFAULT_BING_BASE_URL = "https://www.bing.com"
2733
2834
2935def _get_project_root () -> Path :
@@ -346,7 +352,7 @@ def save_text_providers_config(cls, config: Dict[str, Any]) -> None:
346352 @classmethod
347353 def _default_search_providers_config (cls ) -> Dict [str , Any ]:
348354 return {
349- "active_provider" : SEARCH_PROVIDER_FIRECRAWL ,
355+ "active_provider" : SEARCH_PROVIDER_BING ,
350356 "providers" : {
351357 SEARCH_PROVIDER_FIRECRAWL : {
352358 "type" : SEARCH_PROVIDER_FIRECRAWL ,
@@ -360,6 +366,25 @@ def _default_search_providers_config(cls) -> Dict[str, Any]:
360366 "api_key" : "" ,
361367 "base_url" : "" ,
362368 },
369+ SEARCH_PROVIDER_TAVILY : {
370+ "type" : SEARCH_PROVIDER_TAVILY ,
371+ "enabled" : False ,
372+ "api_key" : "" ,
373+ "base_url" : "" ,
374+ },
375+ SEARCH_PROVIDER_PERPLEXITY : {
376+ "type" : SEARCH_PROVIDER_PERPLEXITY ,
377+ "enabled" : False ,
378+ "api_key" : "" ,
379+ "base_url" : "" ,
380+ "model" : "sonar" ,
381+ },
382+ SEARCH_PROVIDER_BING : {
383+ "type" : SEARCH_PROVIDER_BING ,
384+ "enabled" : True ,
385+ "api_key" : "" ,
386+ "base_url" : "" ,
387+ },
363388 },
364389 }
365390
@@ -371,6 +396,8 @@ def _normalize_search_provider_item(cls, provider_name: str, item: Any) -> Dict[
371396 normalized ["enabled" ] = bool (normalized .get ("enabled" , False ))
372397 normalized ["api_key" ] = (normalized .get ("api_key" ) or "" ).strip ()
373398 normalized ["base_url" ] = (normalized .get ("base_url" ) or "" ).strip ()
399+ if normalized .get ("model" ) is not None :
400+ normalized ["model" ] = str (normalized .get ("model" ) or "" ).strip ()
374401 return normalized
375402
376403 @classmethod
@@ -387,23 +414,17 @@ def _normalize_search_providers_config(cls, config: Dict[str, Any]) -> Dict[str,
387414 else :
388415 providers = deepcopy (cls ._default_search_providers_config ()["providers" ])
389416
390- # 保证 firecrawl/exa 至少存在
391- if SEARCH_PROVIDER_FIRECRAWL not in providers :
392- providers [SEARCH_PROVIDER_FIRECRAWL ] = cls ._normalize_search_provider_item (
393- SEARCH_PROVIDER_FIRECRAWL ,
394- cls ._default_search_providers_config ()["providers" ][SEARCH_PROVIDER_FIRECRAWL ],
395- )
396- if SEARCH_PROVIDER_EXA not in providers :
397- providers [SEARCH_PROVIDER_EXA ] = cls ._normalize_search_provider_item (
398- SEARCH_PROVIDER_EXA ,
399- cls ._default_search_providers_config ()["providers" ][SEARCH_PROVIDER_EXA ],
400- )
417+ # 保证默认搜索服务商都存在
418+ default_providers = cls ._default_search_providers_config ()["providers" ]
419+ for provider_name , provider_item in default_providers .items ():
420+ if provider_name not in providers :
421+ providers [provider_name ] = cls ._normalize_search_provider_item (provider_name , provider_item )
401422
402423 active_provider = str (config .get ("active_provider" ) or "" ).strip ()
403424 if not active_provider or active_provider not in providers :
404425 active_provider = cls ._resolve_active_provider (
405426 {"active_provider" : active_provider , "providers" : providers },
406- SEARCH_PROVIDER_FIRECRAWL ,
427+ SEARCH_PROVIDER_BING ,
407428 )
408429
409430 return {
@@ -430,7 +451,7 @@ def save_search_providers_config(cls, config: Dict[str, Any]) -> None:
430451 @classmethod
431452 def get_active_search_provider (cls ) -> str :
432453 config = cls .load_search_providers_config ()
433- return cls ._resolve_active_provider (config , SEARCH_PROVIDER_FIRECRAWL )
454+ return cls ._resolve_active_provider (config , SEARCH_PROVIDER_BING )
434455
435456 @classmethod
436457 def get_search_provider_config (
@@ -443,6 +464,7 @@ def get_search_provider_config(
443464 if not providers :
444465 raise ValueError ("未找到任何搜索服务商配置" )
445466
467+ requested_provider_name = provider_name
446468 provider_name = provider_name or cls .get_active_search_provider ()
447469 if provider_name not in providers :
448470 available = ", " .join (providers .keys ())
@@ -452,17 +474,40 @@ def get_search_provider_config(
452474 provider_type = (provider_config .get ("type" ) or provider_name ).strip ().lower ()
453475 provider_config ["type" ] = provider_type
454476
477+ if require_enabled and not bool (provider_config .get ("enabled" , False )):
478+ # 未显式指定 provider 时,自动回退到默认 bing
479+ if requested_provider_name is None and provider_name != SEARCH_PROVIDER_BING :
480+ fallback = providers .get (SEARCH_PROVIDER_BING ) or {}
481+ if bool (fallback .get ("enabled" , False )):
482+ provider_name = SEARCH_PROVIDER_BING
483+ provider_config = fallback .copy ()
484+ provider_type = (provider_config .get ("type" ) or provider_name ).strip ().lower ()
485+ provider_config ["type" ] = provider_type
486+ else :
487+ raise ValueError (f"搜索服务商 [{ provider_name } ] 未启用" )
488+ else :
489+ raise ValueError (f"搜索服务商 [{ provider_name } ] 未启用" )
490+
455491 if require_enabled and not bool (provider_config .get ("enabled" , False )):
456492 raise ValueError (f"搜索服务商 [{ provider_name } ] 未启用" )
457493
458- if provider_type == SEARCH_PROVIDER_EXA and not (provider_config .get ("api_key" ) or "" ).strip ():
494+ requires_api_key = {
495+ SEARCH_PROVIDER_EXA ,
496+ SEARCH_PROVIDER_TAVILY ,
497+ SEARCH_PROVIDER_PERPLEXITY ,
498+ }
499+ if provider_type in requires_api_key and not (provider_config .get ("api_key" ) or "" ).strip ():
459500 raise ValueError (f"搜索服务商 [{ provider_name } ] 未配置 API Key" )
460501
502+ default_base_urls = {
503+ SEARCH_PROVIDER_FIRECRAWL : DEFAULT_FIRECRAWL_BASE_URL ,
504+ SEARCH_PROVIDER_EXA : DEFAULT_EXA_BASE_URL ,
505+ SEARCH_PROVIDER_TAVILY : DEFAULT_TAVILY_BASE_URL ,
506+ SEARCH_PROVIDER_PERPLEXITY : DEFAULT_PERPLEXITY_BASE_URL ,
507+ SEARCH_PROVIDER_BING : DEFAULT_BING_BASE_URL ,
508+ }
461509 if not (provider_config .get ("base_url" ) or "" ).strip ():
462- if provider_type == SEARCH_PROVIDER_FIRECRAWL :
463- provider_config ["base_url" ] = DEFAULT_FIRECRAWL_BASE_URL
464- elif provider_type == SEARCH_PROVIDER_EXA :
465- provider_config ["base_url" ] = DEFAULT_EXA_BASE_URL
510+ provider_config ["base_url" ] = default_base_urls .get (provider_type , "" )
466511
467512 return provider_config
468513
0 commit comments