feat: 项目初始化、完成基本流式传输和语音识别功能

This commit is contained in:
2025-06-28 19:21:46 +08:00
commit d6f9cd7aed
91 changed files with 7827 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
import type { AxiosRequestConfig } from "axios"
import type { IChatWithLLMRequest } from "@/interfaces"
import BaseClientService, { BaseUrl } from "./base_service.ts"
export class ChatService {
public static basePath = BaseUrl
/** Chat with LLM */
public static async ChatWithLLM(
accessToken: string,
request: IChatWithLLMRequest,
onProgress: (content: string) => void,
) {
let response
let buffer = ""
let accumulatedContent = ""
try {
response = await fetch("/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(request),
})
if (!response.ok) {
// eslint-disable-next-line unicorn/error-message
throw new Error()
}
const reader = response.body?.getReader()
const decoder = new TextDecoder()
while (true) {
const { done, value } = await reader!.read()
if (done)
break
// 将二进制数据转为字符串并存入缓冲区
buffer += decoder.decode(value)
// 查找换行符分割数据
const lines = buffer.split("\n")
// 保留未处理完的部分
buffer = lines.pop() || ""
// 处理每一行
for (const line of lines) {
const trimmedLine = line.trim()
if (!trimmedLine)
continue
if (trimmedLine.startsWith("data: ")) {
const jsonStr = trimmedLine.slice(6)
// 处理结束标记
if (jsonStr === "[DONE]") {
onProgress(accumulatedContent) // 最终更新
return
}
try {
const data = JSON.parse(jsonStr)
if (data.choices?.[0]?.delta?.content) {
// 累积内容
accumulatedContent += data.choices[0].delta.content
// 触发回调
onProgress(accumulatedContent)
}
}
catch (err) {
console.error("JSON解析失败:", err)
}
}
}
}
}
catch (err) {
console.error("Error:", err)
}
}
// 获取模型列表
public static GetModelList(config?: AxiosRequestConfig<any>) {
return BaseClientService.get(`${this.basePath}/model/list`, config)
}
}