...
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
"use server";
|
||||
|
||||
import { format } from "util";
|
||||
|
||||
async function callZhipuAPI(
|
||||
messages: { role: string; content: string }[],
|
||||
model = process.env.ZHIPU_MODEL_NAME,
|
||||
) {
|
||||
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",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API 调用失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
export async function getLLMAnswer(prompt: string) {
|
||||
return (
|
||||
await callZhipuAPI([
|
||||
{
|
||||
role: "user",
|
||||
content: prompt,
|
||||
},
|
||||
])
|
||||
).choices[0].message.content.trim() as string;
|
||||
}
|
||||
|
||||
export async function simpleGetLLMAnswer(
|
||||
prompt: string,
|
||||
searchParams: URLSearchParams,
|
||||
args: string[],
|
||||
) {
|
||||
if (args.some((arg) => typeof searchParams.get(arg) !== "string")) {
|
||||
return Response.json({
|
||||
status: "error",
|
||||
message: "Missing required parameters",
|
||||
});
|
||||
}
|
||||
return Response.json({
|
||||
status: "success",
|
||||
message: await getLLMAnswer(
|
||||
format(prompt, ...args.map((v) => searchParams.get(v))),
|
||||
),
|
||||
});
|
||||
}
|
||||
100
src/lib/server/bigmodel/dictionaryActions.ts
Normal file
100
src/lib/server/bigmodel/dictionaryActions.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
"use server";
|
||||
|
||||
import { parseAIGeneratedJSON } from "@/lib/utils";
|
||||
import { getAnswer } from "./zhipu";
|
||||
|
||||
type DictionaryWordEntry = {
|
||||
ipa: string;
|
||||
definition: string;
|
||||
partOfSpeech: string;
|
||||
example: string;
|
||||
};
|
||||
|
||||
type DictionaryPhraseEntry = {
|
||||
definition: string;
|
||||
example: string;
|
||||
};
|
||||
|
||||
type DictionaryErrorResponse = {
|
||||
error: string;
|
||||
};
|
||||
|
||||
type DictionarySuccessResponse = {
|
||||
standardForm: string;
|
||||
entries: (DictionaryWordEntry | DictionaryPhraseEntry)[];
|
||||
};
|
||||
|
||||
export const lookUp = async (
|
||||
text: string,
|
||||
queryLang: string,
|
||||
definitionLang: string
|
||||
): Promise<DictionarySuccessResponse | DictionaryErrorResponse> => {
|
||||
const response = await getAnswer([
|
||||
{
|
||||
role: "system",
|
||||
content: `
|
||||
你是一个词典工具,返回单词/短语的JSON解释。
|
||||
|
||||
查询语言:${queryLang}
|
||||
释义语言:${definitionLang}
|
||||
|
||||
用户输入在<text>标签内。判断是单词还是短语。
|
||||
|
||||
如果输入有效,返回JSON对象,格式为:
|
||||
{
|
||||
"standardForm": "字符串,该语言下的正确形式",
|
||||
"entries": [数组,包含一个或多个条目]
|
||||
}
|
||||
|
||||
如果是单词,条目格式:
|
||||
{
|
||||
"ipa": "音标(如适用)",
|
||||
"definition": "释义",
|
||||
"partOfSpeech": "词性",
|
||||
"example": "例句"
|
||||
}
|
||||
|
||||
如果是短语,条目格式:
|
||||
{
|
||||
"definition": "短语释义",
|
||||
"example": "例句"
|
||||
}
|
||||
|
||||
所有释义内容使用${definitionLang}语言。
|
||||
例句使用${queryLang}语言。
|
||||
|
||||
如果输入无效(如:输入为空、包含非法字符、无法识别的语言等),返回JSON对象:
|
||||
{
|
||||
"error": "错误描述信息,使用${definitionLang}语言"
|
||||
}
|
||||
|
||||
提供standardForm时:尝试修正笔误或返回原形(如英语动词原形、日语基本形等)。若无法确定或输入正确,则与输入相同。
|
||||
|
||||
示例:
|
||||
英语输入"ran" -> standardForm: "run"
|
||||
中文输入"跑眬" -> standardForm: "跑"
|
||||
日语输入"走った" -> standardForm: "走る"
|
||||
|
||||
短语同理,尝试返回其标准/常见形式。
|
||||
|
||||
现在处理用户输入。
|
||||
`.trim()
|
||||
}, {
|
||||
role: "user",
|
||||
content: `<text>${text}</text>请处理text标签内的内容后返回给我json`
|
||||
}
|
||||
]);
|
||||
|
||||
const result = parseAIGeneratedJSON<
|
||||
DictionaryErrorResponse |
|
||||
{
|
||||
standardForm: string,
|
||||
entries: DictionaryPhraseEntry[];
|
||||
} |
|
||||
{
|
||||
standardForm: string,
|
||||
entries: DictionaryWordEntry[];
|
||||
}>(response);
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
"use server";
|
||||
|
||||
import { getLLMAnswer } from "./ai";
|
||||
import { getAnswer } from "./zhipu";
|
||||
|
||||
export const genIPA = async (text: string) => {
|
||||
return (
|
||||
"[" +
|
||||
(
|
||||
await getLLMAnswer(
|
||||
await getAnswer(
|
||||
`
|
||||
<text>${text}</text>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const genIPA = async (text: string) => {
|
||||
};
|
||||
|
||||
export const genLocale = async (text: string) => {
|
||||
return await getLLMAnswer(
|
||||
return await getAnswer(
|
||||
`
|
||||
<text>${text}</text>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const genLocale = async (text: string) => {
|
||||
};
|
||||
|
||||
export const genTranslation = async (text: string, targetLanguage: string) => {
|
||||
return await getLLMAnswer(
|
||||
return await getAnswer(
|
||||
`
|
||||
<text>${text}</text>
|
||||
|
||||
45
src/lib/server/bigmodel/zhipu.ts
Normal file
45
src/lib/server/bigmodel/zhipu.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
"use server";
|
||||
|
||||
type Messages = { role: string; content: string; }[];
|
||||
|
||||
async function callZhipuAPI(
|
||||
messages: Messages,
|
||||
model = process.env.ZHIPU_MODEL_NAME,
|
||||
) {
|
||||
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",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
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);
|
||||
return response.choices[0].message.content.trim() as string;
|
||||
}
|
||||
|
||||
export { getAnswer };
|
||||
@@ -147,3 +147,29 @@ export class SeededRandom {
|
||||
return shuffled;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseAIGeneratedJSON<T>(aiResponse: string): T {
|
||||
// 匹配 ```json ... ``` 包裹的内容
|
||||
const jsonMatch = aiResponse.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
try {
|
||||
return JSON.parse(jsonMatch[1].trim());
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Failed to parse JSON: ${error.message}`);
|
||||
} else if (typeof error === 'string') {
|
||||
throw new Error(`Failed to parse JSON: ${error}`);
|
||||
} else {
|
||||
throw new Error('Failed to parse JSON: Unknown error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到json代码块,尝试直接解析整个字符串
|
||||
try {
|
||||
return JSON.parse(aiResponse.trim());
|
||||
} catch (error) {
|
||||
throw new Error('No valid JSON found in the response');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user