diff --git a/src/agents/productionAgent/index.ts b/src/agents/productionAgent/index.ts index 465a15f..709df70 100644 --- a/src/agents/productionAgent/index.ts +++ b/src/agents/productionAgent/index.ts @@ -123,6 +123,7 @@ function runSubAgent(parentCtx: AgentContext) { prompt: z.string().max(100).describe("交给子Agent的任务简约描述"), }), execute: async ({ agent, prompt }) => { + //todo 传入md有问题 const fn = [executionAI, supervisionAI][subAgentList.indexOf(agent)]; //运行子Agent const subTextStream = await fn({ ...parentCtx, text: prompt }); diff --git a/src/agents/scriptAgent/index.ts b/src/agents/scriptAgent/index.ts index df0a344..e507e7f 100644 --- a/src/agents/scriptAgent/index.ts +++ b/src/agents/scriptAgent/index.ts @@ -46,7 +46,7 @@ export async function decisionAI(ctx: AgentContext) { const systemPrompt = buildSystemPrompt(skill.prompt, mem); const projectData = await u.db("o_project").where("id", resTool.data.projectId).first(); - const novelData = await u.db("o_novel").select("id", "chapterIndex as index"); + const novelData = await u.db("o_novel").where("projectId", resTool.data.projectId).select("id", "chapterIndex as index"); const projectInfo = [ "## 项目信息", @@ -70,6 +70,7 @@ export async function decisionAI(ctx: AgentContext) { ...useTools(ctx.resTool), }, onFinish: async (completion) => { + console.log("%c Line:73 🍧 completion", "background:#93c0a4", completion); await memory.add("assistant:decision", completion.text); }, }); @@ -99,6 +100,7 @@ export async function executionAI(ctx: AgentContext) { ...useTools(ctx.resTool), }, onFinish: async (completion) => { + console.log("%c Line:102 🍻 completion", "background:#fca650", completion); await memory.add("assistant:execution", completion.text); }, }); @@ -125,6 +127,7 @@ export async function supervisionAI(ctx: AgentContext) { ...useTools(ctx.resTool), }, onFinish: async (completion) => { + console.log("%c Line:129 🍣 completion", "background:#3f7cff", completion); await memory.add("assistant:supervision", completion.text); }, }); @@ -149,6 +152,7 @@ function runSubAgent(parentCtx: AgentContext) { let fullResponse = ""; for await (const chunk of subTextStream) { + console.log("%c Line:155 🥛 chunk", "background:#fca650", chunk); msg.send(chunk); fullResponse += chunk; } diff --git a/src/agents/scriptAgent/tools.ts b/src/agents/scriptAgent/tools.ts index a5be947..420416e 100644 --- a/src/agents/scriptAgent/tools.ts +++ b/src/agents/scriptAgent/tools.ts @@ -12,7 +12,7 @@ export const AssetSchema = z.object({ type: z.enum(["role", "tool", "scene", "clip"]).describe("资产类型"), }); export const ScriptSchema = z.object({ - id: z.number().describe("剧本ID,如果新增则为空").optional(), + id: z.number().describe("剧本ID"), name: z.string().describe("剧本名称"), content: z.string().describe("剧本内容"), }); @@ -42,7 +42,7 @@ export default (resTool: ResTool, toolsNames?: string[]) => { console.log("[tools] get_novel_events", ids); const data = await u .db("o_novel") - .where("projectId",resTool.data.projectId) + .where("projectId", resTool.data.projectId) .select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState") .whereIn("id", ids); const eventString = data.map((i: any) => [`第${i.index}章,标题:${i.chapter},事件:${i.event}`].join("\n")).join("\n"); @@ -91,41 +91,48 @@ export default (resTool: ResTool, toolsNames?: string[]) => { return true; }, }), - insert_script_to_sqlite: tool({ - description: "将剧本内容插入sqlite数据库,供后续业务使用", + update_script_to_sqlite: tool({ + description: "更新剧本,修改数据库对应剧本,供后续业务使用", inputSchema: z.object({ script: ScriptSchema, - // assetsList: z.array(AssetSchema).describe("剧本所使用资产列表,注意不要包含剧本内容,仅为所使用到的 道具、人物、场景、素材"), }), execute: async ({ script }) => { console.log("%c Line:103 🍷 script", "background:#42b983", script); - // console.log("[tools] insert_script_to_sqlite", assetsList); + await u.db("o_script").where({ id: script.id }).update({ + name: script.name, + content: script.content, + }); + + socket.emit("setPlanData", { key: "script", value: script.id }); + return true; + }, + }), + insert_script_to_sqlite: tool({ + description: "新增剧本,将剧本内容插入sqlite数据库,供后续业务使用", + inputSchema: z.object({ + script: ScriptSchema.omit({ id: true }), + }), + execute: async ({ script }) => { + console.log("%c Line:103 🍷 script", "background:#42b983", script); + const [scriptId] = await u.db("o_script").insert({ name: script.name, content: script.content, projectId: resTool.data.projectId, createTime: Date.now(), }); - // if (assetsList && assetsList.length) { - // const assetId = []; - // for (const i of assetsList) { - // if (i.id) { - // assetId.push(i.id); - // continue; - // } - // const [id] = await u.db("o_assets").insert({ - // name: i.name, - // prompt: i.prompt, - // type: i.type, - // describe: i.desc, - // projectId: resTool.data.projectId, - // startTime: Date.now(), - // }); - // assetId.push(id); - // } - - // await u.db("o_scriptAssets").insert(assetId.map((i) => ({ scriptId, assetId: i }))); - // } + socket.emit("setPlanData", { key: "script", value: scriptId }); + return true; + }, + }), + delete_script_to_sqlite: tool({ + description: "删除剧本,将剧本内容从sqlite数据库中删除", + inputSchema: z.object({ + scriptId: z.string().describe("剧本id"), + }), + execute: async ({ scriptId }) => { + console.log("[tools] delete_script_to_sqlite", scriptId); + await u.db("o_script").where({ id: scriptId }).delete(); socket.emit("setPlanData", { key: "script", value: scriptId }); return true; }, diff --git a/src/routes/assetsGenerate/polishAssetsPrompt.ts b/src/routes/assetsGenerate/polishAssetsPrompt.ts index 887341e..29fdf19 100644 --- a/src/routes/assetsGenerate/polishAssetsPrompt.ts +++ b/src/routes/assetsGenerate/polishAssetsPrompt.ts @@ -122,6 +122,7 @@ export default router.post( messages: [{ role: "user", content: "小说原文" + novelText }], tools: skill.tools, })) as any; + if (!_output) return res.status(500).send("失败"); await u.db("o_assets").where("id", assetsId).update({ prompt: _output }); diff --git a/src/types/database.d.ts b/src/types/database.d.ts index 70b079c..bf2ee8f 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,6 +1,36 @@ -// @db-hash d807205fbb27fc5ddb04cae060fb4430 +// @db-hash 579a004cc745580469a24ee71f5f51c3 //该文件由脚本自动生成,请勿手动修改 +export interface _o_project_old_20260326 { + 'artStyle'?: string | null; + 'createTime'?: number | null; + 'id'?: number | null; + 'intro'?: string | null; + 'name'?: string | null; + 'projectType'?: string | null; + 'type'?: string | null; + 'userId'?: number | null; + 'videoRatio'?: string | null; +} +export interface _o_storyboard_old_20260325 { + 'camera'?: string | null; + 'createTime'?: number | null; + 'description'?: string | null; + 'duration'?: string | null; + 'filePath'?: string | null; + 'frameMode'?: string | null; + 'id'?: number; + 'lines'?: string | null; + 'mode'?: string | null; + 'model'?: string | null; + 'prompt'?: string | null; + 'reason'?: string | null; + 'resolution'?: string | null; + 'scriptId'?: number | null; + 'sound'?: string | null; + 'state'?: string | null; + 'title'?: string | null; +} export interface memories { 'content': string; 'createTime': number; @@ -220,6 +250,8 @@ export interface o_videoConfig { } export interface DB { + "_o_project_old_20260326": _o_project_old_20260326; + "_o_storyboard_old_20260325": _o_storyboard_old_20260325; "memories": memories; "o_agentDeploy": o_agentDeploy; "o_agentWorkData": o_agentWorkData; diff --git a/src/utils/stripThink.ts b/src/utils/stripThink.ts new file mode 100644 index 0000000..9867a93 --- /dev/null +++ b/src/utils/stripThink.ts @@ -0,0 +1,106 @@ +/** + * 去除深度思考模型输出的 ... 标签及其内容 + * + * 1. stripThink(text) — 用于非流式,直接去除完整文本中的 块 + * 2. createThinkStreamFilter() — 用于流式,返回有状态的过滤器,逐 chunk 过滤 + */ + +/** + * 非流式:去除完整文本中的 ... + */ +export function stripThink(text: string): string { + return text.replace(/[\s\S]*?<\/think>/g, "").trim(); +} + +/** + * 流式:创建一个有状态的 chunk 过滤器 + * + * 用法: + * ```ts + * const filter = createThinkStreamFilter(); + * for await (const chunk of textStream) { + * const filtered = filter.push(chunk); + * if (filtered) msg.send(filtered); + * } + * ``` + */ +export function createThinkStreamFilter() { + let insideThink = false; + let buffer = ""; + + return { + /** + * 输入一个 chunk,返回过滤后需要输出的文本(可能为空字符串) + */ + push(chunk: string): string { + let output = ""; + let i = 0; + + while (i < chunk.length) { + if (insideThink) { + // 正在 内部,寻找 + const closeIdx = chunk.indexOf("", i); + if (closeIdx !== -1) { + // 找到闭合标签,跳过标签内容 + insideThink = false; + i = closeIdx + "".length; + } else { + // 整个剩余 chunk 都在 think 内,全部丢弃 + break; + } + } else { + // 不在 内部 + const openIdx = chunk.indexOf("", i); + if (openIdx !== -1) { + // 找到开启标签,输出标签之前的内容 + output += buffer + chunk.slice(i, openIdx); + buffer = ""; + insideThink = true; + i = openIdx + "".length; + } else { + // 没有发现 ,但可能 chunk 末尾是不完整的 "" 的不完整前缀 + * 如 "<", "