Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
402 changes: 392 additions & 10 deletions astrbot/builtin_stars/web_searcher/main.py

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion astrbot/core/astr_agent_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ async def on_tool_end(
platform_name = run_context.context.event.get_platform_name()
if (
platform_name == "webchat"
and tool.name in ["web_search_tavily", "web_search_bocha"]
and tool.name
in [
"web_search_tavily",
"web_search_bocha",
"web_search_exa",
"exa_find_similar",
]
and len(run_context.messages) > 0
and tool_result
and len(tool_result.content)
Expand Down
39 changes: 38 additions & 1 deletion astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,11 @@
"web_search": False,
"websearch_provider": "default",
"websearch_tavily_key": [],
"websearch_tavily_base_url": "https://api.tavily.com",
"websearch_bocha_key": [],
"websearch_baidu_app_builder_key": "",
"websearch_exa_key": [],
"websearch_exa_base_url": "https://api.exa.ai",
"web_search_link": False,
"display_reasoning_text": False,
"identifier": False,
Expand Down Expand Up @@ -3084,7 +3087,13 @@ class ChatProviderTemplate(TypedDict):
"provider_settings.websearch_provider": {
"description": "网页搜索提供商",
"type": "string",
"options": ["default", "tavily", "baidu_ai_search", "bocha"],
"options": [
"default",
"tavily",
"baidu_ai_search",
"bocha",
"exa",
],
"condition": {
"provider_settings.web_search": True,
},
Expand Down Expand Up @@ -3117,6 +3126,34 @@ class ChatProviderTemplate(TypedDict):
"provider_settings.websearch_provider": "baidu_ai_search",
},
},
"provider_settings.websearch_tavily_base_url": {
"description": "Tavily API Base URL",
"type": "string",
"hint": "默认为 https://api.tavily.com,可改为代理地址。",
"condition": {
"provider_settings.websearch_provider": "tavily",
"provider_settings.web_search": True,
},
},
"provider_settings.websearch_exa_key": {
"description": "Exa API Key",
"type": "list",
"items": {"type": "string"},
"hint": "可添加多个 Key 进行轮询。",
"condition": {
"provider_settings.websearch_provider": "exa",
"provider_settings.web_search": True,
},
},
"provider_settings.websearch_exa_base_url": {
"description": "Exa API Base URL",
"type": "string",
"hint": "默认为 https://api.exa.ai,可改为代理地址。",
"condition": {
"provider_settings.websearch_provider": "exa",
"provider_settings.web_search": True,
},
},
"provider_settings.web_search_link": {
"description": "显示来源引用",
"type": "bool",
Expand Down
8 changes: 7 additions & 1 deletion astrbot/core/knowledge_base/kb_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,12 +518,18 @@ async def upload_from_url(
"Error: Tavily API key is not configured in provider_settings."
)

tavily_base_url = config.get("provider_settings", {}).get(
"websearch_tavily_base_url", "https://api.tavily.com"
)

# 阶段1: 从 URL 提取内容
if progress_callback:
await progress_callback("extracting", 0, 100)

try:
text_content = await extract_text_from_url(url, tavily_keys)
text_content = await extract_text_from_url(
url, tavily_keys, tavily_base_url
)
except Exception as e:
logger.error(f"Failed to extract content from URL {url}: {e}")
raise OSError(f"Failed to extract content from URL {url}: {e}") from e
Expand Down
15 changes: 11 additions & 4 deletions astrbot/core/knowledge_base/parsers/url_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@
class URLExtractor:
"""URL 内容提取器,封装了 Tavily API 调用和密钥管理"""

def __init__(self, tavily_keys: list[str]) -> None:
def __init__(
self, tavily_keys: list[str], tavily_base_url: str = "https://api.tavily.com"
) -> None:
"""
初始化 URL 提取器

Args:
tavily_keys: Tavily API 密钥列表
tavily_base_url: Tavily API 基础 URL
"""
if not tavily_keys:
raise ValueError("Error: Tavily API keys are not configured.")

self.tavily_keys = tavily_keys
self.tavily_key_index = 0
self.tavily_key_lock = asyncio.Lock()
self.tavily_base_url = tavily_base_url.rstrip("/")

async def _get_tavily_key(self) -> str:
"""并发安全的从列表中获取并轮换Tavily API密钥。"""
Expand Down Expand Up @@ -47,7 +51,7 @@ async def extract_text_from_url(self, url: str) -> str:
raise ValueError("Error: url must be a non-empty string.")

tavily_key = await self._get_tavily_key()
api_url = "https://api.tavily.com/extract"
api_url = f"{self.tavily_base_url}/extract"
headers = {
"Authorization": f"Bearer {tavily_key}",
"Content-Type": "application/json",
Expand Down Expand Up @@ -88,16 +92,19 @@ async def extract_text_from_url(self, url: str) -> str:


# 为了向后兼容,提供一个简单的函数接口
async def extract_text_from_url(url: str, tavily_keys: list[str]) -> str:
async def extract_text_from_url(
url: str, tavily_keys: list[str], tavily_base_url: str = "https://api.tavily.com"
) -> str:
"""
简单的函数接口,用于从 URL 提取文本内容

Args:
url: 要提取内容的网页 URL
tavily_keys: Tavily API 密钥列表
tavily_base_url: Tavily API 基础 URL

Returns:
提取的文本内容
"""
extractor = URLExtractor(tavily_keys)
extractor = URLExtractor(tavily_keys, tavily_base_url)
return await extractor.extract_text_from_url(url)
7 changes: 6 additions & 1 deletion astrbot/dashboard/routes/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,12 @@ def _extract_web_search_refs(
Returns:
包含 used 列表的字典,记录被引用的搜索结果
"""
supported = ["web_search_tavily", "web_search_bocha"]
supported = [
"web_search_tavily",
"web_search_bocha",
"web_search_exa",
"exa_find_similar",
]
# 从 accumulated_parts 中找到所有 web_search_tavily 的工具调用结果
web_search_results = {}
tool_call_parts = [
Expand Down
7 changes: 6 additions & 1 deletion astrbot/dashboard/routes/live_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,12 @@ def _extract_web_search_refs(
self, accumulated_text: str, accumulated_parts: list
) -> dict:
"""从消息中提取 web_search 引用。"""
supported = ["web_search_tavily", "web_search_bocha"]
supported = [
"web_search_tavily",
"web_search_bocha",
"web_search_exa",
"exa_find_similar",
]
web_search_results = {}
tool_call_parts = [
p
Expand Down
5 changes: 3 additions & 2 deletions dashboard/src/components/chat/MessageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,9 @@ export default {
}

part.tool_calls.forEach(toolCall => {
// 检查是否是 web_search_tavily 工具调用
if (toolCall.name !== 'web_search_tavily' || !toolCall.result) {
// 检查是否是网页搜索工具调用
const supportedTools = ['web_search_tavily', 'web_search_bocha', 'web_search_exa', 'exa_find_similar'];
if (!supportedTools.includes(toolCall.name) || !toolCall.result) {
return;
}

Expand Down
12 changes: 12 additions & 0 deletions dashboard/src/i18n/locales/en-US/features/config-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@
"description": "Tavily API Key",
"hint": "Multiple keys can be added for rotation."
},
"websearch_tavily_base_url": {
"description": "Tavily API Base URL",
"hint": "Default: https://api.tavily.com. Change to use a proxy or self-hosted instance."
},
"websearch_bocha_key": {
"description": "BoCha API Key",
"hint": "Multiple keys can be added for rotation."
Expand All @@ -125,6 +129,14 @@
"description": "Baidu Qianfan Smart Cloud APP Builder API Key",
"hint": "Reference: [https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)"
},
"websearch_exa_key": {
"description": "Exa API Key",
"hint": "Multiple keys can be added for rotation."
},
"websearch_exa_base_url": {
"description": "Exa API Base URL",
"hint": "Default: https://api.exa.ai. Change to use a proxy or self-hosted instance."
},
"web_search_link": {
"description": "Display Source Citations"
}
Expand Down
12 changes: 12 additions & 0 deletions dashboard/src/i18n/locales/ru-RU/features/config-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@
"description": "API-ключ Tavily",
"hint": "Можно добавить несколько ключей для ротации."
},
"websearch_tavily_base_url": {
"description": "Базовый URL API Tavily",
"hint": "По умолчанию: https://api.tavily.com. Можно изменить на прокси-адрес."
},
"websearch_bocha_key": {
"description": "API-ключ BoCha",
"hint": "Можно добавить несколько ключей для ротации."
Expand All @@ -125,6 +129,14 @@
"description": "API-ключ Baidu Qianfan APP Builder",
"hint": "Ссылка: [https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)"
},
"websearch_exa_key": {
"description": "API-ключ Exa",
"hint": "Можно добавить несколько ключей для ротации."
},
"websearch_exa_base_url": {
"description": "Базовый URL API Exa",
"hint": "По умолчанию: https://api.exa.ai. Можно изменить на прокси-адрес."
},
"web_search_link": {
"description": "Показывать ссылки на источники"
}
Expand Down
12 changes: 12 additions & 0 deletions dashboard/src/i18n/locales/zh-CN/features/config-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
"description": "Tavily API Key",
"hint": "可添加多个 Key 进行轮询。"
},
"websearch_tavily_base_url": {
"description": "Tavily API Base URL",
"hint": "默认为 https://api.tavily.com,可改为代理地址。"
},
"websearch_bocha_key": {
"description": "BoCha API Key",
"hint": "可添加多个 Key 进行轮询。"
Expand All @@ -127,6 +131,14 @@
"description": "百度千帆智能云 APP Builder API Key",
"hint": "参考:[https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)"
},
"websearch_exa_key": {
"description": "Exa API Key",
"hint": "可添加多个 Key 进行轮询。"
},
"websearch_exa_base_url": {
"description": "Exa API Base URL",
"hint": "默认为 https://api.exa.ai,可改为代理地址。"
},
"web_search_link": {
"description": "显示来源引用"
}
Expand Down
Loading
Loading