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();
}
});
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/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/types/database.d.ts b/src/types/database.d.ts
index 4544078..0aca397 100644
--- a/src/types/database.d.ts
+++ b/src/types/database.d.ts
@@ -1,13 +1,6 @@
-// @db-hash f7bc2fdb80756d5536929eb47155578b
+// @db-hash 6be0a80e9c8f541987a4c1e907736237
//该文件由脚本自动生成,请勿手动修改
-export interface _o_script_old_20260327 {
- 'content'?: string | null;
- 'createTime'?: number | null;
- 'id'?: number;
- 'name'?: string | null;
- 'projectId'?: number | null;
-}
export interface memories {
'content': string;
'createTime': number;
@@ -28,7 +21,7 @@ export interface o_agentDeploy {
'model'?: string | null;
'modelName'?: string | null;
'name'?: string | null;
- 'vendorId'?: number | null;
+ 'vendorId'?: string | null;
}
export interface o_agentWorkData {
'createTime'?: number | null;
@@ -54,6 +47,7 @@ export interface o_assets {
'name'?: string | null;
'projectId'?: number | null;
'prompt'?: string | null;
+ 'promptState'?: string | null;
'remark'?: string | null;
'scriptId'?: number | null;
'startTime'?: number | null;
@@ -173,7 +167,7 @@ export interface o_storyboard {
'filePath'?: string | null;
'frameMode'?: string | null;
'id'?: number;
- 'index'?: string | null;
+ 'index'?: number | null;
'lines'?: string | null;
'mode'?: string | null;
'model'?: string | null;
@@ -184,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;
@@ -217,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;
@@ -238,7 +234,6 @@ export interface o_videoConfig {
}
export interface DB {
- "_o_script_old_20260327": _o_script_old_20260327;
"memories": memories;
"o_agentDeploy": o_agentDeploy;
"o_agentWorkData": o_agentWorkData;
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);