From bcf130b2f0a908416d0a14407a461302c8f9835a Mon Sep 17 00:00:00 2001 From: zhishi <1951671751@qq.com> Date: Fri, 6 Feb 2026 14:18:30 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=89=80=E6=9C=89ai?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../storyboard/generateImagePromptsTool.ts | 4 +- backup/agents/storyboard/generateImageTool.ts | 26 +++--- src/agents/storyboard/generateImageTool.ts | 2 +- src/routes/assets/generateAssets.ts | 2 +- src/routes/storyboard/batchSuperScoreImage.ts | 2 +- src/routes/storyboard/chatStoryboard.ts | 5 -- src/routes/video/addVideoConfig.ts | 79 +++++++++---------- src/routes/video/generateVideo.ts | 21 +++-- src/routes/video/upDateVideoConfig.ts | 43 +++++----- src/utils/ai/image/index.ts | 2 +- src/utils/ai/text/modelList.ts | 13 ++- src/utils/editImage.ts | 2 +- 12 files changed, 102 insertions(+), 99 deletions(-) diff --git a/backup/agents/storyboard/generateImagePromptsTool.ts b/backup/agents/storyboard/generateImagePromptsTool.ts index 4057221..7c99dfa 100644 --- a/backup/agents/storyboard/generateImagePromptsTool.ts +++ b/backup/agents/storyboard/generateImagePromptsTool.ts @@ -104,9 +104,7 @@ async function generateGridPrompt(options: GridPromptOptions): Promise `- ${r.name}:${r.intro}`).join("\n")} 请仅选择在分镜中明确出现或被提及的角色、场景、道具。不要选择与分镜内容无关的资产。`, }, ], - responseFormat: { - type: "json_schema", - jsonSchema: { - name: "filteredAssets", - strict: true, - schema: z.toJSONSchema(filteredAssetsSchema), - }, + output: { + relevantAssets: z + .array( + z.object({ + name: z.string().describe("资产名称"), + reason: z.string().describe("选择该资产的原因"), + }), + ) + .describe("与分镜内容相关的资产列表"), }, }); - const data = result?.json as z.infer; - if (!data?.relevantAssets || data.relevantAssets.length === 0) { + if (!result?.relevantAssets || result.relevantAssets.length === 0) { return availableImages; } - const relevantNames = new Set(data.relevantAssets.map((a) => a.name)); + const relevantNames = new Set(result.relevantAssets.map((a) => a.name)); const filteredImages = availableImages.filter((img) => relevantNames.has(img.name)); return filteredImages.length > 0 ? filteredImages : availableImages; @@ -318,7 +318,7 @@ export default async (cells: { prompt: string }[], scriptId: number, projectId: const processedImages = await processImages(filteredImages); - const contentStr = await u.ai.generateImage({ + const contentStr = await u.ai.image({ systemPrompt: resourcesMapPrompts, prompt: prompts, size: "4K", diff --git a/src/agents/storyboard/generateImageTool.ts b/src/agents/storyboard/generateImageTool.ts index e43e20a..8cf9f79 100644 --- a/src/agents/storyboard/generateImageTool.ts +++ b/src/agents/storyboard/generateImageTool.ts @@ -343,7 +343,7 @@ export default async (cells: { prompt: string }[], scriptId: number, projectId: const processedImages = await processImages(filteredImages); - const contentStr = await u.ai.generateImage({ + const contentStr = await u.ai.image({ systemPrompt: resourcesMapPrompts, prompt: prompts, size: "4K", diff --git a/src/routes/assets/generateAssets.ts b/src/routes/assets/generateAssets.ts index 2243b4f..53a3310 100644 --- a/src/routes/assets/generateAssets.ts +++ b/src/routes/assets/generateAssets.ts @@ -124,7 +124,7 @@ export default router.post( assetsId: id, }); - const contentStr = await u.ai.generateImage({ + const contentStr = await u.ai.image({ systemPrompt, prompt: userPrompt, imageBase64: base64 ? [base64] : [], diff --git a/src/routes/storyboard/batchSuperScoreImage.ts b/src/routes/storyboard/batchSuperScoreImage.ts index 846974b..f10f97d 100644 --- a/src/routes/storyboard/batchSuperScoreImage.ts +++ b/src/routes/storyboard/batchSuperScoreImage.ts @@ -22,7 +22,7 @@ async function superResolutionAndSave( projectId: number, videoRatio: string, ): Promise<{ ossPath: string; base64: string }> { - const contentStr = await u.ai.generateImage({ + const contentStr = await u.ai.image({ aspectRatio: videoRatio, size: "1K", resType: "b64", diff --git a/src/routes/storyboard/chatStoryboard.ts b/src/routes/storyboard/chatStoryboard.ts index 048563f..e2854bc 100644 --- a/src/routes/storyboard/chatStoryboard.ts +++ b/src/routes/storyboard/chatStoryboard.ts @@ -8,7 +8,6 @@ expressWs(router as unknown as Application); router.ws("/", async (ws, req) => { let agent: Storyboard; - const config = await u.getConfig("language"); const projectId = req.query.projectId; const scriptId = req.query.scriptId; @@ -20,10 +19,6 @@ router.ws("/", async (ws, req) => { agent = new Storyboard(Number(projectId), Number(scriptId)); - agent.modelName = config.model ?? ""; - agent.baseURL = config.baseURL ?? ""; - agent.apiKey = config.apiKey ?? ""; - const existing = await u .db("t_chatHistory") .where({ projectId: Number(projectId) }) diff --git a/src/routes/video/addVideoConfig.ts b/src/routes/video/addVideoConfig.ts index 7722637..e055144 100644 --- a/src/routes/video/addVideoConfig.ts +++ b/src/routes/video/addVideoConfig.ts @@ -6,11 +6,13 @@ import { z } from "zod"; const router = express.Router(); // 图片项schema -const imageItemSchema = z.object({ - id: z.number(), - filePath: z.string(), - prompt: z.string().optional(), -}).nullable(); +const imageItemSchema = z + .object({ + id: z.number(), + filePath: z.string(), + prompt: z.string().optional(), + }) + .nullable(); // 新增视频配置 export default router.post( @@ -22,31 +24,24 @@ export default router.post( mode: z.enum(["startEnd", "multi", "single"]), startFrame: imageItemSchema.optional(), endFrame: imageItemSchema.optional(), - images: z.array(z.object({ - id: z.number(), - filePath: z.string(), - prompt: z.string().optional(), - })).optional(), + images: z + .array( + z.object({ + id: z.number(), + filePath: z.string(), + prompt: z.string().optional(), + }), + ) + .optional(), resolution: z.string(), duration: z.number(), prompt: z.string().optional(), }), async (req, res) => { - const { - scriptId, - projectId, - manufacturer, - mode, - startFrame, - endFrame, - images, - resolution, - duration, - prompt - } = req.body; + const { scriptId, projectId, manufacturer, mode, startFrame, endFrame, images, resolution, duration, prompt } = req.body; // 生成新ID - const maxIdResult = await u.db("t_videoConfig").max("id as maxId").first(); + const maxIdResult: any = await u.db("t_videoConfig").max("id as maxId").first(); const newId = (maxIdResult?.maxId || 0) + 1; const now = Date.now(); @@ -68,23 +63,25 @@ export default router.post( updateTime: now, }); - res.status(200).send(success({ - message: "新增视频配置成功", - data: { - id: newId, - scriptId, - projectId, - manufacturer, - mode, - startFrame, - endFrame, - images: images || [], - resolution, - duration, - prompt: prompt || "", - selectedResultId: null, - createdAt: new Date(now).toISOString(), - } - })); + res.status(200).send( + success({ + message: "新增视频配置成功", + data: { + id: newId, + scriptId, + projectId, + manufacturer, + mode, + startFrame, + endFrame, + images: images || [], + resolution, + duration, + prompt: prompt || "", + selectedResultId: null, + createdAt: new Date(now).toISOString(), + }, + }), + ); }, ); diff --git a/src/routes/video/generateVideo.ts b/src/routes/video/generateVideo.ts index a2fdd75..ae18410 100644 --- a/src/routes/video/generateVideo.ts +++ b/src/routes/video/generateVideo.ts @@ -44,7 +44,7 @@ export default router.post( // 过滤掉空值 let fileUrl = filePath.filter((p: string) => p && p.trim() !== ""); - + if (fileUrl.length === 0) { return res.status(400).send(error("请至少选择一张图片")); } @@ -149,17 +149,14 @@ ${prompt} 3. 关键人物在画面中全部清晰显示,不得被遮挡、缺失或省略 4. 画面真实、细致,无畸形、无模糊、无杂物、无多余人物、无文字、水印、logo `; - - const videoPath = await u.ai.generateVideo( - { - imageBase64, - savePath, - prompt: inputPrompt, - duration: duration as any, - aspectRatio: resolution as any, - }, - type!, - ); + const videoPath = await u.ai.video({ + imageBase64, + savePath, + prompt: inputPrompt, + duration: duration as any, + aspectRatio: resolution as any, + resolution: resolution as any, + }); if (videoPath) { // 生成成功,更新状态为 1 diff --git a/src/routes/video/upDateVideoConfig.ts b/src/routes/video/upDateVideoConfig.ts index b690d1d..ea58c6f 100644 --- a/src/routes/video/upDateVideoConfig.ts +++ b/src/routes/video/upDateVideoConfig.ts @@ -47,24 +47,29 @@ export default router.post( // 获取更新后的数据 const updatedConfig = await u.db("t_videoConfig").where({ id }).first(); - - res.status(200).send(success({ - message: "更新视频配置成功", - data: { - id: updatedConfig.id, - scriptId: updatedConfig.scriptId, - projectId: updatedConfig.projectId, - manufacturer: updatedConfig.manufacturer, - mode: updatedConfig.mode, - startFrame: updatedConfig.startFrame ? JSON.parse(updatedConfig.startFrame) : null, - endFrame: updatedConfig.endFrame ? JSON.parse(updatedConfig.endFrame) : null, - images: updatedConfig.images ? JSON.parse(updatedConfig.images) : [], - resolution: updatedConfig.resolution, - duration: updatedConfig.duration, - prompt: updatedConfig.prompt, - selectedResultId: updatedConfig.selectedResultId, - createdAt: new Date(updatedConfig.createTime).toISOString(), - } - })); + if (updatedConfig) { + res.status(200).send( + success({ + message: "更新视频配置成功", + data: { + id: updatedConfig.id, + scriptId: updatedConfig.scriptId, + projectId: updatedConfig.projectId, + manufacturer: updatedConfig.manufacturer, + mode: updatedConfig.mode, + startFrame: updatedConfig.startFrame ? JSON.parse(updatedConfig.startFrame) : null, + endFrame: updatedConfig.endFrame ? JSON.parse(updatedConfig.endFrame) : null, + images: updatedConfig.images ? JSON.parse(updatedConfig.images) : [], + resolution: updatedConfig.resolution, + duration: updatedConfig.duration, + prompt: updatedConfig.prompt, + selectedResultId: updatedConfig.selectedResultId, + createdAt: new Date(updatedConfig.createTime!).toISOString(), + }, + }), + ); + } else { + res.status(200).send(error("更新配置失败")); + } }, ); diff --git a/src/utils/ai/image/index.ts b/src/utils/ai/image/index.ts index 77076ec..281742a 100644 --- a/src/utils/ai/image/index.ts +++ b/src/utils/ai/image/index.ts @@ -63,5 +63,5 @@ export default async (input: ImageConfig, config?: AIConfig) => { let imageUrl = await manufacturerFn(input, { model, apiKey, baseURL }); if (!input.resType) input.resType = "b64"; if (input.resType === "b64" && imageUrl.startsWith("http")) imageUrl = await urlToBase64(imageUrl); - return input; + return imageUrl; }; diff --git a/src/utils/ai/text/modelList.ts b/src/utils/ai/text/modelList.ts index d56b7c5..a37ea8a 100644 --- a/src/utils/ai/text/modelList.ts +++ b/src/utils/ai/text/modelList.ts @@ -4,6 +4,7 @@ import { createZhipu } from "zhipu-ai-provider"; import { createQwen } from "qwen-ai-provider"; import { createGoogleGenerativeAI } from "@ai-sdk/google"; import { createAnthropic } from "@ai-sdk/anthropic"; +import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; interface Owned { manufacturer: string; @@ -18,7 +19,8 @@ interface Owned { | typeof createZhipu | typeof createQwen | typeof createGoogleGenerativeAI - | typeof createAnthropic; + | typeof createAnthropic + | typeof createOpenAICompatible; } const modelList: Owned[] = [ @@ -409,6 +411,15 @@ const modelList: Owned[] = [ instance: createAnthropic, tool: true, }, + { + manufacturer: "other", + model: "gpt-4.1", + responseFormat: "schema", + image: true, + think: false, + instance: createOpenAICompatible, + tool: true, + }, ]; export default modelList; diff --git a/src/utils/editImage.ts b/src/utils/editImage.ts index af058a4..14aaec9 100644 --- a/src/utils/editImage.ts +++ b/src/utils/editImage.ts @@ -79,7 +79,7 @@ async function convertDirectiveAndImages(images: Record, directi */ export default async (images: Record, directive: string, projectId: number) => { const { prompt, images: base64Images } = await convertDirectiveAndImages(images, directive); - const contentStr = await u.ai.generateImage({ + const contentStr = await u.ai.image({ systemPrompt: "根据用户提供的具体修改指令,对上传的图片进行智能编辑。", prompt: prompt, imageBase64: base64Images, From 7abda02b2537e5ad7a61510d5b98ba4482e20d8b Mon Sep 17 00:00:00 2001 From: zhishi <1951671751@qq.com> Date: Fri, 6 Feb 2026 14:47:32 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=B1=86=E5=8C=85text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/env.ts | 20 +++--- src/router.ts | 122 ++++++++++++++++++++----------------- src/utils/ai/text/index.ts | 4 +- 3 files changed, 80 insertions(+), 66 deletions(-) diff --git a/src/env.ts b/src/env.ts index d5cb46f..3cfbfad 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,13 +1,18 @@ -import { readFileSync, existsSync } from "fs"; +import { readFileSync, existsSync, writeFileSync } from "fs"; + +function createDefaultEnvFile(path: string) { + const defaultContent = ["# 环境变量配置", "NODE_ENV=dev"].join("\n"); + writeFileSync(path, defaultContent, { encoding: "utf8" }); + console.log(`[环境变量]: 已创建默认的 ${path}`); +} function loadDotenvESM(envPath = ".env.local") { - // 尝试从 userData 目录读取环境变量,如果不存在则使用当前目录 let finalPath: string; if (typeof process.versions?.electron !== "undefined") { const { app } = require("electron"); - finalPath = app.getPath("userData"); - // 如果 userData 目录中不存在,尝试使用当前目录 + finalPath = app.getPath("userData") + `/${envPath}`; + // 如果 userData 目录下的 env 文件不存在,则尝试当前目录 if (!existsSync(finalPath)) { finalPath = envPath; } @@ -15,9 +20,9 @@ function loadDotenvESM(envPath = ".env.local") { finalPath = envPath; } + // 若文件不存在,自动创建一个带默认内容的环境变量文件 if (!existsSync(finalPath)) { - console.log(`[环境变量]: ${envPath} 文件不存在`); - return; + createDefaultEnvFile(finalPath); } const text = readFileSync(finalPath, "utf8"); @@ -25,7 +30,8 @@ function loadDotenvESM(envPath = ".env.local") { const idx = line.indexOf("="); if (idx > 0) process.env[line.slice(0, idx).trim()] = line.slice(idx + 1).trim(); } - console.log(`[环境变量]: ${finalPath}`); + console.log(`[环境变量]: 已加载 ${finalPath}`); } +// 若非 Electron 环境,则加载 .env.local if (typeof process.versions?.electron == "undefined") loadDotenvESM(".env.local"); diff --git a/src/router.ts b/src/router.ts index 7a7b09e..b0c9207 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash e51790d6b94bfa5992116635a72e547b +// @routes-hash df8fc2bdd69f6bbf900ca75396098390 import { Express } from "express"; import route1 from "./routes/assets/addAssets"; @@ -44,34 +44,38 @@ import route40 from "./routes/prompt/updatePrompt"; import route41 from "./routes/script/generateScriptApi"; import route42 from "./routes/script/generateScriptSave"; import route43 from "./routes/script/geScriptApi"; -import route44 from "./routes/setting/getLog"; -import route45 from "./routes/setting/getSetting"; -import route46 from "./routes/setting/updateSetting"; -import route47 from "./routes/storyboard/batchSuperScoreImage"; -import route48 from "./routes/storyboard/chatStoryboard"; -import route49 from "./routes/storyboard/generateShotImage"; -import route50 from "./routes/storyboard/generateStoryboardApi"; -import route51 from "./routes/storyboard/generateVideoPrompt"; -import route52 from "./routes/storyboard/getStoryboard"; -import route53 from "./routes/storyboard/keepStoryboard"; -import route54 from "./routes/storyboard/saveStoryboard"; -import route55 from "./routes/storyboard/uploadImage"; -import route56 from "./routes/task/getTaskApi"; -import route57 from "./routes/task/taskDetails"; -import route58 from "./routes/user/getUser"; -import route59 from "./routes/video/addVideo"; -import route60 from "./routes/video/addVideoConfig"; -import route61 from "./routes/video/deleteVideoConfig"; -import route62 from "./routes/video/generatePrompt"; -import route63 from "./routes/video/generateVideo"; -import route64 from "./routes/video/getManufacturer"; -import route65 from "./routes/video/getVideo"; -import route66 from "./routes/video/getVideoConfigs"; -import route67 from "./routes/video/getVideoModel"; -import route68 from "./routes/video/getVideoStoryboards"; -import route69 from "./routes/video/reviseVideoStoryboards"; -import route70 from "./routes/video/saveVideo"; -import route71 from "./routes/video/upDateVideoConfig"; +import route44 from "./routes/setting/addModel"; +import route45 from "./routes/setting/configurationModel"; +import route46 from "./routes/setting/delModel"; +import route47 from "./routes/setting/getAiModelMap"; +import route48 from "./routes/setting/getLog"; +import route49 from "./routes/setting/getSetting"; +import route50 from "./routes/setting/updeteModel"; +import route51 from "./routes/storyboard/batchSuperScoreImage"; +import route52 from "./routes/storyboard/chatStoryboard"; +import route53 from "./routes/storyboard/generateShotImage"; +import route54 from "./routes/storyboard/generateStoryboardApi"; +import route55 from "./routes/storyboard/generateVideoPrompt"; +import route56 from "./routes/storyboard/getStoryboard"; +import route57 from "./routes/storyboard/keepStoryboard"; +import route58 from "./routes/storyboard/saveStoryboard"; +import route59 from "./routes/storyboard/uploadImage"; +import route60 from "./routes/task/getTaskApi"; +import route61 from "./routes/task/taskDetails"; +import route62 from "./routes/user/getUser"; +import route63 from "./routes/video/addVideo"; +import route64 from "./routes/video/addVideoConfig"; +import route65 from "./routes/video/deleteVideoConfig"; +import route66 from "./routes/video/generatePrompt"; +import route67 from "./routes/video/generateVideo"; +import route68 from "./routes/video/getManufacturer"; +import route69 from "./routes/video/getVideo"; +import route70 from "./routes/video/getVideoConfigs"; +import route71 from "./routes/video/getVideoModel"; +import route72 from "./routes/video/getVideoStoryboards"; +import route73 from "./routes/video/reviseVideoStoryboards"; +import route74 from "./routes/video/saveVideo"; +import route75 from "./routes/video/upDateVideoConfig"; export default async (app: Express) => { app.use("/assets/addAssets", route1); @@ -117,32 +121,36 @@ export default async (app: Express) => { app.use("/script/generateScriptApi", route41); app.use("/script/generateScriptSave", route42); app.use("/script/geScriptApi", route43); - app.use("/setting/getLog", route44); - app.use("/setting/getSetting", route45); - app.use("/setting/updateSetting", route46); - app.use("/storyboard/batchSuperScoreImage", route47); - app.use("/storyboard/chatStoryboard", route48); - app.use("/storyboard/generateShotImage", route49); - app.use("/storyboard/generateStoryboardApi", route50); - app.use("/storyboard/generateVideoPrompt", route51); - app.use("/storyboard/getStoryboard", route52); - app.use("/storyboard/keepStoryboard", route53); - app.use("/storyboard/saveStoryboard", route54); - app.use("/storyboard/uploadImage", route55); - app.use("/task/getTaskApi", route56); - app.use("/task/taskDetails", route57); - app.use("/user/getUser", route58); - app.use("/video/addVideo", route59); - app.use("/video/addVideoConfig", route60); - app.use("/video/deleteVideoConfig", route61); - app.use("/video/generatePrompt", route62); - app.use("/video/generateVideo", route63); - app.use("/video/getManufacturer", route64); - app.use("/video/getVideo", route65); - app.use("/video/getVideoConfigs", route66); - app.use("/video/getVideoModel", route67); - app.use("/video/getVideoStoryboards", route68); - app.use("/video/reviseVideoStoryboards", route69); - app.use("/video/saveVideo", route70); - app.use("/video/upDateVideoConfig", route71); + app.use("/setting/addModel", route44); + app.use("/setting/configurationModel", route45); + app.use("/setting/delModel", route46); + app.use("/setting/getAiModelMap", route47); + app.use("/setting/getLog", route48); + app.use("/setting/getSetting", route49); + app.use("/setting/updeteModel", route50); + app.use("/storyboard/batchSuperScoreImage", route51); + app.use("/storyboard/chatStoryboard", route52); + app.use("/storyboard/generateShotImage", route53); + app.use("/storyboard/generateStoryboardApi", route54); + app.use("/storyboard/generateVideoPrompt", route55); + app.use("/storyboard/getStoryboard", route56); + app.use("/storyboard/keepStoryboard", route57); + app.use("/storyboard/saveStoryboard", route58); + app.use("/storyboard/uploadImage", route59); + app.use("/task/getTaskApi", route60); + app.use("/task/taskDetails", route61); + app.use("/user/getUser", route62); + app.use("/video/addVideo", route63); + app.use("/video/addVideoConfig", route64); + app.use("/video/deleteVideoConfig", route65); + app.use("/video/generatePrompt", route66); + app.use("/video/generateVideo", route67); + app.use("/video/getManufacturer", route68); + app.use("/video/getVideo", route69); + app.use("/video/getVideoConfigs", route70); + app.use("/video/getVideoModel", route71); + app.use("/video/getVideoStoryboards", route72); + app.use("/video/reviseVideoStoryboards", route73); + app.use("/video/saveVideo", route74); + app.use("/video/upDateVideoConfig", route75); } diff --git a/src/utils/ai/text/index.ts b/src/utils/ai/text/index.ts index c57c9c3..433f84b 100644 --- a/src/utils/ai/text/index.ts +++ b/src/utils/ai/text/index.ts @@ -45,14 +45,14 @@ const buildOptions = async (input: AIInput, config: AIConfig) => { }, }; - const output = input.output ? outputBuilders[owned.responseFormat]?.(input.output) ?? null : null; + const output = input.output ? (outputBuilders[owned.responseFormat]?.(input.output) ?? null) : null; return { config: { model: process.env.NODE_ENV === "dev" ? wrapLanguageModel({ - model: modelInstance(model!) as any, + model: modelInstance.chat(model!) as any, middleware: devToolsMiddleware(), }) : (modelInstance(model!) as LanguageModel),