-
-
Notifications
You must be signed in to change notification settings - Fork 72
Chat API.zh CN
LobeVidol 的大模型 AI 实现主要依赖于 OpenAI 的 API,包括后端的核心会话 API 和前端的集成 API。接下来,我们将分别介绍后端和前端的实现思路和代码。
以下代码中移除了鉴权、错误处理等逻辑,仅保留了核心的主要功能逻辑。
在 src/app/api/chat/openai/route.ts
中,定义了一个处理 POST 请求的方法,主要负责从请求体中提取 OpenAIChatStreamPayload
类型的 payload,并使用 createBizOpenAI
函数根据请求和模型信息创建 OpenAI 实例。随后,该方法调用 createChatCompletion
来处理实际的会话,并返回响应结果。如果创建 OpenAI 实例过程中出现错误,则直接返回错误响应。
export const POST = async (req: Request) => {
const payload = (await req.json()) as OpenAIChatStreamPayload;
const openaiOrErrResponse = createBizOpenAI(req, payload.model);
// if resOrOpenAI is a Response, it means there is an error,just return it
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
return createChatCompletion({ openai: openaiOrErrResponse, payload });
};
而在 src/app/api/openai/chat/createChatCompletion.ts
文件中,createChatCompletion
方法主要负责与 OpenAI API 进行交互,处理会话请求。它首先对 payload 中的消息进行预处理,然后通过 openai.chat.completions.create
方法发送 API 请求,并使用 OpenAIStream
将返回的响应转换为流式格式。如果在 API 调用过程中出现错误,方法将生成并处理相应的错误响应。
import { OpenAIStream, StreamingTextResponse } from 'ai';
export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
// 预处理消息
const { messages, ...params } = payload;
// 发送 API 请求
try {
const response = await openai.chat.completions.create(
{
messages,
...params,
stream: true,
} as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
{ headers: { Accept: '*/*' } },
);
const stream = OpenAIStream(response);
return new StreamingTextResponse(stream);
} catch (error) {
// 检查错误是否为 OpenAI APIError
if (error instanceof OpenAI.APIError) {
let errorResult: any;
// 如果错误是 OpenAI APIError,那么会有一个 error 对象
if (error.error) {
errorResult = error.error;
} else if (error.cause) {
errorResult = error.cause;
}
// 如果没有其他请求错误,错误对象是一个类似 Response 的对象
else {
errorResult = { headers: error.headers, stack: error.stack, status: error.status };
}
console.error(errorResult);
// 返回错误响应
return createErrorResponse(ChatErrorType.OpenAIBizError, {
endpoint: openai.baseURL,
error: errorResult,
});
}
console.error(error);
return createErrorResponse(ChatErrorType.InternalServerError, {
endpoint: openai.baseURL,
error: JSON.stringify(error),
});
}
};
在 src/services/chat.ts
文件中,我们定义了 ChatService
类。这个类提供了一些方法来处理与 OpenAI 聊天 API 的交互。
createAssistantMessage
方法用于创建一个新的助手消息。它接收一个包含插件、消息和其他参数的对象,以及一个可选的 FetchOptions
对象。这个方法会合并默认的代理配置和传入的参数,预处理消息和工具,然后调用 getChatCompletion
方法获取聊天完成任务。
getChatCompletion
方法用于获取聊天完成任务。它接收一个 OpenAIChatStreamPayload
对象和一个可选的 FetchOptions
对象。这个方法会合并默认的代理配置和传入的参数,然后发送 POST 请求到 OpenAI 的聊天 API。
processMessages
方法用于处理聊天消息。它接收一个聊天消息数组,一个可选的模型名称,和一个可选的工具数组。这个方法会处理消息内容,将输入的 messages
数组映射为 OpenAIChatMessage
类型的数组,如果存在启用的工具,将工具的系统角色添加到系统消息中。
class ChatService {
// 创建一个新的助手消息
createAssistantMessage(params: object, fetchOptions?: FetchOptions) {
// 实现细节...
}
// 获取聊天完成任务
getChatCompletion(payload: OpenAIChatStreamPayload, fetchOptions?: FetchOptions) {
// 实现细节...
}
// 处理聊天消息
processMessages(messages: ChatMessage[], modelName?: string, tools?: Tool[]) {
// 实现细节...
}
}
在 src/utils/fetch.ts
文件中,我们定义了 fetchSSE
方法,该方法使用流式方法获取数据,当读取到新的数据块时,会调用 onMessageHandle
回调函数处理数据块,进而实现打字机输出效果。
export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchSSEOptions = {}) => {
const response = await fetchFn();
// 如果不 ok 说明有请求错误
if (!response.ok) {
const chatMessageError = await getMessageError(response);
options.onErrorHandle?.(chatMessageError);
return;
}
const returnRes = response.clone();
const data = response.body;
if (!data) return;
let output = '';
const reader = data.getReader();
const decoder = new TextDecoder();
let done = false;
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
const chunkValue = decoder.decode(value, { stream: true });
output += chunkValue;
options.onMessageHandle?.(chunkValue);
}
await options?.onFinish?.(output);
return returnRes;
};
以上就是 LobeVidol 会话 API 的核心实现。在理解了这些核心代码的基础上,便可以进一步扩展和优化 LobeVidol 的 AI 功能。
This is the 🤗 / 🤖 Lobe Vidol wiki. Wiki Home