@konbakuyomu/smart-search-darwin-arm64 0.1.28 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/runtime/smart-search/_internal/base_library.zip +0 -0
- package/runtime/smart-search/_internal/certifi/cacert.pem +0 -23
- package/runtime/smart-search/_internal/smart_search/assets/i18n/messages.json +164 -0
- package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/cli-core.md +1 -1
- package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/command-patterns.md +2 -0
- package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/provider-routing.md +11 -3
- package/runtime/smart-search/_internal/smart_search/assets/skills/smart-search-cli/references/setup-config.md +2 -0
- package/runtime/smart-search/_internal/smart_search/assets/ui/index.html +1 -1
- package/runtime/smart-search/_internal/smart_search/cli.py +35 -3
- package/runtime/smart-search/_internal/smart_search/config.py +20 -1
- package/runtime/smart-search/_internal/smart_search/jev_search.py +9 -1
- package/runtime/smart-search/_internal/smart_search/provider_descriptions.py +14 -0
- package/runtime/smart-search/_internal/smart_search/providers/__init__.py +2 -0
- package/runtime/smart-search/_internal/smart_search/providers/deepseek.py +110 -0
- package/runtime/smart-search/_internal/smart_search/service.py +84 -10
- package/runtime/smart-search/_internal/smart_search/ui_metadata.py +18 -0
- package/runtime/smart-search/_internal/smart_search/ui_server.py +44 -3
- package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/METADATA +1 -1
- package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/RECORD +25 -23
- package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/WHEEL +1 -1
- package/runtime/smart-search/package.json +7 -7
- package/runtime/smart-search/smart-search +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/INSTALLER +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/REQUESTED +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/direct_url.json +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/entry_points.txt +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/licenses/LICENSE +0 -0
- /package/runtime/smart-search/_internal/{smart_search-0.1.28.dist-info → smart_search-0.1.30.dist-info}/top_level.txt +0 -0
package/package.json
CHANGED
|
Binary file
|
|
@@ -2005,29 +2005,6 @@ RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx
|
|
|
2005
2005
|
iN66zB+Afko=
|
|
2006
2006
|
-----END CERTIFICATE-----
|
|
2007
2007
|
|
|
2008
|
-
# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
|
|
2009
|
-
# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI
|
|
2010
|
-
# Label: "emSign ECC Root CA - G3"
|
|
2011
|
-
# Serial: 287880440101571086945156
|
|
2012
|
-
# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40
|
|
2013
|
-
# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1
|
|
2014
|
-
# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b
|
|
2015
|
-
-----BEGIN CERTIFICATE-----
|
|
2016
|
-
MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG
|
|
2017
|
-
EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo
|
|
2018
|
-
bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g
|
|
2019
|
-
RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ
|
|
2020
|
-
TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s
|
|
2021
|
-
b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw
|
|
2022
|
-
djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0
|
|
2023
|
-
WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS
|
|
2024
|
-
fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB
|
|
2025
|
-
zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq
|
|
2026
|
-
hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB
|
|
2027
|
-
CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD
|
|
2028
|
-
+JbNR6iC8hZVdyR+EhCVBCyj
|
|
2029
|
-
-----END CERTIFICATE-----
|
|
2030
|
-
|
|
2031
2008
|
# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
|
|
2032
2009
|
# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI
|
|
2033
2010
|
# Label: "emSign Root CA - C1"
|
|
@@ -17858,5 +17858,169 @@
|
|
|
17858
17858
|
"{0} 另外,联网搜索能力检查未通过:{1}。OpenAI-compatible 不会发送搜索工具;如果该模型只能通过 Responses 的 web_search 工具联网,请把 XAI_API_URL、XAI_API_KEY、XAI_MODEL 指向支持 /v1/responses 的中转站。": [
|
|
17859
17859
|
"{0} 另外,联网搜索能力检查未通过:{1}。OpenAI-compatible 不会发送搜索工具;如果该模型只能通过 Responses 的 web_search 工具联网,请把 XAI_API_URL、XAI_API_KEY、XAI_MODEL 指向支持 /v1/responses 的中转站。",
|
|
17860
17860
|
"{0} Also, the web search capability check did not pass: {1}. OpenAI-compatible sends no search tool; if this model can only search through the Responses web_search tool, point XAI_API_URL, XAI_API_KEY and XAI_MODEL at a relay that supports /v1/responses."
|
|
17861
|
+
],
|
|
17862
|
+
"DeepSeek search response is not a JSON object": [
|
|
17863
|
+
"DeepSeek 搜索响应不是 JSON 对象",
|
|
17864
|
+
"DeepSeek search response is not a JSON object"
|
|
17865
|
+
],
|
|
17866
|
+
"DeepSeek search response is missing results": [
|
|
17867
|
+
"DeepSeek 搜索响应缺少 results",
|
|
17868
|
+
"DeepSeek search response is missing results"
|
|
17869
|
+
],
|
|
17870
|
+
"DeepSeek fetch response is not a JSON object": [
|
|
17871
|
+
"DeepSeek 抓取响应不是 JSON 对象",
|
|
17872
|
+
"DeepSeek fetch response is not a JSON object"
|
|
17873
|
+
],
|
|
17874
|
+
"DeepSeek fetch content is not text": [
|
|
17875
|
+
"DeepSeek 抓取内容不是文本",
|
|
17876
|
+
"DeepSeek fetch content is not text"
|
|
17877
|
+
],
|
|
17878
|
+
"DeepSeek search: {0}": [
|
|
17879
|
+
"DeepSeek 搜索:{0}",
|
|
17880
|
+
"DeepSeek search: {0}"
|
|
17881
|
+
],
|
|
17882
|
+
"DeepSeek fetch: {0}": [
|
|
17883
|
+
"DeepSeek 抓取:{0}",
|
|
17884
|
+
"DeepSeek fetch: {0}"
|
|
17885
|
+
],
|
|
17886
|
+
"DeepSeek fetch returned no content for {0}": [
|
|
17887
|
+
"DeepSeek 抓取 {0} 没有返回正文",
|
|
17888
|
+
"DeepSeek fetch returned no content for {0}"
|
|
17889
|
+
],
|
|
17890
|
+
"DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable.": [
|
|
17891
|
+
"DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用",
|
|
17892
|
+
"DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable."
|
|
17893
|
+
],
|
|
17894
|
+
"DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用": [
|
|
17895
|
+
"DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用",
|
|
17896
|
+
"DEEPSEEK_API_KEY is not set. DeepSeek search and fetch are unavailable."
|
|
17897
|
+
],
|
|
17898
|
+
"Both DeepSeek search and fetch APIs are available": [
|
|
17899
|
+
"DeepSeek 搜索与抓取 API 均可用",
|
|
17900
|
+
"Both DeepSeek search and fetch APIs are available"
|
|
17901
|
+
],
|
|
17902
|
+
"DeepSeek 搜索与抓取 API 均可用": [
|
|
17903
|
+
"DeepSeek 搜索与抓取 API 均可用",
|
|
17904
|
+
"Both DeepSeek search and fetch APIs are available"
|
|
17905
|
+
],
|
|
17906
|
+
"DeepSeek API request timed out": [
|
|
17907
|
+
"DeepSeek API 请求超时",
|
|
17908
|
+
"DeepSeek API request timed out"
|
|
17909
|
+
],
|
|
17910
|
+
"DeepSeek API 请求超时": [
|
|
17911
|
+
"DeepSeek API 请求超时",
|
|
17912
|
+
"DeepSeek API request timed out"
|
|
17913
|
+
],
|
|
17914
|
+
"Save DEEPSEEK_API_KEY.": [
|
|
17915
|
+
"保存 DEEPSEEK_API_KEY。",
|
|
17916
|
+
"Save DEEPSEEK_API_KEY."
|
|
17917
|
+
],
|
|
17918
|
+
"Save DEEPSEEK_API_URL.": [
|
|
17919
|
+
"保存 DEEPSEEK_API_URL。",
|
|
17920
|
+
"Save DEEPSEEK_API_URL."
|
|
17921
|
+
],
|
|
17922
|
+
"Save DEEPSEEK_TIMEOUT_SECONDS.": [
|
|
17923
|
+
"保存 DEEPSEEK_TIMEOUT_SECONDS。",
|
|
17924
|
+
"Save DEEPSEEK_TIMEOUT_SECONDS."
|
|
17925
|
+
],
|
|
17926
|
+
"没有找到 WSL 发行版。": [
|
|
17927
|
+
"没有找到 WSL 发行版。",
|
|
17928
|
+
"No WSL distribution was found."
|
|
17929
|
+
],
|
|
17930
|
+
"没有在 WSL 中找到 smart-search。请先在 WSL 中安装 CLI,并确认 bash 能直接运行 smart-search。": [
|
|
17931
|
+
"没有在 WSL 中找到 smart-search。请先在 WSL 中安装 CLI,并确认 bash 能直接运行 smart-search。",
|
|
17932
|
+
"smart-search was not found in WSL. Install the CLI in WSL first and make sure bash can run smart-search directly."
|
|
17933
|
+
],
|
|
17934
|
+
"旧版": [
|
|
17935
|
+
"旧版",
|
|
17936
|
+
"old version"
|
|
17937
|
+
],
|
|
17938
|
+
"当前连接的是 WSL 中的 CLI,只观察它自己的配置目录。": [
|
|
17939
|
+
"当前连接的是 WSL 中的 CLI,只观察它自己的配置目录。",
|
|
17940
|
+
"Connected to a CLI in WSL; only its own configuration directory is observed."
|
|
17941
|
+
],
|
|
17942
|
+
"这是 WSL 中的目录。需要更换时,在 WSL 中设置 SMART_SEARCH_CONFIG_DIR。": [
|
|
17943
|
+
"这是 WSL 中的目录。需要更换时,在 WSL 中设置 SMART_SEARCH_CONFIG_DIR。",
|
|
17944
|
+
"This directory is inside WSL. To use another one, set SMART_SEARCH_CONFIG_DIR in WSL."
|
|
17945
|
+
],
|
|
17946
|
+
"WSL 中的 CLI 使用该发行版自己的配置和 Skills。未运行的发行版不会自动启动。": [
|
|
17947
|
+
"WSL 中的 CLI 使用该发行版自己的配置和 Skills。未运行的发行版不会自动启动。",
|
|
17948
|
+
"A CLI in WSL uses that distribution's own configuration and Skills. Stopped distributions are not started automatically."
|
|
17949
|
+
],
|
|
17950
|
+
"未运行:{0}。“检测 WSL”会启动这些发行版并查找 CLI。": [
|
|
17951
|
+
"未运行:{0}。“检测 WSL”会启动这些发行版并查找 CLI。",
|
|
17952
|
+
"Not running: {0}. Detect WSL starts them to look for the CLI."
|
|
17953
|
+
],
|
|
17954
|
+
"检测 WSL": [
|
|
17955
|
+
"检测 WSL",
|
|
17956
|
+
"Detect WSL"
|
|
17957
|
+
],
|
|
17958
|
+
"{0} · 需要更新": [
|
|
17959
|
+
"{0} · 需要更新",
|
|
17960
|
+
"{0} · update needed"
|
|
17961
|
+
],
|
|
17962
|
+
"位于 WSL {0},使用该发行版中的配置和 Skills。": [
|
|
17963
|
+
"位于 WSL {0},使用该发行版中的配置和 Skills。",
|
|
17964
|
+
"Installed in WSL {0}; it uses that distribution's configuration and Skills."
|
|
17965
|
+
],
|
|
17966
|
+
"WSL {0} 中的 CLI 版本过旧,App 无法连接。": [
|
|
17967
|
+
"WSL {0} 中的 CLI 版本过旧,App 无法连接。",
|
|
17968
|
+
"The CLI in WSL {0} is too old for the App to connect."
|
|
17969
|
+
],
|
|
17970
|
+
"此 CLI 由 mise 管理,请在 WSL 终端更新:mise use -g npm:@konbakuyomu/smart-search@latest": [
|
|
17971
|
+
"此 CLI 由 mise 管理,请在 WSL 终端更新:mise use -g npm:@konbakuyomu/smart-search@latest",
|
|
17972
|
+
"mise manages this CLI. Update it from a WSL terminal: mise use -g npm:@konbakuyomu/smart-search@latest"
|
|
17973
|
+
],
|
|
17974
|
+
"npm 全局目录需要管理员权限,请在 WSL 终端更新:sudo npm install -g @konbakuyomu/smart-search@latest": [
|
|
17975
|
+
"npm 全局目录需要管理员权限,请在 WSL 终端更新:sudo npm install -g @konbakuyomu/smart-search@latest",
|
|
17976
|
+
"Its npm global directory needs administrator rights. Update it from a WSL terminal: sudo npm install -g @konbakuyomu/smart-search@latest"
|
|
17977
|
+
],
|
|
17978
|
+
"App 无法确定它的安装方式,请在 WSL 终端用原安装方式更新。": [
|
|
17979
|
+
"App 无法确定它的安装方式,请在 WSL 终端用原安装方式更新。",
|
|
17980
|
+
"The App cannot tell how it was installed. Update it from a WSL terminal with the tool you installed it with."
|
|
17981
|
+
],
|
|
17982
|
+
"WSL {0} 的 npm 全局目录需要管理员权限,App 不使用 sudo。请在 WSL 终端运行:sudo npm install -g @konbakuyomu/smart-search@latest": [
|
|
17983
|
+
"WSL {0} 的 npm 全局目录需要管理员权限,App 不使用 sudo。请在 WSL 终端运行:sudo npm install -g @konbakuyomu/smart-search@latest",
|
|
17984
|
+
"The npm global directory in WSL {0} needs administrator rights, and the App does not use sudo. In a WSL terminal, run: sudo npm install -g @konbakuyomu/smart-search@latest"
|
|
17985
|
+
],
|
|
17986
|
+
"WSL {0} 中的 Node.js 没有配套的 npm。请重新安装 Node.js,再点击“检测 WSL”。": [
|
|
17987
|
+
"WSL {0} 中的 Node.js 没有配套的 npm。请重新安装 Node.js,再点击“检测 WSL”。",
|
|
17988
|
+
"Node.js in WSL {0} has no matching npm. Reinstall Node.js, then click Detect WSL."
|
|
17989
|
+
],
|
|
17990
|
+
"WSL {0} 中没有找到 Node.js 18 或更新版本。请先在 WSL 中安装 Node.js,再点击“检测 WSL”。": [
|
|
17991
|
+
"WSL {0} 中没有找到 Node.js 18 或更新版本。请先在 WSL 中安装 Node.js,再点击“检测 WSL”。",
|
|
17992
|
+
"No Node.js 18 or later was found in WSL {0}. Install Node.js in WSL, then click Detect WSL."
|
|
17993
|
+
],
|
|
17994
|
+
"{0} 中的 CLI 已安装或更新为 {1}。": [
|
|
17995
|
+
"{0} 中的 CLI 已安装或更新为 {1}。",
|
|
17996
|
+
"The CLI in {0} is now {1}."
|
|
17997
|
+
],
|
|
17998
|
+
"更新 {0} 中的 CLI": [
|
|
17999
|
+
"更新 {0} 中的 CLI",
|
|
18000
|
+
"Update the CLI in {0}"
|
|
18001
|
+
],
|
|
18002
|
+
"这些环境还没有 Smart Search CLI。安装使用该环境自己的 npm,不会改动其他环境。": [
|
|
18003
|
+
"这些环境还没有 Smart Search CLI。安装使用该环境自己的 npm,不会改动其他环境。",
|
|
18004
|
+
"These environments have no Smart Search CLI yet. Installing uses that environment's own npm and leaves the others unchanged."
|
|
18005
|
+
],
|
|
18006
|
+
"安装到 {0}": [
|
|
18007
|
+
"安装到 {0}",
|
|
18008
|
+
"Install in {0}"
|
|
18009
|
+
],
|
|
18010
|
+
"在 WSL 中安装 Node.js": [
|
|
18011
|
+
"在 WSL 中安装 Node.js",
|
|
18012
|
+
"Install Node.js in WSL"
|
|
18013
|
+
],
|
|
18014
|
+
"未安装 CLI 的环境": [
|
|
18015
|
+
"未安装 CLI 的环境",
|
|
18016
|
+
"Environments without the CLI"
|
|
18017
|
+
],
|
|
18018
|
+
"WSL {0} · npm {1} · {2}": [
|
|
18019
|
+
"WSL {0} · npm {1} · {2}",
|
|
18020
|
+
"WSL {0} · npm {1} · {2}"
|
|
18021
|
+
],
|
|
18022
|
+
"正在卸载 CLI…": [
|
|
18023
|
+
"正在卸载 CLI…",
|
|
18024
|
+
"Removing the CLI…"
|
|
17861
18025
|
]
|
|
17862
18026
|
}
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
- `smart-search map URL [--instructions TEXT] [--max-depth N] [--max-breadth N] [--limit N] [--timeout SECONDS] [--format json|markdown|content] [--output PATH]`
|
|
49
49
|
- `smart-search doctor [--format json|markdown|content] [--output PATH]`
|
|
50
50
|
- `smart-search diagnose openai-compatible [--timeout SECONDS] [--format json|markdown] [--output PATH]`
|
|
51
|
-
- `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--skip-skills] [--install-skills CSV] [--skills-root PATH] [--xai-api-url URL] [--xai-api-key KEY] [--xai-model ID] [--xai-tools-explicit CSV] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-fallback-models CSV] [--openai-compatible-api-mode chat-completions|responses] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--intent-router jev|local] [--search-timeout SECONDS] [--provider-cooldown SECONDS] [--provider-failure-threshold N] [--exa-key KEY] [--context7-key KEY] [--zhipu-key KEY] [--zhipu-api-url URL] [--zhipu-search-engine ENGINE] [--zhipu-mcp-key KEY] [--zhipu-mcp-search-api-url URL] [--zhipu-mcp-reader-api-url URL] [--zhipu-mcp-zread-api-url URL] [--zhipu-mcp-timeout SECONDS] [--jina-key KEY] [--jina-reader-api-url URL] [--jina-respond-with MODE] [--jina-timeout SECONDS] [--tavily-api-url URL] [--tavily-key KEY] [--firecrawl-api-url URL] [--firecrawl-key KEY] [--tinyfish-key KEY] [--tinyfish-search-api-url URL] [--tinyfish-fetch-api-url URL] [--tinyfish-timeout SECONDS] [--anysearch-api-url URL] [--anysearch-key KEY] [--anysearch-timeout SECONDS] [--sciverse-api-url URL] [--sciverse-token TOKEN] [--sciverse-timeout SECONDS] [--format json|markdown|content] [--output PATH]`
|
|
51
|
+
- `smart-search setup [--lang zh|en] [--advanced] [--non-interactive] [--skip-skills] [--install-skills CSV] [--skills-root PATH] [--xai-api-url URL] [--xai-api-key KEY] [--xai-model ID] [--xai-tools-explicit CSV] [--openai-compatible-api-url URL] [--openai-compatible-api-key KEY] [--openai-compatible-model ID] [--openai-compatible-fallback-models CSV] [--openai-compatible-api-mode chat-completions|responses] [--openai-compatible-stream true|false] [--validation-level fast|balanced|strict] [--fallback-mode auto|off] [--minimum-profile standard|off] [--intent-router jev|local] [--search-timeout SECONDS] [--provider-cooldown SECONDS] [--provider-failure-threshold N] [--exa-key KEY] [--context7-key KEY] [--zhipu-key KEY] [--zhipu-api-url URL] [--zhipu-search-engine ENGINE] [--zhipu-mcp-key KEY] [--zhipu-mcp-search-api-url URL] [--zhipu-mcp-reader-api-url URL] [--zhipu-mcp-zread-api-url URL] [--zhipu-mcp-timeout SECONDS] [--jina-key KEY] [--jina-reader-api-url URL] [--jina-respond-with MODE] [--jina-timeout SECONDS] [--tavily-api-url URL] [--tavily-key KEY] [--firecrawl-api-url URL] [--firecrawl-key KEY] [--tinyfish-key KEY] [--tinyfish-search-api-url URL] [--tinyfish-fetch-api-url URL] [--tinyfish-timeout SECONDS] [--deepseek-key KEY] [--deepseek-api-url URL] [--deepseek-timeout SECONDS] [--anysearch-api-url URL] [--anysearch-key KEY] [--anysearch-timeout SECONDS] [--sciverse-api-url URL] [--sciverse-token TOKEN] [--sciverse-timeout SECONDS] [--format json|markdown|content] [--output PATH]`
|
|
52
52
|
- `smart-search config path|list|set|unset ... [--format json|markdown|content] [--output PATH]`
|
|
53
53
|
- `smart-search model set MODEL [--format json|markdown|content] [--output PATH]`
|
|
54
54
|
- `smart-search model current [--format json|markdown|content] [--output PATH]`
|
|
@@ -96,6 +96,8 @@ smart-search config set FIRECRAWL_API_URL "https://api.firecrawl.dev/v2" --forma
|
|
|
96
96
|
smart-search config set TINYFISH_API_KEY "key" --format json
|
|
97
97
|
smart-search config set TINYFISH_SEARCH_API_URL "https://api.search.tinyfish.ai" --format json
|
|
98
98
|
smart-search config set TINYFISH_FETCH_API_URL "https://api.fetch.tinyfish.ai" --format json
|
|
99
|
+
smart-search config set DEEPSEEK_API_KEY "key" --format json
|
|
100
|
+
smart-search config set DEEPSEEK_API_URL "https://api.deepseek.com" --format json
|
|
99
101
|
smart-search model current --format json
|
|
100
102
|
smart-search doctor --format json
|
|
101
103
|
smart-search doctor --format markdown
|
|
@@ -32,11 +32,11 @@ For research, `SMART_SEARCH_JEV_SYNTHESIZE` accepts `true`, `false`, and `auto`.
|
|
|
32
32
|
- Jina Reader is `web_fetch` only, not a general search provider. `JINA_API_KEY` is required before Jina satisfies the standard minimum profile; anonymous `r.jina.ai` is explicit/experimental fetch behavior.
|
|
33
33
|
- Same-capability fallback is allowed; model answers and web search are mutual fallbacks in ordinary search. Context7 is not used for unrelated broad web queries, and page extraction providers are not used as docs search providers.
|
|
34
34
|
- `TAVILY_ENABLED=false` removes Tavily from registered `web_search` and `web_fetch` routes even when its key is present. Direct Tavily search/extract calls, `map`, `doctor`, and smoke must make no Tavily network request; `map` reports a local configuration error. Firecrawl remains independently configured and all fallback stays within its capability.
|
|
35
|
-
- In Jev mode, Jev chooses among allowed capabilities and providers for the question; there is no mandatory main-model call or fixed global provider order. Local routing prioritizes suitable capabilities and configured preferences.
|
|
36
|
-
- `main_search` contains xAI Responses and OpenAI-compatible model channels; `web_search` contains Zhipu REST/MCP, Tavily, Firecrawl and
|
|
35
|
+
- In Jev mode, Jev chooses among allowed capabilities and providers for the question; there is no mandatory main-model call or fixed global provider order. Local routing prioritizes suitable capabilities and configured preferences; a docs question with no docs channel configured uses web search instead of no channel.
|
|
36
|
+
- `main_search` contains xAI Responses and OpenAI-compatible model channels; `web_search` contains Zhipu REST/MCP, Tavily, Firecrawl, TinyFish and DeepSeek; `docs_search` contains Context7 and Exa. Failure fallback stays within the selected capability family; model answers and web search are mutual fallbacks in ordinary search.
|
|
37
37
|
- Ordinary Context7 uses the first library in Context7's ranked candidates, without a second Jev call. Research may use Jev to select the matching library, using the first candidate if Jev fails or no score reaches its threshold. Empty results may fall through to same-capability Exa; explicit Context7 commands remain unchanged.
|
|
38
38
|
- `--extra-sources N` controls the selected channel's result count. It does not add channels, split a budget between providers, or make Tavily/Firecrawl a required extra stage. A known-URL search follows the configured Jev/local routing; local routing prefers page reading.
|
|
39
|
-
- `fetch` tries Tavily first, then Jina with `JINA_API_KEY`, then Zhipu Coding Plan MCP Reader, then Firecrawl, then TinyFish.
|
|
39
|
+
- `fetch` tries Tavily first, then Jina with `JINA_API_KEY`, then Zhipu Coding Plan MCP Reader, then Firecrawl, then TinyFish, then DeepSeek.
|
|
40
40
|
|
|
41
41
|
TinyFish:
|
|
42
42
|
|
|
@@ -47,6 +47,14 @@ TinyFish:
|
|
|
47
47
|
- Fetch reads `results[0].text`; `errors[]` entries are surfaced as `provider_error` with the upstream message. A payload with neither results nor errors is `provider_error`, and a non-object payload is `parse_error`.
|
|
48
48
|
- Challenge pages such as `Checking if the site connection is secure` are reported as `quality_error` so same-capability fallback can continue.
|
|
49
49
|
- `401`/403 map to `auth_error`, `408` to `timeout`, `429` to `rate_limited`, `5xx` to `network_error`, and `400`/422 to `parameter_error`.
|
|
50
|
+
DeepSeek:
|
|
51
|
+
|
|
52
|
+
- `DEEPSEEK_API_KEY` is optional and registers DeepSeek for both `web_search` and `web_fetch`; it is the same key as the DeepSeek model API. It never satisfies `main_search` or `docs_search`. Jev may select it directly; the direct `fetch` fallback order remains as listed above.
|
|
53
|
+
- `DEEPSEEK_API_URL` defaults to `https://api.deepseek.com`; search calls `POST /v1/tools/search` with `{"query", "count"}` and fetch calls `POST /v1/tools/fetch` with `{"url"}`, both with `Authorization: Bearer`. A trailing `/v1` in the base URL is accepted. Never log or echo the key.
|
|
54
|
+
- DeepSeek accepts queries of at most 128 characters, so longer queries are shortened at a word boundary. `site:`, `-site:`, `lang:`, `after:`/`before:` and quoted phrases in the query pass through unchanged.
|
|
55
|
+
- Search results normalize `summary` (falling back to `snippet`) to `description`, `site_name` to `source`, and `date` to `published_date`. Summaries are not page text: fetch a key page before citing it. An empty result set is a successful empty response.
|
|
56
|
+
- Fetch returns the page Markdown. HTTP 403 (URL refused by DeepSeek's safety policy) and 404 (no usable page content, which currently includes PDF links and private network addresses) mean that page has nothing to read: the attempt is `empty`, fallback continues, and the provider is not cooled down. `401` maps to `auth_error`, `402` (insufficient balance) to `provider_error`, `429` to `rate_limited`, `5xx` to `network_error`.
|
|
57
|
+
- DeepSeek bills each successful search or fetch request. `doctor` and `providers test deepseek` send one real request to each.
|
|
50
58
|
- `map` currently uses Tavily only.
|
|
51
59
|
- `exa-search` and `exa-similar` use Exa only.
|
|
52
60
|
- `context7-library` and `context7-docs` use Context7 only.
|
|
@@ -98,6 +98,8 @@
|
|
|
98
98
|
- Use `smart-search setup --non-interactive --tinyfish-key "key"` to add TinyFish to `web_search` and `web_fetch`. It never satisfies `main_search` or `docs_search`; Jev can select it among the allowed channels.
|
|
99
99
|
- `TINYFISH_SEARCH_API_URL` defaults to `https://api.search.tinyfish.ai`, and `TINYFISH_FETCH_API_URL` defaults to `https://api.fetch.tinyfish.ai`. Both authenticate with the `X-API-Key` header, not a bearer token.
|
|
100
100
|
- `TINYFISH_TIMEOUT_SECONDS` defaults to `60` and covers both TinyFish search and fetch reads.
|
|
101
|
+
- Use `smart-search setup --non-interactive --deepseek-key "key"` to add DeepSeek to `web_search` and `web_fetch` with the same key as the DeepSeek model API. It never satisfies `main_search` or `docs_search`; Jev can select it among the allowed channels.
|
|
102
|
+
- `DEEPSEEK_API_URL` defaults to `https://api.deepseek.com`, and `DEEPSEEK_TIMEOUT_SECONDS` defaults to `60` and covers both DeepSeek search and fetch reads. Each successful DeepSeek request is billed.
|
|
101
103
|
|
|
102
104
|
## Provider Failure Cooldown
|
|
103
105
|
|
|
@@ -691,7 +691,7 @@ details.more[open] > summary::before { content: "▾ "; }
|
|
|
691
691
|
{ capability: "docs_search", title: "stepDocs",
|
|
692
692
|
providers: ["context7", "exa"] },
|
|
693
693
|
{ capability: "web_fetch", title: "stepFetch",
|
|
694
|
-
providers: ["tavily", "jina", "zhipu-mcp-reader", "zhipu-mcp", "firecrawl"] }
|
|
694
|
+
providers: ["tavily", "jina", "zhipu-mcp-reader", "zhipu-mcp", "firecrawl", "deepseek"] }
|
|
695
695
|
];
|
|
696
696
|
|
|
697
697
|
function viewGettingStarted() {
|
|
@@ -627,6 +627,7 @@ def _format_doctor_markdown(data: dict[str, Any]) -> str:
|
|
|
627
627
|
("jina", data.get("jina_connection_test") or {}),
|
|
628
628
|
("firecrawl", data.get("firecrawl_connection_test") or {}),
|
|
629
629
|
("tinyfish", data.get("tinyfish_connection_test") or {}),
|
|
630
|
+
("deepseek", data.get("deepseek_connection_test") or {}),
|
|
630
631
|
("zhipu", data.get("zhipu_connection_test") or {}),
|
|
631
632
|
("zhipu-mcp", data.get("zhipu_mcp_connection_test") or {}),
|
|
632
633
|
("context7", data.get("context7_connection_test") or {}),
|
|
@@ -1478,6 +1479,7 @@ def _display_provider(provider: str, lang: str) -> str:
|
|
|
1478
1479
|
"tavily": "Tavily",
|
|
1479
1480
|
"firecrawl": "Firecrawl",
|
|
1480
1481
|
"tinyfish": "TinyFish",
|
|
1482
|
+
"deepseek": "DeepSeek",
|
|
1481
1483
|
"anysearch": "AnySearch",
|
|
1482
1484
|
"sciverse": "Sciverse",
|
|
1483
1485
|
}
|
|
@@ -2078,7 +2080,7 @@ def _prompt_web_fetch(values: dict[str, str], current: dict[str, str], lang: str
|
|
|
2078
2080
|
"选择 web_fetch provider",
|
|
2079
2081
|
"Choose web_fetch providers",
|
|
2080
2082
|
),
|
|
2081
|
-
["tavily", "jina", "firecrawl", "tinyfish"],
|
|
2083
|
+
["tavily", "jina", "firecrawl", "tinyfish", "deepseek"],
|
|
2082
2084
|
default_selected,
|
|
2083
2085
|
lang,
|
|
2084
2086
|
)
|
|
@@ -2110,6 +2112,13 @@ def _prompt_web_fetch(values: dict[str, str], current: dict[str, str], lang: str
|
|
|
2110
2112
|
current.get("TINYFISH_API_KEY", ""),
|
|
2111
2113
|
lang=lang,
|
|
2112
2114
|
)
|
|
2115
|
+
if "deepseek" in selected:
|
|
2116
|
+
values["DEEPSEEK_API_KEY"] = _prompt_value(
|
|
2117
|
+
"DEEPSEEK_API_KEY",
|
|
2118
|
+
"DeepSeek API key",
|
|
2119
|
+
current.get("DEEPSEEK_API_KEY", ""),
|
|
2120
|
+
lang=lang,
|
|
2121
|
+
)
|
|
2113
2122
|
|
|
2114
2123
|
|
|
2115
2124
|
def _prompt_optional_enhancements(values: dict[str, str], current: dict[str, str], lang: str) -> None:
|
|
@@ -2347,6 +2356,9 @@ def _run_advanced_setup_prompts(values: dict[str, str], current: dict[str, str],
|
|
|
2347
2356
|
("TINYFISH_SEARCH_API_URL", "TinyFish Search API URL", True),
|
|
2348
2357
|
("TINYFISH_FETCH_API_URL", "TinyFish Fetch API URL", True),
|
|
2349
2358
|
("TINYFISH_TIMEOUT_SECONDS", "TinyFish timeout seconds", True),
|
|
2359
|
+
("DEEPSEEK_API_KEY", "DeepSeek API key", True),
|
|
2360
|
+
("DEEPSEEK_API_URL", "DeepSeek API URL", True),
|
|
2361
|
+
("DEEPSEEK_TIMEOUT_SECONDS", "DeepSeek timeout seconds", True),
|
|
2350
2362
|
("ANYSEARCH_API_URL", "AnySearch MCP API URL", True),
|
|
2351
2363
|
("ANYSEARCH_API_KEY", "AnySearch API key", True),
|
|
2352
2364
|
("ANYSEARCH_TIMEOUT_SECONDS", "AnySearch timeout seconds", True),
|
|
@@ -2370,7 +2382,7 @@ def _run_advanced_setup_prompts(values: dict[str, str], current: dict[str, str],
|
|
|
2370
2382
|
value = _normalize_custom_base_url(value)
|
|
2371
2383
|
elif key == "SCIVERSE_API_URL":
|
|
2372
2384
|
value = _normalize_custom_base_url(value)
|
|
2373
|
-
elif key in {"TINYFISH_SEARCH_API_URL", "TINYFISH_FETCH_API_URL"}:
|
|
2385
|
+
elif key in {"TINYFISH_SEARCH_API_URL", "TINYFISH_FETCH_API_URL", "DEEPSEEK_API_URL"}:
|
|
2374
2386
|
value = _normalize_custom_base_url(value)
|
|
2375
2387
|
values[key] = value
|
|
2376
2388
|
|
|
@@ -2775,6 +2787,9 @@ def _run_setup(args: argparse.Namespace) -> int:
|
|
|
2775
2787
|
"TINYFISH_SEARCH_API_URL": _normalize_custom_base_url(args.tinyfish_search_api_url),
|
|
2776
2788
|
"TINYFISH_FETCH_API_URL": _normalize_custom_base_url(args.tinyfish_fetch_api_url),
|
|
2777
2789
|
"TINYFISH_TIMEOUT_SECONDS": args.tinyfish_timeout,
|
|
2790
|
+
"DEEPSEEK_API_KEY": args.deepseek_key,
|
|
2791
|
+
"DEEPSEEK_API_URL": _normalize_custom_base_url(args.deepseek_api_url),
|
|
2792
|
+
"DEEPSEEK_TIMEOUT_SECONDS": args.deepseek_timeout,
|
|
2778
2793
|
"ANYSEARCH_API_URL": _normalize_custom_base_url(args.anysearch_api_url),
|
|
2779
2794
|
"ANYSEARCH_API_KEY": args.anysearch_key,
|
|
2780
2795
|
"ANYSEARCH_TIMEOUT_SECONDS": args.anysearch_timeout,
|
|
@@ -2885,6 +2900,7 @@ def _run_regression() -> int:
|
|
|
2885
2900
|
"tests/test_jina_provider.py",
|
|
2886
2901
|
"tests/test_tinyfish_provider.py",
|
|
2887
2902
|
"tests/test_tinyfish_service.py",
|
|
2903
|
+
"tests/test_deepseek_provider.py",
|
|
2888
2904
|
"tests/test_zhipu_mcp_provider.py",
|
|
2889
2905
|
"tests/test_smoke.py",
|
|
2890
2906
|
"tests/test_intent_router.py",
|
|
@@ -3484,6 +3500,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
3484
3500
|
setup_parser.add_argument("--tinyfish-search-api-url", default="", help="Save TINYFISH_SEARCH_API_URL.")
|
|
3485
3501
|
setup_parser.add_argument("--tinyfish-fetch-api-url", default="", help="Save TINYFISH_FETCH_API_URL.")
|
|
3486
3502
|
setup_parser.add_argument("--tinyfish-timeout", default="", help="Save TINYFISH_TIMEOUT_SECONDS.")
|
|
3503
|
+
setup_parser.add_argument("--deepseek-key", default="", help="Save DEEPSEEK_API_KEY.")
|
|
3504
|
+
setup_parser.add_argument("--deepseek-api-url", default="", help="Save DEEPSEEK_API_URL.")
|
|
3505
|
+
setup_parser.add_argument("--deepseek-timeout", default="", help="Save DEEPSEEK_TIMEOUT_SECONDS.")
|
|
3487
3506
|
setup_parser.add_argument("--anysearch-api-url", default="", help="Save ANYSEARCH_API_URL.")
|
|
3488
3507
|
setup_parser.add_argument("--anysearch-key", default="", help="Save ANYSEARCH_API_KEY.")
|
|
3489
3508
|
setup_parser.add_argument("--anysearch-timeout", default="", help="Save ANYSEARCH_TIMEOUT_SECONDS.")
|
|
@@ -3562,6 +3581,18 @@ def _main(argv: list[str] | None = None) -> int:
|
|
|
3562
3581
|
return EXIT_RUNTIME_ERROR
|
|
3563
3582
|
|
|
3564
3583
|
|
|
3584
|
+
def _desktop_launch_command() -> list[str]:
|
|
3585
|
+
"""Argv that starts this same CLI without a shell, e.g. inside WSL from the Windows App."""
|
|
3586
|
+
package_root = os.environ.get("SMART_SEARCH_PACKAGE_ROOT", "")
|
|
3587
|
+
node = os.environ.get("SMART_SEARCH_NODE_PATH", "")
|
|
3588
|
+
wrapper = Path(package_root) / "npm" / "bin" / "smart-search.js"
|
|
3589
|
+
if package_root and node and wrapper.is_file():
|
|
3590
|
+
return [node, str(wrapper)]
|
|
3591
|
+
if getattr(sys, "frozen", False):
|
|
3592
|
+
return [sys.executable]
|
|
3593
|
+
return [sys.executable, "-m", "smart_search.cli"]
|
|
3594
|
+
|
|
3595
|
+
|
|
3565
3596
|
def main(argv: list[str] | None = None) -> int:
|
|
3566
3597
|
arguments = list(sys.argv[1:] if argv is None else argv)
|
|
3567
3598
|
if arguments == ["--desktop-backend"]:
|
|
@@ -3569,7 +3600,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
3569
3600
|
return desktop_main()
|
|
3570
3601
|
if arguments == ["--desktop-capabilities"]:
|
|
3571
3602
|
print(json.dumps({"product": "smart-search", "version": _get_version(), "activity_protocol_version": 1,
|
|
3572
|
-
"desktop_protocol_version": 1, "skills_protocol_version": 1
|
|
3603
|
+
"desktop_protocol_version": 1, "skills_protocol_version": 1,
|
|
3604
|
+
"launch_command": _desktop_launch_command()}))
|
|
3573
3605
|
return EXIT_OK
|
|
3574
3606
|
try:
|
|
3575
3607
|
arguments, language, warning = _command_language(arguments)
|
|
@@ -58,6 +58,7 @@ class Config:
|
|
|
58
58
|
"tavily": "TAVILY_ENABLED",
|
|
59
59
|
"firecrawl": "FIRECRAWL_ENABLED",
|
|
60
60
|
"tinyfish": "TINYFISH_ENABLED",
|
|
61
|
+
"deepseek": "DEEPSEEK_ENABLED",
|
|
61
62
|
"jina": "JINA_ENABLED",
|
|
62
63
|
"anysearch": "ANYSEARCH_ENABLED",
|
|
63
64
|
"sciverse": "SCIVERSE_ENABLED",
|
|
@@ -122,6 +123,9 @@ class Config:
|
|
|
122
123
|
"TINYFISH_SEARCH_API_URL",
|
|
123
124
|
"TINYFISH_FETCH_API_URL",
|
|
124
125
|
"TINYFISH_TIMEOUT_SECONDS",
|
|
126
|
+
"DEEPSEEK_API_KEY",
|
|
127
|
+
"DEEPSEEK_API_URL",
|
|
128
|
+
"DEEPSEEK_TIMEOUT_SECONDS",
|
|
125
129
|
"ANYSEARCH_API_KEY",
|
|
126
130
|
"ANYSEARCH_API_URL",
|
|
127
131
|
"ANYSEARCH_TIMEOUT_SECONDS",
|
|
@@ -712,7 +716,7 @@ class Config:
|
|
|
712
716
|
if key in JEV_DEFAULTS:
|
|
713
717
|
validate_jev_value(key, value)
|
|
714
718
|
return
|
|
715
|
-
if key in {"SMART_SEARCH_TIMEOUT_SECONDS", "TINYFISH_TIMEOUT_SECONDS"}:
|
|
719
|
+
if key in {"SMART_SEARCH_TIMEOUT_SECONDS", "TINYFISH_TIMEOUT_SECONDS", "DEEPSEEK_TIMEOUT_SECONDS"}:
|
|
716
720
|
self._parse_positive_float_value(key, value)
|
|
717
721
|
return
|
|
718
722
|
if key == "SMART_SEARCH_PROVIDER_COOLDOWN_SECONDS":
|
|
@@ -970,6 +974,18 @@ class Config:
|
|
|
970
974
|
def tinyfish_timeout(self) -> float:
|
|
971
975
|
return self._parse_positive_float_value("TINYFISH_TIMEOUT_SECONDS", self._get_config_value("TINYFISH_TIMEOUT_SECONDS", "150") or "150")
|
|
972
976
|
|
|
977
|
+
@property
|
|
978
|
+
def deepseek_api_key(self) -> str | None:
|
|
979
|
+
return self._get_config_value("DEEPSEEK_API_KEY")
|
|
980
|
+
|
|
981
|
+
@property
|
|
982
|
+
def deepseek_api_url(self) -> str:
|
|
983
|
+
return self._get_config_value("DEEPSEEK_API_URL", "https://api.deepseek.com") or "https://api.deepseek.com"
|
|
984
|
+
|
|
985
|
+
@property
|
|
986
|
+
def deepseek_timeout(self) -> float:
|
|
987
|
+
return self._parse_positive_float_value("DEEPSEEK_TIMEOUT_SECONDS", self._get_config_value("DEEPSEEK_TIMEOUT_SECONDS", "60") or "60")
|
|
988
|
+
|
|
973
989
|
@property
|
|
974
990
|
def anysearch_api_url(self) -> str:
|
|
975
991
|
return self._get_config_value("ANYSEARCH_API_URL", "https://api.anysearch.com/mcp") or "https://api.anysearch.com/mcp"
|
|
@@ -1253,6 +1269,9 @@ class Config:
|
|
|
1253
1269
|
"TINYFISH_FETCH_API_URL": self.tinyfish_fetch_api_url,
|
|
1254
1270
|
"TINYFISH_API_KEY": self._mask_api_key(self.tinyfish_api_key) if self.tinyfish_api_key else tr('未配置'),
|
|
1255
1271
|
"TINYFISH_TIMEOUT_SECONDS": self.tinyfish_timeout,
|
|
1272
|
+
"DEEPSEEK_API_URL": self.deepseek_api_url,
|
|
1273
|
+
"DEEPSEEK_API_KEY": self._mask_api_key(self.deepseek_api_key) if self.deepseek_api_key else tr('未配置'),
|
|
1274
|
+
"DEEPSEEK_TIMEOUT_SECONDS": self.deepseek_timeout,
|
|
1256
1275
|
"ANYSEARCH_API_URL": self.anysearch_api_url,
|
|
1257
1276
|
"ANYSEARCH_API_KEY": self._mask_api_key(self.anysearch_api_key) if self.anysearch_api_key else tr('未配置'),
|
|
1258
1277
|
"ANYSEARCH_TIMEOUT_SECONDS": self.anysearch_timeout,
|
|
@@ -89,9 +89,13 @@ def fallback_channels(svc: Any, query: str, candidates: list[dict], limit: int,
|
|
|
89
89
|
if reading:
|
|
90
90
|
order = svc._research_fetch_order(query, reading[0]["url"])
|
|
91
91
|
return sorted(reading, key=lambda item: order.index(item["provider"]) if item["provider"] in order else len(order))[:limit]
|
|
92
|
+
allowed = []
|
|
92
93
|
if rule.docs_intent:
|
|
93
94
|
allowed = [item for item in candidates if item["operation"] == "search" and item["capability"] == "docs_search"]
|
|
94
|
-
|
|
95
|
+
# With no docs channel configured, a docs question still gets general web
|
|
96
|
+
# discovery instead of no channel at all. Failure fallback passes only
|
|
97
|
+
# same-family candidates, so this cannot cross a capability boundary.
|
|
98
|
+
if not allowed:
|
|
95
99
|
allowed = [item for item in candidates if item["operation"] == "search" and
|
|
96
100
|
(item["capability"] in {"main_search", "web_search"} or item["provider"] == "exa" or
|
|
97
101
|
item["capability"] in rule.required_capabilities)]
|
|
@@ -162,6 +166,8 @@ class ChannelExecutor:
|
|
|
162
166
|
content = _check_data(await svc.zhipu_mcp_reader(url)).get("content")
|
|
163
167
|
elif provider == "tinyfish":
|
|
164
168
|
content = _check_data(await svc.call_tinyfish_fetch(url)).get("content")
|
|
169
|
+
elif provider == "deepseek":
|
|
170
|
+
content = await svc.call_deepseek_fetch(url)
|
|
165
171
|
else:
|
|
166
172
|
raise ProviderCallError("parameter_error", source_message('Unsupported fetch channel'))
|
|
167
173
|
results = [{"url": url, "content": content}]
|
|
@@ -177,6 +183,8 @@ class ChannelExecutor:
|
|
|
177
183
|
results = await svc.call_firecrawl_search(query, self.count) or []
|
|
178
184
|
elif provider == "tinyfish":
|
|
179
185
|
results = await svc.call_tinyfish_search(query, self.count) or []
|
|
186
|
+
elif provider == "deepseek":
|
|
187
|
+
results = await svc.call_deepseek_search(query, self.count) or []
|
|
180
188
|
elif provider == "anysearch":
|
|
181
189
|
results = _check_data(await svc.anysearch_search(query, max_results=self.count)).get("results", [])
|
|
182
190
|
elif provider == "context7":
|
|
@@ -113,6 +113,20 @@ PROVIDER_DESCRIPTIONS: dict[str, dict[str, dict[str, str]]] = {
|
|
|
113
113
|
"guidance_zh": "擅长读取已找到的页面;不适合发现新来源或执行浏览器自动化。",
|
|
114
114
|
},
|
|
115
115
|
},
|
|
116
|
+
"deepseek": {
|
|
117
|
+
"search": {
|
|
118
|
+
"en": "Searches the web with DeepSeek's own search engine and returns linked results with long query-focused summaries.",
|
|
119
|
+
"zh": "通过 DeepSeek 自研搜索引擎检索网页,返回链接和与问题相关的长摘要。",
|
|
120
|
+
"guidance_en": "Best for broad web and Chinese-language source discovery; summaries are not full page text, so read key pages before citing them. Queries longer than 128 characters are shortened.",
|
|
121
|
+
"guidance_zh": "擅长广泛发现网页和中文来源;摘要不是网页全文,引用关键结论前应读取原页。超过 128 个字符的查询会被截短。",
|
|
122
|
+
},
|
|
123
|
+
"fetch": {
|
|
124
|
+
"en": "Fetches a known URL as markdown through DeepSeek Fetch.",
|
|
125
|
+
"zh": "通过 DeepSeek Fetch 将已知链接读取为 Markdown。",
|
|
126
|
+
"guidance_en": "Best for reading public web pages already found; unsuitable for source discovery, PDF files, login-only pages or private network addresses.",
|
|
127
|
+
"guidance_zh": "擅长读取已找到的公开网页;不适合发现新来源,也读不了 PDF、需登录的页面或内网地址。",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
116
130
|
"anysearch": {
|
|
117
131
|
"search": {
|
|
118
132
|
"en": "Searches AnySearch's available domains with a plain query.",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from .base import BaseSearchProvider, SearchResult
|
|
2
2
|
from .anysearch import AnySearchProvider
|
|
3
3
|
from .context7 import Context7Provider
|
|
4
|
+
from .deepseek import DeepSeekToolsProvider
|
|
4
5
|
from .openai_compatible import OpenAICompatibleSearchProvider
|
|
5
6
|
from .xai_responses import XAIResponsesSearchProvider
|
|
6
7
|
from .exa import ExaSearchProvider
|
|
@@ -15,6 +16,7 @@ __all__ = [
|
|
|
15
16
|
"SearchResult",
|
|
16
17
|
"AnySearchProvider",
|
|
17
18
|
"Context7Provider",
|
|
19
|
+
"DeepSeekToolsProvider",
|
|
18
20
|
"OpenAICompatibleSearchProvider",
|
|
19
21
|
"XAIResponsesSearchProvider",
|
|
20
22
|
"ExaSearchProvider",
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""DeepSeek web search and page fetch.
|
|
2
|
+
|
|
3
|
+
Both tools live under the DeepSeek API host (`/v1/tools/search` and
|
|
4
|
+
`/v1/tools/fetch`) and use the same Bearer key as the DeepSeek chat API.
|
|
5
|
+
"""
|
|
6
|
+
from ..i18n import source_message
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
from ..provider_errors import ProviderCallError, provider_call_error
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
MAX_QUERY_CHARS = 128
|
|
16
|
+
MAX_COUNT = 50
|
|
17
|
+
# Fetch answers 403 when its safety policy refuses a URL and 404 when the page
|
|
18
|
+
# has no usable Markdown. Both describe that one page, not the key or the
|
|
19
|
+
# service, so they read as "no content" and leave provider health alone.
|
|
20
|
+
PAGE_UNAVAILABLE_STATUSES = frozenset({403, 404})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def tools_endpoint(api_url: str, tool: str) -> str:
|
|
24
|
+
base = api_url.rstrip("/")
|
|
25
|
+
# The chat API documents both https://api.deepseek.com and .../v1 as its base.
|
|
26
|
+
if base.endswith("/v1"):
|
|
27
|
+
base = base[: -len("/v1")]
|
|
28
|
+
return f"{base}/v1/tools/{tool}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def search_query(query: str) -> str:
|
|
32
|
+
"""Fit a query into the 128-character limit, preferring a word boundary."""
|
|
33
|
+
text = " ".join(query.split())
|
|
34
|
+
if len(text) <= MAX_QUERY_CHARS:
|
|
35
|
+
return text
|
|
36
|
+
cut = text[:MAX_QUERY_CHARS]
|
|
37
|
+
space = cut.rfind(" ")
|
|
38
|
+
return cut[:space] if space >= MAX_QUERY_CHARS // 2 else cut
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _normalize_search_result(item: dict[str, Any]) -> dict[str, Any]:
|
|
42
|
+
out: dict[str, Any] = {
|
|
43
|
+
"title": item.get("title") or "",
|
|
44
|
+
"url": item.get("url") or "",
|
|
45
|
+
# summary is the longer, query-focused excerpt DeepSeek writes for models.
|
|
46
|
+
"description": item.get("summary") or item.get("snippet") or "",
|
|
47
|
+
"provider": "deepseek",
|
|
48
|
+
}
|
|
49
|
+
site_name = item.get("site_name") or ""
|
|
50
|
+
if site_name:
|
|
51
|
+
out["source"] = site_name
|
|
52
|
+
published = item.get("date") or ""
|
|
53
|
+
if published:
|
|
54
|
+
out["published_date"] = published
|
|
55
|
+
return out
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DeepSeekToolsProvider:
|
|
59
|
+
def __init__(self, api_url: str, api_key: str, timeout: float = 60.0):
|
|
60
|
+
self.api_url = api_url
|
|
61
|
+
self.api_key = api_key or ""
|
|
62
|
+
self.timeout = timeout
|
|
63
|
+
|
|
64
|
+
def get_provider_name(self) -> str:
|
|
65
|
+
return "deepseek"
|
|
66
|
+
|
|
67
|
+
async def _post(self, tool: str, body: dict[str, Any]) -> httpx.Response:
|
|
68
|
+
headers = {
|
|
69
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
"Accept": "application/json",
|
|
72
|
+
}
|
|
73
|
+
timeout = httpx.Timeout(connect=6.0, read=self.timeout, write=10.0, pool=None)
|
|
74
|
+
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
|
|
75
|
+
return await client.post(tools_endpoint(self.api_url, tool), headers=headers, json=body)
|
|
76
|
+
|
|
77
|
+
async def search(self, query: str, max_results: int = 5) -> list[dict[str, Any]]:
|
|
78
|
+
"""Return normalized results; an empty list means DeepSeek found nothing."""
|
|
79
|
+
body = {"query": search_query(query), "count": max(1, min(int(max_results), MAX_COUNT))}
|
|
80
|
+
try:
|
|
81
|
+
response = await self._post("search", body)
|
|
82
|
+
response.raise_for_status()
|
|
83
|
+
payload = response.json()
|
|
84
|
+
if not isinstance(payload, dict):
|
|
85
|
+
raise ProviderCallError("parse_error", source_message('DeepSeek search response is not a JSON object'))
|
|
86
|
+
results = payload.get("results")
|
|
87
|
+
if not isinstance(results, list):
|
|
88
|
+
raise ProviderCallError("parse_error", source_message('DeepSeek search response is missing results'))
|
|
89
|
+
except Exception as exc:
|
|
90
|
+
raise provider_call_error(exc, additional_secrets=(self.api_key,)) from exc
|
|
91
|
+
return [_normalize_search_result(item) for item in results[:max_results] if isinstance(item, dict)]
|
|
92
|
+
|
|
93
|
+
async def fetch(self, url: str) -> dict[str, str] | None:
|
|
94
|
+
"""Return {title, content}, or None when DeepSeek has no usable text for this page."""
|
|
95
|
+
try:
|
|
96
|
+
response = await self._post("fetch", {"url": url})
|
|
97
|
+
if response.status_code in PAGE_UNAVAILABLE_STATUSES:
|
|
98
|
+
return None
|
|
99
|
+
response.raise_for_status()
|
|
100
|
+
payload = response.json()
|
|
101
|
+
if not isinstance(payload, dict):
|
|
102
|
+
raise ProviderCallError("parse_error", source_message('DeepSeek fetch response is not a JSON object'))
|
|
103
|
+
content = payload.get("content")
|
|
104
|
+
if not isinstance(content, str):
|
|
105
|
+
raise ProviderCallError("parse_error", source_message('DeepSeek fetch content is not text'))
|
|
106
|
+
except Exception as exc:
|
|
107
|
+
raise provider_call_error(exc, additional_secrets=(self.api_key,)) from exc
|
|
108
|
+
if not content.strip():
|
|
109
|
+
return None
|
|
110
|
+
return {"title": str(payload.get("title") or ""), "content": content}
|
|
@@ -30,6 +30,7 @@ from .intent_router import (
|
|
|
30
30
|
from .logger import log_info
|
|
31
31
|
from .providers.anysearch import AnySearchProvider
|
|
32
32
|
from .providers.context7 import Context7Provider
|
|
33
|
+
from .providers.deepseek import DeepSeekToolsProvider
|
|
33
34
|
from .providers.exa import ExaSearchProvider
|
|
34
35
|
from .providers.jina import JinaReaderProvider
|
|
35
36
|
from .providers.openai_compatible import (
|
|
@@ -176,9 +177,9 @@ RESEARCH_JS_HEAVY_KEYWORDS = {
|
|
|
176
177
|
RESEARCH_PDF_KEYWORDS = {"pdf", "arxiv", "论文", "paper", ".pdf"}
|
|
177
178
|
RESEARCH_PROFILE_ORDER = {
|
|
178
179
|
"main_search": ["xai-responses", "openai-compatible"],
|
|
179
|
-
"web_search": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
|
|
180
|
+
"web_search": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
|
|
180
181
|
"docs_search": ["context7", "exa"],
|
|
181
|
-
"web_fetch": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
|
|
182
|
+
"web_fetch": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
|
|
182
183
|
"vertical_search": ["anysearch"],
|
|
183
184
|
"site_map": ["tavily"],
|
|
184
185
|
"synthesis": ["main-search"],
|
|
@@ -264,6 +265,14 @@ PROVIDER_PROFILES: dict[str, dict[str, Any]] = {
|
|
|
264
265
|
"quality_filters": ["non-empty normalized result", "non-empty extracted content", "challenge page rejection"],
|
|
265
266
|
"route_reasons": ["broad source discovery", "known URL extraction"],
|
|
266
267
|
},
|
|
268
|
+
"deepseek": {
|
|
269
|
+
"capability": "web_search",
|
|
270
|
+
"capabilities": ["web_search", "web_fetch"],
|
|
271
|
+
"fallback_group": "web_search/web_fetch",
|
|
272
|
+
"minimum_profile_role": "",
|
|
273
|
+
"quality_filters": ["non-empty normalized result", "non-empty extracted content"],
|
|
274
|
+
"route_reasons": ["broad source discovery", "Chinese/current discovery", "known URL extraction"],
|
|
275
|
+
},
|
|
267
276
|
"anysearch": {
|
|
268
277
|
"capability": "vertical_search",
|
|
269
278
|
"fallback_group": "vertical_search",
|
|
@@ -524,6 +533,7 @@ PROVIDER_CREDENTIAL_SOURCES: dict[str, Any] = {
|
|
|
524
533
|
"tavily": lambda: (config.tavily_api_key, config.tavily_api_url),
|
|
525
534
|
"firecrawl": lambda: (config.firecrawl_api_key, config.firecrawl_api_url),
|
|
526
535
|
"tinyfish": lambda: (config.tinyfish_api_key, config.tinyfish_search_api_url, config.tinyfish_fetch_api_url),
|
|
536
|
+
"deepseek": lambda: (config.deepseek_api_key, config.deepseek_api_url),
|
|
527
537
|
"exa": lambda: (config.exa_api_key, config.exa_base_url),
|
|
528
538
|
"context7": lambda: (config.context7_api_key, config.context7_base_url),
|
|
529
539
|
"jina": lambda: (config.jina_api_key, config.jina_reader_api_url),
|
|
@@ -981,6 +991,8 @@ def _provider_configured(provider: str) -> bool:
|
|
|
981
991
|
return bool(config.firecrawl_api_key)
|
|
982
992
|
if provider == "tinyfish":
|
|
983
993
|
return bool(config.tinyfish_api_key)
|
|
994
|
+
if provider == "deepseek":
|
|
995
|
+
return bool(config.deepseek_api_key)
|
|
984
996
|
if provider == "anysearch":
|
|
985
997
|
return bool(config.anysearch_api_key)
|
|
986
998
|
if provider == "sciverse":
|
|
@@ -1507,10 +1519,11 @@ def get_capability_status() -> dict[str, Any]:
|
|
|
1507
1519
|
("tavily", _provider_configured("tavily")),
|
|
1508
1520
|
("firecrawl", _provider_configured("firecrawl")),
|
|
1509
1521
|
("tinyfish", _provider_configured("tinyfish")),
|
|
1522
|
+
("deepseek", _provider_configured("deepseek")),
|
|
1510
1523
|
]
|
|
1511
1524
|
if enabled
|
|
1512
1525
|
],
|
|
1513
|
-
"fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
|
|
1526
|
+
"fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
|
|
1514
1527
|
},
|
|
1515
1528
|
"docs_search": {
|
|
1516
1529
|
"configured": [
|
|
@@ -1532,10 +1545,11 @@ def get_capability_status() -> dict[str, Any]:
|
|
|
1532
1545
|
("zhipu-mcp-reader", _provider_configured("zhipu-mcp-reader")),
|
|
1533
1546
|
("firecrawl", _provider_configured("firecrawl")),
|
|
1534
1547
|
("tinyfish", _provider_configured("tinyfish")),
|
|
1548
|
+
("deepseek", _provider_configured("deepseek")),
|
|
1535
1549
|
]
|
|
1536
1550
|
if enabled
|
|
1537
1551
|
],
|
|
1538
|
-
"fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
|
|
1552
|
+
"fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
|
|
1539
1553
|
},
|
|
1540
1554
|
"vertical_search": {
|
|
1541
1555
|
"configured": [
|
|
@@ -1800,6 +1814,8 @@ async def _run_web_fetch_fallback(
|
|
|
1800
1814
|
providers.append("firecrawl")
|
|
1801
1815
|
if _provider_configured("tinyfish"):
|
|
1802
1816
|
providers.append("tinyfish")
|
|
1817
|
+
if _provider_configured("deepseek"):
|
|
1818
|
+
providers.append("deepseek")
|
|
1803
1819
|
if preferred_order:
|
|
1804
1820
|
allowed = {provider for provider in providers}
|
|
1805
1821
|
ordered = [provider for provider in preferred_order if provider in allowed]
|
|
@@ -1849,6 +1865,8 @@ async def _run_web_fetch_fallback(
|
|
|
1849
1865
|
health_extra = _record_provider_result(provider, status, data.get("error_type", ""), data.get("error", ""))
|
|
1850
1866
|
attempts.append(_attempt("web_fetch", provider, status, start, error_type=data.get("error_type", ""), error=data.get("error", ""), extra=health_extra))
|
|
1851
1867
|
continue
|
|
1868
|
+
elif provider == "deepseek":
|
|
1869
|
+
content = await bounded(call_deepseek_fetch(url))
|
|
1852
1870
|
else:
|
|
1853
1871
|
content = await bounded(call_firecrawl_scrape(url))
|
|
1854
1872
|
if content and content.strip():
|
|
@@ -2086,6 +2104,25 @@ async def call_tinyfish_fetch(url: str) -> dict[str, Any]:
|
|
|
2086
2104
|
return await _decode_provider_json(raw, provider="tinyfish")
|
|
2087
2105
|
|
|
2088
2106
|
|
|
2107
|
+
def _deepseek_provider() -> DeepSeekToolsProvider:
|
|
2108
|
+
return DeepSeekToolsProvider(config.deepseek_api_url, config.deepseek_api_key or "", config.deepseek_timeout)
|
|
2109
|
+
|
|
2110
|
+
|
|
2111
|
+
async def call_deepseek_search(query: str, max_results: int = 6) -> list[dict] | None:
|
|
2112
|
+
"""Return normalized DeepSeek candidates; a provider failure raises for fallback."""
|
|
2113
|
+
if not config.provider_enabled("deepseek") or not config.deepseek_api_key:
|
|
2114
|
+
return None
|
|
2115
|
+
return await _deepseek_provider().search(query, max_results=max_results)
|
|
2116
|
+
|
|
2117
|
+
|
|
2118
|
+
async def call_deepseek_fetch(url: str) -> str | None:
|
|
2119
|
+
"""Return page Markdown, or None when DeepSeek has no usable text for the URL."""
|
|
2120
|
+
if not config.provider_enabled("deepseek") or not config.deepseek_api_key:
|
|
2121
|
+
return None
|
|
2122
|
+
page = await _deepseek_provider().fetch(url)
|
|
2123
|
+
return page["content"] if page else None
|
|
2124
|
+
|
|
2125
|
+
|
|
2089
2126
|
async def call_jina_reader(url: str) -> dict[str, Any]:
|
|
2090
2127
|
if disabled := _provider_disabled_result("jina"):
|
|
2091
2128
|
return disabled
|
|
@@ -2221,7 +2258,7 @@ async def fetch(url: str, timeout_seconds: float | None = None) -> dict[str, Any
|
|
|
2221
2258
|
|
|
2222
2259
|
configured_fetch_providers = [
|
|
2223
2260
|
provider
|
|
2224
|
-
for provider in ("tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish")
|
|
2261
|
+
for provider in ("tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek")
|
|
2225
2262
|
if _provider_configured(provider)
|
|
2226
2263
|
]
|
|
2227
2264
|
if not configured_fetch_providers:
|
|
@@ -3390,6 +3427,33 @@ async def _test_tinyfish_connection() -> dict[str, Any]:
|
|
|
3390
3427
|
return {"status": "ok", "message": source_message('TinyFish 搜索与抓取 API 均可用'), "response_time_ms": _elapsed_ms(start)}
|
|
3391
3428
|
|
|
3392
3429
|
|
|
3430
|
+
async def _test_deepseek_connection() -> dict[str, Any]:
|
|
3431
|
+
"""Probe search and fetch once each; DeepSeek bills every successful request."""
|
|
3432
|
+
if disabled := _provider_disabled_result("deepseek"):
|
|
3433
|
+
return disabled
|
|
3434
|
+
if not config.deepseek_api_key:
|
|
3435
|
+
return {"status": "not_configured", "message": source_message('DEEPSEEK_API_KEY 未设置,DeepSeek 搜索与抓取不可用')}
|
|
3436
|
+
start = time.time()
|
|
3437
|
+
provider = _deepseek_provider()
|
|
3438
|
+
search_result, fetch_result = await asyncio.gather(
|
|
3439
|
+
provider.search("DeepSeek", max_results=1),
|
|
3440
|
+
provider.fetch("https://example.com"),
|
|
3441
|
+
return_exceptions=True,
|
|
3442
|
+
)
|
|
3443
|
+
# Both tools share one key and one health fingerprint, so either failing fails the probe.
|
|
3444
|
+
failed = search_result if isinstance(search_result, Exception) else fetch_result
|
|
3445
|
+
if isinstance(failed, Exception):
|
|
3446
|
+
error_type, error = classify_provider_exception(failed, additional_secrets=(config.deepseek_api_key,))
|
|
3447
|
+
status = error_type if error_type in APPROVED_PROVIDER_ERROR_TYPES else "warning"
|
|
3448
|
+
message = (source_message('DeepSeek search: {0}', error) if failed is search_result
|
|
3449
|
+
else source_message('DeepSeek fetch: {0}', error))
|
|
3450
|
+
return {"status": status, "message": message, "response_time_ms": _elapsed_ms(start)}
|
|
3451
|
+
if fetch_result is None:
|
|
3452
|
+
return {"status": "warning", "message": source_message('DeepSeek fetch returned no content for {0}', "https://example.com"),
|
|
3453
|
+
"response_time_ms": _elapsed_ms(start)}
|
|
3454
|
+
return {"status": "ok", "message": source_message('DeepSeek 搜索与抓取 API 均可用'), "response_time_ms": _elapsed_ms(start)}
|
|
3455
|
+
|
|
3456
|
+
|
|
3393
3457
|
DOCTOR_PROBE_PROVIDERS = {
|
|
3394
3458
|
"exa_connection_test": "exa",
|
|
3395
3459
|
"tavily_connection_test": "tavily",
|
|
@@ -3398,6 +3462,7 @@ DOCTOR_PROBE_PROVIDERS = {
|
|
|
3398
3462
|
"zhipu_mcp_connection_test": "zhipu-mcp",
|
|
3399
3463
|
"context7_connection_test": "context7",
|
|
3400
3464
|
"tinyfish_connection_test": "tinyfish",
|
|
3465
|
+
"deepseek_connection_test": "deepseek",
|
|
3401
3466
|
}
|
|
3402
3467
|
DOCTOR_PROBE_NEUTRAL_STATUSES = {"not_configured", "configured", "skipped", "disabled"}
|
|
3403
3468
|
|
|
@@ -3441,6 +3506,7 @@ PROBE_KIND: dict[str, str] = {
|
|
|
3441
3506
|
"zhipu-mcp": "live",
|
|
3442
3507
|
"context7": "live",
|
|
3443
3508
|
"tinyfish": "live",
|
|
3509
|
+
"deepseek": "live",
|
|
3444
3510
|
"anysearch": "live",
|
|
3445
3511
|
"sciverse": "live",
|
|
3446
3512
|
"zhipu-mcp-reader": "shared:zhipu-mcp",
|
|
@@ -3454,6 +3520,7 @@ _LIVE_PROBES: dict[str, Any] = {
|
|
|
3454
3520
|
"zhipu-mcp": _test_zhipu_mcp_connection,
|
|
3455
3521
|
"context7": _test_context7_connection,
|
|
3456
3522
|
"tinyfish": _test_tinyfish_connection,
|
|
3523
|
+
"deepseek": _test_deepseek_connection,
|
|
3457
3524
|
"anysearch": _test_anysearch_connection,
|
|
3458
3525
|
"sciverse": _test_sciverse_connection,
|
|
3459
3526
|
"firecrawl": _test_firecrawl_presence,
|
|
@@ -3686,6 +3753,13 @@ async def doctor() -> dict[str, Any]:
|
|
|
3686
3753
|
except Exception as e:
|
|
3687
3754
|
info["tinyfish_connection_test"] = {"status": "error", "message": sanitize_provider_error_message(e)}
|
|
3688
3755
|
|
|
3756
|
+
try:
|
|
3757
|
+
info["deepseek_connection_test"] = await _test_deepseek_connection()
|
|
3758
|
+
except httpx.TimeoutException:
|
|
3759
|
+
info["deepseek_connection_test"] = {"status": "timeout", "message": source_message('DeepSeek API 请求超时')}
|
|
3760
|
+
except Exception as e:
|
|
3761
|
+
info["deepseek_connection_test"] = {"status": "error", "message": sanitize_provider_error_message(e)}
|
|
3762
|
+
|
|
3689
3763
|
_record_doctor_probes(info)
|
|
3690
3764
|
info["provider_health"] = provider_health_status()
|
|
3691
3765
|
|
|
@@ -3755,7 +3829,7 @@ async def doctor() -> dict[str, Any]:
|
|
|
3755
3829
|
else:
|
|
3756
3830
|
info["jev_connection_test"] = {"status": "skipped", "message": source_message('Using local routing; no Jev probe was sent.')}
|
|
3757
3831
|
retrieval_tests = [*main_connection_tests.values(), *(info.get(f"{name}_connection_test", {}) for name in (
|
|
3758
|
-
"exa", "tavily", "jina", "firecrawl", "zhipu", "zhipu_mcp", "context7", "tinyfish",
|
|
3832
|
+
"exa", "tavily", "jina", "firecrawl", "zhipu", "zhipu_mcp", "context7", "tinyfish", "deepseek",
|
|
3759
3833
|
))]
|
|
3760
3834
|
retrieval_ready = any(test.get("status") in {"ok", "configured"} for test in retrieval_tests)
|
|
3761
3835
|
info["retrieval_connection_verified"] = any(test.get("status") == "ok" for test in retrieval_tests)
|
|
@@ -3941,9 +4015,9 @@ async def _smoke_mock(start: float) -> dict[str, Any]:
|
|
|
3941
4015
|
"fallback_chain": MAIN_SEARCH_FALLBACK_CHAIN,
|
|
3942
4016
|
"ok": True,
|
|
3943
4017
|
},
|
|
3944
|
-
"web_search": {"configured": ["zhipu"], "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"], "ok": True},
|
|
4018
|
+
"web_search": {"configured": ["zhipu"], "fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"], "ok": True},
|
|
3945
4019
|
"docs_search": {"configured": ["context7"], "fallback_chain": ["context7", "exa"], "ok": True},
|
|
3946
|
-
"web_fetch": {"configured": ["tavily"], "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"], "ok": True},
|
|
4020
|
+
"web_fetch": {"configured": ["tavily"], "fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"], "ok": True},
|
|
3947
4021
|
"vertical_search": {
|
|
3948
4022
|
"configured": [],
|
|
3949
4023
|
"fallback_chain": ["anysearch"],
|
|
@@ -4172,13 +4246,13 @@ async def _smoke_mock(start: float) -> dict[str, Any]:
|
|
|
4172
4246
|
**minimum_status,
|
|
4173
4247
|
"web_search": {
|
|
4174
4248
|
"configured": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
|
|
4175
|
-
"fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish"],
|
|
4249
|
+
"fallback_chain": ["zhipu", "zhipu-mcp", "tavily", "firecrawl", "tinyfish", "deepseek"],
|
|
4176
4250
|
"ok": True,
|
|
4177
4251
|
},
|
|
4178
4252
|
"docs_search": {"configured": ["context7", "exa"], "fallback_chain": ["context7", "exa"], "ok": True},
|
|
4179
4253
|
"web_fetch": {
|
|
4180
4254
|
"configured": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
|
|
4181
|
-
"fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish"],
|
|
4255
|
+
"fallback_chain": ["tavily", "jina", "zhipu-mcp-reader", "firecrawl", "tinyfish", "deepseek"],
|
|
4182
4256
|
"ok": True,
|
|
4183
4257
|
},
|
|
4184
4258
|
"vertical_search": {
|
|
@@ -382,6 +382,24 @@ CONFIG_FIELDS: tuple[ConfigField, ...] = (
|
|
|
382
382
|
label_zh="TinyFish 超时(秒)", label_en="TinyFish timeout (seconds)",
|
|
383
383
|
default="150", provider="tinyfish", capabilities=("web_search", "web_fetch")),
|
|
384
384
|
|
|
385
|
+
# ---- providers: DeepSeek ----------------------------------------------
|
|
386
|
+
_f(key="DEEPSEEK_API_KEY", section="providers", tier="enhancement", kind="secret",
|
|
387
|
+
label_zh="DeepSeek API Key", label_en="DeepSeek API key",
|
|
388
|
+
help_zh="与 DeepSeek 模型 API 共用 Key,一个 Key 支持网页搜索和抓取;按成功请求次数计费。测试会各发起一次真实请求。",
|
|
389
|
+
help_en="The same key as the DeepSeek model API; one key enables web search and page fetching. Billed per successful request. Testing makes one real request for each capability.",
|
|
390
|
+
provider="deepseek", capabilities=("web_search", "web_fetch"),
|
|
391
|
+
key_url="https://platform.deepseek.com/api_keys", docs_url="https://api-docs.deepseek.com/guides/search"),
|
|
392
|
+
_f(key="DEEPSEEK_API_URL", section="providers", tier="advanced", kind="url",
|
|
393
|
+
help_zh="DeepSeek API 基础地址,程序会自动拼接 /v1/tools/search 和 /v1/tools/fetch;使用官方服务时保留默认值。",
|
|
394
|
+
help_en="DeepSeek API base URL. /v1/tools/search and /v1/tools/fetch are appended automatically; keep the default for the official service.",
|
|
395
|
+
label_zh="DeepSeek 地址", label_en="DeepSeek API URL",
|
|
396
|
+
default="https://api.deepseek.com", provider="deepseek", capabilities=("web_search", "web_fetch")),
|
|
397
|
+
_f(key="DEEPSEEK_TIMEOUT_SECONDS", section="providers", tier="advanced", kind="float",
|
|
398
|
+
help_zh="等待此服务单次响应的最长秒数;超时会返回错误或按当前策略兜底,通常保留默认值。",
|
|
399
|
+
help_en="Maximum seconds to wait for one response from this service. A timeout returns an error or uses the configured fallback; normally leave the default.",
|
|
400
|
+
label_zh="DeepSeek 超时(秒)", label_en="DeepSeek timeout (seconds)",
|
|
401
|
+
default="60", provider="deepseek", capabilities=("web_search", "web_fetch")),
|
|
402
|
+
|
|
385
403
|
# ---- providers: AnySearch ---------------------------------------------
|
|
386
404
|
_f(key="ANYSEARCH_API_KEY", section="providers", tier="enhancement", kind="secret",
|
|
387
405
|
label_zh="AnySearch API Key", label_en="AnySearch API key",
|
|
@@ -25,7 +25,9 @@ import hmac
|
|
|
25
25
|
import json
|
|
26
26
|
import os
|
|
27
27
|
import secrets
|
|
28
|
+
import shutil
|
|
28
29
|
import socket
|
|
30
|
+
import subprocess
|
|
29
31
|
import sys
|
|
30
32
|
import threading
|
|
31
33
|
import time
|
|
@@ -438,10 +440,35 @@ def _should_open_browser(options: UIServerConfig) -> bool:
|
|
|
438
440
|
if sys.platform.startswith("linux"):
|
|
439
441
|
if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"):
|
|
440
442
|
return True
|
|
441
|
-
return
|
|
443
|
+
return _is_wsl()
|
|
442
444
|
return True
|
|
443
445
|
|
|
444
446
|
|
|
447
|
+
def _is_wsl() -> bool:
|
|
448
|
+
return sys.platform.startswith("linux") and bool(os.environ.get("WSL_DISTRO_NAME") or os.environ.get("WSL_INTEROP"))
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _open_browser(url: str) -> bool:
|
|
452
|
+
"""Open the page, handing it to the Windows default browser inside WSL.
|
|
453
|
+
|
|
454
|
+
WSL rarely has a Linux browser, so ``webbrowser`` finds nothing or falls back
|
|
455
|
+
to a terminal browser. The server listens on 127.0.0.1, which WSL forwards to
|
|
456
|
+
Windows. An explicit $BROWSER still wins.
|
|
457
|
+
"""
|
|
458
|
+
if not _is_wsl() or os.environ.get("BROWSER"):
|
|
459
|
+
return webbrowser.open(url)
|
|
460
|
+
for opener in (shutil.which("wslview"), shutil.which("explorer.exe"), "/mnt/c/Windows/explorer.exe"):
|
|
461
|
+
if not opener or not os.path.isfile(opener):
|
|
462
|
+
continue
|
|
463
|
+
try:
|
|
464
|
+
# explorer.exe exits non-zero even when it opened the page, so do not wait on it.
|
|
465
|
+
subprocess.Popen([opener, url], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
466
|
+
except OSError:
|
|
467
|
+
continue
|
|
468
|
+
return True
|
|
469
|
+
return False
|
|
470
|
+
|
|
471
|
+
|
|
445
472
|
def _startup_notes(runtime: UIRuntime, opened: bool, lang: str) -> list[str]:
|
|
446
473
|
zh = lang == "zh"
|
|
447
474
|
notes = [
|
|
@@ -454,7 +481,21 @@ def _startup_notes(runtime: UIRuntime, opened: bool, lang: str) -> list[str]:
|
|
|
454
481
|
else "The token in that link is regenerated on every run and only works on this machine."
|
|
455
482
|
),
|
|
456
483
|
]
|
|
457
|
-
if
|
|
484
|
+
if _is_wsl():
|
|
485
|
+
if not opened:
|
|
486
|
+
notes.append(
|
|
487
|
+
"没有自动打开浏览器,在 Windows 的浏览器里打开上面的地址即可。"
|
|
488
|
+
if zh
|
|
489
|
+
else "No browser was opened; open the URL above in a Windows browser."
|
|
490
|
+
)
|
|
491
|
+
# NAT mode forwards localhost; mirrored mode's Hyper-V firewall blocks
|
|
492
|
+
# Windows-to-WSL connections until inbound traffic is allowed.
|
|
493
|
+
notes.append(
|
|
494
|
+
"WSL 使用 networkingMode=mirrored 时,Hyper-V 防火墙默认拦截 Windows 访问 WSL 端口;页面打不开时用 --port 固定端口,并按排障手册放行该端口。"
|
|
495
|
+
if zh
|
|
496
|
+
else "With WSL networkingMode=mirrored, the Hyper-V firewall blocks Windows from reaching WSL ports by default. If the page does not load, fix the port with --port and allow it as the troubleshooting guide describes."
|
|
497
|
+
)
|
|
498
|
+
elif not opened:
|
|
458
499
|
notes.append(
|
|
459
500
|
"没有自动打开浏览器,复制上面的地址即可。" if zh else "No browser was opened; copy the URL above."
|
|
460
501
|
)
|
|
@@ -480,7 +521,7 @@ def serve(options: UIServerConfig, *, announce: Callable[[str], None] = lambda l
|
|
|
480
521
|
opened = False
|
|
481
522
|
if _should_open_browser(options):
|
|
482
523
|
try:
|
|
483
|
-
opened =
|
|
524
|
+
opened = _open_browser(runtime.url)
|
|
484
525
|
except Exception:
|
|
485
526
|
opened = False
|
|
486
527
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: smart-search
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.30
|
|
4
4
|
Summary: CLI-first multi-source web research with xAI/OpenAI-compatible search, Exa, Context7, Zhipu, Tavily, Firecrawl, and Deep Research planning.
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
../../../bin/smart-search,sha256=q9upy4clf9uCDMAJIt-pBkuMwhZ7KupZeJLPO-SMjG4,206
|
|
2
|
-
smart_search-0.1.
|
|
3
|
-
smart_search-0.1.
|
|
4
|
-
smart_search-0.1.
|
|
5
|
-
smart_search-0.1.
|
|
6
|
-
smart_search-0.1.
|
|
7
|
-
smart_search-0.1.
|
|
8
|
-
smart_search-0.1.
|
|
9
|
-
smart_search-0.1.
|
|
10
|
-
smart_search-0.1.
|
|
2
|
+
smart_search-0.1.30.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
|
3
|
+
smart_search-0.1.30.dist-info/METADATA,sha256=l6OMXCfjwCZZZEfsUFDStyJSbAZzizOfhNTxWTN9JTQ,6721
|
|
4
|
+
smart_search-0.1.30.dist-info/RECORD,,
|
|
5
|
+
smart_search-0.1.30.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
smart_search-0.1.30.dist-info/WHEEL,sha256=a-udL2m4V_6Yn8b-uKzWb12RjRxwwMo6ohYFM24tpaY,91
|
|
7
|
+
smart_search-0.1.30.dist-info/direct_url.json,sha256=uxzJhtkKbMt-CGh9XSoaSD7MmHAe_3DVycaTwRP2IhQ,76
|
|
8
|
+
smart_search-0.1.30.dist-info/entry_points.txt,sha256=fiHguhz7hQ6ybeepO0OPpZm19XCi2J-Bkigf8DGBuKM,55
|
|
9
|
+
smart_search-0.1.30.dist-info/licenses/LICENSE,sha256=o_zbH827gkTiZfsNLJGQn1z8Z4eLAvb8heFaSqSGVv8,1067
|
|
10
|
+
smart_search-0.1.30.dist-info/top_level.txt,sha256=mBPprcb_b3tBApeRa2nlLaGj3UVNxXCdyz6QQvDzJpc,13
|
|
11
11
|
smart_search/__init__.py,sha256=da1PTClDMl-IBkrSvq6JC1lnS-K_BASzCvxVhNxN5Ls,13
|
|
12
12
|
smart_search/__pycache__/__init__.cpython-313.pyc,,
|
|
13
13
|
smart_search/__pycache__/activity.cpython-313.pyc,,
|
|
@@ -40,19 +40,19 @@ smart_search/__pycache__/ui_metadata.cpython-313.pyc,,
|
|
|
40
40
|
smart_search/__pycache__/ui_server.cpython-313.pyc,,
|
|
41
41
|
smart_search/__pycache__/utils.cpython-313.pyc,,
|
|
42
42
|
smart_search/activity.py,sha256=66yPPAfuaDSF4UTEuRRDefp2m1-l8eVnr8K5_ShoQCU,13368
|
|
43
|
-
smart_search/assets/i18n/messages.json,sha256=
|
|
43
|
+
smart_search/assets/i18n/messages.json,sha256=2cEW-_sOWuQbqb3va9CixuxROAdZNgPujOy5HygLVSc,638801
|
|
44
44
|
smart_search/assets/skills/smart-search-cli/SKILL.md,sha256=465CCVKGCHfiJLDD9LlYbIhlbxWucwLiNVVAcRRlEf8,8561
|
|
45
45
|
smart_search/assets/skills/smart-search-cli/agents/openai.yaml,sha256=4S1VuEER7Puw9JvxDsyI__Tdrr1j2h6aF26yHho_x8E,162
|
|
46
46
|
smart_search/assets/skills/smart-search-cli/references/cli-contract.md,sha256=mZyQRioJ4_ySkxcl9WQBMGXk5fcWOEMBMnXXUGnRoEA,1843
|
|
47
|
-
smart_search/assets/skills/smart-search-cli/references/cli-core.md,sha256=
|
|
48
|
-
smart_search/assets/skills/smart-search-cli/references/command-patterns.md,sha256=
|
|
47
|
+
smart_search/assets/skills/smart-search-cli/references/cli-core.md,sha256=7Wf9csFgjRfQmk7EyssAZBn7BWbw6yNQcu_1-T9LJXA,15029
|
|
48
|
+
smart_search/assets/skills/smart-search-cli/references/command-patterns.md,sha256=PvMBXjDg0acGf-9uZAyK3p_Ox_kI5aQuhae12WsqRj0,12534
|
|
49
49
|
smart_search/assets/skills/smart-search-cli/references/deep-research-mode.md,sha256=i1ZK5yo7ni5LIs79WrNOY75BmTS9G4IIuxxgQ6b6jSM,14398
|
|
50
|
-
smart_search/assets/skills/smart-search-cli/references/provider-routing.md,sha256=
|
|
50
|
+
smart_search/assets/skills/smart-search-cli/references/provider-routing.md,sha256=XQXOI6xZUcCzg1UtDXBpggKVQAC7cNDZwYOU5tOrG8Q,27195
|
|
51
51
|
smart_search/assets/skills/smart-search-cli/references/regression-release.md,sha256=SkOUwxpPm_HeBGlpUil5ybTBiwa-wAzt-765zw9kYqk,5673
|
|
52
|
-
smart_search/assets/skills/smart-search-cli/references/setup-config.md,sha256=
|
|
53
|
-
smart_search/assets/ui/index.html,sha256=
|
|
54
|
-
smart_search/cli.py,sha256=
|
|
55
|
-
smart_search/config.py,sha256=
|
|
52
|
+
smart_search/assets/skills/smart-search-cli/references/setup-config.md,sha256=Bi-yqcoGb0T1SV5tzJLwW9gW_ygx00LPTKXHAHM2t7c,18213
|
|
53
|
+
smart_search/assets/ui/index.html,sha256=i3se6JX5iHnGXIEXhHvLKZgPzNh8JMJAeRqfIlqac3U,63800
|
|
54
|
+
smart_search/cli.py,sha256=TDRYJ99KPbudCwKP2w5Mvta6TiCVrCyljUNEPqnopzs,166464
|
|
55
|
+
smart_search/config.py,sha256=ci_r_EMotBLHP8NxyAybcRPVhw11kZCW1s7aRKorEYI,56793
|
|
56
56
|
smart_search/desktop_backend.py,sha256=VYFc83YBllSljgbgx9oPPNbQLwGADZGVtN_CF08ShD8,42417
|
|
57
57
|
smart_search/desktop_catalog.py,sha256=VMIsLFAD98Czetwy-ka606QnyUSk0gDBhsfz7k4QDl8,11485
|
|
58
58
|
smart_search/desktop_cli.py,sha256=dpXLwf6b9Dgw9lKPTDZpKH1Z85OpIgrYyNJM2hXIu68,11069
|
|
@@ -64,16 +64,17 @@ smart_search/desktop_worker.py,sha256=Nkw_FMNNejrt8oJ6aNUmziSd7vVu_Xy9ijvfgstdaw
|
|
|
64
64
|
smart_search/i18n.py,sha256=-mDfpi-P4AQPCUpnuI--W9c1AFurN5QPhAGYMXB02kU,4869
|
|
65
65
|
smart_search/intent_router.py,sha256=bj4DnveNEFj9oIpndOZKRDlyAJwuT5q4dam_dY4cG60,7340
|
|
66
66
|
smart_search/jev.py,sha256=C80OBG2RMOJCMtogltVDHUg0xoCHO_8uIT69736myq8,27169
|
|
67
|
-
smart_search/jev_search.py,sha256=
|
|
67
|
+
smart_search/jev_search.py,sha256=BWBpLVDLcOl2DhRAp3iKJXeeo2twDp3csnUoAcNIkhE,48555
|
|
68
68
|
smart_search/logger.py,sha256=pwfjP50ywoTRirH4H7qYTNgt4TgoMhZlX2pb8dNvO9k,1203
|
|
69
|
-
smart_search/provider_descriptions.py,sha256=
|
|
69
|
+
smart_search/provider_descriptions.py,sha256=Y5I1vOz-trKoVfXZ3bQLVfA1aHIRAJ4maN42ya-Zqds,10234
|
|
70
70
|
smart_search/provider_errors.py,sha256=IXyLo6Kc4WSP7faH5mMTQyAIVKR0BR0680qd0ZApKUo,5384
|
|
71
71
|
smart_search/provider_health.py,sha256=-yIA4vvJAvyVjGLvnjEnXR1OT4cfVp0hrJTIYbKYNwY,11625
|
|
72
|
-
smart_search/providers/__init__.py,sha256=
|
|
72
|
+
smart_search/providers/__init__.py,sha256=0uh_h4VhGGgJ88jS6VL4Ih_0PcytrdpIXpgTx2uvBhw,951
|
|
73
73
|
smart_search/providers/__pycache__/__init__.cpython-313.pyc,,
|
|
74
74
|
smart_search/providers/__pycache__/anysearch.cpython-313.pyc,,
|
|
75
75
|
smart_search/providers/__pycache__/base.cpython-313.pyc,,
|
|
76
76
|
smart_search/providers/__pycache__/context7.cpython-313.pyc,,
|
|
77
|
+
smart_search/providers/__pycache__/deepseek.cpython-313.pyc,,
|
|
77
78
|
smart_search/providers/__pycache__/exa.cpython-313.pyc,,
|
|
78
79
|
smart_search/providers/__pycache__/jina.cpython-313.pyc,,
|
|
79
80
|
smart_search/providers/__pycache__/openai_compatible.cpython-313.pyc,,
|
|
@@ -85,6 +86,7 @@ smart_search/providers/__pycache__/zhipu_mcp.cpython-313.pyc,,
|
|
|
85
86
|
smart_search/providers/anysearch.py,sha256=rtuxnORxV4DsBParAR7YVKIV_06v0QdFOx3dI36jFZI,13640
|
|
86
87
|
smart_search/providers/base.py,sha256=8PQWAWJ3XCmlTKV3_oaPUQ2CSU-BXGULRzKg6n3zt9s,995
|
|
87
88
|
smart_search/providers/context7.py,sha256=zOImhEiKUVg60h9SbYdJRBSUfoVvS-8N6O62sudGs_s,6481
|
|
89
|
+
smart_search/providers/deepseek.py,sha256=br_XHY0lc5xvgppp_4dPd_g5UU9jzEVCronn_UvyoH4,4678
|
|
88
90
|
smart_search/providers/exa.py,sha256=6BLnSxDLM0PKXlkcqRKw49G728yBkRicDzQiJt2vIzo,6875
|
|
89
91
|
smart_search/providers/jina.py,sha256=hz8KAcFSstWT4DIBWKdEhymYjBjUvo_SFeZcwCdPkTs,4048
|
|
90
92
|
smart_search/providers/openai_compatible.py,sha256=CL-zuya3gx8-8_hGn7rTT5vXtubYKfnHe2aC2jzcSI0,41439
|
|
@@ -94,12 +96,12 @@ smart_search/providers/xai_responses.py,sha256=3mkh0UuQBzxONT8lWzuwEOISXYHWst_5N
|
|
|
94
96
|
smart_search/providers/zhipu.py,sha256=PN0_Vc51rDYlxEmz2B1vgSxEt9jXf_RIsRobGp-LBPs,7088
|
|
95
97
|
smart_search/providers/zhipu_mcp.py,sha256=4XgckrHzwSdhUtMlTSvhVxrAA7Fr7QX_4BYWfSHmXUg,10976
|
|
96
98
|
smart_search/sciverse_schema.py,sha256=ml5iDXEpY7cnEMz74BGTo4yxX7UvPOe_i7w4Pi5t1wY,18178
|
|
97
|
-
smart_search/service.py,sha256=
|
|
99
|
+
smart_search/service.py,sha256=7G9YJUDLom5jzCrYkFGKcLnix3uQCL2QKVCHYHWC-sA,192433
|
|
98
100
|
smart_search/skill_installer.py,sha256=CvM9GQShaTR8ttNhilx8Up0vVsHZfsnYD5uM7wZrzBs,21782
|
|
99
101
|
smart_search/skill_maintenance.py,sha256=2s2-1ZEEjRg2MfRKlaw6Y51OO86kOf9axl7fW6KMv0g,8818
|
|
100
102
|
smart_search/sources.py,sha256=WvyRXqX_IdbDPpsqcSbulipxNtZvQbWv1MsZ2Ud5bTY,14376
|
|
101
103
|
smart_search/state_files.py,sha256=K02dlR3Xuj2vIkRiEdrFr3OSiEsPxN5_HSo4NTmz0sc,2063
|
|
102
104
|
smart_search/ui_api.py,sha256=x3ULS415MPjZ96iZSOeNK4npzOLJ6GK_DymYi6_tPgA,10027
|
|
103
|
-
smart_search/ui_metadata.py,sha256=
|
|
104
|
-
smart_search/ui_server.py,sha256=
|
|
105
|
+
smart_search/ui_metadata.py,sha256=6WO8WT-P9bwkmgi2i4XXXUqSL4xUijd4gJnImS_cka8,51028
|
|
106
|
+
smart_search/ui_server.py,sha256=pcAPiYWmJXTmX1Xpy12v97JhkbRx-nJ3zRYefGW1Tc0,20508
|
|
105
107
|
smart_search/utils.py,sha256=HVpxtyveEHcpGSkIObf97GbCJVRd68f1X6ecQeH14Ks,8992
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konbakuyomu/smart-search",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "CLI-first multi-source web research with xAI/OpenAI-compatible search, Exa, Context7, Zhipu, Tavily, Firecrawl, and Deep Research planning.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/konbakuyomu/smartsearch#readme",
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
},
|
|
40
40
|
"smartSearchBinary": true,
|
|
41
41
|
"optionalDependencies": {
|
|
42
|
-
"@konbakuyomu/smart-search-darwin-x64": "0.1.
|
|
43
|
-
"@konbakuyomu/smart-search-darwin-arm64": "0.1.
|
|
44
|
-
"@konbakuyomu/smart-search-win32-x64": "0.1.
|
|
45
|
-
"@konbakuyomu/smart-search-win32-arm64": "0.1.
|
|
46
|
-
"@konbakuyomu/smart-search-linux-x64": "0.1.
|
|
47
|
-
"@konbakuyomu/smart-search-linux-arm64": "0.1.
|
|
42
|
+
"@konbakuyomu/smart-search-darwin-x64": "0.1.30",
|
|
43
|
+
"@konbakuyomu/smart-search-darwin-arm64": "0.1.30",
|
|
44
|
+
"@konbakuyomu/smart-search-win32-x64": "0.1.30",
|
|
45
|
+
"@konbakuyomu/smart-search-win32-arm64": "0.1.30",
|
|
46
|
+
"@konbakuyomu/smart-search-linux-x64": "0.1.30",
|
|
47
|
+
"@konbakuyomu/smart-search-linux-arm64": "0.1.30"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|