feat: 重构配置管理,使用数据库替代环境变量,优化系统配置读取
This commit is contained in:
+20
-14
@@ -1,8 +1,6 @@
|
||||
// server/utils/openai.ts - OpenAI Chat Completions 流式调用工具
|
||||
import { serverEnv } from "~~/server/utils/env";
|
||||
import { getSysConfig } from "~~/server/utils/sysConfig";
|
||||
|
||||
/** 旧项目的 `OPENAI_API_BASE` 语义是完整 base,例如 `https://api.openai.com/v1` */
|
||||
const CHAT_COMPLETIONS_URL = `${serverEnv.openAiApiBase}/chat/completions`;
|
||||
/** 复用编码器计算 SSE data 字节长度,避免循环里频繁创建对象 */
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
@@ -56,21 +54,25 @@ export const askAnswerStream = async ({
|
||||
prompt: string;
|
||||
systemPrompt: string;
|
||||
}): Promise<AskAnswerStreamResult> => {
|
||||
if (!serverEnv.openAiApiKey) {
|
||||
const cfg = await getSysConfig();
|
||||
|
||||
if (!cfg.openAiApiKey) {
|
||||
throw new Error("OPENAI_API_KEY is not set.");
|
||||
}
|
||||
|
||||
const chatCompletionsUrl = `${cfg.openAiApiBase}/chat/completions`;
|
||||
|
||||
// 这里不要记录 request body:其中包含完整题目和可能的选项文本
|
||||
const response = await fetch(CHAT_COMPLETIONS_URL, {
|
||||
const response = await fetch(chatCompletionsUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${serverEnv.openAiApiKey}`,
|
||||
Authorization: `Bearer ${cfg.openAiApiKey}`,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: serverEnv.openAiModel,
|
||||
temperature: serverEnv.temperature,
|
||||
max_tokens: serverEnv.maxTokens,
|
||||
model: cfg.openAiModel,
|
||||
temperature: cfg.temperature,
|
||||
max_tokens: cfg.maxTokens,
|
||||
stream: true,
|
||||
messages: [
|
||||
{
|
||||
@@ -94,7 +96,10 @@ export const askAnswerStream = async ({
|
||||
}
|
||||
|
||||
// 将 SSE delta 读完后再返回给 API handler,由 handler 统一做答案清洗和缓存
|
||||
const upstreamResponse = await readChatCompletionStream(response.body);
|
||||
const upstreamResponse = await readChatCompletionStream(
|
||||
response.body,
|
||||
cfg.maxSseLineBytes
|
||||
);
|
||||
|
||||
return {
|
||||
answer: upstreamResponse.content.trim(),
|
||||
@@ -112,7 +117,8 @@ export const askAnswerStream = async ({
|
||||
* 网络 chunk 不一定按行对齐,所以用 buffer 保存尚未拼完整的一行
|
||||
*/
|
||||
export const readChatCompletionStream = async (
|
||||
stream: ReadableStream<Uint8Array>
|
||||
stream: ReadableStream<Uint8Array>,
|
||||
maxSseLineBytes: number
|
||||
): Promise<ChatCompletionStreamResult> => {
|
||||
const reader = stream.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
@@ -146,14 +152,14 @@ export const readChatCompletionStream = async (
|
||||
|
||||
// stream: true 可以正确处理跨 chunk 的多字节字符,例如中文
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
assertSseBufferSize(buffer, serverEnv.maxSseLineBytes);
|
||||
assertSseBufferSize(buffer, maxSseLineBytes);
|
||||
|
||||
// 只处理已经遇到换行的完整 SSE 行,最后一段留到下次 chunk 再拼
|
||||
const lines = buffer.split(/\r?\n/u);
|
||||
buffer = lines.pop() ?? "";
|
||||
|
||||
for (const line of lines) {
|
||||
const chunk = parseSseDataLine(line, serverEnv.maxSseLineBytes);
|
||||
const chunk = parseSseDataLine(line, maxSseLineBytes);
|
||||
if (chunk) collectChunk(chunk);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +167,7 @@ export const readChatCompletionStream = async (
|
||||
// 处理流结束时仍留在 buffer 中的最后一行
|
||||
const finalText = buffer + decoder.decode();
|
||||
for (const line of finalText.split(/\r?\n/u)) {
|
||||
const chunk = parseSseDataLine(line, serverEnv.maxSseLineBytes);
|
||||
const chunk = parseSseDataLine(line, maxSseLineBytes);
|
||||
if (chunk) collectChunk(chunk);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user