refactor: 使用 openai SDK 替换 fetch 调用 LLM
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- 安装 openai 包 - 重命名 zhipu.ts -> llm.ts - 使用 OpenAI SDK 替代原生 fetch 实现 - 更新所有导入路径
This commit is contained in:
@@ -22,6 +22,7 @@
|
|||||||
"next": "16.1.1",
|
"next": "16.1.1",
|
||||||
"next-intl": "^4.7.0",
|
"next-intl": "^4.7.0",
|
||||||
"nodemailer": "^8.0.2",
|
"nodemailer": "^8.0.2",
|
||||||
|
"openai": "^6.27.0",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
"react-dom": "19.2.3",
|
"react-dom": "19.2.3",
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -45,6 +45,9 @@ importers:
|
|||||||
nodemailer:
|
nodemailer:
|
||||||
specifier: ^8.0.2
|
specifier: ^8.0.2
|
||||||
version: 8.0.2
|
version: 8.0.2
|
||||||
|
openai:
|
||||||
|
specifier: ^6.27.0
|
||||||
|
version: 6.27.0(zod@4.3.5)
|
||||||
pg:
|
pg:
|
||||||
specifier: ^8.16.3
|
specifier: ^8.16.3
|
||||||
version: 8.16.3
|
version: 8.16.3
|
||||||
@@ -2731,6 +2734,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
|
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
openai@6.27.0:
|
||||||
|
resolution: {integrity: sha512-osTKySlrdYrLYTt0zjhY8yp0JUBmWDCN+Q+QxsV4xMQnnoVFpylgKGgxwN8sSdTNw0G4y+WUXs4eCMWpyDNWZQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
ws: ^8.18.0
|
||||||
|
zod: ^3.25 || ^4.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
ws:
|
||||||
|
optional: true
|
||||||
|
zod:
|
||||||
|
optional: true
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
@@ -6162,6 +6177,10 @@ snapshots:
|
|||||||
is-inside-container: 1.0.0
|
is-inside-container: 1.0.0
|
||||||
wsl-utils: 0.1.0
|
wsl-utils: 0.1.0
|
||||||
|
|
||||||
|
openai@6.27.0(zod@4.3.5):
|
||||||
|
optionalDependencies:
|
||||||
|
zod: 4.3.5
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAnswer } from "../zhipu";
|
import { getAnswer } from "../llm";
|
||||||
import { parseAIGeneratedJSON } from "@/utils/json";
|
import { parseAIGeneratedJSON } from "@/utils/json";
|
||||||
import { PreprocessResult } from "./types";
|
import { PreprocessResult } from "./types";
|
||||||
import { createLogger } from "@/lib/logger";
|
import { createLogger } from "@/lib/logger";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAnswer } from "../zhipu";
|
import { getAnswer } from "../llm";
|
||||||
import { parseAIGeneratedJSON } from "@/utils/json";
|
import { parseAIGeneratedJSON } from "@/utils/json";
|
||||||
import { EntriesGenerationResult } from "./types";
|
import { EntriesGenerationResult } from "./types";
|
||||||
import { createLogger } from "@/lib/logger";
|
import { createLogger } from "@/lib/logger";
|
||||||
|
|||||||
37
src/lib/bigmodel/llm.ts
Normal file
37
src/lib/bigmodel/llm.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import OpenAI from "openai";
|
||||||
|
|
||||||
|
const openai = new OpenAI({
|
||||||
|
apiKey: process.env.ZHIPU_API_KEY,
|
||||||
|
baseURL: "https://open.bigmodel.cn/api/paas/v4",
|
||||||
|
});
|
||||||
|
|
||||||
|
type Messages = Array<
|
||||||
|
| { role: "system"; content: string }
|
||||||
|
| { role: "user"; content: string }
|
||||||
|
| { role: "assistant"; content: string }
|
||||||
|
>;
|
||||||
|
|
||||||
|
async function getAnswer(prompt: string): Promise<string>;
|
||||||
|
async function getAnswer(prompt: Messages): Promise<string>;
|
||||||
|
async function getAnswer(prompt: string | Messages): Promise<string> {
|
||||||
|
const messages: Messages = typeof prompt === "string"
|
||||||
|
? [{ role: "user", content: prompt }]
|
||||||
|
: prompt;
|
||||||
|
|
||||||
|
const response = await openai.chat.completions.create({
|
||||||
|
model: process.env.ZHIPU_MODEL_NAME || "glm-4",
|
||||||
|
messages: messages as OpenAI.Chat.Completions.ChatCompletionMessageParam[],
|
||||||
|
temperature: 0.2,
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = response.choices[0]?.message?.content;
|
||||||
|
if (!content) {
|
||||||
|
throw new Error("AI API 返回空响应");
|
||||||
|
}
|
||||||
|
|
||||||
|
return content.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getAnswer };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAnswer } from "../zhipu";
|
import { getAnswer } from "../llm";
|
||||||
import { parseAIGeneratedJSON } from "@/utils/json";
|
import { parseAIGeneratedJSON } from "@/utils/json";
|
||||||
import { LanguageDetectionResult, TranslationLLMResponse } from "./types";
|
import { LanguageDetectionResult, TranslationLLMResponse } from "./types";
|
||||||
import { createLogger } from "@/lib/logger";
|
import { createLogger } from "@/lib/logger";
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
type Messages = { role: string; content: string; }[];
|
|
||||||
|
|
||||||
const LLM_TIMEOUT_MS = 30000;
|
|
||||||
|
|
||||||
async function callZhipuAPI(
|
|
||||||
messages: Messages,
|
|
||||||
model = process.env.ZHIPU_MODEL_NAME,
|
|
||||||
) {
|
|
||||||
const controller = new AbortController();
|
|
||||||
const timeoutId = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
|
|
||||||
|
|
||||||
const url = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer " + process.env.ZHIPU_API_KEY,
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: model,
|
|
||||||
messages: messages,
|
|
||||||
temperature: 0.2,
|
|
||||||
thinking: {
|
|
||||||
type: "disabled",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
signal: controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`API 调用失败: ${response.status} ${response.statusText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await response.json();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getAnswer(prompt: string): Promise<string>;
|
|
||||||
async function getAnswer(prompt: Messages): Promise<string>;
|
|
||||||
async function getAnswer(prompt: string | Messages): Promise<string> {
|
|
||||||
const messages = typeof prompt === "string"
|
|
||||||
? [{ role: "user", content: prompt }]
|
|
||||||
: prompt;
|
|
||||||
|
|
||||||
const response = await callZhipuAPI(messages);
|
|
||||||
|
|
||||||
if (!response.choices?.[0]?.message?.content) {
|
|
||||||
throw new Error("AI API 返回空响应");
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.choices[0].message.content.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
export { getAnswer };
|
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
import { ValidateError } from "@/lib/errors";
|
import { ValidateError } from "@/lib/errors";
|
||||||
import { createLogger } from "@/lib/logger";
|
import { createLogger } from "@/lib/logger";
|
||||||
import { serviceTranslateText } from "./translator-service";
|
import { serviceTranslateText } from "./translator-service";
|
||||||
import { getAnswer } from "@/lib/bigmodel/zhipu";
|
import { getAnswer } from "@/lib/bigmodel/llm";
|
||||||
|
|
||||||
const log = createLogger("translator-action");
|
const log = createLogger("translator-action");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user