mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 17:47:27 +00:00
127 lines
4 KiB
Markdown
127 lines
4 KiB
Markdown
# 会话 API 实现逻辑
|
||
|
||
LobeChat 的大模型 AI 实现主要依赖于 OpenAI 的 API,包括后端的核心会话 API 和前端的集成 API。接下来,我们将分别介绍后端和前端的实现思路和代码。
|
||
|
||
## 后端实现
|
||
|
||
以下代码中移除了鉴权、错误处理等逻辑,仅保留了核心的主要功能逻辑。
|
||
|
||
### 核心会话 API
|
||
|
||
在 `src/app/api/openai/chat/handler.ts` 文件中,我们定义了 `POST` 方法,该方法首先从请求中解析出 payload 数据(即客户端发送的会话内容),然后从请求中获取 OpenAI 的授权信息。之后,我们创建一个 `openai` 对象,并调用 `createChatCompletion` 方法,该方法负责发送会话请求到 OpenAI 并返回结果。
|
||
|
||
```ts
|
||
export const POST = async (req: Request) => {
|
||
const payload = await req.json();
|
||
|
||
const { apiKey, endpoint } = getOpenAIAuthFromRequest(req);
|
||
|
||
const openai = createOpenai(apiKey, endpoint);
|
||
|
||
return createChatCompletion({ openai, payload });
|
||
};
|
||
```
|
||
|
||
### 会话结果处理
|
||
|
||
在 `src/app/api/openai/chat/createChatCompletion.ts` 文件中,我们定义了 `createChatCompletion` 方法,该方法首先对 payload 数据进行预处理,然后调用 OpenAI 的 `chat.completions.create` 方法发送请求,并使用 [Vercel AI SDK](https://sdk.vercel.ai/docs) 中的 `OpenAIStream` 将返回的结果转化为流式响应。
|
||
|
||
```ts
|
||
import { OpenAIStream, StreamingTextResponse } from 'ai';
|
||
|
||
export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
|
||
const { messages, ...params } = payload;
|
||
|
||
const formatMessages = messages.map((m) => ({
|
||
content: m.content,
|
||
name: m.name,
|
||
role: m.role,
|
||
}));
|
||
|
||
const response = await openai.chat.completions.create(
|
||
{
|
||
messages: formatMessages,
|
||
...params,
|
||
stream: true,
|
||
},
|
||
{ headers: { Accept: '*/*' } },
|
||
);
|
||
const stream = OpenAIStream(response);
|
||
return new StreamingTextResponse(stream);
|
||
};
|
||
```
|
||
|
||
## 前端实现
|
||
|
||
### 前端集成
|
||
|
||
在 `src/services/chatModel.ts` 文件中,我们定义了 `fetchChatModel` 方法,该方法首先对 payload 数据进行前置处理,然后发送 POST 请求到后端的 `/chat` 接口,并将请求结果返回。
|
||
|
||
```ts
|
||
export const fetchChatModel = (
|
||
{ plugins: enabledPlugins, ...params }: Partial<OpenAIStreamPayload>,
|
||
options?: FetchChatModelOptions,
|
||
) => {
|
||
const payload = merge(
|
||
{
|
||
model: initialLobeAgentConfig.model,
|
||
stream: true,
|
||
...initialLobeAgentConfig.params,
|
||
},
|
||
params,
|
||
);
|
||
|
||
const filterFunctions: ChatCompletionFunctions[] = pluginSelectors.enabledSchema(enabledPlugins)(
|
||
usePluginStore.getState(),
|
||
);
|
||
|
||
const functions = filterFunctions.length === 0 ? undefined : filterFunctions;
|
||
|
||
return fetch(OPENAI_URLS.chat, {
|
||
body: JSON.stringify({ ...payload, functions }),
|
||
headers: createHeaderWithOpenAI({ 'Content-Type': 'application/json' }),
|
||
method: 'POST',
|
||
signal: options?.signal,
|
||
});
|
||
};
|
||
```
|
||
|
||
### 使用流式获取结果
|
||
|
||
在 `src/utils/fetch.ts` 文件中,我们定义了 `fetchSSE` 方法,该方法使用流式方法获取数据,当读取到新的数据块时,会调用 `onMessageHandle` 回调函数处理数据块,进而实现打字机输出效果。
|
||
|
||
```ts
|
||
export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchSSEOptions = {}) => {
|
||
const response = await fetchFn();
|
||
|
||
if (!response.ok) {
|
||
const chatMessageError = await getMessageError(response);
|
||
|
||
options.onErrorHandle?.(chatMessageError);
|
||
return;
|
||
}
|
||
|
||
const returnRes = response.clone();
|
||
|
||
const data = response.body;
|
||
|
||
if (!data) return;
|
||
|
||
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);
|
||
|
||
options.onMessageHandle?.(chunkValue);
|
||
}
|
||
|
||
return returnRes;
|
||
};
|
||
```
|
||
|
||
以上就是 LobeChat 会话 API 的核心实现。在理解了这些核心代码的基础上,便可以进一步扩展和优化 LobeChat 的 AI 功能。
|