新增: 多模态图片 URL 转 image part + 修复 / 技能联想浮层定位

前端发送前扫图片扩展 URL 转 ContentPart image(模型原生视觉,非工具下载);
SkillMention 移入 .ai-input-wrap relative 内(治 / 浮层定位错位不可见)。
This commit is contained in:
lxy
2026-08-02 02:21:32 +08:00
parent 023377ab24
commit e4f7b432aa
3 changed files with 250 additions and 11 deletions
+55
View File
@@ -0,0 +1,55 @@
/**
* AI composables 通用纯函数工具(无 Vue 依赖,便于单测)。
*
* 当前仅含 F-260614-05 Phase 2c 扩展:输入框文本中的图片 URL → ContentPart[] image 片。
* 治会话 01f05167:用户贴图片 URL(文本)→ 没转 parts → 模型只收 URL 字符串 →
* AI 转 fetch_url/download 失败 L1 熔断卡死。这里在发送前扫 URL 转 image 片,
* 让 vision 模型直接拿到图片(provider 端 url 模式转发 image_url{url} 给 OpenAI/商汤)。
*/
import type { ContentPart } from '../../api/types'
/**
* 图片 URL 正则:
* - https?:// 协议头
* - \S+? 主机+路径(非贪婪,避免贪婪吞掉后续 URL)
* - \.(png|jpg|jpeg|webp|gif) 图片扩展名(大小写不敏感)
* - (\?\S*)? 可选查询参数(?foo=bar,直到空白为止)
*
* g 全局 + i 忽略大小写。不锚定行首/尾,允许 URL 嵌在任意文本里(markdown `![](url)` / 纯 URL / 句中)。
*
* 边界说明:可选组 (?:\?\S*)? 仅在扩展名后紧跟 ? 时才吞后续非空白字符,因此 URL 末尾的标点
* (逗号/句号/中文标点/fragment #anchor)不会被吞进 url —— 这是正确行为,vision fetch 拿干净 URL。
*/
const IMAGE_URL_RE = /https?:\/\/\S+?\.(?:png|jpe?g|webp|gif)(?:\?\S*)?/gi
/**
* 从文本中扫描图片 URL,转成 ContentPart[] image 片(url 模式)。
*
* - 仅 url 模式(base64 / media_type 留空):vision provider 端按 url 模式直接转发 http URL 给上游,
* OpenAI/商汤无需 base64。
* - 同 URL 去重(保持首次出现顺序)。
* - 非 URL(本地路径 / data URI / 邮件附件)不提取:这些不是 http(s) URL,vision provider 也取不到。
* - 非图片扩展(.html/.com/.pdf 等)不提取。
*
* @param text 输入框文本(原始,未 trim 也可)
* @returns image 片数组(可能为空)
*/
export function extractImageUrlParts(text: string): ContentPart[] {
if (!text) return []
const seen = new Set<string>()
const parts: ContentPart[] = []
// 注意:lastIndex 在 g 标志正则上是状态化的,这里每次新建迭代器从 0 开始,无需手动 reset
for (const m of text.matchAll(IMAGE_URL_RE)) {
const url = m[0]
if (seen.has(url)) continue
seen.add(url)
parts.push({
type: 'image',
url,
base64: null,
media_type: null,
alt: null,
})
}
return parts
}