From de362e03128c81e38f9ddfd796dacf661eecab88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ACT=E4=B8=B6=E6=B5=81=E6=98=9F=E9=9B=A8?= <1340145680@qq.com> Date: Mon, 30 Mar 2026 23:45:58 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=AE=B0=E5=BF=86?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/test/test.ts | 2 +- src/socket/routes/productionAgent.ts | 87 +++++++++++++--------------- src/socket/routes/scriptAgent.ts | 85 +++++++++++++-------------- src/utils/ai.ts | 46 ++++++++++----- 4 files changed, 115 insertions(+), 105 deletions(-) diff --git a/src/routes/test/test.ts b/src/routes/test/test.ts index 21d4b99..58de179 100644 --- a/src/routes/test/test.ts +++ b/src/routes/test/test.ts @@ -23,7 +23,7 @@ function buildMemPrompt(mem: Awaited>): string { export default router.get("/", async (req, res) => { - const isolationKey = "test"; + const isolationKey = "1:productionAgent:1"; const input = "你好" const memory = new Memory("productionAgent", isolationKey); diff --git a/src/socket/routes/productionAgent.ts b/src/socket/routes/productionAgent.ts index 367f7b8..c2b183d 100644 --- a/src/socket/routes/productionAgent.ts +++ b/src/socket/routes/productionAgent.ts @@ -3,7 +3,6 @@ import u from "@/utils"; import { Namespace, Socket } from "socket.io"; import * as agent from "@/agents/productionAgent/index"; import ResTool from "@/socket/resTool"; -import Memory from "@/utils/agent/memory"; async function verifyToken(rawToken: string): Promise { const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first(); @@ -57,7 +56,6 @@ export default (nsp: Namespace) => { abortController?.abort(); abortController = new AbortController(); const currentController = abortController; - const memory = new Memory("scriptAgent", isolationKey); const msg = resTool.newMessage("assistant", "视频策划"); const ctx: agent.AgentContext = { @@ -70,54 +68,51 @@ export default (nsp: Namespace) => { msg, }; - const textStream = await agent.decisionAI(ctx); - - let currentMsg = ctx.msg; - let text = currentMsg.text(); - let currentContent = ""; - - const persistCurrentMessage = async () => { - if (!currentContent.trim()) return; - await memory.add("assistant:decision", currentContent, { - name: "视频策划", - createTime: new Date(currentMsg.datetime).getTime(), - }); - currentContent = ""; - }; - - const syncCurrentMessage = async () => { - if (ctx.msg === currentMsg) return; - text.complete(); - currentMsg.complete(); - await persistCurrentMessage(); - currentMsg = ctx.msg; - text = currentMsg.text(); - }; - - let aborted = false; try { - for await (const chunk of textStream) { - await syncCurrentMessage(); - text.append(chunk); - currentContent += chunk; - } - } catch (err: any) { - if (err.name === "AbortError" || currentController.signal.aborted) { - aborted = true; - } else { - throw err; - } - } finally { - await syncCurrentMessage(); - if (aborted) { - text.append("[已停止]"); - text.complete(); - currentMsg.stop(); - } else { + const textStream = await agent.decisionAI(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) { + 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(); + } } - await persistCurrentMessage(); + } catch (err: any) { + if (err.name !== "AbortError" && !currentController.signal.aborted) { + const errorMsg = u.error(err).message; + console.error("[productionAgent] chat error:", errorMsg); + ctx.msg.text(errorMsg).complete(); + ctx.msg.error(); + } + } finally { if (abortController === currentController) { abortController = null; } diff --git a/src/socket/routes/scriptAgent.ts b/src/socket/routes/scriptAgent.ts index 0da1c56..8450881 100644 --- a/src/socket/routes/scriptAgent.ts +++ b/src/socket/routes/scriptAgent.ts @@ -3,7 +3,6 @@ import u from "@/utils"; import { Namespace, Socket } from "socket.io"; import * as agent from "@/agents/scriptAgent/index"; import ResTool from "@/socket/resTool"; -import Memory from "@/utils/agent/memory"; async function verifyToken(rawToken: string): Promise { const setting = await u.db("o_setting").where("key", "tokenKey").select("value").first(); @@ -46,7 +45,6 @@ export default (nsp: Namespace) => { abortController?.abort(); abortController = new AbortController(); const currentController = abortController; - const memory = new Memory("scriptAgent", isolationKey); const msg = resTool.newMessage("assistant", "统筹"); const ctx: agent.AgentContext = { @@ -59,53 +57,50 @@ export default (nsp: Namespace) => { msg, }; - const textStream = await agent.decisionAI(ctx); - - let currentMsg = ctx.msg; - let text = currentMsg.text(); - let currentContent = ""; - - const persistCurrentMessage = async () => { - if (!currentContent.trim()) return; - await memory.add("assistant:decision", currentContent, { - name: "统筹", - createTime: new Date(currentMsg.datetime).getTime(), - }); - currentContent = ""; - }; - - const syncCurrentMessage = async () => { - if (ctx.msg === currentMsg) return; - text.complete(); - currentMsg.complete(); - await persistCurrentMessage(); - currentMsg = ctx.msg; - text = currentMsg.text(); - }; - - let aborted = false; try { - for await (const chunk of textStream) { - await syncCurrentMessage(); - text.append(chunk); - currentContent += chunk; - } - } catch (err: any) { - if (err.name === "AbortError" || currentController.signal.aborted) { - aborted = true; - } else { - throw err; - } - } finally { - await syncCurrentMessage(); - if (aborted) { - text.complete(); - currentMsg.stop(); - } else { + const textStream = await agent.decisionAI(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) { + 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(); + } } - await persistCurrentMessage(); + } catch (err: any) { + if (err.name !== "AbortError" && !currentController.signal.aborted) { + const errorMsg = u.error(err).message; + console.error("[scriptAgent] chat error:", errorMsg); + ctx.msg.text(errorMsg).complete(); + ctx.msg.error(); + } + } finally { if (abortController === currentController) { abortController = null; } diff --git a/src/utils/ai.ts b/src/utils/ai.ts index 4f3548d..958d43b 100644 --- a/src/utils/ai.ts +++ b/src/utils/ai.ts @@ -25,7 +25,12 @@ async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${strin const selectedModel = modelList.find((i: any) => i.modelName == name); if (!selectedModel) throw new Error(`未找到模型 ${name} id=${id}`); const jsCode = transform(vendorConfigData.code!, { transforms: ["typescript"] }).code; - const fn = u.vm(jsCode)[fnName]; + const running = u.vm(jsCode); + if (running.vendor) { + Object.assign(running.vendor.inputValues, JSON.parse(vendorConfigData.inputValues ?? "{}")); + running.vendor.models = modelList; + } + const fn = running[fnName]; if (!fn) throw new Error(`未找到供应商配置中的函数 ${fnName} id=${id}`); if (fnName == "textRequest") return fn(selectedModel); else return (input: T) => fn(input, selectedModel); @@ -115,13 +120,18 @@ class AiImage { constructor(key: `${string}:${string}`) { this.key = key; } - async run(input: ImageConfig, taskRecord: TaskRecord) { - return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { - const fn = await getVendorTemplateFn("imageRequest", modelName); + async run(input: ImageConfig, taskRecord?: TaskRecord) { + const modelName = await resolveModelName(this.key); + const exec = async (mn: `${string}:${string}`) => { + const fn = await getVendorTemplateFn("imageRequest", mn); this.result = await fn(input); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); return this; - }); + }; + if (taskRecord) { + return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec); + } + return exec(modelName); } async save(path: string) { await u.oss.writeFile(path, this.result); @@ -144,13 +154,18 @@ class AiVideo { constructor(key: `${string}:${string}`) { this.key = key; } - async run(input: VideoConfig, taskRecord: TaskRecord) { - return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { - const fn = await getVendorTemplateFn("videoRequest", modelName); + async run(input: VideoConfig, taskRecord?: TaskRecord) { + const modelName = await resolveModelName(this.key); + const exec = async (mn: `${string}:${string}`) => { + const fn = await getVendorTemplateFn("videoRequest", mn); this.result = await fn(input); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); return this; - }); + }; + if (taskRecord) { + return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec); + } + return exec(modelName); } async save(path: string) { await u.oss.writeFile(path, this.result); @@ -163,13 +178,18 @@ class AiAudio { constructor(key: `${string}:${string}`) { this.key = key; } - async run(input: VideoConfig, taskRecord: TaskRecord) { - return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, async (modelName) => { - const fn = await getVendorTemplateFn("ttsRequest", modelName); + async run(input: VideoConfig, taskRecord?: TaskRecord) { + const modelName = await resolveModelName(this.key); + const exec = async (mn: `${string}:${string}`) => { + const fn = await getVendorTemplateFn("ttsRequest", mn); this.result = await fn(input); if (this.result.startsWith("http")) this.result = await urlToBase64(this.result); return this; - }); + }; + if (taskRecord) { + return withTaskRecord(this.key, taskRecord.taskClass, taskRecord.describe, taskRecord.relatedObjects, taskRecord.projectId, exec); + } + return exec(modelName); } async save(path: string) { await u.oss.writeFile(path, this.result); From ec6ddfa7e7384305611e251bb318e7b8fbe0dd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=B8=85?= <2944435683> Date: Tue, 31 Mar 2026 00:03:21 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E5=AD=90=E8=B5=84=E4=BA=A7=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=8E=B7=E5=8F=96url=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/assets/getAssetsApi.ts | 10 +++++++++- src/routes/production/workbench/generateVideo.ts | 1 - src/types/database.d.ts | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/routes/assets/getAssetsApi.ts b/src/routes/assets/getAssetsApi.ts index 1573148..8910fbe 100644 --- a/src/routes/assets/getAssetsApi.ts +++ b/src/routes/assets/getAssetsApi.ts @@ -43,11 +43,19 @@ export default router.post( } const childAssets = await childQuery; + // 为每个子资产添加图片地址 + const childAssetsWithSrc = await Promise.all( + childAssets.map(async (child) => ({ + ...child, + src: child.filePath && (await u.oss.getFileUrl(child.filePath!)), + })), + ); + // 为每个父资产添加子资产 const result = await Promise.all( parentAssets.map(async (parent) => ({ ...parent, - sonAssets: childAssets.filter((child) => child.assetsId === parent.id), + sonAssets: childAssetsWithSrc.filter((child) => child.assetsId === parent.id), src: parent.filePath && (await u.oss.getFileUrl(parent.filePath!)), })), ); diff --git a/src/routes/production/workbench/generateVideo.ts b/src/routes/production/workbench/generateVideo.ts index bb62d46..3d82760 100644 --- a/src/routes/production/workbench/generateVideo.ts +++ b/src/routes/production/workbench/generateVideo.ts @@ -103,7 +103,6 @@ export default router.post( 3. 视频风格应与用户指定的模式数据相匹配,包括色彩、音乐、特效等元素。 4. 视频中应包含用户提供的图片,并在视频中适当展示,以增强视频的视觉效果。 5. 如果用户指定了音频,请确保视频中的音频与视频内容相匹配,符合用户的创意意图。`; - console.log("%c Line:110 🍑 prompt", "background:#b03734", prompt); const aiVideo = u.Ai.Video(model); await aiVideo.run( diff --git a/src/types/database.d.ts b/src/types/database.d.ts index 2a3bc57..0aca397 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,4 +1,4 @@ -// @db-hash 93b2462070c45c2b449e9a18c4e88763 +// @db-hash 6be0a80e9c8f541987a4c1e907736237 //该文件由脚本自动生成,请勿手动修改 export interface memories { @@ -178,6 +178,7 @@ export interface o_storyboard { 'sound'?: string | null; 'state'?: string | null; 'title'?: string | null; + 'videoPrompt'?: string | null; } export interface o_tasks { 'describe'?: string | null; @@ -211,6 +212,7 @@ export interface o_video { 'errorReason'?: string | null; 'filePath'?: string | null; 'id'?: number; + 'projectId'?: number | null; 'scriptId'?: number | null; 'state'?: string | null; 'storyboardId'?: number | null; From fa8262af411c5f9f6968f8d6f64a0e3d639b5fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ACT=E4=B8=B6=E6=B5=81=E6=98=9F=E9=9B=A8?= <1340145680@qq.com> Date: Tue, 31 Mar 2026 00:56:50 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/main.ts | 64 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/scripts/main.ts b/scripts/main.ts index 5a3b748..80da606 100644 --- a/scripts/main.ts +++ b/scripts/main.ts @@ -73,6 +73,52 @@ function requireWithCustomPaths(modulePath: string): any { } let mainWindow: BrowserWindow | null = null; +let loadingWindow: BrowserWindow | null = null; + +const loadingHtml = `data:text/html;charset=utf-8,${encodeURIComponent(` +

正在启动服务…

`)}`; + +function showLoading(): void { + loadingWindow = new BrowserWindow({ + width: 1000, + height: 700, + minWidth: 800, + minHeight: 500, + frame: false, + resizable: false, + maximizable: false, + minimizable: false, + show: true, + backgroundColor: "#ffffff", + autoHideMenuBar: true, + titleBarStyle: "hidden", + titleBarOverlay: { + color: "#ffffff", + symbolColor: "#333333", + height: 36, + }, + }); + loadingWindow.setMenuBarVisibility(false); + loadingWindow.removeMenu(); + loadingWindow.on("closed", () => { loadingWindow = null; }); + void loadingWindow.loadURL(loadingHtml); +} + +function closeLoading(): void { + if (loadingWindow && !loadingWindow.isDestroyed()) { + loadingWindow.close(); + loadingWindow = null; + } +} function createMainWindow(): void { const win = new BrowserWindow({ @@ -117,6 +163,9 @@ protocol.registerSchemesAsPrivileged([ ]); app.whenReady().then(async () => { + // 先显示加载窗口 + showLoading(); + try { let servePath: string; if (app.isPackaged) { @@ -169,6 +218,17 @@ app.whenReady().then(async () => { mainWindow?.webContents.openDevTools(); return { ok: true }; }, + openurlwithbrowser: () => { + const search = url.searchParams; + const targetUrl = search.get("url"); + if (targetUrl) { + const { shell } = require("electron"); + shell.openExternal(targetUrl); + return { ok: true }; + } else { + return { ok: false, error: "缺少url参数" }; + } + } }; const handler = handlers[pathname]; const responseData = handler ? handler() : { error: "未知接口" }; @@ -180,10 +240,12 @@ app.whenReady().then(async () => { }); }); + // 服务启动成功,关闭加载窗口,创建主窗口 + closeLoading(); createMainWindow(); } catch (err) { console.error("[服务启动失败]:", err); - // 如果服务启动失败,仍然创建窗口 + closeLoading(); createMainWindow(); } });