Merge branch '108' of https://github.com/HBAI-Ltd/Toonflow-app into 108
# Conflicts: # src/types/database.d.ts
This commit is contained in:
commit
38a29ad0fe
@ -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(`<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
body{height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;
|
||||
background:#fff;color:#333;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
||||
user-select:none;-webkit-app-region:drag}
|
||||
.spinner{width:48px;height:48px;border:4px solid rgba(0,0,0,.1);
|
||||
border-top-color:#000;border-radius:50%;animation:spin .8s linear infinite}
|
||||
@keyframes spin{to{transform:rotate(360deg)}}
|
||||
p{margin-top:20px;font-size:14px;opacity:.6}
|
||||
</style></head><body><div class="spinner"></div><p>正在启动服务…</p></body></html>`)}`;
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
@ -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!)),
|
||||
})),
|
||||
);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -23,7 +23,7 @@ function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
|
||||
|
||||
export default router.get("/", async (req, res) => {
|
||||
|
||||
const isolationKey = "test";
|
||||
const isolationKey = "1:productionAgent:1";
|
||||
const input = "你好"
|
||||
|
||||
const memory = new Memory("productionAgent", isolationKey);
|
||||
|
||||
@ -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<Boolean> {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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<Boolean> {
|
||||
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;
|
||||
}
|
||||
|
||||
17
src/types/database.d.ts
vendored
17
src/types/database.d.ts
vendored
@ -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;
|
||||
|
||||
@ -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 <T>(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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user