Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions astrbot/core/astr_main_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
FirecrawlWebSearchTool,
SerpApiFinanceSearchTool,
SerpApiImageSearchTool,
SerpApiNewsSearchTool,
SerpApiScholarSearchTool,
SerpApiWebSearchTool,
TavilyExtractWebPageTool,
Expand Down Expand Up @@ -170,6 +171,7 @@
"web_search_anysearch",
"web_search_deepseek",
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down Expand Up @@ -1312,6 +1314,7 @@ async def _apply_web_search_tools(
req.func_tool.add_tool(tool_mgr.get_builtin_tool(DeepSeekFetchWebPageTool))
elif provider == "serpapi":
req.func_tool.add_tool(tool_mgr.get_builtin_tool(SerpApiWebSearchTool))
req.func_tool.add_tool(tool_mgr.get_builtin_tool(SerpApiNewsSearchTool))
req.func_tool.add_tool(tool_mgr.get_builtin_tool(SerpApiImageSearchTool))
req.func_tool.add_tool(tool_mgr.get_builtin_tool(SerpApiFinanceSearchTool))
req.func_tool.add_tool(tool_mgr.get_builtin_tool(SerpApiScholarSearchTool))
Expand Down
4 changes: 2 additions & 2 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -3695,7 +3695,7 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
"AnySearch",
],
"subtitles": [
"Google 网页、图片、学术和财经搜索",
"Google 网页、新闻、图片、学术和财经搜索",
"DeepSeek 官方搜索接口",
"",
"",
Expand Down Expand Up @@ -3799,7 +3799,7 @@ def get_local_permission_defaults(system: str | None = None) -> dict:
"description": "SerpApi API Key",
"type": "list",
"items": {"type": "string"},
"hint": "支持 Google 网页、图片、学术和财经搜索。可添加多个 Key 轮询。获取 Key:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)",
"hint": "支持 Google 网页、新闻、图片、学术和财经搜索。可添加多个 Key 轮询。获取 Key:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)",
"secret": True,
"condition": {
"provider_settings.websearch_provider": "serpapi",
Expand Down
104 changes: 104 additions & 0 deletions astrbot/core/tools/web_search_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"exa_get_contents",
"web_search_anysearch",
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down Expand Up @@ -604,6 +605,108 @@ async def call(self, context, **kwargs) -> ToolExecResult:
return _search_result_payload(results[:max_results])


@builtin_tool(config=_SERPAPI_WEB_SEARCH_TOOL_CONFIG)
@pydantic_dataclass
class SerpApiNewsSearchTool(FunctionTool[AstrAgentContext]):
name: str = "news_search_serpapi"
description: str = (
"Search current news using the SerpApi Google News API. Returns article "
"titles, source URLs, publishers, authors, snippets, and publication times."
)
parameters: dict = Field(
default_factory=lambda: {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Required. News search query. Google News operators such as site: and when: are supported.",
},
"max_results": {
"type": "integer",
"description": "Maximum articles to return, 1-10. Default is 5; fewer may be available.",
},
"country": {
"type": "string",
"description": "Optional. Google News country code, such as us, uk, or cn.",
},
"search_lang": {
"type": "string",
"description": "Optional. Google News language code, such as en or zh-cn.",
},
},
"required": ["query"],
}
)

async def call(self, context, **kwargs) -> ToolExecResult:
"""Search Google News and preserve article metadata for citations.
Args:
context: Agent context containing the active profile settings.
**kwargs: News query, result limit, country, and language.
Returns:
News results as JSON, or a configuration or validation error.
Raises:
RuntimeError: If the search request fails.
"""
_, provider_settings, _ = _get_runtime(context)
if not provider_settings.get("websearch_serpapi_key", []):
return "Error: SerpApi API key is not configured in AstrBot."

query = kwargs["query"].strip()
if not query:
return "Error: query must not be empty."
max_results = max(1, min(int(kwargs.get("max_results", 5)), 10))
payload = {"q": query}
for name, parameter in (("country", "gl"), ("search_lang", "hl")):
if kwargs.get(name):
payload[parameter] = kwargs[name]
data = await _serpapi_request(provider_settings, payload, engine="google_news")

ref_uuid = str(uuid.uuid4())[:4]
results = []
seen_urls = set()
for group in data.get("news_results", []):
articles = []
if group.get("link"):
articles.append(group)
if isinstance(group.get("highlight"), dict):
articles.append(group["highlight"])
articles.extend(
story for story in group.get("stories", []) if isinstance(story, dict)
)
for article in articles:
source_url = article.get("link")
if not source_url or source_url in seen_urls:
continue
seen_urls.add(source_url)
source = article.get("source") or {}
results.append(
{
"title": article.get("title") or "",
"url": source_url,
"snippet": article.get("snippet") or "",
Comment thread
Soulter marked this conversation as resolved.
"source": source.get("name") or source.get("title") or "",
"authors": source.get("authors") or [],
"published_at": article.get("iso_date")
or article.get("date")
or "",
"thumbnail_url": article.get("thumbnail")
or article.get("thumbnail_small")
or "",
"index": f"{ref_uuid}.{len(results) + 1}",
}
)
_cache_favicon(source_url, source.get("icon"))
if len(results) >= max_results:
break
if len(results) >= max_results:
break
return json.dumps({"results": results}, ensure_ascii=False)


@builtin_tool(config=_SERPAPI_WEB_SEARCH_TOOL_CONFIG)
@pydantic_dataclass
class SerpApiImageSearchTool(FunctionTool[AstrAgentContext]):
Expand Down Expand Up @@ -2128,6 +2231,7 @@ async def call(self, context, **kwargs) -> ToolExecResult:
"ExaWebSearchTool",
"SerpApiFinanceSearchTool",
"SerpApiImageSearchTool",
"SerpApiNewsSearchTool",
"SerpApiScholarSearchTool",
"SerpApiWebSearchTool",
"TavilyExtractWebPageTool",
Expand Down
1 change: 1 addition & 0 deletions astrbot/dashboard/services/chat_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ def extract_web_search_refs(accumulated_text: str, accumulated_parts: list) -> d
"web_search_brave",
"web_search_deepseek",
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down
1 change: 1 addition & 0 deletions astrbot/dashboard/services/live_chat_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ def extract_web_search_refs(accumulated_text: str, accumulated_parts: list) -> d
"web_search_brave",
"web_search_deepseek",
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down
2 changes: 2 additions & 0 deletions changelogs/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

- Simplify Context Management Strategy settings: Handling for History Limits or Context Window Pressure → Compression Strategy; Model Provider ID for Context Compression → Compression Model. Move maximum turns, discarded turns, compression instruction, recent-context ratio and fallback context window size under the collapsed More Settings section. Preserve existing configuration keys, defaults and runtime behavior.

- Add SerpApi Google News search with publisher, author, publication time, and WebUI citation support.

- Add compact data-cleanup checkboxes to the existing bot deletion dialog. Custom rules, preferences, configuration routes and scheduled tasks are selected by default; AI conversations, message history, local traces and usage statistics are retained unless selected. Preserve shared resources and match adapter IDs exactly.

- Resolve Lark group-message sender names through the existing user-info lookup, sharing the same per-user cache with private chats (30 minutes for successful names and one minute for failures). Application senders skip user-directory lookups.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"AnySearch"
],
"subtitles": [
"Google web, image, scholar, and finance search",
"Google web, news, image, scholar, and finance search",
"Official DeepSeek search API",
"",
"",
Expand Down Expand Up @@ -232,7 +232,7 @@
},
"websearch_serpapi_key": {
"description": "SerpApi API Key",
"hint": "Supports Google web, image, scholar, and finance search. Multiple keys can be added for rotation. Get a key: [https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
"hint": "Supports Google web, news, image, scholar, and finance search. Multiple keys can be added for rotation. Get a key: [https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
},
"websearch_deepseek_key": {
"description": "DeepSeek API Key",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
"AnySearch"
],
"subtitles": [
"Google のウェブ・画像・学術・金融検索",
"Google のウェブ・ニュース・画像・学術・金融検索",
"DeepSeek 公式検索 API",
"",
"",
Expand Down Expand Up @@ -320,7 +320,7 @@
},
"websearch_serpapi_key": {
"description": "SerpApi API Key",
"hint": "Google のウェブ・画像・学術・金融検索に対応。複数の Key を追加してローテーションできます。Key の取得:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
"hint": "Google のウェブ・ニュース・画像・学術・金融検索に対応。複数の Key を追加してローテーションできます。Key の取得:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
},
"websearch_deepseek_key": {
"description": "DeepSeek API Key",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@
"AnySearch"
],
"subtitles": [
"Веб-поиск, изображения, научные публикации и финансы Google",
"Веб-поиск, новости, изображения, научные публикации и финансы Google",
"Официальный поисковый API DeepSeek",
"",
"",
Expand Down Expand Up @@ -320,7 +320,7 @@
},
"websearch_serpapi_key": {
"description": "SerpApi API Key",
"hint": "Поддерживает веб-поиск, поиск изображений, научных публикаций и финансовых данных Google. Можно добавить несколько ключей для ротации. Получить ключ: [https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
"hint": "Поддерживает веб-поиск, поиск новостей, изображений, научных публикаций и финансовых данных Google. Можно добавить несколько ключей для ротации. Получить ключ: [https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
},
"websearch_deepseek_key": {
"description": "API-ключ DeepSeek",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@
"AnySearch"
],
"subtitles": [
"Google 网页、图片、学术和财经搜索",
"Google 网页、新闻、图片、学术和财经搜索",
"DeepSeek 官方搜索接口",
"",
"",
Expand Down Expand Up @@ -248,7 +248,7 @@
},
"websearch_serpapi_key": {
"description": "SerpApi API Key",
"hint": "支持 Google 网页、图片、学术和财经搜索。可添加多个 Key 轮询。获取 Key:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
"hint": "支持 Google 网页、新闻、图片、学术和财经搜索。可添加多个 Key 轮询。获取 Key:[https://serpapi.com/manage-api-key](https://serpapi.com/manage-api-key)"
},
"websearch_deepseek_key": {
"description": "DeepSeek API Key",
Expand Down
4 changes: 2 additions & 2 deletions docs/en/dev/astrbot-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ Whether to enable AstrBot's built-in web search capability. Default is `false`.

Web search provider type. Default is `tavily`. Currently supports `serpapi`, `deepseek`, `tavily`, `bocha`, `baidu_ai_search`, `brave`, `firecrawl`, `exa`, and `anysearch`.

- `serpapi`: Uses SerpApi Google Search, Google Images Light, Google Finance, and Google Scholar.
- `serpapi`: Uses SerpApi Google Search, Google News, Google Images Light, Google Finance, and Google Scholar.
- `deepseek`: Uses the DeepSeek Search API.
- `tavily`: Uses the Tavily search engine.
- `bocha`: Uses the BoCha search engine.
Expand Down Expand Up @@ -328,7 +328,7 @@ API Key list for the DeepSeek search engine. Required when using `deepseek` as t

#### `provider_settings.websearch_serpapi_key`

API Key list shared by SerpApi Google Search, Google Images Light, Google Finance, and Google Scholar. Required when using `serpapi` as the web search provider. Obtain a key at [SerpApi](https://serpapi.com/manage-api-key). Multiple keys rotate, with failover on authentication and quota errors.
API Key list shared by SerpApi Google Search, Google News, Google Images Light, Google Finance, and Google Scholar. Required when using `serpapi` as the web search provider. Obtain a key at [SerpApi](https://serpapi.com/manage-api-key). Multiple keys rotate, with failover on authentication and quota errors.

#### `provider_settings.web_search_link`

Expand Down
2 changes: 1 addition & 1 deletion docs/en/use/websearch.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ AstrBot supports nine providers. Configure one to start; you do not need an acco

| Provider | Get a key | Capabilities available in AstrBot |
| --- | --- | --- |
| <span id="serpapi">[SerpApi](./websearch/serpapi.md)</span> | [SerpApi API key](https://serpapi.com/manage-api-key) | Google web search, Images Light, Finance, and Scholar |
| <span id="serpapi">[SerpApi](./websearch/serpapi.md)</span> | [SerpApi API key](https://serpapi.com/manage-api-key) | Google web search, News, Images Light, Finance, and Scholar |
| <span id="deepseek">[DeepSeek Search API](./websearch/deepseek.md)</span> | [DeepSeek Platform](https://platform.deepseek.com/api_keys) / [Search Docs](https://api-docs.deepseek.com/guides/search) | Web search and page content extraction from a URL |
| <span id="tavily">Tavily</span> | [Tavily dashboard](https://app.tavily.com/home) | Web search and page content extraction from a URL |
| <span id="bocha">Bocha</span> | [BoCha](https://bochaai.com/) | Web search |
Expand Down
4 changes: 2 additions & 2 deletions docs/zh/dev/astrbot-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ ID 白名单。填写后,将只处理所填写的 ID 发来的消息事件。

网页搜索提供商类型。默认为 `tavily`。目前支持 `serpapi`、`deepseek`、`tavily`、`bocha`、`baidu_ai_search`、`brave`、`firecrawl`、`exa`、`anysearch`。

- `serpapi`:使用 SerpApi Google Search、Google Images Light、Google Finance 和 Google Scholar。
- `serpapi`:使用 SerpApi Google Search、Google News、Google Images Light、Google Finance 和 Google Scholar。
- `deepseek`:使用 DeepSeek Search API。
- `tavily`:使用 Tavily 搜索引擎。
- `bocha`:使用 BoCha 搜索引擎。
Expand Down Expand Up @@ -328,7 +328,7 @@ DeepSeek 搜索引擎的 API Key 列表。使用 `deepseek` 作为网页搜索

#### `provider_settings.websearch_serpapi_key`

SerpApi Google Search、Google Images Light、Google Finance 和 Google Scholar 共用的 API Key 列表。使用 `serpapi` 作为网页搜索提供商时需要填写。前往 [SerpApi](https://serpapi.com/manage-api-key) 获取 Key。多个 Key 轮询使用,鉴权或额度错误时会尝试下一个 Key。
SerpApi Google Search、Google News、Google Images Light、Google Finance 和 Google Scholar 共用的 API Key 列表。使用 `serpapi` 作为网页搜索提供商时需要填写。前往 [SerpApi](https://serpapi.com/manage-api-key) 获取 Key。多个 Key 轮询使用,鉴权或额度错误时会尝试下一个 Key。

#### `provider_settings.web_search_link`

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/use/websearch.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

| 提供商 | Key 申请入口 | AstrBot 提供的能力 |
| --- | --- | --- |
| <span id="serpapi">[SerpApi](./websearch/serpapi.md)</span> | [SerpApi API Key](https://serpapi.com/manage-api-key) | Google 网页搜索、Images Light 图片搜索、Finance 金融查询、Scholar 学术搜索 |
| <span id="serpapi">[SerpApi](./websearch/serpapi.md)</span> | [SerpApi API Key](https://serpapi.com/manage-api-key) | Google 网页搜索、News 新闻、Images Light 图片搜索、Finance 金融查询、Scholar 学术搜索 |
| <span id="deepseek">[DeepSeek Search API](./websearch/deepseek.md)</span> | [DeepSeek 开放平台](https://platform.deepseek.com/api_keys) / [搜索文档](https://api-docs.deepseek.com/zh-cn/guides/search) | 网页搜索、按 URL 提取网页正文 |
| <span id="tavily">Tavily</span> | [Tavily 控制台](https://app.tavily.com/home) | 网页搜索、按 URL 提取网页正文 |
| <span id="bocha">Bocha</span> | [博查](https://bochaai.com/) | 网页搜索 |
Expand Down
3 changes: 3 additions & 0 deletions tests/unit/test_astr_main_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ async def test_serpapi_injects_all_search_tools(self, mock_event, mock_context):

assert req.func_tool.names() == [
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down Expand Up @@ -898,6 +899,7 @@ async def test_apply_web_search_tools_adds_search_and_extract_tools(
"web_search_tavily",
"web_search_deepseek",
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down Expand Up @@ -1583,6 +1585,7 @@ async def test_ensure_tools_from_persona(self, mock_event, mock_context):
"serpapi",
[
"web_search_serpapi",
"news_search_serpapi",
"image_search_serpapi",
"finance_search_serpapi",
"scholar_search_serpapi",
Expand Down
Loading