diff --git a/README.md b/README.md index a762e33..376338d 100644 --- a/README.md +++ b/README.md @@ -114,10 +114,10 @@ Toonflow 是面向短剧生产的 AI 工作台,围绕“策划 → 编剧 → ## 📺 视频教程 -https://www.bilibili.com/video/BV1na6wB6Ea2 -[![Toonflow 8 分钟快速上手 AI 视频](./docs/videoCover.png)](https://www.bilibili.com/video/BV1oXD7BqEqJ) +https://www.bilibili.com/video/BV1oXD7BqEqJ +[![Toonflow 12 分钟快速上手 AI 视频](./docs/videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**Toonflow 8 分钟快速上手 AI 视频** +**Toonflow 12 分钟快速上手 AI 视频** 👉 [点击观看](https://www.bilibili.com/video/BV1oXD7BqEqJ) 📱 手机微信扫码观看 diff --git a/data/version.txt b/data/version.txt index 1b87bcd..eb8b7a0 100644 --- a/data/version.txt +++ b/data/version.txt @@ -1 +1 @@ -1.1.4 \ No newline at end of file +1.1.5-dev \ No newline at end of file diff --git a/docs/README.en.md b/docs/README.en.md index a9be70f..7c62fd3 100644 --- a/docs/README.en.md +++ b/docs/README.en.md @@ -107,11 +107,11 @@ With Toonflow, you can complete the entire workflow from text to final video wit ## 📺 Video Tutorial -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![Toonflow 8-Minute AI Video Quick Start](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![Toonflow 12-Minute AI Video Quick Start](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) **Toonflow: 8-Minute AI Video Quick Start** -👉 [Click to Watch](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +👉 [Click to Watch](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 Scan the QR code to watch on mobile diff --git a/docs/README.ja.md b/docs/README.ja.md index e639351..0bfb112 100644 --- a/docs/README.ja.md +++ b/docs/README.ja.md @@ -101,11 +101,11 @@ Toonflow は、AI技術を活用して小説を自動的に脚本へ変換し、 ## 📺 動画チュートリアル -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![Toonflow 8分でわかるAI動画作成](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![Toonflow 12分でわかるAI動画作成](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**Toonflow 8分でわかるクイックスタート AI動画作成** -👉 [クリックして視聴](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +**Toonflow 12分でわかるクイックスタート AI動画作成** +👉 [クリックして視聴](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 QRコードをスキャンして視聴 diff --git a/docs/README.ru.md b/docs/README.ru.md index 8b9ba80..e34bc78 100644 --- a/docs/README.ru.md +++ b/docs/README.ru.md @@ -105,11 +105,11 @@ Toonflow — это мощный ИИ-инструмент для создани ## 📺 Видеоуроки -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![Toonflow: Быстрый старт за 8 минут](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![Toonflow: Быстрый старт за 12 минут](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**Toonflow: Быстрый старт в AI-видео за 8 минут** -👉 [Нажмите для просмотра](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +**Toonflow: Быстрый старт в AI-видео за 12 минут** +👉 [Нажмите для просмотра](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 Отсканируйте QR-код для просмотра видео на телефоне diff --git a/docs/README.th.md b/docs/README.th.md index d14814a..a3c78de 100644 --- a/docs/README.th.md +++ b/docs/README.th.md @@ -102,11 +102,11 @@ Toonflow เป็นเครื่องมือ AI สำหรับสร ## 📺 วิดีโอสอนการใช้งาน -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![เริ่มต้นสร้างวิดีโอ AI กับ Toonflow ใน 8 นาที](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![เริ่มต้นสร้างวิดีโอ AI กับ Toonflow ใน 12 นาที](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**เริ่มต้นสร้างวิดีโอ AI กับ Toonflow ใน 8 นาที** -👉 [คลิกเพื่อรับชม](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +**เริ่มต้นสร้างวิดีโอ AI กับ Toonflow ใน 12 นาที** +👉 [คลิกเพื่อรับชม](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 **สแกน QR Code เพื่อรับชมวิดีโอบนมือถือ** diff --git a/docs/README.vi.md b/docs/README.vi.md index c2e2b36..43a50d1 100644 --- a/docs/README.vi.md +++ b/docs/README.vi.md @@ -97,11 +97,11 @@ Toonflow là công cụ AI chuyên tạo phim ngắn và truyện tranh, có kh ## 📺 Hướng dẫn bằng Video -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![Toonflow - 8 phút làm quen với Video AI](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![Toonflow - 12 phút làm quen với Video AI](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**Toonflow - 8 phút làm quen nhanh với Video AI** -👉 [Nhấn để xem](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +**Toonflow - 12 phút làm quen nhanh với Video AI** +👉 [Nhấn để xem](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 **Quét mã QR để xem video trên điện thoại** Quét mã QR để xem video diff --git a/docs/README.zhtw.md b/docs/README.zhtw.md index 4c760aa..18e394e 100644 --- a/docs/README.zhtw.md +++ b/docs/README.zhtw.md @@ -104,11 +104,11 @@ Toonflow 是一款 AI 短劇與漫畫創作工具,能夠利用 AI 技術將小 ## 📺 影片教學 -[https://www.bilibili.com/video/BV1na6wB6Ea2](https://www.bilibili.com/video/BV1na6wB6Ea2) -[![Toonflow 8 分鐘快速上手 AI 影片](./videoCover.png)](https://www.bilibili.com/video/BV1na6wB6Ea2) +[https://www.bilibili.com/video/BV1oXD7BqEqJ](https://www.bilibili.com/video/BV1oXD7BqEqJ) +[![Toonflow 12 分鐘快速上手 AI 影片](./videoCover.jpg)](https://www.bilibili.com/video/BV1oXD7BqEqJ) -**Toonflow 8 分鐘快速上手 AI 影片** -👉 [點擊觀看](https://www.bilibili.com/video/BV1na6wB6Ea2/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) +**Toonflow 12 分鐘快速上手 AI 影片** +👉 [點擊觀看](https://www.bilibili.com/video/BV1oXD7BqEqJ/?share_source=copy_web&vd_source=5b718c25439a901a34c7bc0c1d35b38e) 📱 使用手機掃描 QR Code 觀看 diff --git a/docs/videoCover.jpg b/docs/videoCover.jpg new file mode 100644 index 0000000..bf87f66 Binary files /dev/null and b/docs/videoCover.jpg differ diff --git a/docs/videoCover.png b/docs/videoCover.png deleted file mode 100644 index a6aa056..0000000 Binary files a/docs/videoCover.png and /dev/null differ diff --git a/package.json b/package.json index 001e568..c9ef650 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "toonflow", - "version": "1.1.4", + "version": "1.1.5-dev", "description": "Toonflow 是一款 AI 短剧漫剧工具,能够利用 AI 技术将小说自动转化为剧本,并结合 AI 生成的图片和视频,实现高效的短剧创作。", "author": "HBAI-Ltd ", "license": "Apache-2.0", @@ -47,7 +47,7 @@ "ai": "^6.0.67", "axios": "^1.13.2", "axios-retry": "^4.5.0", - "better-sqlite3": "^12.8.0", + "better-sqlite3": "^12.9.0", "compressing": "^2.1.0", "cors": "^2.8.5", "dotenv": "^17.2.3", diff --git a/src/agents/productionAgent/index.ts b/src/agents/productionAgent/index.ts index 21b5a9b..4e3c01e 100644 --- a/src/agents/productionAgent/index.ts +++ b/src/agents/productionAgent/index.ts @@ -50,8 +50,8 @@ export async function runDecisionAI(ctx: AgentContext) { const projectInfo = await u.db("o_project").where("id", ctx.resTool.data.projectId).first(); if (!projectInfo) throw new Error(`项目不存在,ID: ${ctx.resTool.data.projectId}`); - const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/) - const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/) + const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/); + const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/); const models = await u.vendor.getModelList(id); if (!models.length) throw new Error(`项目使用的模型不存在,ID: ${projectInfo.videoModel}`); const findData = models.find((i: any) => i.modelName == videoModelName); @@ -60,7 +60,7 @@ export async function runDecisionAI(ctx: AgentContext) { const mem = buildMemPrompt(await memory.get(text)); - const { fullStream } = await u.Ai.Text("productionAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({ + const { fullStream } = await u.Ai.Text("productionAgent:decisionAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({ messages: [ { role: "system", content: prompt }, { role: "assistant", content: mem + "\n" + modelInfo }, @@ -90,6 +90,7 @@ async function createSubAgent(parentCtx: AgentContext) { const { resTool, abortSignal } = parentCtx; const memory = new Memory("productionAgent", parentCtx.isolationKey); async function runAgent({ + key, prompt, system, name, @@ -97,6 +98,7 @@ async function createSubAgent(parentCtx: AgentContext) { tools: extraTools, messages, }: { + key: `${string}:${string}`; prompt: string; system: string; name: string; @@ -107,7 +109,7 @@ async function createSubAgent(parentCtx: AgentContext) { parentCtx.msg.complete(); const subMsg = resTool.newMessage("assistant", name); - const { fullStream } = await u.Ai.Text("productionAgent", parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel).stream({ + const { fullStream } = await u.Ai.Text(key, parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel).stream({ system, messages: messages ?? [{ role: "user", content: prompt }], abortSignal, @@ -135,8 +137,8 @@ async function createSubAgent(parentCtx: AgentContext) { if (!projectInfo) throw new Error(`项目不存在,ID: ${resTool.data.projectId}`); const artSkills = await createArtSkills(projectInfo?.artStyle!, projectInfo?.directorManual!); - const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/) - const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/) + const [_, imageModelName] = projectInfo.imageModel!.split(/:(.+)/); + const [id, videoModelName] = projectInfo.videoModel!.split(/:(.+)/); const models = await u.vendor.getModelList(id); if (!models.length) throw new Error(`项目使用的模型不存在,ID: ${projectInfo.videoModel}`); const findData = models.find((i: any) => i.modelName == videoModelName); @@ -181,6 +183,7 @@ async function createSubAgent(parentCtx: AgentContext) { const skill = path.join(u.getPath("skills"), "production_execution_derive_assets.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); return runAgent({ + key: "productionAgent:deriveAssetsAgent", prompt, system: systemPrompt, name: "执行导演", @@ -202,6 +205,7 @@ async function createSubAgent(parentCtx: AgentContext) { const skill = path.join(u.getPath("skills"), "production_execution_generate_assets.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); return runAgent({ + key: "productionAgent:generateAssetsAgent", prompt, system: systemPrompt, name: "执行导演", @@ -226,6 +230,7 @@ async function createSubAgent(parentCtx: AgentContext) { const addPrompt = "\n你必须使用如下XML格式写入工作区:\n```\n内容\n```"; return runAgent({ + key: "productionAgent:directorPlanAgent", prompt, system: systemPrompt + addPrompt, name: "执行导演", @@ -247,6 +252,7 @@ async function createSubAgent(parentCtx: AgentContext) { const skill = path.join(u.getPath("skills"), "production_execution_storyboard_gen.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); return runAgent({ + key: "productionAgent:storyboardGenAgent", prompt, system: systemPrompt, name: "执行导演", @@ -284,6 +290,7 @@ async function createSubAgent(parentCtx: AgentContext) { "\n你必须使用如下XML格式写入工作区:\n```\n\n```"; return runAgent({ + key: "productionAgent:storyboardPanelAgent", prompt, system: systemPrompt + addPrompt, name: "执行导演", @@ -308,6 +315,7 @@ async function createSubAgent(parentCtx: AgentContext) { const addPrompt = "\n你必须使用如下XML格式写入工作区:\n```\n内容\n```"; return runAgent({ + key: "productionAgent:storyboardTableAgent", prompt, system: systemPrompt + addPrompt, name: "执行导演", @@ -328,6 +336,7 @@ async function createSubAgent(parentCtx: AgentContext) { const skill = path.join(u.getPath("skills"), "production_agent_supervision.md"); const systemPrompt = await fs.promises.readFile(skill, "utf-8"); return runAgent({ + key: "productionAgent:supervisionAgent", prompt, system: systemPrompt, name: "监制", diff --git a/src/agents/scriptAgent/index.ts b/src/agents/scriptAgent/index.ts index 57a8fd8..d8f19d6 100644 --- a/src/agents/scriptAgent/index.ts +++ b/src/agents/scriptAgent/index.ts @@ -63,7 +63,7 @@ export async function runDecisionAI(ctx: AgentContext) { `章节数量:${novelData.length}章`, ].join("\n"); - const { fullStream } = await u.Ai.Text("scriptAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({ + const { fullStream } = await u.Ai.Text("scriptAgent:decisionAgent", ctx.thinkConfig.think, ctx.thinkConfig.thinlLevel).stream({ messages: [ { role: "system", content: prompt }, { role: "assistant", content: projectInfo + "\n" + mem }, @@ -94,6 +94,7 @@ function createSubAgent(parentCtx: AgentContext) { const memory = new Memory("scriptAgent", parentCtx.isolationKey); async function runAgent({ + key, prompt, system, name, @@ -101,6 +102,7 @@ function createSubAgent(parentCtx: AgentContext) { tools: extraTools, messages, }: { + key: `${string}:${string}`; prompt: string; system: string; name: string; @@ -111,7 +113,7 @@ function createSubAgent(parentCtx: AgentContext) { parentCtx.msg.complete(); const subMsg = resTool.newMessage("assistant", name); - const { fullStream } = await u.Ai.Text("scriptAgent", parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel).stream({ + const { fullStream } = await u.Ai.Text(key, parentCtx.thinkConfig.think, parentCtx.thinkConfig.thinlLevel).stream({ system, messages: messages ?? [{ role: "user", content: prompt }], abortSignal, @@ -145,6 +147,7 @@ function createSubAgent(parentCtx: AgentContext) { const formatPrompt = "\n你必须使用如下XML格式写入工作区:\n故事骨架内容"; return runAgent({ + key: "scriptAgent:storySkeletonAgent", prompt, system: systemPrompt + formatPrompt, name: "编剧", @@ -164,6 +167,7 @@ function createSubAgent(parentCtx: AgentContext) { const formatPrompt = "\n你必须使用如下XML格式写入工作区:\n改编策略内容"; return runAgent({ + key: "scriptAgent:adaptationStrategyAgent", prompt, system: systemPrompt + formatPrompt, name: "编剧", @@ -190,6 +194,7 @@ function createSubAgent(parentCtx: AgentContext) { const formatPrompt = `\n你必须使用如下XML格式写入工作区:\nXML不得添加任何额外标签剧本内容剧本内容剧本内容`; return runAgent({ + key: "scriptAgent:scriptAgent", prompt, system: systemPrompt + formatPrompt, messages: [ @@ -210,6 +215,7 @@ function createSubAgent(parentCtx: AgentContext) { const systemPrompt = await fs.promises.readFile(skill, "utf-8"); return runAgent({ + key: "scriptAgent:supervisionAgent", prompt, system: systemPrompt, name: "编辑", diff --git a/src/lib/fixDB.ts b/src/lib/fixDB.ts index 1f36766..fd468ca 100644 --- a/src/lib/fixDB.ts +++ b/src/lib/fixDB.ts @@ -59,7 +59,45 @@ export default async (knex: Knex): Promise => { // 添加新字段 await addColumn("o_prompt", "useData", "text"); + // 添加新字段 + await addColumn("o_agentDeploy", "type", "string"); + // 添加新字段 + await addColumn("o_agentDeploy", "temperature", "integer"); + // 添加新字段 + await addColumn("o_agentDeploy", "maxOutputTokens", "integer"); + //添加数据高级配置 + const advancedAgentList = [ + { key: "scriptAgent:decisionAgent", name: "剧本Agent:决策层", desc: "决策层" }, + { key: "scriptAgent:supervisionAgent", name: "剧本Agent:监督层", desc: "监督层" }, + { key: "scriptAgent:storySkeletonAgent", name: "剧本Agent:故事骨架", desc: "故事骨架生成" }, + { key: "scriptAgent:adaptationStrategyAgent", name: "剧本Agent:改编策略", desc: "改编策略生成" }, + { key: "scriptAgent:scriptAgent", name: "剧本Agent:剧本生成", desc: "剧本生成" }, + { key: "productionAgent:decisionAgent", name: "生产Agent:决策层", desc: "决策层" }, + { key: "productionAgent:supervisionAgent", name: "生产Agent:监督层", desc: "监督层" }, + { key: "productionAgent:deriveAssetsAgent", name: "生产Agent:衍生资产", desc: "衍生资产" }, + { key: "productionAgent:generateAssetsAgent", name: "生产Agent:生成资产", desc: "生成资产" }, + { key: "productionAgent:directorPlanAgent", name: "生产Agent:导演规划", desc: "导演规划" }, + { key: "productionAgent:storyboardGenAgent", name: "生产Agent:分镜生成", desc: "分镜生成" }, + { key: "productionAgent:storyboardPanelAgent", name: "生产Agent:分镜面板", desc: "分镜面板生成" }, + { key: "productionAgent:storyboardTableAgent", name: "生产Agent:分镜表格", desc: "分镜表格生成" }, + ]; + for (const agent of advancedAgentList) { + const exists = await db("o_agentDeploy").where("key", agent.key).select("*").first(); + if (!exists) { + await db("o_agentDeploy").insert({ + model: "", + modelName: "", + vendorId: null, + key: agent.key, + name: agent.name, + desc: agent.desc, + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }); + } + } //矫正提示词 await db("o_prompt").where("type", "scriptAssetExtraction").update({ data: `---\nname: universal_agent\ndescription: 专注于从剧本内容中提取所使用的资产(角色、场景、道具)并生成结构化资产列表的助手。\n---\n\n# Script Assets Extract\n\n你是一个专业的剧本内容分析助手,专注于从剧本文本中识别和提取所有涉及的资产(角色、场景、道具),并为每项资产生成可供下游制作流程使用的结构化描述和提示词。\n\n## 何时使用\n\n用户提供剧本内容,你需要逐段阅读并提取其中涉及的所有资产(人物角色、场景地点、道具物件),输出为结构化的资产列表。产出的资产描述将用于后续 AI 图片生成和制作流程。\n\n## 与系统的对应关系\n\n- 资产类型:\n - \`role\` — 角色(对应 \`o_assets.type = "role"\`)\n - \`scene\` — 场景(对应 \`o_assets.type = "scene"\`)\n - \`tool\` — 道具(对应 \`o_assets.type = "tool"\`)\n- 下游用途:资产提示词生成 → AI 资产图生成 → 分镜制作\n\n## 输出要求\n\n**必须通过调用 \`resultTool\` 工具返回结果**,禁止以纯文本、Markdown 表格或 JSON 代码块等形式直接输出资产列表。\n\`resultTool\` 的 schema 会对字段类型和枚举值做强校验,调用时请严格按照下方字段定义填写,确保数据结构正确、字段完整、类型匹配。\n\n每个资产对象包含以下字段:\n\n| 字段 | 类型 | 必填 | 说明 |\n| ---- | ---- | ---- | ---- |\n| \`name\` | string | 是 | 资产名称,使用剧本中的原始称呼,不做其他多余描述 |\n| \`desc\` | string | 是 | 资产描述,30-80 字的视觉化描述 |\n| \`prompt\` | string | 是 | 生成提示词,英文,用于 AI 图片生成 |\n| \`type\` | enum | 是 | 资产类型:\`role\` / \`scene\` / \`tool\` |\n\n## 提取规则\n\n### 角色(role)\n\n- 提取剧本中出现的所有有名字的角色\n- \`desc\`:包含性别、外貌特征、服饰风格、体态气质等视觉要素,需在描述开头明确标注角色性别(如"男性,……"或"女性,……")\n- \`prompt\`:英文提示词,描述角色的外观特征,需以性别词开头(如 \`a young man, ...\` 或 \`a young woman, ...\`),适用于 AI 角色图生成\n- 同一角色有多个称呼时,取最常用的作为 \`name\`\n- 无名龙套(如"路人甲"、"士兵")可跳过,除非其造型对剧情有重要视觉意义\n\n### 场景(scene)\n\n- 提取剧本中出现的所有场景/地点\n- \`desc\`:包含空间结构、光照氛围、关键陈设、色调基调等视觉要素\n- \`prompt\`:英文提示词,描述场景的整体视觉风格,适用于 AI 场景图生成\n- 同一场景的不同状态(如白天/夜晚)不重复提取,在 \`desc\` 中注明即可\n\n### 道具(tool)\n\n- 提取剧本中出现的重要道具/物品\n- \`desc\`:包含外观形状、颜色材质、尺寸参考、特殊效果等视觉要素\n- \`prompt\`:英文提示词,描述道具的外观细节,适用于 AI 道具图生成\n- 仅提取有独立视觉意义或剧情功能的道具,通用物品可跳过\n\n\n## 提示词(prompt)生成规范\n\n- 采用逗号分隔的关键词/短语格式\n- 优先描述**视觉特征**,避免抽象概念\n- 包含风格关键词(如 anime style, manga style 等,根据项目风格决定)\n- 角色 prompt 示例:\`a young man, sharp eyebrows, black hair, pale skin, wearing a gray Taoist robe, slender build, cold expression\`\n- 场景 prompt 示例:\`dark cave interior, glowing crystals on walls, misty atmosphere, dim blue lighting, stone altar in center\`\n- 道具 prompt 示例:\`ancient jade pendant, oval shape, translucent green, carved dragon pattern, glowing faintly\`\n\n## 提取流程\n\n1. 通读剧本全文,识别所有出现的角色、场景、道具\n2. 对每个资产生成结构化的 \`name\`、\`desc\`、\`prompt\`、\`type\`\n3. 去重:同一资产不重复提取\n4. **必须通过调用 \`resultTool\` 工具输出完整资产列表**,不要分多次调用,一次性将所有资产放入 \`assetsList\` 数组中提交\n\n## 提取原则\n\n1. **忠于剧本**:所有提取基于剧本中的实际内容,不臆造未出现的资产\n2. **视觉优先**:描述和提示词聚焦视觉特征,便于 AI 图片生成\n3. **精简实用**:只提取对制作有实际意义的资产,避免过度提取\n4. **分类准确**:严格按照 role/scene/tool 分类,不混淆\n5. **提示词质量**:英文提示词应具体、可执行,能直接用于 AI 图片生成\n\n## 注意事项\n\n- 资产列表中**不要包含剧本内容本身**,仅提取所使用到的资产\n- 角色的随身物品如果有独立剧情功能,应单独作为道具提取\n- 场景中的固定陈设不需要单独提取为道具,除非该物件有独立剧情作用`, diff --git a/src/lib/initDB.ts b/src/lib/initDB.ts index 21901a2..128a171 100644 --- a/src/lib/initDB.ts +++ b/src/lib/initDB.ts @@ -58,7 +58,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.primary(["id"]); table.unique(["id"]); }, - initData: async (knex) => {}, + initData: async (knex) => { }, }, //Agent配置表 { @@ -71,6 +71,8 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.text("vendorId"); table.string("desc"); table.string("name"); + table.integer("temperature"); + table.integer("maxOutputTokens"); table.boolean("disabled").defaultTo(false); table.primary(["id"]); table.unique(["id"]); @@ -113,6 +115,150 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => desc: "根据剧本内容生成角色配音,支持多种声音风格和情绪", disabled: true, }, + { + model: "", + modelName: "", + vendorId: null, + key: "scriptAgent:decisionAgent", + name: "剧本Agent:决策层", + desc: "决策层", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "scriptAgent:supervisionAgent", + name: "剧本Agent:监督层", + desc: "监督层", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "scriptAgent:storySkeletonAgent", + name: "剧本Agent:故事骨架", + desc: "故事骨架生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "scriptAgent:adaptationStrategyAgent", + name: "剧本Agent:改编策略", + desc: "改编策略生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "scriptAgent:scriptAgent", + name: "剧本Agent:剧本生成", + desc: "剧本生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:decisionAgent", + name: "生产Agent:决策层", + desc: "决策层", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:supervisionAgent", + name: "生产Agent:监督层", + desc: "监督层", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:deriveAssetsAgent", + name: "生产Agent:衍生资产", + desc: "衍生资产", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:generateAssetsAgent", + name: "生产Agent:生成资产", + desc: "生成资产", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:directorPlanAgent", + name: "生产Agent:导演规划", + desc: "导演规划", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:storyboardGenAgent", + name: "生产Agent:分镜生成", + desc: "分镜生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:storyboardPanelAgent", + name: "生产Agent:分镜面板", + desc: "分镜面板生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + { + model: "", + modelName: "", + vendorId: null, + key: "productionAgent:storyboardTableAgent", + name: "生产Agent:分镜表格", + desc: "分镜表格生成", + temperature: 1, + maxOutputTokens: 0, + disabled: false, + }, + ]); }, }, @@ -186,7 +332,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.primary(["id"]); table.unique(["id"]); }, - initData: async (knex) => {}, + initData: async (knex) => { }, }, //提示词表 { @@ -231,7 +377,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.primary(["id"]); table.unique(["id"]); }, - initData: async (knex) => {}, + initData: async (knex) => { }, }, //小说原文表 { @@ -310,7 +456,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => table.primary(["id"]); table.unique(["id"]); }, - initData: async (knex) => {}, + initData: async (knex) => { }, }, //生成图片表 { diff --git a/src/router.ts b/src/router.ts index d58b591..04a0e44 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash cc267cab29401f6ad9055dac01879652 +// @routes-hash 0eb42cb2928b13229e60a26b83f977a9 import { Express } from "express"; import route1 from "./routes/agents/clearMemory"; @@ -103,50 +103,51 @@ import route99 from "./routes/script/batchAddScript"; import route100 from "./routes/script/delScript"; import route101 from "./routes/script/exportScript"; import route102 from "./routes/script/extractAssets"; -import route103 from "./routes/script/getScrptApi"; -import route104 from "./routes/script/pollScriptAssets"; -import route105 from "./routes/script/updateScript"; -import route106 from "./routes/scriptAgent/getPlanData"; -import route107 from "./routes/scriptAgent/setPlanData"; -import route108 from "./routes/scriptAgent/updateData"; -import route109 from "./routes/setting/about/checkUpdate"; -import route110 from "./routes/setting/about/downloadApp"; -import route111 from "./routes/setting/agentDeploy/agentSetKey"; -import route112 from "./routes/setting/agentDeploy/deployAgentModel"; -import route113 from "./routes/setting/agentDeploy/getAgentDeploy"; -import route114 from "./routes/setting/dbConfig/clearData"; -import route115 from "./routes/setting/dev/getSwitchAiDevTool"; -import route116 from "./routes/setting/dev/updateSwitchAiDevTool"; -import route117 from "./routes/setting/fileManagement/openFolder"; -import route118 from "./routes/setting/getTextModel"; -import route119 from "./routes/setting/loginConfig/getUser"; -import route120 from "./routes/setting/loginConfig/updateUserPwd"; -import route121 from "./routes/setting/memoryConfig/delAllMemory"; -import route122 from "./routes/setting/memoryConfig/getMemory"; -import route123 from "./routes/setting/memoryConfig/sureMemory"; -import route124 from "./routes/setting/modelMap/bindingPrompt"; -import route125 from "./routes/setting/modelMap/getImageAndVideoModel"; -import route126 from "./routes/setting/promptManage/getPrompt"; -import route127 from "./routes/setting/promptManage/updatePrompt"; -import route128 from "./routes/setting/skillManagement/getSkillContent"; -import route129 from "./routes/setting/skillManagement/getSkillList"; -import route130 from "./routes/setting/skillManagement/saveSkillContent"; -import route131 from "./routes/setting/vendorConfig/addVendor"; -import route132 from "./routes/setting/vendorConfig/addVendorModel"; -import route133 from "./routes/setting/vendorConfig/deleteVendor"; -import route134 from "./routes/setting/vendorConfig/delVendorModel"; -import route135 from "./routes/setting/vendorConfig/enableVendor"; -import route136 from "./routes/setting/vendorConfig/getCodeByLink"; -import route137 from "./routes/setting/vendorConfig/getVendorList"; -import route138 from "./routes/setting/vendorConfig/modelTest"; -import route139 from "./routes/setting/vendorConfig/updateCode"; -import route140 from "./routes/setting/vendorConfig/updateVendorInputs"; -import route141 from "./routes/setting/vendorConfig/upVendorModel"; -import route142 from "./routes/task/getProject"; -import route143 from "./routes/task/getTaskApi"; -import route144 from "./routes/task/getTaskCategories"; -import route145 from "./routes/task/taskDetails"; -import route146 from "./routes/test/test"; +import route103 from "./routes/script/getAiRegex"; +import route104 from "./routes/script/getScrptApi"; +import route105 from "./routes/script/pollScriptAssets"; +import route106 from "./routes/script/updateScript"; +import route107 from "./routes/scriptAgent/getPlanData"; +import route108 from "./routes/scriptAgent/setPlanData"; +import route109 from "./routes/scriptAgent/updateData"; +import route110 from "./routes/setting/about/checkUpdate"; +import route111 from "./routes/setting/about/downloadApp"; +import route112 from "./routes/setting/agentDeploy/agentSetKey"; +import route113 from "./routes/setting/agentDeploy/deployAgentModel"; +import route114 from "./routes/setting/agentDeploy/getAgentDeploy"; +import route115 from "./routes/setting/dbConfig/clearData"; +import route116 from "./routes/setting/dev/getSwitchAiDevTool"; +import route117 from "./routes/setting/dev/updateSwitchAiDevTool"; +import route118 from "./routes/setting/fileManagement/openFolder"; +import route119 from "./routes/setting/getTextModel"; +import route120 from "./routes/setting/loginConfig/getUser"; +import route121 from "./routes/setting/loginConfig/updateUserPwd"; +import route122 from "./routes/setting/memoryConfig/delAllMemory"; +import route123 from "./routes/setting/memoryConfig/getMemory"; +import route124 from "./routes/setting/memoryConfig/sureMemory"; +import route125 from "./routes/setting/modelMap/bindingPrompt"; +import route126 from "./routes/setting/modelMap/getImageAndVideoModel"; +import route127 from "./routes/setting/promptManage/getPrompt"; +import route128 from "./routes/setting/promptManage/updatePrompt"; +import route129 from "./routes/setting/skillManagement/getSkillContent"; +import route130 from "./routes/setting/skillManagement/getSkillList"; +import route131 from "./routes/setting/skillManagement/saveSkillContent"; +import route132 from "./routes/setting/vendorConfig/addVendor"; +import route133 from "./routes/setting/vendorConfig/addVendorModel"; +import route134 from "./routes/setting/vendorConfig/deleteVendor"; +import route135 from "./routes/setting/vendorConfig/delVendorModel"; +import route136 from "./routes/setting/vendorConfig/enableVendor"; +import route137 from "./routes/setting/vendorConfig/getCodeByLink"; +import route138 from "./routes/setting/vendorConfig/getVendorList"; +import route139 from "./routes/setting/vendorConfig/modelTest"; +import route140 from "./routes/setting/vendorConfig/updateCode"; +import route141 from "./routes/setting/vendorConfig/updateVendorInputs"; +import route142 from "./routes/setting/vendorConfig/upVendorModel"; +import route143 from "./routes/task/getProject"; +import route144 from "./routes/task/getTaskApi"; +import route145 from "./routes/task/getTaskCategories"; +import route146 from "./routes/task/taskDetails"; +import route147 from "./routes/test/test"; export default async (app: Express) => { app.use("/api/agents/clearMemory", route1); @@ -251,48 +252,49 @@ export default async (app: Express) => { app.use("/api/script/delScript", route100); app.use("/api/script/exportScript", route101); app.use("/api/script/extractAssets", route102); - app.use("/api/script/getScrptApi", route103); - app.use("/api/script/pollScriptAssets", route104); - app.use("/api/script/updateScript", route105); - app.use("/api/scriptAgent/getPlanData", route106); - app.use("/api/scriptAgent/setPlanData", route107); - app.use("/api/scriptAgent/updateData", route108); - app.use("/api/setting/about/checkUpdate", route109); - app.use("/api/setting/about/downloadApp", route110); - app.use("/api/setting/agentDeploy/agentSetKey", route111); - app.use("/api/setting/agentDeploy/deployAgentModel", route112); - app.use("/api/setting/agentDeploy/getAgentDeploy", route113); - app.use("/api/setting/dbConfig/clearData", route114); - app.use("/api/setting/dev/getSwitchAiDevTool", route115); - app.use("/api/setting/dev/updateSwitchAiDevTool", route116); - app.use("/api/setting/fileManagement/openFolder", route117); - app.use("/api/setting/getTextModel", route118); - app.use("/api/setting/loginConfig/getUser", route119); - app.use("/api/setting/loginConfig/updateUserPwd", route120); - app.use("/api/setting/memoryConfig/delAllMemory", route121); - app.use("/api/setting/memoryConfig/getMemory", route122); - app.use("/api/setting/memoryConfig/sureMemory", route123); - app.use("/api/setting/modelMap/bindingPrompt", route124); - app.use("/api/setting/modelMap/getImageAndVideoModel", route125); - app.use("/api/setting/promptManage/getPrompt", route126); - app.use("/api/setting/promptManage/updatePrompt", route127); - app.use("/api/setting/skillManagement/getSkillContent", route128); - app.use("/api/setting/skillManagement/getSkillList", route129); - app.use("/api/setting/skillManagement/saveSkillContent", route130); - app.use("/api/setting/vendorConfig/addVendor", route131); - app.use("/api/setting/vendorConfig/addVendorModel", route132); - app.use("/api/setting/vendorConfig/deleteVendor", route133); - app.use("/api/setting/vendorConfig/delVendorModel", route134); - app.use("/api/setting/vendorConfig/enableVendor", route135); - app.use("/api/setting/vendorConfig/getCodeByLink", route136); - app.use("/api/setting/vendorConfig/getVendorList", route137); - app.use("/api/setting/vendorConfig/modelTest", route138); - app.use("/api/setting/vendorConfig/updateCode", route139); - app.use("/api/setting/vendorConfig/updateVendorInputs", route140); - app.use("/api/setting/vendorConfig/upVendorModel", route141); - app.use("/api/task/getProject", route142); - app.use("/api/task/getTaskApi", route143); - app.use("/api/task/getTaskCategories", route144); - app.use("/api/task/taskDetails", route145); - app.use("/api/test/test", route146); + app.use("/api/script/getAiRegex", route103); + app.use("/api/script/getScrptApi", route104); + app.use("/api/script/pollScriptAssets", route105); + app.use("/api/script/updateScript", route106); + app.use("/api/scriptAgent/getPlanData", route107); + app.use("/api/scriptAgent/setPlanData", route108); + app.use("/api/scriptAgent/updateData", route109); + app.use("/api/setting/about/checkUpdate", route110); + app.use("/api/setting/about/downloadApp", route111); + app.use("/api/setting/agentDeploy/agentSetKey", route112); + app.use("/api/setting/agentDeploy/deployAgentModel", route113); + app.use("/api/setting/agentDeploy/getAgentDeploy", route114); + app.use("/api/setting/dbConfig/clearData", route115); + app.use("/api/setting/dev/getSwitchAiDevTool", route116); + app.use("/api/setting/dev/updateSwitchAiDevTool", route117); + app.use("/api/setting/fileManagement/openFolder", route118); + app.use("/api/setting/getTextModel", route119); + app.use("/api/setting/loginConfig/getUser", route120); + app.use("/api/setting/loginConfig/updateUserPwd", route121); + app.use("/api/setting/memoryConfig/delAllMemory", route122); + app.use("/api/setting/memoryConfig/getMemory", route123); + app.use("/api/setting/memoryConfig/sureMemory", route124); + app.use("/api/setting/modelMap/bindingPrompt", route125); + app.use("/api/setting/modelMap/getImageAndVideoModel", route126); + app.use("/api/setting/promptManage/getPrompt", route127); + app.use("/api/setting/promptManage/updatePrompt", route128); + app.use("/api/setting/skillManagement/getSkillContent", route129); + app.use("/api/setting/skillManagement/getSkillList", route130); + app.use("/api/setting/skillManagement/saveSkillContent", route131); + app.use("/api/setting/vendorConfig/addVendor", route132); + app.use("/api/setting/vendorConfig/addVendorModel", route133); + app.use("/api/setting/vendorConfig/deleteVendor", route134); + app.use("/api/setting/vendorConfig/delVendorModel", route135); + app.use("/api/setting/vendorConfig/enableVendor", route136); + app.use("/api/setting/vendorConfig/getCodeByLink", route137); + app.use("/api/setting/vendorConfig/getVendorList", route138); + app.use("/api/setting/vendorConfig/modelTest", route139); + app.use("/api/setting/vendorConfig/updateCode", route140); + app.use("/api/setting/vendorConfig/updateVendorInputs", route141); + app.use("/api/setting/vendorConfig/upVendorModel", route142); + app.use("/api/task/getProject", route143); + app.use("/api/task/getTaskApi", route144); + app.use("/api/task/getTaskCategories", route145); + app.use("/api/task/taskDetails", route146); + app.use("/api/test/test", route147); } diff --git a/src/routes/script/getAiRegex.ts b/src/routes/script/getAiRegex.ts new file mode 100644 index 0000000..ad79409 --- /dev/null +++ b/src/routes/script/getAiRegex.ts @@ -0,0 +1,35 @@ +import express from "express"; +import u from "@/utils"; +import { z } from "zod"; +import { success } from "@/lib/responseFormat"; +import { validateFields } from "@/middleware/middleware"; +const router = express.Router(); + +export default router.post( + "/", + validateFields({ + content: z.string(), + }), + async (req, res) => { + const { content } = req.body; + const systemPrompt = `你是一个正则表达式专家。用户会提供一段剧本文本,你需要分析其中的集/章节分隔模式,返回一个JavaScript正则表达式字符串。 + +要求: +1. 正则必须包含两个捕获组:第一个捕获组匹配集数/章节编号(数字或中文数字),第二个捕获组匹配该集的标题/名称(scriptName)。 +2. 返回格式为 /正则表达式/g,例如:/第\s*([0-9一二三四五六七八九十百千万]+)\s*集\s*([^\n\r]*)/g +3. 只返回正则表达式字符串本身,不要有任何其他解释文字或markdown格式。 +4. 如果文本中没有明显的章节分隔模式,返回空字符串。`; + + const resText = await u.Ai.Text("universalAi").invoke({ + system: systemPrompt, + messages: [ + { + role: "user", + content: content.slice(0, 2000), + }, + ], + }); + const result = (resText.text || "").trim(); + res.status(200).send(success(result)); + }, +); diff --git a/src/routes/setting/agentDeploy/agentSetKey.ts b/src/routes/setting/agentDeploy/agentSetKey.ts index f7c5e4f..b79923c 100644 --- a/src/routes/setting/agentDeploy/agentSetKey.ts +++ b/src/routes/setting/agentDeploy/agentSetKey.ts @@ -43,6 +43,97 @@ export default router.post( modelName: "toonflow:claude-haiku-4-5-20251001", vendorId: "toonflow", }); + await u.db("o_agentDeploy").where("key", "scriptAgent:decisionAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "scriptAgent:supervisionAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "scriptAgent:storySkeletonAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "scriptAgent:adaptationStrategyAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "scriptAgent:scriptAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:decisionAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:supervisionAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:deriveAssetsAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:generateAssetsAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:directorPlanAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:storyboardGenAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:storyboardPanelAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); + await u.db("o_agentDeploy").where("key", "productionAgent:storyboardTableAgent").update({ + model: "claude-sonnet-4-6", + modelName: "toonflow:claude-sonnet-4-6", + vendorId: "toonflow", + temperature: 1, + maxOutputTokens: 8192, + }); res.status(200).send(success("一键填入成功")); } } catch (err) { diff --git a/src/routes/setting/agentDeploy/deployAgentModel.ts b/src/routes/setting/agentDeploy/deployAgentModel.ts index f0894b3..f46c546 100644 --- a/src/routes/setting/agentDeploy/deployAgentModel.ts +++ b/src/routes/setting/agentDeploy/deployAgentModel.ts @@ -14,10 +14,12 @@ export default router.post( modelName: z.string(), vendorId: z.string().nullable(), desc: z.string(), + temperature: z.number().optional(), + maxOutputTokens: z.number().optional(), }), async (req, res) => { - const { id, name, model, modelName, vendorId, desc } = req.body; - await u.db("o_agentDeploy").where({ id }).update({ id, name, model, modelName, vendorId, desc }); + const { id, name, model, modelName, vendorId, desc, temperature, maxOutputTokens } = req.body; + await u.db("o_agentDeploy").where({ id }).update({ id, name, model, modelName, vendorId, desc, temperature, maxOutputTokens }); res.status(200).send(success("配置成功")); }, ); diff --git a/src/routes/setting/agentDeploy/getAgentDeploy.ts b/src/routes/setting/agentDeploy/getAgentDeploy.ts index 5bf76f0..c2c0d61 100644 --- a/src/routes/setting/agentDeploy/getAgentDeploy.ts +++ b/src/routes/setting/agentDeploy/getAgentDeploy.ts @@ -4,6 +4,8 @@ import u from "@/utils"; const router = express.Router(); export default router.post("/", async (req, res) => { - const data = await u.db("o_agentDeploy").leftJoin("o_vendorConfig", "o_vendorConfig.id", "o_agentDeploy.vendorId").select("o_agentDeploy.*"); - res.status(200).send(success(data)); + const allData = await u.db("o_agentDeploy").leftJoin("o_vendorConfig", "o_vendorConfig.id", "o_agentDeploy.vendorId").select("o_agentDeploy.*"); + const qrdinaryData = allData.filter((item: any) => !item.key?.includes(":")); + const advancedData = allData.filter((item: any) => item.key?.includes(":")); + res.status(200).send(success({ qrdinaryData, advancedData })); }); diff --git a/src/types/database.d.ts b/src/types/database.d.ts index 1108312..eb4d216 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,37 +1,6 @@ -// @db-hash e2ce409a953a516777e836e1ff0ca34b +// @db-hash 5364c2db0bf42b520761b813ce040489 //该文件由脚本自动生成,请勿手动修改 -export interface _o_project_old_20260404 { - 'artStyle'?: string | null; - 'createTime'?: number | null; - 'directorManual'?: string | null; - 'id'?: number | null; - 'imageModel'?: string | null; - 'imageQuality'?: string | null; - 'intro'?: string | null; - 'mode'?: string | null; - 'name'?: string | null; - 'projectType'?: string | null; - 'type'?: string | null; - 'userId'?: number | null; - 'videoModel'?: string | null; - 'videoRatio'?: string | null; -} -export interface _o_prompt_old_20260406 { - 'data'?: string | null; - 'id'?: number; - 'name'?: string | null; - 'type'?: string | null; - 'useData'?: string | null; -} -export interface _o_prompt_old_20260406_1 { - 'data'?: string | null; - 'id'?: number; - 'name'?: string | null; - 'TEXT'?: any | null; - 'type'?: string | null; - 'useData'?: string | null; -} export interface memories { 'content': string; 'createTime': number; @@ -49,9 +18,13 @@ export interface o_agentDeploy { 'disabled'?: boolean | null; 'id'?: number; 'key'?: string | null; + 'maxOutputTokens'?: number | null; 'model'?: string | null; 'modelName'?: string | null; 'name'?: string | null; + 'temperature'?: number | null; + 'topP'?: number | null; + 'type'?: string | null; 'vendorId'?: string | null; } export interface o_agentWorkData { @@ -266,9 +239,6 @@ export interface o_videoTrack { } export interface DB { - "_o_project_old_20260404": _o_project_old_20260404; - "_o_prompt_old_20260406": _o_prompt_old_20260406; - "_o_prompt_old_20260406_1": _o_prompt_old_20260406_1; "memories": memories; "o_agentDeploy": o_agentDeploy; "o_agentWorkData": o_agentWorkData; diff --git a/src/utils/ai.ts b/src/utils/ai.ts index 3a5c2fc..0743699 100644 --- a/src/utils/ai.ts +++ b/src/utils/ai.ts @@ -4,19 +4,75 @@ import axios from "axios"; import { transform } from "sucrase"; import u from "@/utils"; -type AiType = "scriptAgent" | "productionAgent" | "universalAi"; +type AiType = + | "scriptAgent" + | "productionAgent" + | "universalAi" + | "scriptAgent:decisionAgent" + | "scriptAgent:supervisionAgent" + | "scriptAgent:storySkeletonAgent" + | "scriptAgent:adaptationStrategyAgent" + | "scriptAgent:scriptAgent" + | "productionAgent:decisionAgent" + | "productionAgent:supervisionAgent" + | "productionAgent:deriveAssetsAgent" + | "productionAgent:generateAssetsAgent" + | "productionAgent:directorPlanAgent" + | "productionAgent:storyboardGenAgent" + | "productionAgent:storyboardPanelAgent" + | "productionAgent:storyboardTableAgent"; + type FnName = "textRequest" | "imageRequest" | "videoRequest" | "ttsRequest"; -const AiTypeValues: AiType[] = ["scriptAgent", "productionAgent", "universalAi"]; +const AiTypeValues: AiType[] = [ + "scriptAgent", + "productionAgent", + "universalAi", + "scriptAgent:decisionAgent", + "scriptAgent:supervisionAgent", + "scriptAgent:storySkeletonAgent", + "scriptAgent:adaptationStrategyAgent", + "scriptAgent:scriptAgent", + "productionAgent:decisionAgent", + "productionAgent:supervisionAgent", + "productionAgent:deriveAssetsAgent", + "productionAgent:generateAssetsAgent", + "productionAgent:directorPlanAgent", + "productionAgent:storyboardGenAgent", + "productionAgent:storyboardPanelAgent", + "productionAgent:storyboardTableAgent", + "universalAi", +]; async function resolveModelName(value: AiType | `${string}:${string}`): Promise<`${string}:${string}`> { if (AiTypeValues.includes(value as AiType)) { const agentDeployData = await u.db("o_agentDeploy").where("key", value).first(); - if (!agentDeployData?.modelName) throw new Error(`${value}模型未配置`); - return agentDeployData.modelName as `${number}:${string}`; + let modelName = null; + if (!agentDeployData?.modelName) { + const [mainly] = agentDeployData!.key!.split(/:(.+)/); + const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first(); + if (!mainlyData?.modelName) throw new Error(`未找到部署配置 ${value}`); + modelName = mainlyData.modelName; + } + modelName = agentDeployData?.modelName || modelName; + return modelName as `${number}:${string}`; } return value as `${number}:${string}`; } +async function getModelConfig(value: AiType | `${string}:${string}`) { + if (AiTypeValues.includes(value as AiType)) { + const agentDeployData = await u.db("o_agentDeploy").where("key", value).first(); + if (!agentDeployData?.modelName) { + const [mainly] = agentDeployData!.key!.split(/:(.+)/); + const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first(); + if (!mainlyData?.modelName) throw new Error(`未找到部署配置 ${value}`); + return mainlyData; + } + return agentDeployData; + } + return null; +} + async function getVendorTemplateFn( fnName: "textRequest", modelName: `${string}:${string}`, @@ -101,21 +157,26 @@ class AiText { return mws.length > 0 ? wrapLanguageModel({ model: baseModel, middleware: mws.length === 1 ? mws[0] : mws }) : baseModel; } async invoke(input: Omit[0], "model">) { + const config = await getModelConfig(this.AiType); + console.log("%c Line:161 🥃 config", "background:#3f7cff", config); + return generateText({ ...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }), ...input, model: await this.resolveModel(), - temperature: 2, + ...(config?.temperature && { temperature: config.temperature }), + ...(config?.maxOutputTokens && { maxOutputTokens: config.maxOutputTokens }), } as Parameters[0]); } async stream(input: Omit[0], "model">) { + const config = await getModelConfig(this.AiType); + return streamText({ ...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }), ...input, model: await this.resolveModel(extractReasoningMiddleware({ tagName: "reasoning_content", separator: "\n" })), - topP: 1, - temperature: 2, - maxOutputTokens: 9999999999, + ...(config?.temperature && { temperature: config.temperature }), + ...(config?.maxOutputTokens && { maxOutputTokens: config.maxOutputTokens }), } as Parameters[0]); } } diff --git a/yarn.lock b/yarn.lock index 3375e6f..fed2901 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1338,10 +1338,10 @@ basic-auth@~2.0.1: dependencies: safe-buffer "5.1.2" -better-sqlite3@^12.8.0: - version "12.8.0" - resolved "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-12.8.0.tgz#ec9ccd4a426a35f3b9355c147af6c92a6ddd6862" - integrity sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ== +better-sqlite3@^12.9.0: + version "12.9.0" + resolved "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-12.9.0.tgz#32498c99ba3fb36f604fbb5c70667c5f68c00414" + integrity sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ== dependencies: bindings "^1.5.0" prebuild-install "^7.1.1"