Repository navigation
feat(websearch): add DeepSeek search and page fetching - #10599
Conversation
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
There was a problem hiding this comment.
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-ai review again thx |
There was a problem hiding this comment.
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
| results.append( | ||
| SearchResult( | ||
| title=item.get("title", ""), | ||
| url=item.get("url", ""), |
There was a problem hiding this comment.
🟡 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`.| "web_search_tavily", | ||
| "web_search_bocha", | ||
| "web_search_brave", | ||
| "web_search_deepseek", |
There was a problem hiding this comment.
🟡 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`.
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 / 改动点
Screenshots or Test Results / 运行截图或测试结果
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/zhanddocs/enagainst 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.txtandpyproject.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:
Enhancements:
Documentation:
Tests: