fix(vision): 服务端自己取回图片 URL 并内联为 data URI,修自部署下取不到图(F3,优先级「高」)
- 原缺陷:vision 适配器把 http(s) URL **原样转给上游 provider**,而 upload 返回的是同源局域网地址 ⇒ 云 provider 根本取不到 ⇒ **B1(大图绕开 WS 4096 字节上限)在自部署形态下等于没做** - 修法:① `imageURL` → `localizeRef`:**彻底不再直传 URL**;同源 upload 产物走**本地读取**, 其余 http(s) 由服务端自己取回 → 一律内联为 `data:` URI 再交 provider(OpenAI 与 Anthropic 双协议都能收图) - 顺手修「类型瞎猜」:新增 `vision/mime.go`,**字节嗅探是唯一裁决**——判得出白名单光栅图就用字节结果; 字节是文本(HTML/PROXY 错误页、SVG)→ `ErrNotImage`;判不定但来源明确声明 `image/*` 才采信声明。 错误信息只含类型名,**不含响应体/URL**(不打印图片内容) - 防护:取回复用既有 http/https 白名单 + 50MiB 上限,新增 **30s 取回超时**; 本地读取路径 `localUploadPath` 严格认定 `<host>/api/v1/files/<name>`(无 query/fragment、单段路径)+ 32 位小写十六进制名 + `filepath.Dir` 包含性第二道 ⇒ `../`、`%2e%2e%2f`、反斜杠等编码变体在触碰 FS 前即拒 - 生产接线(主会话补):`cmd/cyrene/vision.go` 传 `vision.WithUploadsDir(<DataDir>/uploads)` - 复核:build/vet 绿;新增 51 条断言(含 httptest 真取回 → 断言上游收到 data URI 且不含源 URL、 同源 upload 用不可达 host 证明走本地、HTML 谎报 image/png 被字节否决、越界名 19 例); **3 条变异反证**(恢复 URL 直传 8 红 / 文本静默回退 png 红 / 去掉穿越检查 12 红)均已还原 - 未做(已登记):历史里 URL 形态图片每轮重新取回(缓解=持久化时内联/缓存); Anthropic 只认 png/jpeg/gif/webp 而白名单含 bmp;`fetchURL` 无 IP 过滤(链路本地 169.254.169.254 可达)→ 需收紧
This commit is contained in:
1 parent
88d7dd0026
commit
983bc49218
11 files changed
+971
-55
No files matched your search
@@ -73,8 +73,16 @@ func (r *MessageResolver) resolveImage(ctx context.Context, imageURL string) (mo
|
||||
decision := r.router.Route(target, RouteRequest{})
|
||||
switch decision.Kind {
|
||||
case KindMain:
|
||||
// 支持视觉的主模型直接看到这张图。
|
||||
return model.Content{Type: model.ContentTypeImage, ImageURL: imageURL}, nil
|
||||
// 支持视觉的主模型直接看到这张图;但 URL 与本地路径必须先由服务端
|
||||
// 取回并内联为 data: URI——云 provider 取不到自部署的局域网地址
|
||||
// (如 http://192.168.x.x:10942/api/v1/files/…),Anthropic 协议更是
|
||||
// 只接受 base64 data: URI(model/anthropic_encode.go 会拒绝裸 URL)。
|
||||
// 取回失败时返回错误,由 loop 保留原消息(该轮不因此中断)。
|
||||
src, err := inlineImageSrc(ctx, imageURL)
|
||||
if err != nil {
|
||||
return model.Content{}, err
|
||||
}
|
||||
return model.Content{Type: model.ContentTypeImage, ImageURL: src}, nil
|
||||
case KindVisionTool:
|
||||
return r.invokeTool(ctx, decision.Tool, map[string]any{
|
||||
"image": imageURL,
|
||||
|
||||
Reference in new issue
Block a user