目前要等待 AI 输出完再显示,阻塞太久了体验很差。下面是简单的流式输出答案的代码(真实可用,仅供参考),我没开发过思源插件,也不知道官方 API 是否支持对接 AI 输出,但我觉得这个可能由官方支持比较靠谱,衷心希望官方考虑下...
async function main() {
// 定义可用模型列表
const modelList = [
'deepseek-r1:14b',
'qwen2.5'
];
// 创建 petite-vue 应用
createApp({
model: modelList[0],
question: 'strawberry里有几个r', // 默认问题
output: null, // 输出内容,初始为 null
history: [], // 聊天记录
// 定义异步聊天函数
async chat(question, container) {
// 如果输入为空,则不执行操作
if (question === '') return
// 清空输入框内容
this.question = ''
// 将新问题和空的答案添加到聊天记录中
this.history.push({ question, answer: '' })
let action = this.history[this.history.length - 1]
// Ollama 的 API 地址
const apiUrl = "http://localhost:11434/api/generate"
// 选定的模型名称
const modelName = this.model
// 发送请求到 API
const resp = await fetch(apiUrl, {
method: "POST", // 请求方法为 POST
headers: {
"Content-Type": "application/json" // 请求头内容类型为 JSON
},
body: JSON.stringify({
model: modelName, // 指定使用的模型
prompt: question // 发送的问题内容
})
})
// 获取响应体的读取器,用于逐步读取流式内容
const reader = resp.body.getReader()
// 创建文本解码器,将二进制数据解码为文本
const textDecoder = new TextDecoder("utf-8")
// 配置 Showdown 转换器的选项
const options = {
parseImgDimensions: true, /* 支持定义图片尺寸 */
tables: true, /* 启用表格语法 */
strikethrough: true, /* 启用删除线 */
tasklists: true, /* 启用待办列表 */
emoji: true, /* 启用 emoji 表情 */
splitAdjacentBlockquotes: true, /* 分离相邻的引用块 */
moreStyling: true, /* 启用更多样式支持 */
}
// 使用 Showdown 转换器将答案从 Markdown 转为 HTML
const converter = new showdown.Converter(options)
// 循环读取 API 返回的流式内容
while (1) {
const { done, value } = await reader.read() // 读取一段内容
if (done) {
// 针对deepseek的思考部分
if (action.answer.includes('</think>')) {
const arr = action.answer.split('</think>')
const think = arr[0] + '</think>'
const answer = arr[1]
action.answer = think + converter.makeHtml(answer)
} else {
action.answer = converter.makeHtml(action.answer)
}
log(action.answer)
log('=== END ===') // 打印结束标志
break // 退出循环
}
// 解码流式内容为字符串
const answer = textDecoder.decode(value).trim()
// 将流式内容按行分割并逐行处理
const arr = answer.split('\n').map(item => {
log(item) // 打印每行内容
try {
// 尝试将 JSON 字符串解析为对象,并返回响应内容
const obj = JSON.parse(item.trim())
return obj.response
} catch (err) {
// 如果解析失败,返回空字符串
return ''
}
})
// 将解析的内容添加到当前问题的答案中
action.answer += arr.join('')
}
}
}).mount('#app') // 将应用挂载到页面上的 #app 容器中
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于