接入位表六家扩到八家,端点形状与错误码一字未改(契约 v0.87,仅两处名单)。
- tencent_wsa:POST {base}/SearchPro + Authorization Bearer,零签名。
两条官方口径决定实现:HTTP 状态码恒 200(判失败只能读 Response.Error.Code);
Pages 是「JSON 字符串数组」需二次解析。错误码按官方表分类
(UnauthorizedOperation/AuthFailure.* = 凭据、ResourceUnavailable = 欠费、
RequestLimitExceeded* = 限流),文案只带 code 不带上游 Message。
- metaso:POST {base}/api/v1/search,响应 schema 仅第三方来源且互相打架,
实现走宽松解析(数组候选与字段名回退),逐处标「未实测」。
- 零新 env 键:两家只走 $DATA_DIR/config/plugins/web-search.json,
.env / .env.example 未动;组合根 websearch_providers.go 零功能改动。
- 契约 v0.87:values.provider 合法闭集与 plugin.providers 候选各加两条,
计数不变(14 + 25 + 40 = 79)。
82 lines
3.5 KiB
Go
82 lines
3.5 KiB
Go
package search
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
)
|
||
|
||
// Provider 是一种搜索后端的**窄接口**(design/27 §4)。实现必须并发安全:
|
||
// 工具循环可能从多个 goroutine 调它。
|
||
type Provider interface {
|
||
// Name 返回该 provider 的配置名(即 WEB_SEARCH_PROVIDER / WEB_SEARCH_FALLBACK 里的取值)。
|
||
Name() string
|
||
// Search 执行一次检索,返回**已归一化**的结果(可能为空)。
|
||
//
|
||
// 返回空切片(len==0)**不是错误**:它表示「上游正常应答但什么都没查到」,
|
||
// 由调用方按「空结果 ≠ 成功」处置(见 ErrNoResults 的注释)。
|
||
Search(ctx context.Context, q Query) ([]Result, error)
|
||
}
|
||
|
||
// Query 是一次检索的输入。工具 schema 仍是 `query`/`count`(design/27 §4:不破坏契约)。
|
||
//
|
||
// 域名过滤**不在这里**:按 design/27 §4,域名过滤/去重/排序统一在归一化之后做一次
|
||
// (见 Finalize 的 Options),各家 provider 一律不许自己过滤。
|
||
type Query struct {
|
||
// Text 是检索词。
|
||
Text string
|
||
// Count 是调用方期望的结果条数(>0 才有意义)。provider 支持就透传,
|
||
// 不支持(如阿里云 IQS 默认 10 条不可调)就忽略 —— 本地截断仍然生效。
|
||
Count int
|
||
}
|
||
|
||
// Result 是所有 provider **统一归一化**后的结果(design/27 §4)。
|
||
type Result struct {
|
||
Link string
|
||
Title string
|
||
Snippet string
|
||
Content string
|
||
Published string
|
||
Score float64
|
||
}
|
||
|
||
// 本期内置的 provider 名(对外配置值 WEB_SEARCH_PROVIDER 的合法取值之一)。
|
||
const (
|
||
// NameSearXNG 是自建 SearXNG(A 档、零 key、本地兜底)。
|
||
NameSearXNG = "searxng"
|
||
// NameZhipu 是智谱 BigModel 联网搜索(design/27 §2.1 国内首选)。
|
||
NameZhipu = "zhipu"
|
||
// NameAliyunIQS 是阿里云信息查询服务 IQS(HTTP 免签名免 SDK)。
|
||
NameAliyunIQS = "aliyun_iqs"
|
||
// NameBocha 是博查 Bocha(中文优先)。
|
||
NameBocha = "bocha"
|
||
// NameVolcengine 是火山引擎·豆包搜索 SearchInfinity。
|
||
NameVolcengine = "volcengine"
|
||
// NameBaiduQianfan 是百度千帆 AI 搜索。
|
||
NameBaiduQianfan = "baidu_qianfan"
|
||
// NameTencentWSA 是腾讯云·元宝搜索(联网搜索 API / WSA,服务 API KEY 方式)。
|
||
NameTencentWSA = "tencent_wsa"
|
||
// NameMetaso 是秘塔搜索 Metaso(开放平台 /api/v1/search)。
|
||
NameMetaso = "metaso"
|
||
)
|
||
|
||
// Config 是构造一家 provider 所需的一切接线(密钥/地址/客户端/响应上限/URL 守卫)。
|
||
//
|
||
// 它由组合根(plugins/tools/io 的 searchWires)填好 —— provider 自己不读配置、
|
||
// 不读环境变量、不开新的 HTTP 客户端(单二进制、零新依赖:design/27 §1.5)。
|
||
type Config struct {
|
||
// APIKey 是上游凭据。**绝不进日志/响应/提示词**。
|
||
APIKey string
|
||
// BaseURL 是上游基地址(不含各家自己的路径),可被各自的环境变量覆盖。
|
||
BaseURL string
|
||
// Client 是这一套工具共用的 HTTP 客户端(已带 SSRF 重定向守卫与超时)。
|
||
Client *http.Client
|
||
// MaxBytes 是单次响应体读取上限(字节);≤0 用包内默认。
|
||
MaxBytes int64
|
||
// Guard 是**组合根注入的 SSRF 守卫**:发请求前对最终地址做一次判定
|
||
// (scheme / host / 解析结果)。它复用 plugins/tools/io 的既有守卫与本工具自己
|
||
// 那份白名单,**不改** http / web-fetch 共用的白名单。
|
||
//
|
||
// 为 nil 时不做判定(search 包的夹具测试直接用 httptest 客户端)。
|
||
Guard func(ctx context.Context, rawURL string) error
|
||
}
|