@szjy/ai-coms
v0.1.17
Published
vue3 ai component
Readme
@szjy/ai-coms — Vue3 AI 聊天组件库
一个基于 Vue 3 与 Element Plus 的即插即用 AI 对话组件(SzAiChat)。支持多家大模型流式对话(Spark、Ollama、SiliconFlow、Kimi Moonshot、DashScope、数智建管、Dify OpenAI 兼容等),内置消息面板、Markdown 渲染与高亮、流式增量显示、复制、消息后处理(messagePostRender)与路由链接渲染(routeLinkRender)等常用能力,帮助你在业务系统中快速集成 AI 助手。
当前版本:
0.1.8
适用场景
- 企业 Web 系统内嵌 AI 助手(客服、知识问答、项目辅助)
- 快速验证多模型 Chat 能力与对比
- 低侵入集成到现有前端应用(只需安装与注册组件)
亮点
- 统一组件:提供
SzAiChat组件与安装器create,一行注册、开箱即用。 - 多模型接入:内置多种大模型接入方法与流式解析器,通过
llmName属性或?llmName=参数轻松切换。 - 纯端侧语音识别:内置 Whisper 端侧模型与 Web Worker;麦克风与上传入口默认关闭,通过
enableVoiceInput/enableVoiceUpload显式开启。 - 体验完善:欢迎页、快捷提问、消息气泡、Markdown 渲染、复制、抽屉模式切换。
- 消息后处理:通过
messagePostRender回调在消息渲染完成后自定义处理 DOM,内置routeLinkRender支持#router:xxx#路由链接渲染。 - 友好扩展:将流解析、API 调用、渲染拆分,
extraConfig支持供应商级参数透传,方便替换与扩展。
目录结构说明
ai-coms/
├─ index.ts # 库入口:导出组件、安装器、自定义渲染器与音频工具
├─ vite.config.mts # Vite 构建配置(库模式)
├─ package.json # 包信息、脚本与依赖
├─ docs/
│ ├─ message-post-render.md # messagePostRender 与 routeLinkRender 使用教程
│ └─ voice-recognition.md # 端侧语音识别与音频功能开发指南
├─ scripts/
│ └─ download_model.py # Whisper 端侧模型一键下载脚本
├─ public/ # 静态资源(图片、脚本、svg、语音模型)
│ ├─ sounds/ # Whisper 本地静态大模型目录
│ ├─ images/ # 组件使用的图片与图标
│ └─ scripts/ai/ # markdown-it 与 highlight.js 静态脚本
├─ packages/
│ ├─ api/ai.ts # 多家大模型的流式请求封装(Fetch + SSE/JSON)
│ ├─ common/
│ │ ├─ utils/ # 通用工具(剪贴板、URL、loader 等)
│ │ └─ custom-render/ # 自定义消息渲染器
│ │ ├─ index.ts # 出口汇总
│ │ └─ routeLinkRender.ts # #router:xxx# 路由链接渲染器
│ └─ components/
│ ├─ components.ts # 组件出口汇总(当前导出 AiChat)
│ ├─ installer.ts # 全量安装器,循环注册所有导出的组件
│ ├─ contants.ts # 版本常量、组件名前缀
│ ├─ create.ts # 可选按需安装器工厂 `create()`
│ ├─ icons/ # SvgIcon 组件与内置 svg 资源
│ └─ AiChat/
│ ├─ index.ts # 导出 `SzAiChat`
│ └─ src/
│ ├─ index.vue # 包裹入口,悬浮按钮与面板开关、依赖脚本加载
│ ├─ components/
│ │ ├─ ChatPanel.vue # 主聊天面板、输入区、消息列表
│ │ ├─ Previewer.vue # Markdown 流式渲染、复制、滚动控制
│ │ └─ plugins/markdown.ts # 生成 markdown-it 实例
│ ├─ transform/ # 流解析与模型映射
│ │ └─ index.ts # LLMTypes、transformStreamValue、splitStream
│ └─ writer.ts # 桥接 API 与流 Reader,向预览器输出
└─ examples/ # 运行示例(Vite)
├─ main.ts # 示例入口,注册 Element Plus 与组件
├─ App.vue # 示例页面,包含 Tab 与 AiChat 使用
└─ components/
├─ AiChatTab.vue # 组件使用样例(token/identifier/baseUrl 传参)
└─ DifyOpenAIChatTab.vue # Dify OpenAI 兼容 provider 使用样例模块依赖与调用流向(简化):
graph TD
A[App 应用] --> B[create() 安装器]
B --> C[SzAiChat 组件]
C --> D[ChatPanel]
D --> E[createAssistantWriterStylized]
E --> F[LLMTypes.api (packages/api/ai.ts)]
F --> G[服务端流接口]
E --> H[splitStream/transformStreamValue]
H --> I[Previewer 渲染 Markdown]环境与依赖配置
- Node.js: 推荐 >= 18(Vite 5 要求)
- 包管理:pnpm(推荐)/ npm / yarn
- 框架:Vue 3(Peer/External)、Element Plus
- 构建:Vite 5,TypeScript 5
运行所需依赖(节选,详见 package.json):
- 生产依赖:
vue@^3.4.21、[email protected]、aa-loader@^0.1.5 - 开发依赖:
vite@^5、@vitejs/plugin-vue、@vitejs/plugin-vue-jsx、vite-plugin-dts、vite-plugin-svg-icons、unplugin-icons+@iconify-json/mdi(示例用~icons/mdi)、unplugin-vue-components、typescript、vue-tsc、sass/less等
浏览器兼容:现代浏览器(需支持 ReadableStream/TransformStream 的流式能力;如需更广兼容度请添加 Polyfill)。
推荐工具:
- VS Code + Volar 扩展(Vue)
- ESLint/Prettier(建议但当前仓库未内置配置,可按团队规范自加)
安装与项目运行指南
安装依赖:
pnpm install
# 或
npm install开发模式(示例站点):
pnpm dev
# 访问 http://localhost:8810构建库:
pnpm build # 生产构建(ES/UMD,外部化 vue/element-plus)
pnpm build:dev # 开发模式构建(保留标识符,便于调试)预览(示例站点打包后预览):
pnpm preview关于静态依赖:组件运行时会按需加载 public/scripts/ai 下的 markdown-it.min.js 与 highlight.11.11.1.min.js,请确保这些静态文件可被正确访问(Vite 开发与构建后默认可用)。
关于后端代理:多数大模型的请求路径形如 ${location.origin}/spark/...、/moonshot/... 等,需要在网关或本地开发服务器配置反向代理与鉴权,保证浏览器可直接访问同源路径。
快速上手与主要功能
注册并使用组件:
// examples/main.ts(示例)
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import { SzAiChat, create } from '../index'
const app = createApp(App)
const szAIKits = create({ components: [SzAiChat] })
app.use(ElementPlus).use(szAIKits).mount('#app')在页面中引入:
<template>
<sz-ai-chat
:token="token"
:identifier="identifier"
:baseUrl="baseUrl"
:title="'数智AI助理'"
llmName="szjy"
:messagePostRender="handleMessagePostRender"
:extraConfig="{ projectId: 26 }"
/>
<!-- 右下角悬浮按钮点击后弹出对话面板 -->
<!-- 可通过 llmName 属性或 ?llmName=szjy 切换默认大模型 -->
<!-- 支持 standard/spark/ollama3/siliconflow/moonshot/dashscope/szjy/dify-openai -->
<!-- e.g. http://localhost:8810/?llmName=spark -->
</template>组件 Props:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| token | string | '' | 大模型 API 的鉴权 token |
| identifier | string | '' | 项目/租户标识符(数智建管、Dify 等使用) |
| baseUrl | string | '' | 自定义接口前缀(数智建管、Dify 等使用) |
| title | string | '数智AI助理' | 对话面板标题 |
| llmName | string | 'szjy' | 大模型名称,对应 LLMTypes 中的 modelName |
| messagePostRender | Function | null | 消息渲染完成后的回调 (container, textContent) => void |
| extraConfig | Object | {} | 额外配置参数,透传给供应商 API(如 { projectId }、{ authToken }) |
| enableVoiceInput | boolean | false | 是否显示麦克风语音输入入口 |
| enableVoiceUpload | boolean | false | 是否显示上传音频/视频识别入口 |
主要功能:
- 悬浮入口与关闭:右下角按钮打开对话,悬浮关闭按钮隐藏组件。
- 欢迎页与快捷提问:无对话时显示欢迎与常见问题,点击自动填充发送。
- 流式对话:使用 Fetch Response.body + Reader,配合 TransformStream 分割,逐字输出。
- Markdown 渲染与代码高亮:外链
markdown-it与highlight.js,支持标题/列表/链接/代码块。 - 复制消息:一键复制渲染后的文本内容。
- 抽屉模式:点击图标切换为右侧抽屉铺满高的会话模式。
核心流程(发送到渲染):
sequenceDiagram
participant U as 用户
participant P as ChatPanel
participant W as createAssistantWriterStylized
participant A as API 调用 (packages/api/ai.ts)
participant T as splitStream/transform
participant R as Previewer
U->>P: 输入问题并提交
P->>W: 调用并传入 LLM 类型与参数
W->>A: 发起 Fetch (stream: true)
A-->>W: 返回 Response.body ReadableStream
W->>T: TextDecoderStream + splitStream 按行/JSON 解析
T-->>R: Reader.read() 增量输出片段
R-->>U: Markdown 渲染、滚动到底部、完成复制API 接口文档(前端调用封装)
文件:packages/api/ai.ts(基于浏览器 fetch,返回 Promise<Response>,调用处继续基于 Response.body 进行流式读取)
公共说明:所有接口均使用 POST,默认 stream: true,对于需要鉴权的接口通过 Authorization: Bearer <token> 头传递。所有函数签名统一为 (text, token, identifier, baseUrl, extraConfig),未使用的参数可留空。
| 函数名 | 说明 | 请求路径 | 认证 | 主要参数 | 备注 |
|---|---|---|---|---|---|
| createDifyOpenAIStylized(text, token, identifier, baseUrl, extraConfig) | Dify(OpenAI 兼容) | ${ensureFullURL(baseUrl)}/v1/chat/completions | Bearer(authToken) | text, token, identifier, baseUrl, extraConfig | 请求体含 inputs.token、inputs.projectCode,extraConfig.authToken 为 Dify 应用密钥,extraConfig.projectId 可选 |
| createOllama3Stylized(text) | 本地 Ollama3 | http://localhost:11434/api/chat | 否 | text | 模型固定 llama3 |
| createSparkStylized(text, token) | 科大讯飞 Spark | ${location.origin}/spark/v1/chat/completions | Bearer | text, token | SSE data: JSON 行,模型 generalv4.0Ultra |
| createSiliconFlowStylized(text, token) | SiliconFlow | ${location.origin}/siliconflow/v1/chat/completions | Bearer | text, token | 兼容 OpenAI 接口格式 |
| createKimiMoonshotStylized(text, token) | Moonshot | ${location.origin}/moonshot/v1/chat/completions | Bearer | text, token | 提供 system 提示 |
| createDashScopeStylized(text, token) | 通义千问 | ${location.origin}/dashscope/compatible-mode/v1/chat/completions | Bearer | text, token | 兼容 OpenAI 路径 |
| createSZJYStylized(text, token, identifier, baseUrl) | 数智建管 | ${ensureFullURL(baseUrl)}/support/${identifier}/a | Bearer | text, token, identifier, baseUrl | 业务网关需配置 |
大模型映射(LLMTypes,packages/components/AiChat/src/transform/index.ts):
| modelName | label | api | |---|---|---| | standard | 模拟数据模型 | null(本地模拟,不发起请求) | | spark | Spark 星火大模型 | createSparkStylized | | ollama3 | Ollama 3 大模型 | createOllama3Stylized | | siliconflow | SiliconFlow 硅基流动大模型 | createSiliconFlowStylized | | moonshot | Kimi Moonshot 月之暗面大模型 | createKimiMoonshotStylized | | dashscope | 通义千问大模型 | createDashScopeStylized | | szjy | 数智建管大模型 | createSZJYStylized | | dify-openai | Dify (OpenAI 兼容) | createDifyOpenAIStylized |
流解析器(packages/components/AiChat/src/transform/index.ts):
splitStream('\n')将ReadableStream<string>按行切分,兼容SSE data:与直接 JSON 片段。transformStreamValue针对各模型对单行数据进行解析,统一返回{ content, done };dify-openai、siliconflow、moonshot、dashscope、szjy均复用spark的 OpenAI 兼容解析逻辑。
在组件中,createAssistantWriterStylized 会把 Response.body 管道化为可读 reader,再交给 Previewer 逐步渲染。
配置与扩展
- 切换默认模型:使用
llmName属性,或在地址栏加上?llmName=<name>(支持:standard、spark、ollama3、siliconflow、moonshot、dashscope、szjy、dify-openai)。 - 供应商参数透传:通过
extraConfig传入供应商级配置(如 Dify 的authToken、projectId)。 - 消息后处理:通过
messagePostRender回调在消息渲染完成后自定义处理 DOM(详见下节)。 - 自定义图标:
SvgIcon支持传入绝对路径或/images/svg/*.svg,颜色通过滤镜近似实现。 - 组件前缀:
packages/components/contants.ts中COMP_PREFIX = 'Sz',通过create({ componentPrefix })可定制。 - 版本常量:
VERSION注入自package.json,在 Vite 中以__PKG_VERSION__注入。
消息后处理与路由链接渲染
组件提供 messagePostRender 回调,在每条 AI 消息流式输出完成、DOM 更新后触发一次,可用于自定义处理渲染后的内容(高亮关键词、埋点、插入自定义元素等)。
// 函数签名
messagePostRender?: (container: HTMLElement, textContent: string) => void库内置了 routeLinkRender(从 @szjy/ai-coms 导出),可将 AI 输出中的 #router:xxx# 标记自动渲染为可点击的路由链接:
<template>
<sz-ai-chat :token="token" :messagePostRender="handleMessagePostRender" />
</template>
<script setup lang="ts">
import { routeLinkRender } from '@szjy/ai-coms'
import { useRouter } from 'vue-router'
const router = useRouter()
const handleMessagePostRender = (container: HTMLElement, textContent: string) => {
routeLinkRender(container, textContent, {
onRouteClick: (routeName, queryParams) => {
const routeData = router.resolve({ name: routeName, query: queryParams })
window.open(routeData.href, '_blank')
},
// 可选:自定义链接样式(会与默认样式合并)
linkStyle: { color: '#536fec', fontWeight: 'bold', textDecoration: 'underline' }
})
}
</script>支持的标记格式:
| 格式 | 说明 |
|---|---|
| #router:routeName# | 简单路由,只有路由名 |
| #router:routeName?a=1&b=2# | 带查询参数的路由,? 后解析为 queryParams 对象 |
完整教程见
docs/message-post-render.md。
szjy-ui fence:AI 结构化输出渲染为可交互组件(驱动 BIM 沙盘等外部页面)
AI 响应中的 ```szjy-ui fence 块(JSON)会渲染为可交互组件(内置 list 容器 / button),用户点击触发动作(内置 postMessage),把 actionParams 原样作为消息体发送到目标窗口——典型场景为 BIM 沙盘构件定位(BIM_SANDBOX 协议,详见《BIM沙盘外部通信文档》)。
<template>
<!-- 聊天与沙盘同窗口:target 默认 self 即可 -->
<sz-ai-chat llmName="dify-openai" ... />
<!-- 宿主页嵌沙盘 iframe:指定目标窗口与真实源 -->
<sz-ai-chat
:customUi="{ postMessage: { target: () => sandboxIframe.contentWindow, targetOrigin: 'http://172.17.102.17:5174' } }"
/>
</template>```szjy-ui
{
"title": "构件列表",
"type": "list",
"gap": 12,
"items": [
{ "type": "button", "props": { "type": "primary", "content": "查看构件1" },
"action": "postMessage",
"actionParams": { "type": "BIM_SANDBOX", "action": "locateComponent", "payload": { "wbsCode": "CHKSLDY-SD-001" } } }
]
}
```特性:流式中 JSON 未完整时降级为代码块、完成后自动切换;回执反馈(成功/失败就地展示,未就绪自动重试);组件/动作均注册表模式,宿主可用 registerSzjyUiComponent / registerSzjyUiAction 扩展。
配套 Mock 模式(不调真实后端):llmName="standard" + :mockResponse="文本" 即可流式输出自定义响应(内置样例 DEFAULT_MOCK_RESPONSE),examples 第 4 个 tab「BIM 沙盘 Mock 联调」提供完整联调环境(mock 编辑 + 沙盘 iframe + 协议消息日志)。
完整文档见
docs/szjy-ui-fence.md。
项目维护规范
- 代码风格:
- 建议接入 ESLint + Prettier(当前仓库未内置,推荐团队按规范落地)。
- TypeScript 严格模式,避免
any;函数与导出接口显式类型标注。
- 分支管理:
main/release稳定分支;feat/*、fix/*、chore/*、docs/*分支用于日常迭代。
- 提交规范:
- 使用 Conventional Commits(如
feat: ...、fix: ...、docs: ...)。 - 已内置版本发布脚本:
pnpm release(npx commit-and-tag-version)。
- 使用 Conventional Commits(如
- CI/CD:
- 建议在 CI 中执行
pnpm i && pnpm build && vue-tsc --noEmit校验类型与构建产物。
- 建议在 CI 中执行
- 代码审核:
- 所有 PR 需通过代码评审与预览构建,确保可回滚、可追踪。
常见问题与故障排查(FAQ)
- 无法渲染 Markdown 或高亮无效
- 可能原因:
public/scripts/ai/markdown-it.min.js或highlight.11.11.1.min.js未能被加载。 - 解决:确认构建后静态资源是否在同路径;开发模式下检查网络请求是否 200;必要时调整
base或静态资源路径。
- 流式输出不显示或中断
- 可能原因:后端未按行输出
data:或 JSON 片段;跨域/CORS 拦截;代理未配置。 - 解决:
- 确认后端开启
stream: true并按行刷写; - 开发环境配置本地域名反向代理到真实 API;
- 浏览器控制台查看网络流(Fetch/XHR)与 JS 报错。
- 确认后端开启
- 请求 401/403 或鉴权失败
- 可能原因:未正确传入
token、identifier、baseUrl;网关校验不通过。 - 解决:
- 在
<sz-ai-chat>传入合法token、identifier,并确认baseUrl为网关前缀; - 检查服务端对
Authorization: Bearer <token>的解析逻辑。
- 在
- 图标不显示或颜色不生效
- 可能原因:
SvgIcon传入的是外链/多色 SVG;过滤器无法覆盖填充色。 - 解决:优先使用单色 SVG,或在设计时约定可控样式;必要时改为内联 SVG 方案。
- 生产打包后
vue/element-plus重复打包
- 可能原因:未将其外部化。
- 解决:本库构建已在
vite.config.mts配置external,作为使用方请在你的应用构建中确保仅引用一次依赖。
- 使用 Dify(OpenAI 兼容)报鉴权错误或提示缺少 authToken
- 可能原因:未通过
extraConfig.authToken传入 Dify 应用密钥(app-xxx),此时会回退到内网默认值并在控制台报错。 - 解决:在
<sz-ai-chat>传入:extraConfig="{ authToken: 'app-xxx', projectId: 26 }",并确认baseUrl指向 Dify 实例地址。
#router:xxx#链接未渲染或点击无跳转
- 可能原因:未绑定
messagePostRender,或未在routeLinkRender的onRouteClick中实现跳转逻辑。 - 解决:参考「消息后处理与路由链接渲染」一节绑定回调;
#为分隔符,路由名与参数不能包含#。
参考资料与延伸阅读
- Vite 官方文档:
https://vitejs.dev/ - Vue 3 官方文档:
https://vuejs.org/ - Element Plus:
https://element-plus.org/ - Streams API(ReadableStream/TransformStream):
https://developer.mozilla.org/docs/Web/API/Streams_API - markdown-it:
https://markdown-it.github.io/ - highlight.js:
https://highlightjs.org/ - OpenAI 风格 Chat Completions 接口模式(兼容生态):
https://platform.openai.com/docs/api-reference/chat - Ollama 本地模型:
https://ollama.com/ - Moonshot API:
https://platform.moonshot.cn/ - DashScope(通义千问):
https://dashscope.aliyun.com/ - SiliconFlow:
https://docs.siliconflow.cn/ - Dify(OpenAI 兼容接口):
https://docs.dify.ai/ - 科大讯飞 Spark(参考其服务端/网关说明)
如有问题或建议,欢迎提交 Issue/PR 共同完善组件能力与生态。
