修正供应商,修正多余代码

This commit is contained in:
ACT丶流星雨 2026-04-12 22:31:29 +08:00
parent 493e9c2ef0
commit 4a5ac8732f
19 changed files with 243 additions and 450 deletions

View File

@ -133,7 +133,7 @@ declare const exports: {
const vendor: VendorConfig = { const vendor: VendorConfig = {
id: "volcengine", id: "volcengine",
version: "2.1", version: "2.2",
author: "leeqi", author: "leeqi",
name: "火山引擎(豆包)", name: "火山引擎(豆包)",
description: description:
@ -301,10 +301,10 @@ const textRequest = (model: TextModel, think: boolean, thinkLevel: 0 | 1 | 2 | 3
3: "high", 3: "high",
}; };
return createOpenAI({ return createOpenAICompatible({
name: "volcengine",
baseURL: getBaseUrl(), baseURL: getBaseUrl(),
apiKey, apiKey,
compatibility: "compatible",
fetch: async (url: string, options?: RequestInit) => { fetch: async (url: string, options?: RequestInit) => {
const rawBody = JSON.parse((options?.body as string) ?? "{}"); const rawBody = JSON.parse((options?.body as string) ?? "{}");
const modifiedBody = { const modifiedBody = {
@ -319,7 +319,7 @@ const textRequest = (model: TextModel, think: boolean, thinkLevel: 0 | 1 | 2 | 3
body: JSON.stringify(modifiedBody), body: JSON.stringify(modifiedBody),
}); });
}, },
}).chat(model.modelName); }).chatModel(model.modelName);
}; };
const imageRequest = async (config: ImageConfig, model: ImageModel): Promise<string> => { const imageRequest = async (config: ImageConfig, model: ImageModel): Promise<string> => {

View File

@ -1 +1 @@
1.1.3 1.1.4

View File

@ -99,4 +99,4 @@
"better-sqlite3": "^12.8.0" "better-sqlite3": "^12.8.0"
} }
} }
} }

View File

@ -17,6 +17,11 @@ export interface AgentContext {
abortSignal?: AbortSignal; abortSignal?: AbortSignal;
resTool: ResTool; resTool: ResTool;
msg: ReturnType<ResTool["newMessage"]>; msg: ReturnType<ResTool["newMessage"]>;
messages?: { role: "user" | "assistant" | "system"; content: string }[];
thinkConfig: {
think: boolean;
thinlLevel: 0 | 1 | 2 | 3;
};
} }
function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string { function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
@ -35,7 +40,7 @@ function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
return `## Memory\n以下是你对用户的记忆可作为参考但不要主动提及\n${memoryContext}`; return `## Memory\n以下是你对用户的记忆可作为参考但不要主动提及\n${memoryContext}`;
} }
export async function decisionAI(ctx: AgentContext) { export async function runDecisionAI(ctx: AgentContext) {
const { isolationKey, text, abortSignal } = ctx; const { isolationKey, text, abortSignal } = ctx;
const memory = new Memory("productionAgent", isolationKey); const memory = new Memory("productionAgent", isolationKey);
await memory.add("user", text); await memory.add("user", text);
@ -45,17 +50,17 @@ export async function decisionAI(ctx: AgentContext) {
const projectInfo = await u.db("o_project").where("id", ctx.resTool.data.projectId).first(); const projectInfo = await u.db("o_project").where("id", ctx.resTool.data.projectId).first();
if (!projectInfo) throw new Error(`项目不存在ID: ${ctx.resTool.data.projectId}`); if (!projectInfo) throw new Error(`项目不存在ID: ${ctx.resTool.data.projectId}`);
const [_, imageModelName] = projectInfo.imageModel!.split(":"); const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/)
const [id, videoModelName] = projectInfo.videoModel!.split(":"); const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/)
const models = await u.vendor.getModelList(id); const models = await u.vendor.getModelList(id);
if(!models.length) throw new Error(`项目使用的模型不存在ID: ${projectInfo.videoModel}`); if (!models.length) throw new Error(`项目使用的模型不存在ID: ${projectInfo.videoModel}`);
const findData = models.find((i: any) => i.modelName == videoModelName); const findData = models.find((i: any) => i.modelName == videoModelName);
const isRef = findData.mode.every((i: any) => Array.isArray(i)); const isRef = findData.mode.every((i: any) => Array.isArray(i));
const modelInfo = `项目使用的模型如下:\n图像模型${imageModelName}\n视频模型${videoModelName}\n多参${isRef ? "是" : "否"}`; const modelInfo = `项目使用的模型如下:\n图像模型${imageModelName}\n视频模型${videoModelName}\n多参${isRef ? "是" : "否"}`;
const mem = buildMemPrompt(await memory.get(text)); const mem = buildMemPrompt(await memory.get(text));
const { textStream } = await u.Ai.Text("productionAgent").stream({ const { fullStream } = await u.Ai.Text("productionAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({
messages: [ messages: [
{ role: "system", content: prompt }, { role: "system", content: prompt },
{ role: "assistant", content: mem + "\n" + modelInfo }, { role: "assistant", content: mem + "\n" + modelInfo },
@ -72,7 +77,13 @@ export async function decisionAI(ctx: AgentContext) {
}, },
}); });
return textStream; let currentMsg = ctx.msg;
await consumeFullStream(fullStream, currentMsg, () => {
if (ctx.msg === currentMsg) return currentMsg;
currentMsg.complete();
currentMsg = ctx.msg;
return currentMsg;
});
} }
async function createSubAgent(parentCtx: AgentContext) { async function createSubAgent(parentCtx: AgentContext) {
@ -95,29 +106,15 @@ async function createSubAgent(parentCtx: AgentContext) {
}) { }) {
parentCtx.msg.complete(); parentCtx.msg.complete();
const subMsg = resTool.newMessage("assistant", name); const subMsg = resTool.newMessage("assistant", name);
const text = subMsg.text();
let fullResponse = "";
const { textStream } = await u.Ai.Text("productionAgent").stream({ const { fullStream } = await u.Ai.Text("productionAgent", parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel).stream({
system, system,
messages: messages ?? [{ role: "user", content: prompt }], messages: messages ?? [{ role: "user", content: prompt }],
abortSignal, abortSignal,
tools: { ...extraTools, ...useTools({ resTool, msg: subMsg }) }, tools: { ...extraTools, ...useTools({ resTool, msg: subMsg }) },
}); });
try { const fullResponse = await consumeFullStream(fullStream, subMsg);
for await (const chunk of textStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
text.append(chunk);
fullResponse += chunk;
}
text.complete();
subMsg.complete();
} catch (err: any) {
text.complete();
subMsg.stop();
throw err;
}
if (fullResponse.trim()) { if (fullResponse.trim()) {
await memory.add(memoryKey, removeAllXmlTags(fullResponse), { await memory.add(memoryKey, removeAllXmlTags(fullResponse), {
@ -138,10 +135,10 @@ async function createSubAgent(parentCtx: AgentContext) {
if (!projectInfo) throw new Error(`项目不存在ID: ${resTool.data.projectId}`); if (!projectInfo) throw new Error(`项目不存在ID: ${resTool.data.projectId}`);
const artSkills = await createArtSkills(projectInfo?.artStyle!, projectInfo?.directorManual!); const artSkills = await createArtSkills(projectInfo?.artStyle!, projectInfo?.directorManual!);
const [_, imageModelName] = projectInfo.imageModel!.split(":"); const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/)
const [id, videoModelName] = projectInfo.videoModel!.split(":"); const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/)
const models = await u.vendor.getModelList(id); const models = await u.vendor.getModelList(id);
if(!models.length) throw new Error(`项目使用的模型不存在ID: ${projectInfo.videoModel}`); if (!models.length) throw new Error(`项目使用的模型不存在ID: ${projectInfo.videoModel}`);
const findData = models.find((i: any) => i.modelName == videoModelName); const findData = models.find((i: any) => i.modelName == videoModelName);
const isRef = findData.mode.every((i: any) => Array.isArray(i)); const isRef = findData.mode.every((i: any) => Array.isArray(i));
const modelInfo = `项目使用的模型如下:\n图像模型${imageModelName}\n视频模型${videoModelName}\n多参${isRef ? "是" : "否"}`; const modelInfo = `项目使用的模型如下:\n图像模型${imageModelName}\n视频模型${videoModelName}\n多参${isRef ? "是" : "否"}`;
@ -370,7 +367,53 @@ ${buildSkillPrompt(mainSkills)}`,
}; };
return res; return res;
} }
async function consumeFullStream(
fullStream: AsyncIterable<any>,
initialMsg: ReturnType<ResTool["newMessage"]>,
syncMsg?: () => ReturnType<ResTool["newMessage"]>,
): Promise<string> {
let msg = initialMsg;
let text = msg.text();
let thinking: ReturnType<typeof msg.thinking> | null = null;
let thinkTime = 0;
let fullResponse = "";
try {
for await (const chunk of fullStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
if (syncMsg) {
const newMsg = syncMsg();
if (newMsg !== msg) {
msg = newMsg;
text = msg.text();
}
}
if (chunk.type === "reasoning-start") {
thinkTime = Date.now();
thinking = msg.thinking("思考中...");
} else if (chunk.type === "reasoning-delta") {
thinking?.append(chunk.text);
} else if (chunk.type === "reasoning-end") {
thinkTime = Date.now() - thinkTime;
thinking?.updateTitle(`思考完毕(${(thinkTime / 1000).toFixed(1)} 秒)`);
thinking?.complete();
thinking = null;
} else if (chunk.type === "text-delta") {
text.append(chunk.text);
fullResponse += chunk.text;
}
}
text.complete();
msg.complete();
} catch (err: any) {
thinking?.complete();
text.complete();
msg.stop();
throw err;
}
return fullResponse;
}
function removeAllXmlTags(text: string): string { function removeAllXmlTags(text: string): string {
text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?>([\s\S]*?)<\/\1>/g, ""); text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?>([\s\S]*?)<\/\1>/g, "");
text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?\/>/g, ""); text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?\/>/g, "");

View File

@ -16,6 +16,10 @@ export interface AgentContext {
abortSignal?: AbortSignal; abortSignal?: AbortSignal;
resTool: ResTool; resTool: ResTool;
msg: ReturnType<ResTool["newMessage"]>; msg: ReturnType<ResTool["newMessage"]>;
thinkConfig: {
think: boolean;
thinlLevel: 0 | 1 | 2 | 3;
};
} }
function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string { function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
@ -34,7 +38,7 @@ function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
return `## Memory\n以下是你对用户的记忆可作为参考但不要主动提及\n${memoryContext}`; return `## Memory\n以下是你对用户的记忆可作为参考但不要主动提及\n${memoryContext}`;
} }
export async function decisionAI(ctx: AgentContext) { export async function runDecisionAI(ctx: AgentContext) {
const { isolationKey, text, userMessageTime, abortSignal, resTool } = ctx; const { isolationKey, text, userMessageTime, abortSignal, resTool } = ctx;
const memory = new Memory("scriptAgent", isolationKey); const memory = new Memory("scriptAgent", isolationKey);
@ -59,7 +63,7 @@ export async function decisionAI(ctx: AgentContext) {
`章节数量:${novelData.length}`, `章节数量:${novelData.length}`,
].join("\n"); ].join("\n");
const { textStream } = await u.Ai.Text("scriptAgent").stream({ const { fullStream } = await u.Ai.Text("scriptAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({
messages: [ messages: [
{ role: "system", content: prompt }, { role: "system", content: prompt },
{ role: "assistant", content: projectInfo + "\n" + mem }, { role: "assistant", content: projectInfo + "\n" + mem },
@ -76,7 +80,13 @@ export async function decisionAI(ctx: AgentContext) {
}, },
}); });
return textStream; let currentMsg = ctx.msg;
await consumeFullStream(fullStream, currentMsg, () => {
if (ctx.msg === currentMsg) return currentMsg;
currentMsg.complete();
currentMsg = ctx.msg;
return currentMsg;
});
} }
function createSubAgent(parentCtx: AgentContext) { function createSubAgent(parentCtx: AgentContext) {
@ -100,29 +110,15 @@ function createSubAgent(parentCtx: AgentContext) {
}) { }) {
parentCtx.msg.complete(); parentCtx.msg.complete();
const subMsg = resTool.newMessage("assistant", name); const subMsg = resTool.newMessage("assistant", name);
const text = subMsg.text();
let fullResponse = "";
const { textStream } = await u.Ai.Text("scriptAgent").stream({ const { fullStream } = await u.Ai.Text("scriptAgent", parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel ).stream({
system, system,
messages: messages ?? [{ role: "user", content: prompt }], messages: messages ?? [{ role: "user", content: prompt }],
abortSignal, abortSignal,
tools: { ...extraTools, ...useTools({ resTool, msg: subMsg }) }, tools: { ...extraTools, ...useTools({ resTool, msg: subMsg }) },
}); });
try { const fullResponse = await consumeFullStream(fullStream, subMsg);
for await (const chunk of textStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
text.append(chunk);
fullResponse += chunk;
}
text.complete();
subMsg.complete();
} catch (err: any) {
text.complete();
subMsg.stop();
throw err;
}
if (fullResponse.trim()) { if (fullResponse.trim()) {
await memory.add(memoryKey, removeAllXmlTags(fullResponse), { await memory.add(memoryKey, removeAllXmlTags(fullResponse), {
@ -230,6 +226,54 @@ function createSubAgent(parentCtx: AgentContext) {
}; };
} }
async function consumeFullStream(
fullStream: AsyncIterable<any>,
initialMsg: ReturnType<ResTool["newMessage"]>,
syncMsg?: () => ReturnType<ResTool["newMessage"]>,
): Promise<string> {
let msg = initialMsg;
let text = msg.text();
let thinking: ReturnType<typeof msg.thinking> | null = null;
let thinkTime = 0;
let fullResponse = "";
try {
for await (const chunk of fullStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
if (syncMsg) {
const newMsg = syncMsg();
if (newMsg !== msg) {
msg = newMsg;
text = msg.text();
}
}
if (chunk.type === "reasoning-start") {
thinkTime = Date.now();
thinking = msg.thinking("思考中...");
} else if (chunk.type === "reasoning-delta") {
thinking?.append(chunk.text);
} else if (chunk.type === "reasoning-end") {
thinkTime = Date.now() - thinkTime;
thinking?.updateTitle(`思考完毕(${(thinkTime / 1000).toFixed(1)} 秒)`);
thinking?.complete();
thinking = null;
} else if (chunk.type === "text-delta") {
text.append(chunk.text);
fullResponse += chunk.text;
}
}
text.complete();
msg.complete();
} catch (err: any) {
thinking?.complete();
text.complete();
msg.stop();
throw err;
}
return fullResponse;
}
function removeAllXmlTags(text: string): string { function removeAllXmlTags(text: string): string {
text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?>([\s\S]*?)<\/\1>/g, ""); text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?>([\s\S]*?)<\/\1>/g, "");
text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?\/>/g, ""); text = text.replace(/<([a-zA-Z][\w-]*)(\s+[^>]*)?\/>/g, "");

File diff suppressed because one or more lines are too long

View File

@ -261,29 +261,6 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
table.unique(["id"]); table.unique(["id"]);
}, },
}, },
//大纲表
{
name: "o_outline",
builder: (table) => {
table.integer("id").notNullable();
table.integer("episode");
table.text("data");
table.integer("projectId");
table.primary(["id"]);
table.unique(["id"]);
},
},
//大纲-原文表
{
name: "o_outlineNovel",
builder: (table) => {
table.integer("id").notNullable();
table.integer("outlineId").unsigned().references("id").inTable("o_outline");
table.integer("novelId").unsigned().references("id").inTable("o_novel");
table.primary(["id"]);
table.unique(["id"]);
},
},
//剧本 //剧本
{ {
name: "o_script", name: "o_script",

View File

@ -1,4 +1,4 @@
// @routes-hash 62534cff632db5d31442f1bca1932925 // @routes-hash 4d6cbfaad479bdfafe13bc61e7550f55
import { Express } from "express"; import { Express } from "express";
import route1 from "./routes/agents/clearMemory"; import route1 from "./routes/agents/clearMemory";
@ -34,22 +34,22 @@ import route30 from "./routes/general/generalStatistics";
import route31 from "./routes/general/getSingleProject"; import route31 from "./routes/general/getSingleProject";
import route32 from "./routes/general/updateProject"; import route32 from "./routes/general/updateProject";
import route33 from "./routes/login/login"; import route33 from "./routes/login/login";
import route34 from "./routes/migrate/migrateData"; import route34 from "./routes/modelSelect/getModelDetail";
import route35 from "./routes/modelSelect/getModelDetail"; import route35 from "./routes/modelSelect/getModelList";
import route36 from "./routes/modelSelect/getModelList"; import route36 from "./routes/novel/addNovel";
import route37 from "./routes/novel/addNovel"; import route37 from "./routes/novel/batchDeleteNovel";
import route38 from "./routes/novel/batchDeleteNovel"; import route38 from "./routes/novel/delNovel";
import route39 from "./routes/novel/delNovel"; import route39 from "./routes/novel/event/batchDeleteEvent";
import route40 from "./routes/novel/event/batchDeleteEvent"; import route40 from "./routes/novel/event/deletEvent";
import route41 from "./routes/novel/event/deletEvent"; import route41 from "./routes/novel/event/generateEvents";
import route42 from "./routes/novel/event/generateEvents"; import route42 from "./routes/novel/event/getEvent";
import route43 from "./routes/novel/event/getEvent"; import route43 from "./routes/novel/getNovel";
import route44 from "./routes/novel/getNovel"; import route44 from "./routes/novel/getNovelData";
import route45 from "./routes/novel/getNovelData"; import route45 from "./routes/novel/getNovelEventState";
import route46 from "./routes/novel/getNovelEventState"; import route46 from "./routes/novel/getNovelIndex";
import route47 from "./routes/novel/getNovelIndex"; import route47 from "./routes/novel/updateNovel";
import route48 from "./routes/novel/updateNovel"; import route48 from "./routes/other/deleteAllData";
import route49 from "./routes/other/deleteAllData"; import route49 from "./routes/other/getModelDetails";
import route50 from "./routes/other/getVersion"; import route50 from "./routes/other/getVersion";
import route51 from "./routes/production/assets/batchGenerateAssetsImage"; import route51 from "./routes/production/assets/batchGenerateAssetsImage";
import route52 from "./routes/production/assets/deleteAssetsDireve"; import route52 from "./routes/production/assets/deleteAssetsDireve";
@ -179,22 +179,22 @@ export default async (app: Express) => {
app.use("/api/general/getSingleProject", route31); app.use("/api/general/getSingleProject", route31);
app.use("/api/general/updateProject", route32); app.use("/api/general/updateProject", route32);
app.use("/api/login/login", route33); app.use("/api/login/login", route33);
app.use("/api/migrate/migrateData", route34); app.use("/api/modelSelect/getModelDetail", route34);
app.use("/api/modelSelect/getModelDetail", route35); app.use("/api/modelSelect/getModelList", route35);
app.use("/api/modelSelect/getModelList", route36); app.use("/api/novel/addNovel", route36);
app.use("/api/novel/addNovel", route37); app.use("/api/novel/batchDeleteNovel", route37);
app.use("/api/novel/batchDeleteNovel", route38); app.use("/api/novel/delNovel", route38);
app.use("/api/novel/delNovel", route39); app.use("/api/novel/event/batchDeleteEvent", route39);
app.use("/api/novel/event/batchDeleteEvent", route40); app.use("/api/novel/event/deletEvent", route40);
app.use("/api/novel/event/deletEvent", route41); app.use("/api/novel/event/generateEvents", route41);
app.use("/api/novel/event/generateEvents", route42); app.use("/api/novel/event/getEvent", route42);
app.use("/api/novel/event/getEvent", route43); app.use("/api/novel/getNovel", route43);
app.use("/api/novel/getNovel", route44); app.use("/api/novel/getNovelData", route44);
app.use("/api/novel/getNovelData", route45); app.use("/api/novel/getNovelEventState", route45);
app.use("/api/novel/getNovelEventState", route46); app.use("/api/novel/getNovelIndex", route46);
app.use("/api/novel/getNovelIndex", route47); app.use("/api/novel/updateNovel", route47);
app.use("/api/novel/updateNovel", route48); app.use("/api/other/deleteAllData", route48);
app.use("/api/other/deleteAllData", route49); app.use("/api/other/getModelDetails", route49);
app.use("/api/other/getVersion", route50); app.use("/api/other/getVersion", route50);
app.use("/api/production/assets/batchGenerateAssetsImage", route51); app.use("/api/production/assets/batchGenerateAssetsImage", route51);
app.use("/api/production/assets/deleteAssetsDireve", route52); app.use("/api/production/assets/deleteAssetsDireve", route52);

View File

@ -27,22 +27,6 @@ interface NovelChapter {
type ItemType = "characters" | "props" | "scenes"; type ItemType = "characters" | "props" | "scenes";
interface ResultItem {
type: ItemType;
name: string;
chapterRange: number[];
}
function findItemByName(items: ResultItem[], name: string, type?: ItemType): ResultItem | undefined {
return items.find((item) => (!type || item.type === type) && item.name === name);
}
function mergeNovelText(novelData: NovelChapter[]): string {
if (!Array.isArray(novelData)) return "";
return novelData
.map((chap) => {
return `${chap.chapter.trim()}\n\n${chap.chapterData.trim().replace(/\r?\n/g, "\n")}\n`;
})
.join("\n");
}
//润色提示词 //润色提示词
export default router.post( export default router.post(
"/", "/",
@ -66,23 +50,6 @@ export default router.post(
if (!project) return res.status(500).send(success({ message: "项目为空" })); if (!project) return res.status(500).send(success({ message: "项目为空" }));
// 预加载公共数据 // 预加载公共数据
const allOutlineDataList: { data: string }[] = await u.db("o_outline").where("projectId", projectId).select("data");
const itemMap: Record<string, ResultItem> = {};
if (allOutlineDataList.length > 0)
allOutlineDataList.forEach((row) => {
const data: OutlineData = JSON.parse(row?.data || "{}");
(["characters", "props", "scenes"] as ItemType[]).forEach((type) => {
(data[type] || []).forEach((item) => {
const key = `${type}-${item.name}`;
if (!itemMap[key]) {
itemMap[key] = { type, name: item.name, chapterRange: [...(data.chapterRange || [])] };
} else {
itemMap[key].chapterRange = Array.from(new Set([...itemMap[key].chapterRange, ...(data.chapterRange || [])]));
}
});
});
});
const result: ResultItem[] = Object.values(itemMap);
const assetsIds = items.map((item: { assetsId: number }) => item.assetsId); const assetsIds = items.map((item: { assetsId: number }) => item.assetsId);
//查询所有资产,用于判断每个资产是否是衍生资产 //查询所有资产,用于判断每个资产是否是衍生资产
const assetsDataList = await u.db("o_assets").whereIn("id", assetsIds).select("id", "assetsId"); const assetsDataList = await u.db("o_assets").whereIn("id", assetsIds).select("id", "assetsId");
@ -132,7 +99,6 @@ export default router.post(
await u.db("o_assets").where("id", item.assetsId).update({ promptState: "生成失败", promptErrorReason: "视觉手册未定义" }); await u.db("o_assets").where("id", item.assetsId).update({ promptState: "生成失败", promptErrorReason: "视觉手册未定义" });
return; return;
} }
findItemByName(result, item.name, config.itemType);
const systemPrompt = visualManual; const systemPrompt = visualManual;
try { try {
const { _output } = (await u.Ai.Text("universalAi").invoke({ const { _output } = (await u.Ai.Text("universalAi").invoke({

View File

@ -4,36 +4,10 @@ import * as zod from "zod";
import { error, success } from "@/lib/responseFormat"; import { error, success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware"; import { validateFields } from "@/middleware/middleware";
const router = express.Router(); const router = express.Router();
interface OutlineItem {
description: string;
name: string;
}
interface OutlineData {
chapterRange: number[];
characters?: OutlineItem[];
props?: OutlineItem[];
scenes?: OutlineItem[];
}
interface NovelChapter {
id: number;
reel: string;
chapter: string;
chapterData: string;
projectId: number;
}
type ItemType = "characters" | "props" | "scenes"; type ItemType = "characters" | "props" | "scenes";
interface ResultItem {
type: ItemType;
name: string;
chapterRange: number[];
}
function findItemByName(items: ResultItem[], name: string, type?: ItemType): ResultItem | undefined {
return items.find((item) => (!type || item.type === type) && item.name === name);
}
//润色提示词 //润色提示词
export default router.post( export default router.post(
"/", "/",
@ -51,31 +25,8 @@ export default router.post(
//如果没有找到对应的项目,返回错误 //如果没有找到对应的项目,返回错误
if (!project) return res.status(500).send(success({ message: "项目为空" })); if (!project) return res.status(500).send(success({ message: "项目为空" }));
const allOutlineDataList: { data: string }[] = await u.db("o_outline").where("projectId", projectId).select("data");
await u.db("o_assets").where("id", assetsId).update({ promptState: "生成中" }); await u.db("o_assets").where("id", assetsId).update({ promptState: "生成中" });
const itemMap: Record<string, ResultItem> = {};
if (allOutlineDataList.length > 0)
allOutlineDataList.forEach((row) => {
const data: OutlineData = JSON.parse(row?.data || "{}");
(["characters", "props", "scenes"] as ItemType[]).forEach((type) => {
(data[type] || []).forEach((item) => {
const key = `${type}-${item.name}`;
if (!itemMap[key]) {
itemMap[key] = {
type,
name: item.name,
chapterRange: [...(data.chapterRange || [])],
};
} else {
itemMap[key].chapterRange = Array.from(new Set([...itemMap[key].chapterRange, ...(data.chapterRange || [])]));
}
});
});
});
const result: ResultItem[] = Object.values(itemMap);
//查询资产是否是衍生资产 //查询资产是否是衍生资产
const assetsData = await u.db("o_assets").where("id", assetsId).select("assetsId").first(); const assetsData = await u.db("o_assets").where("id", assetsId).select("assetsId").first();
if (!assetsData) return { code: 500, message: "资产不存在" }; if (!assetsData) return { code: 500, message: "资产不存在" };
@ -109,7 +60,6 @@ export default router.post(
//获取到视觉手册 //获取到视觉手册
const visualManual = await u.getArtPrompt(project.artStyle as string, "art_skills", config.visualManual); const visualManual = await u.getArtPrompt(project.artStyle as string, "art_skills", config.visualManual);
if (!visualManual) return res.status(500).send(error("视觉手册未定义")); if (!visualManual) return res.status(500).send(error("视觉手册未定义"));
findItemByName(result, name, config.itemType);
const systemPrompt = visualManual; const systemPrompt = visualManual;
try { try {
const { _output } = (await u.Ai.Text("universalAi").invoke({ const { _output } = (await u.Ai.Text("universalAi").invoke({

View File

@ -1,133 +0,0 @@
import express from "express";
import { success } from "@/lib/responseFormat";
import db from "@/utils/db";
import type { DB } from "@/types/database";
import knex from "knex";
import path from "path";
import fs from "fs";
import { tr } from "zod/locales";
const router = express.Router();
// 迁移数据
export default router.post(
"/",
async (req, res) => {
// return res.status(200).send({
// success: true,
// message: '数据迁移功能已关闭,建议手动迁移数据后删除旧数据库文件'
// });
//连接旧数据库,读取数据
try {
let db2: knex.Knex | null = null;
//读取旧数据库路径
let db2Path: string;
if (typeof process.versions?.electron !== "undefined") {
const { app } = require("electron");
const userDataDir: string = app.getPath("userData");
db2Path = path.join(userDataDir, "db2.sqlite");
} else {
db2Path = path.join(process.cwd(), "db2.sqlite");
}
const dbDir = path.dirname(db2Path);
// 确保数据库目录存在
if (!fs.existsSync(dbDir)) {
fs.mkdirSync(dbDir, { recursive: true });
}
if (!fs.existsSync(db2Path)) {
return res.status(404).send({
success: false,
message: `源数据库文件不存在: ${db2Path}`
});
}
//连接旧数据库
db2 = knex({
client: "better-sqlite3",
connection: {
filename: db2Path,
},
useNullAsDefault: true,
});
//需要迁移的旧数据表
const db2TableNames = [
't_project',
't_assets',
't_event',
't_image',
't_novel',
't_outline',
't_script',
't_storyboard',
't_video',
]
//新数据库的表
const dbTableNames = [
'o_project',
'o_assets',
'o_event',
'o_eventChapter',
'o_image',
'o_novel',
'o_outline',
'o_outlineNovel',
'o_script',
'o_scriptAssets',
'o_scriptOutline',
'o_storyboard',
'o_storyboardScript',
'o_video',
]
for (const tableName of db2TableNames) {
try {
// 从 db2 读取数据
const sourceData = await db2(tableName).select('*');
for (const item of sourceData) {
//迁移项目表
if (tableName === 't_project') {
// await db("o_project").insert({
// name: item.name,
// intro: item.intro,
// type: item.type,
// artStyle: item.artStyle,
// videoRatio: item.videoRatio,
// createTime: item.createTime,
// userId: item.userId,
// projectType: "基于小说原文"
// })
}
//迁移资产表
if (tableName === 't_assets') {
}
//迁移事件表
if (tableName === 't_event') { }
//迁移图片表
if (tableName === 't_image') { }
//迁移小说表
if (tableName === 't_novel') { }
//迁移大纲表
if (tableName === 't_outline') { }
//迁移脚本表
if (tableName === 't_script') { }
//迁移分镜面板
if (tableName === 't_storyboard') { }
//迁移视频表
if (tableName === 't_video') { }
}
// // 将数据插入到 db 中
// const targetTableName = dbTableNames[db2TableNames.indexOf(tableName)];
// await db(targetTableName).insert(sourceData);
// console.log(`成功迁移表 ${tableName} 的数据到 ${targetTableName}`);
} catch (error) {
console.error(`连接旧数据库失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
} catch (error) {
console.error('连接旧数据库失败:', error);
}
return res.status(200).send({
success: true,
message: '数据迁移功能已关闭,建议手动迁移数据后删除旧数据库文件'
});
}
);

View File

@ -12,7 +12,7 @@ export default router.post(
}), }),
async (req, res) => { async (req, res) => {
const { modelId } = req.body; const { modelId } = req.body;
const [id, name] = modelId.split(":"); const [id, name] = modelId.split(/:(.+)/);
const models = await u.vendor.getModelList(id); const models = await u.vendor.getModelList(id);
const findData = models.find((i: any) => i.modelName == name); const findData = models.find((i: any) => i.modelName == name);
res.status(200).send(success(findData)); res.status(200).send(success(findData));

View File

@ -0,0 +1,21 @@
import express from "express";
import { success, error } from "@/lib/responseFormat";
import u from "@/utils";
import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
const router = express.Router();
export default router.post(
"/",
validateFields({
key: z.string().optional(),
}),
async (req, res) => {
const { key } = req.body;
const [id, modelName] = key ? key.split(":") : [];
const models = await u.vendor.getModelList(id);
const model = models.find((m) => m.modelName === modelName);
if (!model) return res.status(400).send(error("未找到模型"));
res.status(200).send(success(model));
},
);

View File

@ -73,7 +73,7 @@ export default router.post(
shouldGenerateImage: item.shouldGenerateImage, shouldGenerateImage: item.shouldGenerateImage,
}); });
} }
const [id, modelData] = model.split(":"); const [id, modelData] = model.split(/:(.+)/);
const projectData = await u.db("o_project").select("*").where({ id: projectId }).first(); const projectData = await u.db("o_project").select("*").where({ id: projectId }).first();
const videoPrompt = await u.db("o_prompt").where("type", "videoPromptGeneration").first(); const videoPrompt = await u.db("o_prompt").where("type", "videoPromptGeneration").first();
let videoPromptGeneration = "" as string | undefined; let videoPromptGeneration = "" as string | undefined;

View File

@ -18,9 +18,6 @@ export default router.post(
await u.db("o_agentWorkData").where("projectId", id).delete(); await u.db("o_agentWorkData").where("projectId", id).delete();
const novelData = await u.db("o_novel").where("projectId", id).select("id"); const novelData = await u.db("o_novel").where("projectId", id).select("id");
const novelId = novelData.map((item: any) => item.id); const novelId = novelData.map((item: any) => item.id);
if (novelId.length > 0) {
await u.db("o_outlineNovel").whereIn("novelId", novelId).delete();
}
//删除项目下的原文 //删除项目下的原文
await u.db("o_novel").where("projectId", id).delete(); await u.db("o_novel").where("projectId", id).delete();
// 删除项目下的剧本信息 // 删除项目下的剧本信息

View File

@ -41,6 +41,11 @@ export default (nsp: Namespace) => {
}); });
let abortController: AbortController | null = null; let abortController: AbortController | null = null;
const thinkConfig: agent.AgentContext["thinkConfig"] = {
think: false,
thinlLevel: 0,
};
socket.on("updateContext", (data: { isolationKey: string; projectId: number; scriptId: number }, callback) => { socket.on("updateContext", (data: { isolationKey: string; projectId: number; scriptId: number }, callback) => {
isolationKey = data.isolationKey; isolationKey = data.isolationKey;
resTool = new ResTool(socket, { resTool = new ResTool(socket, {
@ -66,46 +71,11 @@ export default (nsp: Namespace) => {
abortSignal: currentController.signal, abortSignal: currentController.signal,
resTool, resTool,
msg, msg,
thinkConfig,
}; };
try { try {
const textStream = await agent.decisionAI(ctx); await agent.runDecisionAI(ctx);
let currentMsg = ctx.msg;
let text = currentMsg.text();
const syncCurrentMessage = () => {
if (ctx.msg === currentMsg) return;
text.complete();
currentMsg.complete();
currentMsg = ctx.msg;
text = currentMsg.text();
};
let aborted = false;
try {
for await (const chunk of textStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
syncCurrentMessage();
text.append(chunk);
}
} catch (err: any) {
if (err.name === "AbortError" || currentController.signal.aborted) {
aborted = true;
} else {
throw err;
}
} finally {
syncCurrentMessage();
if (aborted) {
text.append("[已停止]");
text.complete();
currentMsg.stop();
} else {
text.complete();
currentMsg.complete();
}
}
} catch (err: any) { } catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) { if (err.name !== "AbortError" && !currentController.signal.aborted) {
const errorMsg = u.error(err).message; const errorMsg = u.error(err).message;
@ -120,6 +90,12 @@ export default (nsp: Namespace) => {
} }
}); });
socket.on("updateThinkConfig", (data: { think: boolean; thinlLevel: 0 | 1 | 2 | 3 }) => {
thinkConfig.think = data.think;
thinkConfig.thinlLevel = data.thinlLevel;
console.log("[productionAgent] 更新思考配置:", thinkConfig);
});
socket.on("stop", () => { socket.on("stop", () => {
abortController?.abort(); abortController?.abort();
abortController = null; abortController = null;

View File

@ -40,6 +40,11 @@ export default (nsp: Namespace) => {
}); });
let abortController: AbortController | null = null; let abortController: AbortController | null = null;
const thinkConfig: agent.AgentContext["thinkConfig"] = {
think:false,
thinlLevel: 0,
}
socket.on("chat", async (data: { content: string }) => { socket.on("chat", async (data: { content: string }) => {
const { content } = data; const { content } = data;
abortController?.abort(); abortController?.abort();
@ -55,45 +60,11 @@ export default (nsp: Namespace) => {
abortSignal: currentController.signal, abortSignal: currentController.signal,
resTool, resTool,
msg, msg,
thinkConfig,
}; };
try { try {
const textStream = await agent.decisionAI(ctx); await agent.runDecisionAI(ctx);
let currentMsg = ctx.msg;
let text = currentMsg.text();
const syncCurrentMessage = () => {
if (ctx.msg === currentMsg) return;
text.complete();
currentMsg.complete();
currentMsg = ctx.msg;
text = currentMsg.text();
};
let aborted = false;
try {
for await (const chunk of textStream) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1));
syncCurrentMessage();
text.append(chunk);
}
} catch (err: any) {
if (err.name === "AbortError" || currentController.signal.aborted) {
aborted = true;
} else {
throw err;
}
} finally {
syncCurrentMessage();
if (aborted) {
text.complete();
currentMsg.stop();
} else {
text.complete();
currentMsg.complete();
}
}
} catch (err: any) { } catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) { if (err.name !== "AbortError" && !currentController.signal.aborted) {
const errorMsg = u.error(err).message; const errorMsg = u.error(err).message;
@ -108,6 +79,12 @@ export default (nsp: Namespace) => {
} }
}); });
socket.on("updateThinkConfig", (data: { think: boolean; thinlLevel: 0 | 1 | 2 | 3 }) => {
thinkConfig.think = data.think;
thinkConfig.thinlLevel = data.thinlLevel;
console.log("[scriptAgent] 更新思考配置:", thinkConfig);
});
socket.on("stop", () => { socket.on("stop", () => {
abortController?.abort(); abortController?.abort();
abortController = null; abortController = null;

View File

@ -1,4 +1,4 @@
// @db-hash 3296433eb24314b094ac5d3839c049c5 // @db-hash 9248d7bcfe0a1bc57e5b9bc33d8c7d83
//该文件由脚本自动生成,请勿手动修改 //该文件由脚本自动生成,请勿手动修改
export interface memories { export interface memories {

View File

@ -23,7 +23,7 @@ async function getVendorTemplateFn(
): Promise<(think?: boolean, thinkLevel?: 0 | 1 | 2 | 3) => any>; ): Promise<(think?: boolean, thinkLevel?: 0 | 1 | 2 | 3) => any>;
async function getVendorTemplateFn(fnName: Exclude<FnName, "textRequest">, modelName: `${string}:${string}`): Promise<(input: any) => any>; async function getVendorTemplateFn(fnName: Exclude<FnName, "textRequest">, modelName: `${string}:${string}`): Promise<(input: any) => any>;
async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${string}`): Promise<any> { async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${string}`): Promise<any> {
const [id, name] = modelName.split(":"); const [id, name] = modelName.split(/:(.+)/);
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first(); const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
if (!vendorConfigData) throw new Error(`未找到供应商配置 id=${id}`); if (!vendorConfigData) throw new Error(`未找到供应商配置 id=${id}`);
const modelList = await u.vendor.getModelList(id); const modelList = await u.vendor.getModelList(id);
@ -55,7 +55,7 @@ async function withTaskRecord<T>(
fn: (modelName: `${string}:${string}`, think: Boolean, thinkLevel: 0 | 1 | 2 | 3) => Promise<T>, fn: (modelName: `${string}:${string}`, think: Boolean, thinkLevel: 0 | 1 | 2 | 3) => Promise<T>,
): Promise<T> { ): Promise<T> {
const modelName = await resolveModelName(modelKey); const modelName = await resolveModelName(modelKey);
const [id, model] = modelName.split(":"); const [_, model] = modelName.split(/:(.+)/);
const taskRecord = await u.task(projectId, taskClass, model, { describe: describe, content: relatedObjects }); const taskRecord = await u.task(projectId, taskClass, model, { describe: describe, content: relatedObjects });
try { try {
const result = await fn(modelName, false, 0); const result = await fn(modelName, false, 0);
@ -89,46 +89,29 @@ class AiText {
this.think = think; this.think = think;
this.thinkLevel = thinkLevel; this.thinkLevel = thinkLevel;
} }
async invoke(input: Omit<Parameters<typeof generateText>[0], "model">) { private async resolveModel(middleware?: any | any[]) {
const switchAiDevTool = await u.db("o_setting").where("key", "switchAiDevTool").first(); const switchAiDevTool = await u.db("o_setting").where("key", "switchAiDevTool").first();
const modelName = await resolveModelName(this.AiType); const modelName = await resolveModelName(this.AiType);
const sdkFn = await getVendorTemplateFn("textRequest", modelName); const sdkFn = await getVendorTemplateFn("textRequest", modelName);
const baseModel = await sdkFn(this.think, this.thinkLevel);
const mws = [
...(switchAiDevTool?.value === "1" ? [devToolsMiddleware()] : []),
...(middleware ? (Array.isArray(middleware) ? middleware : [middleware]) : []),
];
return mws.length > 0 ? wrapLanguageModel({ model: baseModel, middleware: mws.length === 1 ? mws[0] : mws }) : baseModel;
}
async invoke(input: Omit<Parameters<typeof generateText>[0], "model">) {
return generateText({ return generateText({
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }), ...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }),
...input, ...input,
model: model: await this.resolveModel(),
switchAiDevTool?.value === "1"
? wrapLanguageModel({
model: await sdkFn(this.think, this.thinkLevel),
middleware: devToolsMiddleware(),
})
: await sdkFn(this.think, this.thinkLevel),
} as Parameters<typeof generateText>[0]); } as Parameters<typeof generateText>[0]);
} }
async stream(input: Omit<Parameters<typeof streamText>[0], "model">) { async stream(input: Omit<Parameters<typeof streamText>[0], "model">) {
const switchAiDevTool = await u.db("o_setting").where("key", "switchAiDevTool").first();
const modelName = await resolveModelName(this.AiType);
const sdkFn = await getVendorTemplateFn("textRequest", modelName);
return streamText({ return streamText({
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }), ...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }),
...input, ...input,
model: model: await this.resolveModel(extractReasoningMiddleware({ tagName: "reasoning_content", separator: "\n" })),
switchAiDevTool?.value == "1"
? wrapLanguageModel({
model: sdkFn(this.think, this.thinkLevel),
middleware: [
devToolsMiddleware(),
extractReasoningMiddleware({
tagName: "reasoning_content",
}),
],
})
: wrapLanguageModel({
model: sdkFn(this.think, this.thinkLevel),
middleware: extractReasoningMiddleware({
tagName: "reasoning_content",
}),
}),
} as Parameters<typeof streamText>[0]); } as Parameters<typeof streamText>[0]);
} }
} }