feat: add web search config

This commit is contained in:
qixinbo
2026-03-29 19:34:58 +08:00
parent fb4fc7437f
commit 99b654bbd2
9 changed files with 308 additions and 5 deletions
+31
View File
@@ -0,0 +1,31 @@
from typing import Optional, Dict, Any
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field
from app.api.llm import get_current_user, get_admin_user, CurrentUser
from app.services.web_search_config_store import get_web_search_config, save_web_search_config
router = APIRouter()
class WebSearchConfigModel(BaseModel):
provider: str = Field(default="duckduckgo", description="Web search provider (brave, tavily, duckduckgo, searxng, jina)")
api_key: Optional[str] = Field(default="", description="API Key for the provider")
base_url: Optional[str] = Field(default="", description="Base URL for SearXNG")
max_results: int = Field(default=5, description="Maximum number of search results")
def _sanitize_config(config: Dict[str, Any], is_admin: bool) -> Dict[str, Any]:
sanitized = config.copy()
if not is_admin:
sanitized["api_key"] = None
return sanitized
@router.get("/web-search/config", response_model=WebSearchConfigModel)
def get_config(current_user: CurrentUser = Depends(get_current_user)):
config = get_web_search_config()
return WebSearchConfigModel(**_sanitize_config(config, current_user.is_admin))
@router.put("/web-search/config", response_model=WebSearchConfigModel)
def update_config(config: WebSearchConfigModel, _: CurrentUser = Depends(get_admin_user)):
config_dict = config.dict()
save_web_search_config(config_dict)
return WebSearchConfigModel(**config_dict)
+13 -2
View File
@@ -33,6 +33,7 @@ from nanobot.config.schema import Config
from app.api.skills import load_skills
from app.core.patched_openai_compat_provider import PatchedOpenAICompatProvider
from app.services.llm_cache import get_llm_configs, get_active_llm_config
from app.services.web_search_config_store import get_web_search_config
from app.core.data_root import get_workspace_root
@@ -103,6 +104,16 @@ class NanobotIntegration:
usage = self._last_usage_by_session.get(session_id)
return dict(usage) if usage else None
def _get_web_search_config(self) -> Any:
from nanobot.config.schema import WebSearchConfig
ws_dict = get_web_search_config()
return WebSearchConfig(
provider=ws_dict.get("provider", "duckduckgo"),
api_key=ws_dict.get("api_key", ""),
base_url=ws_dict.get("base_url", ""),
max_results=ws_dict.get("max_results", 5)
)
def _need_custom_agent_for_target(self, target_config: Dict[str, Any]) -> bool:
if not self.agent:
return False
@@ -173,7 +184,7 @@ class NanobotIntegration:
model=initial_model,
max_iterations=self.config.agents.defaults.max_tool_iterations,
context_window_tokens=self.config.agents.defaults.context_window_tokens,
web_search_config=self.config.tools.web.search,
web_search_config=self._get_web_search_config(),
web_proxy=self.config.tools.web.proxy or None,
exec_config=self.config.tools.exec,
cron_service=self.cron,
@@ -319,7 +330,7 @@ class NanobotIntegration:
model=provider.default_model,
max_iterations=self.config.agents.defaults.max_tool_iterations,
context_window_tokens=self.config.agents.defaults.context_window_tokens,
web_search_config=self.config.tools.web.search,
web_search_config=self._get_web_search_config(),
web_proxy=self.config.tools.web.proxy or None,
exec_config=self.config.tools.exec,
cron_service=self.cron,
@@ -0,0 +1,51 @@
import os
import json
import threading
from typing import Any, Dict
from app.core.data_root import get_data_root
_cache_lock = threading.RLock()
_cache_mtime: float = -1.0
_cache_data: Dict[str, Any] = {}
def get_config_file_path() -> str:
return str(get_data_root() / "web_search_config.json")
def get_web_search_config() -> Dict[str, Any]:
global _cache_mtime, _cache_data
config_file = get_config_file_path()
current_mtime = os.path.getmtime(config_file) if os.path.exists(config_file) else -1.0
with _cache_lock:
if current_mtime != _cache_mtime:
if not os.path.exists(config_file):
_cache_data = {
"provider": "duckduckgo",
"api_key": "",
"base_url": "",
"max_results": 5
}
else:
try:
with open(config_file, "r") as f:
_cache_data = json.load(f)
except json.JSONDecodeError:
_cache_data = {
"provider": "duckduckgo",
"api_key": "",
"base_url": "",
"max_results": 5
}
_cache_mtime = current_mtime
return dict(_cache_data)
def save_web_search_config(config: Dict[str, Any]) -> None:
global _cache_mtime, _cache_data
config_file = get_config_file_path()
os.makedirs(os.path.dirname(config_file), exist_ok=True)
with _cache_lock:
with open(config_file, "w") as f:
json.dump(config, f, indent=2)
_cache_data = dict(config)
_cache_mtime = os.path.getmtime(config_file)
+2 -1
View File
@@ -21,7 +21,7 @@ import re
import os
from datetime import datetime
from app.api import upload, llm, skills, users, datasources, projects, semantic, mcp, subagents, knowledge, embedding_models
from app.api import upload, llm, skills, users, datasources, projects, semantic, mcp, subagents, knowledge, embedding_models, web_search
from app.connectors.postgres import postgres_connector
from app.connectors.clickhouse import clickhouse_connector
from app.core.artifacts import extract_artifacts
@@ -77,6 +77,7 @@ app.include_router(mcp.router, prefix="/api/v1")
app.include_router(subagents.router, prefix="/api/v1")
app.include_router(knowledge.router, prefix="/api/v1")
app.include_router(embedding_models.router, prefix="/api/v1")
app.include_router(web_search.router, prefix="/api/v1")
STREAM_DELTA_CHUNK_SIZE = 48
PREVIEWABLE_TEXT_EXTENSIONS = {