Skip to content

feat(websearch): add DeepSeek search and page fetching - #10599

Merged
Soulter merged 3 commits into
AstrBotDevs:masterfrom
letr007:feat/deepseek-web-search
Oct 17, 2026
Merged

Soulter merged 3 commits into
AstrBotDevs:masterfrom
letr007:feat/deepseek-web-search

Conversation

@letr007

@letr007 letr007 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Add DeepSeek Search and Fetch builtin tools with validated inputs, API key rotation, and WebChat source citations.
Expose the provider in WebUI configuration and update localized metadata, documentation, and tests.

添加带有验证输入、API 密钥轮换和 WebChat 来源引用的 DeepSeek 搜索和抓取内置工具。
在 WebUI 配置中提供商,并更新本地化的元数据、文档和测试。

Refs #10598

Modifications / 改动点

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

BetterShot_2026-10-17-11-53-14 BetterShot_2026-10-17-11-54-00 BetterShot_2026-10-17-11-54-19

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。

  • 📚 I checked the affected WebUI instructions and screenshots in docs/zh and docs/en against the changed navigation, page structure, and labels, and updated them in this PR (or explained why no documentation update is needed). For renamed, moved, or merged entry points, I included an old entry → new entry mapping in the documentation and changelog.
    / 我已对照变化后的 WebUI 入口、页面结构和术语,核对并在本 PR 中更新 docs/zh 和 docs/en 的相关操作说明与截图(或说明无需更新文档的原因)。入口改名、移动或合并时,已在文档和 changelog 中补充 旧入口 → 新入口 对照。

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txt 和 pyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Add DeepSeek web search and page extraction support with configurable credentials, validated inputs, and WebChat citations.

New Features:

  • Add DeepSeek as a web-search provider with built-in search and page-fetching tools.
  • Expose DeepSeek search configuration and API key settings in the WebUI and localized metadata.

Enhancements:

  • Integrate DeepSeek results with WebChat source citations and support rotating configured API keys across search and fetch requests.
  • Validate search parameters and page URLs before making DeepSeek API requests.

Documentation:

  • Document DeepSeek provider configuration, capabilities, API key requirements, and rate limits in English and Chinese web-search and configuration guides.

Tests:

  • Add coverage for DeepSeek tool registration, configuration, request mapping, validation, URL handling, key rotation, API errors, and citation extraction.

Add DeepSeek Search and Fetch builtin tools with validated inputs, API key rotation, and WebChat source citations. Expose the provider in WebUI configuration and update localized metadata, documentation, and tests.

Refs AstrBotDevs#10598

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/tools/web_search_tools.py" line_range="1619-1625" />
<code_context>
+        if not isinstance(url, str) or not 1 <= len(url) <= 8192:
+            return "Error: url must be a string of 1-8192 characters."
+        try:
+            parsed = urlsplit(url)
+            valid_url = (
+                parsed.scheme in {"http", "https"}
+                and bool(parsed.hostname)
+                and parsed.username is None
+                and parsed.password is None
+                and not any(char.isspace() for char in url)
+            )
+        except ValueError:
</code_context>
<issue_to_address>
**Malformed ports reach DeepSeek**

When the supplied URL has a nonnumeric or out-of-range port, the URL validation checks the hostname but never reads `parsed.port`, so the fetch tool sends the malformed URL to DeepSeek and the caller gets an upstream API error instead of an input-validation error.

Read `parsed.port` during URL validation so invalid ports are rejected before sending the URL to DeepSeek.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and when DeepSeek is selected and a tool is called, AstrBot sends the search query or page URL to DeepSeek; anything disclosed to the external service cannot be taken back by reverting, though the setting and API key are opt-in. Reverting would stop future calls, not undo disclosures already made.

Blocking findings: astrbot/core/tools/web_search_tools.py:1625


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread astrbot/core/tools/web_search_tools.py
@letr007

letr007 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review again thx

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/tools/web_search_tools.py" line_range="1566" />
<code_context>
+            results.append(
+                SearchResult(
+                    title=item.get("title", ""),
+                    url=item.get("url", ""),
+                    snippet=snippet,
+                    favicon=item.get("site_icon"),
</code_context>
<issue_to_address>
**Citation link can redirect dashboard**

When a DeepSeek search result points to an attacker-controlled site and a user opens its citation in WebChat, a malicious site returned as a DeepSeek search result is passed into WebChat citations without URL validation. When the user opens that citation, `RefsSidebar.openLink` calls `window.open(url, "_blank")` without `noopener`; the destination can use `window.opener` to redirect the AstrBot dashboard to a phishing page.

Only allow HTTP(S) citation URLs and open them with `noopener,noreferrer`.
</issue_to_address>

### Comment 2
<location path="astrbot/core/tools/web_search_tools.py" line_range="127" />
<code_context>
 _FIRECRAWL_KEY_ROTATOR = _KeyRotator("websearch_firecrawl_key", "Firecrawl")
 _EXA_KEY_ROTATOR = _KeyRotator("websearch_exa_key", "Exa")
 _ANYSEARCH_KEY_ROTATOR = _KeyRotator("websearch_anysearch_key", "AnySearch")
+_DEEPSEEK_KEY_ROTATOR = _KeyRotator("websearch_deepseek_key", "DeepSeek")


</code_context>
<issue_to_address>
**Scalar keys fail authentication**

When `provider_settings.websearch_deepseek_key` is configured as a non-empty string, `normalize_legacy_web_search_config` leaves the string unchanged, so `_KeyRotator.get` indexes it as a sequence and sends one character as the bearer token; DeepSeek authentication fails.

Normalize scalar DeepSeek keys to a one-element list before `_KeyRotator.get` uses them.

Also at `astrbot/core/tools/web_search_tools.py:1539`, `astrbot/core/config/default.py:153`.
</issue_to_address>

### Comment 3
<location path="astrbot/dashboard/services/chat_service.py" line_range="279" />
<code_context>
         "web_search_brave",
         "web_search_exa",
         "web_search_anysearch",
+        "web_search_deepseek",
     }
 )
</code_context>
<issue_to_address>
**Citation sources appear out of order**

When an answer cites multiple DeepSeek results, both extractors convert matched citation indices to a set before building the `used` list, so the saved DeepSeek source order does not follow the answer’s citation order; the chat UI displays the first three sources from that list.

Deduplicate citation indices while preserving their first-occurrence order.

Also at `astrbot/dashboard/services/live_chat_service.py:290`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 3 findings to address first, and search queries and supplied page URLs are sent to DeepSeek using the configured API key. If a query or URL contains sensitive information, reverting stops future requests but cannot undo its disclosure to the provider.

Blocking findings: astrbot/core/tools/web_search_tools.py:1566, astrbot/core/tools/web_search_tools.py:127, astrbot/dashboard/services/chat_service.py:279


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

results.append(
SearchResult(
title=item.get("title", ""),
url=item.get("url", ""),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Citation link can redirect dashboard

When a DeepSeek search result points to an attacker-controlled site and a user opens its citation in WebChat, a malicious site returned as a DeepSeek search result is passed into WebChat citations without URL validation. When the user opens that citation, RefsSidebar.openLink calls window.open(url, "_blank") without noopener; the destination can use window.opener to redirect the AstrBot dashboard to a phishing page.

Only allow HTTP(S) citation URLs and open them with noopener,noreferrer.

Prompt for AI agents
In `astrbot/core/tools/web_search_tools.py` at line 1566:

**Citation link can redirect dashboard**

When a DeepSeek search result points to an attacker-controlled site and a user opens its citation in WebChat, a malicious site returned as a DeepSeek search result is passed into WebChat citations without URL validation. When the user opens that citation, `RefsSidebar.openLink` calls `window.open(url, "_blank")` without `noopener`; the destination can use `window.opener` to redirect the AstrBot dashboard to a phishing page.

Only allow HTTP(S) citation URLs and open them with `noopener,noreferrer`.

Comment thread astrbot/core/tools/web_search_tools.py
"web_search_tavily",
"web_search_bocha",
"web_search_brave",
"web_search_deepseek",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Citation sources appear out of order

When an answer cites multiple DeepSeek results, both extractors convert matched citation indices to a set before building the used list, so the saved DeepSeek source order does not follow the answer’s citation order; the chat UI displays the first three sources from that list.

Deduplicate citation indices while preserving their first-occurrence order.

Also at astrbot/dashboard/services/live_chat_service.py:290.

Prompt for AI agents
In `astrbot/dashboard/services/chat_service.py` at line 279:

**Citation sources appear out of order**

When an answer cites multiple DeepSeek results, both extractors convert matched citation indices to a set before building the `used` list, so the saved DeepSeek source order does not follow the answer’s citation order; the chat UI displays the first three sources from that list.

Deduplicate citation indices while preserving their first-occurrence order.

Also at `astrbot/dashboard/services/live_chat_service.py:290`.

Comment thread astrbot/core/tools/web_search_tools.py Outdated
@Soulter
Soulter merged commit ff22dac into AstrBotDevs:master Oct 17, 2026
23 checks passed
@letr007
letr007 deleted the feat/deepseek-web-search branch October 17, 2026 06:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants