更新记录
1.2.1(2026-04-25)
支持各大小程序 支持http sse模式和websocket模式
v1.0.0(2026-04-21)
- 支持 iOS 与 Android 平台
- 提供
openAIStream,用于 OpenAI 兼容聊天流式请求 - 提供
streamRequest,用于通用 SSE 流式请求 - 支持
abortStream与abortRequest取消请求 - 已适配 DashScope 兼容 OpenAI 接口的流式调用方式
- 提供完整 README、TypeScript 类型说明与示例
平台兼容性
uni-app(3.8.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | × | 5.0 | 15 | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | × | √ | √ | - | × | × |
uni-app x(3.8.0)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 5.0 | 15 | × | × |
vm-openai
vm-openai 是一个 uni-app / uni-app x 插件,用于发起 OpenAI 兼容流式 AI 请求。
核心说明:
vm-openai同时支持原生 HTTP chunk/SSE 和 WebSocket 两种流式模式。业务侧统一调用同一套 API,插件内部会根据http(s)://或ws(s)://协议自动选择对应传输实现,并对不同小程序平台和 App 端的差异做兼容处理。
当前提供两个 API:
openAIStream面向 OpenAI 兼容聊天接口,直接返回增量文本streamRequest面向通用 SSE 接口,原样返回event / id / data
如果你希望直接复用聊天界面,可以搭配 vm-openai-ui 一起使用。
流式协议支持
| 平台 | HTTP chunk/SSE | WebSocket | 协议选择 |
|---|---|---|---|
| App iOS / Android | 支持 | 支持 | 按 http(s):// 或 ws(s):// 自动选择 |
| 微信小程序 | 支持 | 支持 | 按 http(s):// 或 ws(s):// 自动选择 |
| 百度智能小程序 | 支持 | 支持 | 按 http(s):// 或 ws(s):// 自动选择 |
| 抖音小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| 支付宝小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| 小红书小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| 京东小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| 飞书小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| 快手小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
| QQ 小程序 | 不支持 | 支持 | baseURL / url 需要传 ws:// 或 wss:// |
如果使用 WebSocket 流式输出,可以参考服务端转接器项目
vm-openai-ws-proxy。该项目用于接收 vm-openai 的 WebSocket 请求,在服务端调用 OpenAI 兼容 /chat/completions,再把 SSE 流式响应转发回 WebSocket,同时避免在小程序端暴露真实 API Key。
导入方式
业务侧统一从插件根入口导入。插件内部会按平台条件编译分发到 App UTS 或对应小程序适配层:
import {
openAIStream,
abortStream,
streamRequest,
abortRequest
} from '@/uni_modules/vm-openai'
TypeScript 导入方式:
import {
openAIStream,
abortStream,
streamRequest,
abortRequest
} from '@/uni_modules/vm-openai'
import type {
VMOpenAIChatMessage,
VMOpenAISSECompleteEvent,
VMOpenAISSEError,
VMOpenAISSEEvent,
VMOpenAIStreamChatOptions,
VMOpenAIStreamChunkEvent,
VMOpenAIStreamCompleteEvent,
VMOpenAIStreamError,
VMOpenAISSEOptions
} from '@/uni_modules/vm-openai/utssdk/interface.uts'
API 选择
| API | 适合场景 |
|---|---|
openAIStream |
OpenAI 兼容 /chat/completions,直接消费增量文本 |
streamRequest |
通用 SSE 请求,业务层自己处理 data |
快速开始
下面示例演示一个页面里最常见的用法:发送用户输入、流式累积模型回复、支持主动取消。
import {
openAIStream,
abortStream
} from '@/uni_modules/vm-openai'
export default {
data() {
return {
inputText: '',
answerText: '',
loading: false
}
},
methods: {
sendMessage() {
const content = this.inputText.trim()
if (!content || this.loading) {
return
}
this.answerText = ''
this.loading = true
openAIStream({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-plus',
messages: [
{
role: 'system',
content: '你是一个有帮助的助手。'
},
{
role: 'user',
content
}
],
onChunk: (res) => {
this.answerText += res.deltaText
},
onError: (err) => {
this.loading = false
uni.showToast({
title: err.message || '请求失败',
icon: 'none'
})
},
onComplete: () => {
this.loading = false
}
})
},
stopMessage() {
abortStream()
}
}
}
Vue3 组合式写法:
<script setup lang="ts">
import { ref } from 'vue'
import {
openAIStream,
abortStream
} from '@/uni_modules/vm-openai'
import type {
VMOpenAIChatMessage,
VMOpenAIStreamChunkEvent,
VMOpenAIStreamCompleteEvent,
VMOpenAIStreamError,
VMOpenAIStreamOpenEvent
} from '@/uni_modules/vm-openai/utssdk/interface.uts'
const inputText = ref('')
const answerText = ref('')
const loading = ref(false)
function sendMessage() {
const content = inputText.value.trim()
if (!content || loading.value) {
return
}
const messages: VMOpenAIChatMessage[] = [
{
role: 'system',
content: '你是一个有帮助的助手。'
},
{
role: 'user',
content
}
]
answerText.value = ''
loading.value = true
openAIStream({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-plus',
messages,
onOpen(res: VMOpenAIStreamOpenEvent) {
console.log('stream opened:', res.statusCode)
},
onChunk(res: VMOpenAIStreamChunkEvent) {
answerText.value += res.deltaText
},
onError(err: VMOpenAIStreamError) {
loading.value = false
uni.showToast({
title: err.message || '请求失败',
icon: 'none'
})
},
onComplete(res: VMOpenAIStreamCompleteEvent) {
loading.value = false
if (res.cancelled) {
console.log('stream cancelled')
}
}
})
}
function stopMessage() {
abortStream()
}
</script>
使用 WebSocket 转接器时,只需要把 baseURL 换成 WebSocket 地址,回调写法保持不变:
let answerText = ''
openAIStream({
apiKey: 'dummy',
baseURL: 'wss://your-domain.example.com/openai-stream',
model: 'qwen-plus',
messages: [
{ role: 'user', content: '你好' }
],
onOpen(res) {
console.log('websocket opened:', res.statusCode) // WebSocket 连接成功时通常为 101
},
onChunk(res) {
answerText += res.deltaText
console.log('delta:', res.deltaText)
console.log('answer:', answerText)
},
onError(err) {
console.log('websocket stream error:', err.statusCode, err.message, err.raw)
},
onComplete(res) {
if (res.cancelled) {
console.log('websocket stream cancelled')
return
}
console.log('websocket stream complete:', answerText)
}
})
// 需要主动停止时调用:
// abortStream()
说明:如果 API Key 已保存在 vm-openai-ws-proxy 服务端,客户端的 apiKey 可传占位值;真实 Key 不需要下发到小程序端。
WebSocket 服务端正常结束时需要返回 [DONE] 或 { "type": "done" },客户端才会触发 onComplete;连接失败或服务端返回 { "type": "error" } 时会触发 onError。
openAIStream
最小示例:
openAIStream({
apiKey: 'YOUR_API_KEY',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-plus',
messages: [
{ role: 'user', content: '你是谁?' }
],
onOpen(res) {
console.log('statusCode =', res.statusCode)
},
onChunk(res) {
console.log('deltaText =', res.deltaText)
},
onError(err) {
console.log('error =', err.message, err.statusCode, err.raw)
},
onComplete(res) {
console.log('done =', res.cancelled)
}
})
TypeScript 示例:
const messages: VMOpenAIChatMessage[] = [
{ role: 'user', content: '你是谁?' }
]
const options: VMOpenAIStreamChatOptions = {
apiKey: 'YOUR_API_KEY',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-plus',
messages,
onChunk(res: VMOpenAIStreamChunkEvent) {
console.log(res.deltaText)
},
onError(err: VMOpenAIStreamError) {
console.log(err.message)
},
onComplete(res: VMOpenAIStreamCompleteEvent) {
console.log(res.cancelled)
}
}
openAIStream(options)
入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
apiKey |
string |
是 | API Key |
model |
string |
是 | 模型名,例如 qwen-plus |
messages |
Array<VMOpenAIChatMessage> |
是 | 对话消息数组 |
baseURL |
string |
否 | 服务根地址,HTTP 协议会自动补 /chat/completions;App、微信/百度小程序传 ws:// / wss:// 时直接走 WebSocket;抖音/支付宝/小红书/京东/飞书/快手/QQ 小程序端需直接传 ws:// / wss:// 地址 |
url |
string |
否 | 完整请求地址,优先级高于 baseURL |
timeout |
number |
否 | 超时时间,单位毫秒 |
temperature |
number |
否 | 采样温度 |
top_p |
number |
否 | Top P |
max_tokens |
number |
否 | 最大输出 token |
presence_penalty |
number |
否 | presence penalty |
frequency_penalty |
number |
否 | frequency penalty |
onLog |
(res) => void |
否 | 调试日志回调 |
onOpen |
(res) => void |
否 | 连接建立成功回调 |
onChunk |
(res) => void |
否 | 增量文本回调 |
onError |
(err) => void |
否 | 错误回调 |
onComplete |
(res) => void |
否 | 结束回调 |
messages 元素结构:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
role |
string |
是 | 一般为 system / user / assistant |
content |
string |
是 | 文本内容 |
对应 TypeScript 类型:
type VMOpenAIChatMessage = {
role: string
content: string
}
回调出参
onLog(res)
| 字段 | 类型 | 说明 |
|---|---|---|
message |
string |
调试日志文本 |
onOpen(res)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接成功时为 101 |
onChunk(res)
| 字段 | 类型 | 说明 |
|---|---|---|
event |
string |
SSE 事件名,可能为空 |
raw |
string |
当前 chunk 的原始 data |
deltaText |
string |
当前新增文本 |
onError(err)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接失败前可能为 0 |
message |
string |
错误信息 |
raw |
string |
原始错误内容 |
onComplete(res)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接成功时为 101 |
cancelled |
boolean |
是否主动取消 |
text |
string |
当前实现不建议作为主输出使用 |
说明:
- 如果传 HTTP
baseURL,插件会自动补/chat/completions - App 支持
http(s)://原生 chunk/SSE 和ws(s)://WebSocket,会按协议自动分发;不支持的协议会触发onError - 微信小程序支持
http(s)://原生 chunk/SSE 和ws(s)://WebSocket,会按协议自动分发;不支持的协议会触发onError - 百度小程序支持
http(s)://原生 chunk/SSE 和ws(s)://WebSocket,会按协议自动分发;不支持的协议会触发onError - 抖音/支付宝/小红书/京东/飞书/快手/QQ 小程序端只支持 WebSocket 流式输出,
baseURL需要直接传ws:///wss://地址,不会自动转换协议 - 完整文本建议业务侧在
onChunk中自行累积
streamRequest
最小示例:
streamRequest({
url: 'https://example.com/sse',
method: 'POST',
headers: {
Authorization: 'Bearer xxx',
'Content-Type': 'application/json'
},
body: JSON.stringify({
stream: true
}),
onOpen(res) {
console.log('statusCode =', res.statusCode)
},
onEvent(res) {
console.log('event =', res.event)
console.log('id =', res.id)
console.log('data =', res.data)
},
onError(err) {
console.log('error =', err.message, err.statusCode, err.raw)
},
onComplete(res) {
console.log('done =', res.cancelled)
}
})
TypeScript 示例:
const options: VMOpenAISSEOptions = {
url: 'https://example.com/sse',
method: 'POST',
headers: {
Authorization: 'Bearer xxx',
'Content-Type': 'application/json'
} as UTSJSONObject,
body: JSON.stringify({ stream: true }),
onEvent(res: VMOpenAISSEEvent) {
console.log(res.event, res.id, res.data)
},
onError(err: VMOpenAISSEError) {
console.log(err.message)
},
onComplete(res: VMOpenAISSECompleteEvent) {
console.log(res.cancelled)
}
}
streamRequest(options)
OpenAI 兼容 SSE 解析示例:
let answerText = ''
streamRequest({
url: 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'qwen-plus',
stream: true,
messages: [
{ role: 'user', content: '写一个一句话笑话' }
]
}),
onEvent(res) {
if (!res.data || res.data === '[DONE]') {
return
}
try {
const payload = JSON.parse(res.data)
const deltaText = payload.choices &&
payload.choices[0] &&
payload.choices[0].delta &&
payload.choices[0].delta.content
if (deltaText) {
answerText += deltaText
console.log(answerText)
}
} catch (error) {
console.log('SSE data parse failed:', res.data)
}
},
onError(err) {
console.log('streamRequest error:', err.message)
},
onComplete() {
console.log('streamRequest complete:', answerText)
}
})
入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
url |
string |
是 | 请求地址;App、微信/百度小程序支持 http(s):// 和 ws(s):// 自动分发;抖音/支付宝/小红书/京东/飞书/快手/QQ 小程序端需直接传 ws:// / wss:// 地址 |
method |
string |
否 | 请求方法,默认 GET |
headers |
object |
否 | 请求头 |
body |
string |
否 | 请求体字符串 |
timeout |
number |
否 | 超时时间,单位毫秒 |
onLog |
(res) => void |
否 | 调试日志回调 |
onOpen |
(res) => void |
否 | 连接建立成功回调 |
onEvent |
(res) => void |
否 | SSE 事件回调 |
onError |
(err) => void |
否 | 错误回调 |
onComplete |
(res) => void |
否 | 结束回调 |
回调出参
onLog(res)
| 字段 | 类型 | 说明 |
|---|---|---|
message |
string |
调试日志文本 |
onOpen(res)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接成功时为 101 |
onEvent(res)
| 字段 | 类型 | 说明 |
|---|---|---|
event |
string |
SSE 事件名 |
id |
string |
SSE 事件 ID |
data |
string |
SSE 事件数据 |
onError(err)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接失败前可能为 0 |
message |
string |
错误信息 |
raw |
string |
原始错误内容 |
onComplete(res)
| 字段 | 类型 | 说明 |
|---|---|---|
statusCode |
number |
HTTP 状态码;WebSocket 连接成功时为 101 |
cancelled |
boolean |
是否主动取消 |
说明:
streamRequest不会帮你做 OpenAI JSON 解码- 如果服务端返回的是 OpenAI 兼容 SSE,请在业务层自己解析
res.data - App 会根据
streamRequest.url协议选择 HTTP SSE 或 WebSocket,不支持的协议会触发onError - 微信小程序会根据
streamRequest.url协议选择 request 或 WebSocket,不支持的协议会触发onError - 百度小程序会根据
streamRequest.url协议选择 request 或 WebSocket,不支持的协议会触发onError - 抖音/支付宝/小红书/京东/飞书/快手/QQ 小程序端只支持 WebSocket,
streamRequest.url需要直接传ws:///wss://地址
取消请求
| API | 返回值 | 说明 |
|---|---|---|
abortStream() |
void |
取消当前 openAIStream 请求 |
abortRequest() |
void |
取消当前 streamRequest 请求 |
取消后,onComplete 会回调:
onComplete(res) {
if (res.cancelled) {
console.log('request cancelled')
}
}
DashScope / qwen-plus 示例
openAIStream({
apiKey: 'YOUR_DASHSCOPE_API_KEY',
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model: 'qwen-plus',
messages: [
{ role: 'user', content: '你好' }
],
onChunk(res) {
console.log(res.deltaText)
}
})
建议
| 场景 | 建议 |
|---|---|
| OpenAI 兼容聊天接口 | 优先使用 openAIStream |
| 通用 SSE 接口 | 使用 streamRequest |
| 生产环境 | 不要直接暴露长期有效 API Key |

收藏人数:
购买普通授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 24
赞赏 0
下载 12173006
赞赏 1918
赞赏
京公网安备:11010802035340号