koishi-plugin-aka-comfy-agent 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +1781 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1753 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/llm/prompts.ts","../src/llm/client.ts","../src/llm/models.ts","../src/comfy/client.ts","../src/comfy/workflow.ts","../src/agent/diagnose.ts","../src/agent/brain.ts","../src/agent/session.ts","../src/agent/render-facade.ts","../src/bridge/chatluna/runtime.ts","../src/bridge/chatluna/tool-runtime.ts","../src/bridge/chatluna/manager.ts"],"sourcesContent":["import { Context, Logger, Schema, Session as KoishiSession, h } from 'koishi'\nimport { Config } from './config.js'\nimport { LlmClient } from './llm/client.js'\nimport { listModels } from './llm/models.js'\nimport { MAIN_SYSTEM_PROMPT, GLM_PROBE_PROMPT, VISION_USER_TEXT, VISION_CROSSCHECK_TEXT } from './llm/prompts.js'\nimport { ComfyClient } from './comfy/client.js'\nimport { randomSeed } from './comfy/workflow.js'\nimport { runTurn, runAnalysisOnly } from './agent/brain.js'\nimport type { BrainDeps } from './agent/brain.js'\nimport { SerialLock, SessionStore, initialParams } from './agent/session.js'\nimport { createRenderFacade } from './agent/render-facade.js'\nimport type { RenderFacade } from './agent/render-facade.js'\nimport { ChatLunaBridgeManager } from './bridge/chatluna/manager.js'\nimport type { LastGeneratedRecord, LastGeneratedStore } from './bridge/chatluna/types.js'\nimport type { RenderResult, Session, UploadedImage } from './types.js'\n\nexport { Config }\nexport const name = 'aka-comfy-agent'\n\n/**\n * 声明可选依赖 chatluna,避免 ctx.chatluna 访问触发\n * \"property chatluna is not registered\" 警告(与 aka-ai-generator 桥一致)。\n */\nexport const inject = { optional: ['chatluna'] } as const\n\n/**\n * 运行时可变状态挂到模块级(热重载后 apply() 重新引用同一对象)。\n * 参考 aka-yesimbot-voice:把状态锁在 apply() 闭包会因插件重装被抛弃。\n */\ninterface RuntimeState {\n store: SessionStore | null\n lock: SerialLock\n /** 房间级最近一次生成图,供 ChatLuna last_generated 引用;模块级避免热重载丢失 */\n lastGenByConv: Map<string, LastGeneratedRecord>\n /** 用户级并发锁(ChatLuna 工具专用,防同 userId 撞 GPU;命令路径靠 Session.busy) */\n userTaskLocks: Set<string>\n /** 分步接图:`comfy` 指令后等待用户单独发图的待执行意图(key=会话键) */\n awaitingImage: Map<string, PendingAwaitImage>\n}\n/** 等图状态里暂存的意图(聊天无法一条消息同时带图+文字,故先收意图再等图) */\ninterface PendingAwaitImage {\n text: string\n createdAt: number\n}\nconst runtime: RuntimeState = {\n store: null,\n lock: new SerialLock(),\n lastGenByConv: new Map(),\n userTaskLocks: new Set(),\n awaitingImage: new Map(),\n}\n\nexport function apply(ctx: Context, config: Config): void {\n const logger: Logger = ctx.logger(name)\n\n // Store 与 lock 生命周期对齐进程;重载时若 idleTimeout 变了会重建 store。\n if (!runtime.store || (runtime.store as any).__idle !== config.session.idleTimeoutMs) {\n if (runtime.store) runtime.store.stopSweep()\n runtime.store = new SessionStore(config.session.idleTimeoutMs)\n ;(runtime.store as any).__idle = config.session.idleTimeoutMs\n runtime.store.startSweep()\n }\n const store = runtime.store\n const lock = runtime.lock\n const clientId = `koishi-aka-comfy-${Math.floor(Math.random() * 1e9)}`\n\n const awaitingImage = runtime.awaitingImage\n const AWAIT_TIMEOUT_MS = config.session.idleTimeoutMs\n\n // 等图状态兜底回收,避免长时间占内存\n const awaitSweeper = setInterval(() => {\n const now = Date.now()\n for (const [k, p] of awaitingImage) {\n if (now - p.createdAt > AWAIT_TIMEOUT_MS) awaitingImage.delete(k)\n }\n }, 60_000)\n awaitSweeper.unref?.()\n\n const comfy = new ComfyClient(ctx, config.comfy.baseUrl, config.comfy.timeoutMs)\n\n /** 组装 BrainDeps(供 runTurn / runAnalysisOnly / runGenerateOnly 复用) */\n const brainDeps = (): BrainDeps => ({\n main: mainClient,\n vision: visionClient,\n comfy,\n lock,\n logger,\n clientId,\n comfyTimeoutMs: config.comfy.timeoutMs,\n maxDiagnoseRetries: config.session.maxDiagnoseRetries,\n mainSystemPrompt: config.mainLLM.systemPrompt?.trim() || MAIN_SYSTEM_PROMPT,\n visionSystemPrompt: config.visionLLM.systemPrompt?.trim() || GLM_PROBE_PROMPT,\n visionUserText: config.visionLLM.userText?.trim() || VISION_USER_TEXT,\n visionCrosscheckRounds: config.visionCrosscheckRounds,\n visionCrosscheckText: VISION_CROSSCHECK_TEXT,\n defaultBackgroundColor: config.defaultBackgroundColor,\n // Comfy 生成轮询期间编辑「正在生成」进度消息(约每 15s 一次,由 brain onTick 触发)\n onGenerateProgress: (elapsedMs) => {\n const g = genProgress\n if (!g) return\n const sec = Math.floor(elapsedMs / 1000)\n void reportProgress(g.session, g.handle, `正在生成,已等待 ${sec}s…`).then((h) => {\n if (genProgress === g) genProgress.handle = h\n })\n },\n })\n\n // LLM 客户端在 apply 里构建;model 解析异步启动,未解析时 chat 会抛 enabled 校验。\n let mainClient = new LlmClient(ctx, config.mainLLM, config.mainLLM.model, config.comfy.timeoutMs)\n let visionClient = new LlmClient(ctx, config.visionLLM, config.visionLLM.model, config.comfy.timeoutMs)\n\n // 动态模型下拉:探测 {baseUrl}/models 注册选项;能取到列表且当前留空则直接切到第一个并持久化。\n // 控制台保存配置会重载插件 → apply 重跑 → 重新探测,故 dropdown 会跟随 baseUrl 刷新。\n const schemaScope = (ctx as unknown as { schema?: { set: (k: string, v: unknown) => void } }).schema\n\n // 同步占位:在 apply 同步阶段立即注册动态源 key,保证设置页首次 schema 下发时 key 已在\n // store.schema 中,避免\"异步探测完成后才 set → 已渲染设置页拿不到 → dynamic 渲染成空壳\"。\n // 异步探测成功后会用完整列表覆盖。占位用当前 config 里的 model;为空则先给文本框占位。\n const seedDynamic = (label: 'main' | 'vision', cfg: typeof config.mainLLM, key: string) => {\n if (!schemaScope) return\n const cur = cfg.model?.trim()\n if (cur) {\n schemaScope.set(key, Schema.union([Schema.const(cur).description(cur)]))\n } else {\n schemaScope.set(key, Schema.string().description('模型(探测中,完成后可选)'))\n }\n logger.info('%s: 下拉占位已注册 key=%s cur=%s', label, key, cur || '(空)')\n }\n seedDynamic('main', config.mainLLM, 'aka-comfy-agent.models.main')\n seedDynamic('vision', config.visionLLM, 'aka-comfy-agent.models.vision')\n\n async function probeOptions(label: string, cfg: typeof config.mainLLM): Promise<string[]> {\n const name = label === 'vision' ? 'aka-comfy-agent.models.vision' : 'aka-comfy-agent.models.main'\n try {\n const ids = await listModels(ctx, cfg, 15000)\n if (schemaScope) {\n schemaScope.set(\n name,\n Schema.union(ids.map((id) => Schema.const(id).description(id))),\n )\n }\n logger.info('%s: 模型下拉 %d 个选项', label, ids.length)\n return ids\n } catch (err) {\n // 探测失败:退化为手动文本输入,避免下拉空置不可用\n if (schemaScope) schemaScope.set(name, Schema.string().description('模型(无法获取列表,手动填写)'))\n logger.warn('%s: 模型列表探测失败:%s', label, err instanceof Error ? err.message : String(err))\n return []\n }\n }\n\n void (async () => {\n const [mainIds, visionIds] = await Promise.all([\n probeOptions('main', config.mainLLM),\n probeOptions('vision', config.visionLLM),\n ])\n // 运行时用:手动指定优先,否则取第一个\n const mainModel = config.mainLLM.model?.trim() || (config.mainLLM.enabled ? mainIds[0] ?? '' : '')\n const visionModel = config.visionLLM.model?.trim() || (config.visionLLM.enabled ? visionIds[0] ?? '' : '')\n if (mainModel) mainClient = new LlmClient(ctx, config.mainLLM, mainModel, config.comfy.timeoutMs)\n if (visionModel) visionClient = new LlmClient(ctx, config.visionLLM, visionModel, config.comfy.timeoutMs)\n // 能取到列表且当前留空 → 持久化切到第一个(只切一次,改后不再复发)\n const snapMain = config.mainLLM.enabled && !config.mainLLM.model?.trim() && mainIds.length > 0\n const snapVision = config.visionLLM.enabled && !config.visionLLM.model?.trim() && visionIds.length > 0\n if ((snapMain || snapVision) && (ctx as any).scope?.update) {\n try {\n await (ctx as any).scope.update({\n ...config,\n mainLLM: { ...config.mainLLM, model: snapMain ? mainIds[0] : config.mainLLM.model },\n visionLLM: { ...config.visionLLM, model: snapVision ? visionIds[0] : config.visionLLM.model },\n })\n logger.info('已自动切到第一个模型:main=%s vision=%s', snapMain ? mainIds[0] : '-', snapVision ? visionIds[0] : '-')\n } catch (err) {\n logger.warn('自动切换模型持久化失败:%s', err instanceof Error ? err.message : String(err))\n }\n }\n logger.info('models resolved main=%s vision=%s', mainClient.model || '(disabled)', visionClient.model || '(disabled)')\n })()\n\n logger.info('aka-comfy-agent apply comfy=%s clientId=%s', config.comfy.baseUrl, clientId)\n\n // 渲染门面 & ChatLuna 桥:mainClient/visionClient 用 getter 引用当前值(模型异步解析后会被重赋)\n const renderFacade: RenderFacade = createRenderFacade({\n ctx,\n logger,\n comfy,\n getMainClient: () => mainClient,\n getVisionClient: () => visionClient,\n lock,\n clientId,\n comfyTimeoutMs: config.comfy.timeoutMs,\n maxDiagnoseRetries: config.session.maxDiagnoseRetries,\n mainSystemPrompt: config.mainLLM.systemPrompt?.trim() || MAIN_SYSTEM_PROMPT,\n visionSystemPrompt: config.visionLLM.systemPrompt?.trim() || GLM_PROBE_PROMPT,\n visionUserText: config.visionLLM.userText?.trim() || VISION_USER_TEXT,\n visionCrosscheckRounds: config.visionCrosscheckRounds,\n visionCrosscheckText: VISION_CROSSCHECK_TEXT,\n defaultBackgroundColor: config.defaultBackgroundColor,\n })\n\n const lastGeneratedStore: LastGeneratedStore = {\n get: (id) => runtime.lastGenByConv.get(id),\n set: (id, rec) => { runtime.lastGenByConv.set(id, rec) },\n }\n const chatLunaBridge = new ChatLunaBridgeManager(\n ctx,\n { render: renderFacade, lastGenerated: lastGeneratedStore, userTaskLocks: runtime.userTaskLocks },\n config,\n logger,\n )\n\n // ChatLuna 服务可用后再同步一次;未启用/未装则静默禁用\n ;(ctx as Context & {\n inject?: (deps: string[], cb: (ctx: Context) => void | Promise<void>) => void\n }).inject?.(['chatluna'], async () => {\n await chatLunaBridge.sync(Boolean(config.chatlunaEnabled))\n })\n\n ;(ctx as Context & {\n accept?: (keys: string[], listener: (nextConfig: Config) => void, options?: { immediate?: boolean }) => void\n }).accept?.(\n ['chatlunaEnabled', 'chatlunaRoomLastGenerated'],\n (nextConfig) => {\n chatLunaBridge.updateConfig(nextConfig)\n if ((ctx as Context & { chatluna?: unknown }).chatluna) {\n void chatLunaBridge.sync(Boolean(nextConfig.chatlunaEnabled))\n }\n },\n { immediate: true },\n )\n\n ctx.on('dispose', async () => {\n clearInterval(awaitSweeper)\n await chatLunaBridge.dispose()\n })\n\n /** 从消息 elements 里挑第一张图 URL。 */\n function pickImageUrl(elements: h[] | undefined): string | null {\n if (!elements?.length) return null\n const imgs = h.select(elements, 'img')\n for (const el of imgs) {\n const src = el.attrs?.src\n if (typeof src === 'string' && src) return src\n }\n return null\n }\n\n /**\n * 统一进度反馈:已有 mid 则编辑同一条(避免刷屏),否则发一条新消息。\n * 返回可供后续编辑继续更新的 handle;编辑失败会退化为发新消息。\n */\n type ProgressHandle = { mid: string }\n /** 当前活跃生成轮次的进度(含 session:brain 轮询回调拿不到 session,需经此取) */\n let genProgress: { session: KoishiSession; handle: ProgressHandle } | undefined\n async function reportProgress(\n session: KoishiSession,\n handle: ProgressHandle | undefined,\n text: string,\n ): Promise<ProgressHandle> {\n if (handle?.mid) {\n try {\n await session.bot.editMessage(session.channelId ?? '', handle.mid, text)\n return handle\n } catch (err) {\n logger.warn('progress edit failed (will fall back to new message): %s', errMsg(err))\n }\n }\n const mid = await session.send(text)\n return { mid: Array.isArray(mid) ? (mid[0] as string) : String(mid ?? '') }\n }\n\n /** 从 elements 里抽出纯文本(去掉 at/img 等)。 */\n function pickText(elements: h[] | undefined): string {\n if (!elements?.length) return ''\n let out = ''\n const walk = (arr: h[]) => {\n for (const el of arr) {\n if (el.type === 'text' && typeof el.attrs?.content === 'string') out += el.attrs.content\n if (el.children?.length) walk(el.children)\n }\n }\n walk(elements)\n return out.trim()\n }\n\n /** 下载消息里的图片 → Buffer + mime;同时返回 base64 data URL 供 vision 用。 */\n async function fetchImage(url: string): Promise<{ buffer: Buffer; mimeType: string; dataUrl: string }> {\n let buffer: Buffer\n let mimeType = 'image/png'\n if (url.startsWith('internal:') || url.startsWith('file:') || url.startsWith('data:')) {\n const file = await ctx.http.file(url, { timeout: config.comfy.timeoutMs })\n buffer = Buffer.from(file.data)\n if (typeof file.type === 'string' && file.type) mimeType = file.type\n } else {\n const ab = await ctx.http.get(url, { responseType: 'arraybuffer', timeout: config.comfy.timeoutMs })\n buffer = Buffer.from(ab as ArrayBuffer)\n mimeType = sniffMime(buffer) || mimeType\n }\n const dataUrl = `data:${mimeType};base64,${buffer.toString('base64')}`\n return { buffer, mimeType, dataUrl }\n }\n\n function keyFor(session: KoishiSession): string {\n // 稳定的会话键:私聊一律按 userId 独立;群聊按 channel(或 perUserInGroup 时按 channel:user)。\n // 不要用「guildId 是否为空」来判私聊/群聊 —— Lark 等适配器在同一会话里命令消息与裸消息的\n // guildId 字段可能不一致(命令时有、裸消息时缺失),会导致同一会话的 key 在两者间漂移,\n // 进而中间件 store.get(key) 读不到会话,把「确认/反馈」放行给下游(如 ChatLuna)。\n if (session.isDirect) return `dm:${session.userId ?? 'unknown'}`\n const channelId = session.channelId ?? session.userId ?? 'unknown'\n if (config.session.perUserInGroup) return `g:${channelId}:${session.userId ?? 'unknown'}`\n return `g:${channelId}`\n }\n\n async function startOrRestartSession(\n session: KoishiSession,\n imageUrl: string,\n intent: string,\n ): Promise<string | h[]> {\n if (!mainClient.enabled) {\n return '主 LLM 未启用或未解析到 model:先在配置里补 baseUrl/apiKey/model(或让 /v1/models 可用),再试。'\n }\n let downloaded\n try {\n downloaded = await fetchImage(imageUrl)\n } catch (err) {\n logger.warn('fetchImage failed: %s', errMsg(err))\n return `图片下载失败:${errMsg(err)}`\n }\n let uploaded: UploadedImage\n try {\n const filename = `input_${Date.now()}.${extForMime(downloaded.mimeType)}`\n const name = await comfy.uploadImage(downloaded.buffer, filename, downloaded.mimeType)\n uploaded = { name, mimeType: downloaded.mimeType, bytes: downloaded.buffer.length }\n logger.info('uploaded to comfy name=%s bytes=%d', name, downloaded.buffer.length)\n } catch (err) {\n logger.warn('comfy upload failed: %s', errMsg(err))\n return `上传 ComfyUI 失败:${errMsg(err)}`\n }\n\n const params = initialParams(randomSeed())\n const newSession: Session = {\n key: keyFor(session),\n channelId: session.channelId ?? '',\n userId: session.userId ?? '',\n guildId: session.guildId,\n source: uploaded,\n materialAnalysis: '',\n turns: 0,\n params,\n llmContext: [],\n errorHistory: [],\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n busy: false,\n }\n store.set(newSession.key, newSession)\n\n // 生成前确认闸(命令路径):先跑视觉分析,把报告发用户等确认/修正,不立即生成\n if (config.confirmBeforeGenerate.command && downloaded.dataUrl) {\n try {\n // 即时反馈:已收到图片并进入分析(GLM 解析资产清单耗时较长,必须先给用户反馈,避免误以为没收到图)\n await reportProgress(session, undefined, `已收到图片(${(downloaded.buffer.length / 1048576).toFixed(1)} MB)✓ 正在解析角色资产清单…`)\n const analysis = await runAnalysisOnly(brainDeps(), downloaded.dataUrl)\n newSession.materialAnalysis = analysis\n newSession.pendingConfirm = { analysis, intent: intent }\n store.set(newSession.key, newSession)\n logger.info('confirm gate armed key=%s analysis_len=%d', newSession.key, analysis.length)\n return formatConfirmPrompt(analysis, intent)\n } catch (err) {\n logger.warn('confirm gate vision analysis failed, fall back to direct generation: %s', errMsg(err))\n return await performTurn(session, newSession, intent, { runVision: true, sourceDataUrl: downloaded.dataUrl })\n }\n }\n\n return await performTurn(session, newSession, intent, { runVision: true, sourceDataUrl: downloaded.dataUrl })\n }\n\n async function performTurn(\n session: KoishiSession,\n convo: Session,\n feedbackIn: string,\n opts: { runVision: boolean; sourceDataUrl?: string },\n ): Promise<string | h[]> {\n if (convo.busy) return '本会话上一轮还在跑,等它结束再发。'\n if (convo.turns >= config.session.maxTurns) {\n return `会话已达上限(${config.session.maxTurns} 轮)。发「退出」重开。`\n }\n if (!mainClient.enabled) return '主 LLM 未启用/未解析到 model。'\n convo.busy = true\n convo.turns += 1\n store.touch(convo)\n // 确认闸已确认:使用暂存分析 + 覆盖用 feedback,作为本轮意图\n let feedback = feedbackIn\n if (convo.pendingConfirm) {\n convo.materialAnalysis = convo.pendingConfirm.analysis\n const intent = convo.pendingConfirm.intent\n const corrections = convo.pendingConfirm.corrections ?? []\n convo.pendingConfirm = undefined\n feedback = [intent, ...corrections, feedbackIn ? `[用户确认] ${feedbackIn}` : ''].filter(Boolean).join('\\n')\n store.touch(convo)\n }\n try {\n // 生成前反馈:提交 ComfyUI 前先给「正在生成」提示,之后由 brain 轮询逐步更新耗时\n const mid = await reportProgress(session, undefined, '正在生成…')\n genProgress = { session, handle: mid }\n const result: RenderResult = await runTurn(\n brainDeps(),\n convo,\n feedback,\n opts,\n )\n genProgress = undefined\n // 生成完成:把进度消息收尾为完成态,再交付图片+元信息\n await reportProgress(session, mid, '生成完毕 ✓')\n const parts: h[] = []\n parts.push(h.image(result.imageBuffer, mimeFromFilename(result.filename)))\n const noteLine = result.note ? `做法:${result.note}\\n` : ''\n const meta = `seed=${result.params.seed}`\n + (result.material ? ` · 材质=${result.material}` : '')\n + (result.lighting ? ` · 灯光=${result.lighting}` : '')\n + (result.background ? ` · 背景=${result.background}` : '')\n parts.push(h('text', { content: `\\n${noteLine}${meta}\\n${statusBar(convo, config.session.maxTurns, '可改图')}` }))\n return parts\n } catch (err) {\n logger.warn('performTurn failed key=%s turn=%d: %s', convo.key, convo.turns, errMsg(err))\n // 失败时把进度消息收尾为失败态,避免悬空\n if (genProgress) {\n const g = genProgress\n genProgress = undefined\n await reportProgress(g.session, g.handle, `生成失败:${errMsg(err)}`)\n }\n return `本轮失败:${errMsg(err)}`\n } finally {\n convo.busy = false\n store.touch(convo)\n }\n }\n\n ctx.command('comfy [...content:text]', 'ComfyUI FLUX.2 图生图 + agent 迭代(支持分步接图:先 comfy 记意图/等图,再单独发图开始)')\n .alias('v2')\n .example('comfy 把背景换成黄色')\n .example('comfy <图> 把背景换成草原,加强毛绒质感')\n .action(async ({ session }, ...contentArgs) => {\n if (!session) return\n const text = (contentArgs.join(' ') || '').trim()\n const elements = session.elements\n const imageUrl = pickImageUrl(elements)\n const key = keyFor(session)\n const existing = store.get(key)\n\n // 退出 etc.:主命令带 [...content] 位置参数,Koishi 的 inferCommand 会在命中主命令后\n // 因 `command._arguments.length` break,永不继续匹配 comfy.clear 子命令别名 → 必须显式拦截,\n // 否则 \"comfy 退出\" 会被当成内容 feedback 触发 performTurn 又生成一遍。此判断须在所有分支(含\n // 等图态 !existing)之前,否则会先被记意图/回退分支吞掉。\n if (!imageUrl && isExitIntent(text)) {\n return destroyComfy(key)\n }\n\n if (!existing) {\n if (imageUrl) {\n awaitingImage.delete(key)\n return await startOrRestartSession(session, imageUrl, text || '(用户未给具体意图,请按材质分层做保守增强)')\n }\n // 首轮无图:进入分步接图状态,等用户单独发图后再执行(聊天无法一条消息同时带图+文字)\n awaitingImage.set(key, { text, createdAt: Date.now() })\n return text\n ? `已记录意图「${text}」。请发送要处理的图片(一张),收到后立即开始生成。`\n : '请发送要处理的图片(一张),收到后立即开始生成。'\n }\n\n // 已有会话:用户新图则替换源图并重跑 vision;否则纯文本反馈\n if (imageUrl) {\n awaitingImage.delete(key)\n store.delete(key)\n return await startOrRestartSession(session, imageUrl, text || '(用户换了源图,请重新分析并给出保守首轮)')\n }\n if (!text) return '会话已在进行中:附上文字反馈来触发下一轮;或发「退出」重开。'\n return await performTurn(session, existing, text, { runVision: false })\n })\n\n // 帮助 / 状态 / 退出 —— 提为 Koishi 子命令,符合规范:官方 help、参数、权限与错误处理统一\n ctx.command('comfy.help', '查看 comfy 使用说明', { authority: 1 })\n .alias('帮助')\n .action(async () => helpText())\n\n ctx.command('comfy.status', '查看当前 comfy 会话参数与错误史', { authority: 1 })\n .alias('状态')\n .action(async ({ session }) => {\n if (!session) return\n return statusText(store.get(keyFor(session)))\n })\n\n ctx.command('comfy.clear', '销毁当前会话,离开循环回到聊天', { authority: 1 })\n .alias('退出')\n .action(async ({ session }) => {\n if (!session) return\n const key = keyFor(session)\n awaitingImage.delete(key)\n const removed = store.delete(key)\n return removed ? '已销毁本会话,已离开循环。' : '当前没有会话。'\n })\n\n // 销毁会话(清空等图态 + store 会话)。command 与 middleware 共用。\n function destroyComfy(key: string): string {\n const hadAwait = awaitingImage.delete(key)\n const removed = store.delete(key)\n if (removed) return '已销毁本会话,已离开循环。'\n if (hadAwait) return '已离开循环(清空了等待中的会话)。'\n return '当前没有进行中的 comfy 会话。'\n }\n\n // 会话内直接发文字 = 修改反馈(不触发 comfy 命令时才走这条路径);并承担分步接图:\n // `comfy` 指令进入等图状态后,这里接住「单独发来的图」触发执行。\n ctx.middleware(async (session, next) => {\n if (!session.channelId && !session.userId) return next()\n const key = keyFor(session)\n const elements = session.elements\n const imageUrl = pickImageUrl(elements)\n\n // 分步接图:等图状态优先消费带图消息,合并暂存意图后立即生成\n if (imageUrl) {\n const pendingImg = awaitingImage.get(key)\n if (!pendingImg) return next() // 无等图状态 → 放行,别吞图\n // 等图超时:明确提示,避免\"发图没反应\"\n if (Date.now() - pendingImg.createdAt > AWAIT_TIMEOUT_MS) {\n awaitingImage.delete(key)\n return '等图状态已超时(超过空闲时限)。请重新发 comfy,再发图开始。'\n }\n awaitingImage.delete(key)\n const text = (pickText(elements) || pendingImg.text || '').trim()\n return await startOrRestartSession(\n session,\n imageUrl,\n text || '(用户未给具体意图,请按材质分层做保守增强)',\n )\n }\n\n const rawContent = pickText(elements) || (session.content ?? '').toString().trim()\n if (!rawContent) return next()\n if (/^\\s*[./!\\\\]?\\s*(comfy|v2)\\b/i.test(rawContent)) return next()\n\n // 会话内「裸发」清空/退出/结束 → 直接销毁会话(无需 comfy 前缀)。仅当确有 comfy 状态时消费,否则放行。\n if (isExitIntent(rawContent) && (awaitingImage.get(key) || store.get(key))) {\n logger.info('cmt: bare exit key=%s text=%s', key, rawContent.slice(0, 30))\n return destroyComfy(key)\n }\n\n // 等图状态下先给文字 → 更新待执行意图,继续等图\n const pendingAwait = awaitingImage.get(key)\n if (pendingAwait) {\n pendingAwait.text = rawContent\n pendingAwait.createdAt = Date.now()\n return `已更新意图「${rawContent}」,请发送要处理的图片。`\n }\n\n const convo = store.get(key)\n if (!convo) {\n // 会话曾存在但已空闲过期 → 明确提示,避免“静默消失”让用户不知道已退出\n if (store.wasRecentlyExpired(key, AWAIT_TIMEOUT_MS)) {\n logger.info('cmt: expired session key=%s text=%s', key, rawContent.slice(0, 30))\n return `你的图像会话已因空闲超时结束(超过 ${Math.round(config.session.idleTimeoutMs / 60000)} 分钟无操作)。发「comfy <图> <意图>」重新开始。`\n }\n // 无 comfy 会话:放行给下游(ChatLuna 等)。此日志用于诊断“确认进错循环”。\n logger.info('cmt: no session, pass down key=%s direct=%s channel=%s user=%s text=%s',\n key, !!session.isDirect, session.channelId ?? '', session.userId ?? '', rawContent.slice(0, 30))\n return next()\n }\n\n // 生成前确认闸:会话处于待确认态时的处理(确认 / 修正)\n if (convo.pendingConfirm && !convo.busy) {\n logger.info('cmt: pendingConfirm enter key=%s isConfirm=%s text=%s', key,\n isConfirmWord(((rawContent || '')).trim()), (rawContent || '').slice(0, 30))\n // 带图消息 → 视为换新源图,走 startOrRestartSession 重新分步(由上层处理)\n if (imageUrl) return next()\n const text = rawContent.trim()\n if (!text) return '正在等待你对视觉分析结果的确认:回复确认词开始生成,或直接描述需要修正的地方。'\n if (isExitIntent(text)) {\n return destroyComfy(key)\n }\n if (isConfirmWord(text)) {\n logger.info('cmt: confirmed by word key=%s text=%s', key, text)\n return await performTurn(session, convo, '', { runVision: false })\n }\n // 修正:把用户修正意见并入待确认分析 + 累计到 corrections(确认时并入 [本轮反馈] 最高优先),保持待确认态\n convo.pendingConfirm.analysis = `${convo.pendingConfirm.analysis}\\n\\n[用户修正]\\n${text}`\n convo.pendingConfirm.corrections = [...(convo.pendingConfirm.corrections ?? []), text]\n convo.materialAnalysis = convo.pendingConfirm.analysis\n store.touch(convo)\n return `已记录你的修正:「${text}」。\\n\\n${formatConfirmPrompt(convo.pendingConfirm.analysis, convo.pendingConfirm.intent)}`\n }\n\n // 生成中发文字:明确提示,不再静默吞掉\n if (convo.busy) return '本会话上一轮还在跑,稍等它完成再发。'\n\n // 非生成中:先判「这像不像改图指令」。明显闲聊/无关话题给引导提示,不默默当下一轮 prompt。\n // 这正是「用户在 COMFY 循环里想当然聊天却把内容当 prompt」的痛点防护。\n if (isLikelyChatter(rawContent)) {\n logger.info('cmt: chatter detected key=%s text=%s', key, rawContent.slice(0, 30))\n return [\n `「${rawContent.slice(0, 60)}」看起来不像改图指令。`,\n `你当前在 COMFY 工作循环里(第 ${convo.turns}/${config.session.maxTurns} 轮):`,\n `- 想调整这张图:直接说改法,如「毛再蓬一点」「背景换红」`,\n `- 想离开循环回到聊天:直接发「清空」或「退出」,无需 comfy 前缀`,\n ].join('\\n')\n }\n\n // 只截 bot 有会话的这批消息;不影响其他插件命令的分发(其他命令已由 command dispatcher 优先匹配)\n return await performTurn(session, convo, rawContent, { runVision: false })\n })\n\n function statusText(convo: Session | undefined): string {\n if (!convo) return '当前没有会话。发 \"comfy <图> <意图>\" 开新的。'\n const errs = convo.errorHistory.slice(-3).map((e) => `- ${e.message}${e.hint ? `|${e.hint}` : ''}`).join('\\n') || '(无)'\n return [\n `会话:key=${convo.key}`,\n `轮次:${convo.turns}/${config.session.maxTurns}`,\n `源图:name=${convo.source.name} bytes=${convo.source.bytes}`,\n `参数:${JSON.stringify(convo.params)}`,\n `视觉报告:${convo.materialAnalysis.slice(0, 200)}`,\n `最近错误:\\n${errs}`,\n ].join('\\n')\n }\n\n function helpText(): string {\n return [\n 'ComfyUI FLUX.2 图生图 agent(本期仅 i2i):',\n '- comfy <图> <意图>:一条消息带图+意图,立即开始',\n '- comfy <意图>:分步接图,先记意图再等图,收到图自动开始(或 comfy 后直接发图)',\n '- 会话内直接发「退出」:销毁当前会话,离开循环回到聊天(无需 comfy 前缀)',\n '- comfy 状态:查看会话参数与错误史',\n '- comfy 帮助:本页',\n `- 每会话 ${config.session.maxTurns} 轮上限;空闲 ${Math.round(config.session.idleTimeoutMs / 60000)} 分钟自动销毁。`,\n ].join('\\n')\n }\n}\n\nfunction sniffMime(buffer: Buffer): string | null {\n if (buffer.length < 4) return null\n if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) return 'image/png'\n if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return 'image/jpeg'\n if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) return 'image/gif'\n if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46) return 'image/webp'\n return null\n}\n\nfunction extForMime(mime: string): string {\n if (mime === 'image/png') return 'png'\n if (mime === 'image/jpeg') return 'jpg'\n if (mime === 'image/gif') return 'gif'\n if (mime === 'image/webp') return 'webp'\n return 'png'\n}\n\nfunction mimeFromFilename(filename: string): string {\n const lower = filename.toLowerCase()\n if (lower.endsWith('.png')) return 'image/png'\n if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg'\n if (lower.endsWith('.gif')) return 'image/gif'\n if (lower.endsWith('.webp')) return 'image/webp'\n return 'image/png'\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\n/** 状态栏:常驻的状态标签 + 退出引导,追加到每轮 comfy 交互回复末尾,让用户始终知道自己在 COMFY 循环里 */\nfunction statusBar(convo: Session, maxTurns: number, state?: string): string {\n const label = `【COMFY · 第 ${convo.turns}/${maxTurns} 轮${state ? `|${state}` : ''}】`\n return [\n ``,\n label,\n `想继续调整就直接发文字(如「毛再蓬一点」「背景换红」),会在这张图上迭代。`,\n `离开循环回到聊天:直接发「退出」。`,\n ].join('\\n')\n}\n\n/** 确认词识别:单独出现的确认/开始/ok 等 */\nconst CONFIRM_WORDS = new Set(['确认', '开始', '生成', '可以', '好', '好的', 'ok', 'OK', 'Ok', 'oK', 'yes', 'Yes', 'YES', 'go', 'start', '没问题', '就这个', '就这样', '同意', '干'])\n\nfunction isConfirmWord(text: string): boolean {\n const t = text.trim().toLowerCase().replace(/[。..!!??,,~~\\s]/g, '')\n if (!t) return false\n if (CONFIRM_WORDS.has(t)) return true\n if (/^(确认|开始|生成|可以|好|ok|yes|go|没问题|就这样|同意|干)$/.test(t)) return true\n return false\n}\n\n/**\n * 循环内「改图反馈」判别。命中任一改图/视觉信号 → 视为改图反馈(false,走生成);\n * 否则若命中明确闲聊/退出信号 → 判为聊天(true,给引导而非默默当 prompt)。\n * 两者都不中 → 保守当作改图反馈(false),尽量不打断正常迭代。\n */\nconst EDIT_SIGNAL_RE = /(背景|材质|毛绒|毛|质感|灯光|光照|颜色|色调|配色|肤色|毛发|睫毛|亮|暗|深|浅|饱和度|对比度|锐化|模糊|清晰|重画|再画|放大|缩小|构图|比例|裁剪|seed|种子|风格|角度|透视|阴影|高光|线条|细节|氛围|改|换|加|去|变|红|橙|黄|绿|蓝|紫|粉|黑|白|灰|金|银|卡通|写实|厚涂|薄涂|塑料|金属|布料|皮毛|皮肤|眼睛|头发|脸部|衣|帽子|鞋子)/i\nconst CHATTER_SIGNAL_RE = /(你好|在吗|在不在|谢谢|多谢|辛苦|拜拜|再见|哈哈|嘿嘿|不错|太棒|好棒|厉害|牛逼|你是|你是谁|你会|你能|会画|聊天|别的|其他|还能|还会|什么|为什么|怎么用|多少钱|退出|不弄了|算了|到此为止|停)/i\n\nfunction isLikelyChatter(text: string): boolean {\n const t = (text || '').trim()\n if (!t) return false\n if (EDIT_SIGNAL_RE.test(t)) return false\n return CHATTER_SIGNAL_RE.test(t)\n}\n\n/** 离开循环的明确意图(整句/短句),在确认闸等优先分支用于拦截「想退出」而非当修正/prompt */\nconst EXIT_INTENT_RE = /^(退出|算了|不弄了|停止|离开|quit|exit|stop|bye)(了|吧|啦|哦)?$/i\n\nfunction isExitIntent(text: string): boolean {\n const t = (text || '').trim()\n return EXIT_INTENT_RE.test(t)\n}\n\n/** 确认闸提示模板:把待确认分析报告 + 操作指引发给用户 */\nfunction formatConfirmPrompt(analysis: string, intent: string): string {\n const preview = analysis && analysis.length > 900\n ? `${analysis.slice(0, 900)}…`\n : (analysis || '(无)')\n return [\n `【图像分析 · 待确认】`,\n `意图:${intent || '(未提供,将按材质分层做保守增强)'}`,\n ``,\n `视觉分析结果:`,\n preview,\n ``,\n `回复「确认」开始生成;或直接描述要修正的地方(如:它是黑色的毛, 领子是红的, 不要偏色),会并入分析后再等你确认。`,\n `【COMFY 循环 · 待确认】想离开循环回聊天:直接发「清空」或「退出」。`,\n ].join('\\n')\n}\n","import { Schema } from 'koishi'\nimport { GLM_PROBE_PROMPT, MAIN_SYSTEM_PROMPT, VISION_USER_TEXT } from './llm/prompts.js'\n\nexport interface LlmProviderConfig {\n enabled: boolean\n baseUrl: string\n apiKey: string\n /** 空 = 自动拉取 {baseUrl}/models 取第一个 */\n model: string\n /** 该模型专属系统提示(默认 = prompts.ts 里的角色设定;留空则用默认) */\n systemPrompt: string\n /** 视觉模型请求正文(仅 vision 使用;留空则用默认) */\n userText: string\n}\n\nexport interface Config {\n mainLLM: LlmProviderConfig\n visionLLM: LlmProviderConfig\n comfy: {\n baseUrl: string\n timeoutMs: number\n }\n /** 背景色兜底:DS 未给或非法时用此值 */\n defaultBackgroundColor: string\n session: {\n maxTurns: number\n idleTimeoutMs: number\n perUserInGroup: boolean\n maxDiagnoseRetries: number\n }\n /** 生成前确认闸:command=comfy 命令路径,chatluna=ChatLuna 工具路径(工具无法交互确认) */\n confirmBeforeGenerate: {\n command: boolean\n chatluna: boolean\n }\n visionCrosscheckRounds: number\n chatlunaEnabled: boolean\n chatlunaRoomLastGenerated: boolean\n}\n\n/** @param systemPromptDefault 该 provider 的默认系统提示(main=DS 判定,vision=GLM probe) */\nconst llmSchema = (modelsKey: string, systemPromptDefault: string, baseUrlDefault: string): Schema<LlmProviderConfig> =>\n Schema.object({\n enabled: Schema.boolean().default(true).description('启用'),\n baseUrl: Schema.string().default(baseUrlDefault).description('接口地址'),\n apiKey: Schema.string().description('ApiKey(可空)'),\n model: Schema.dynamic(modelsKey as any).description('模型'),\n systemPrompt: Schema.string().role('textarea').default(systemPromptDefault).description('模型专属系统提示(角色/规则)'),\n userText: Schema.string().role('textarea').default(VISION_USER_TEXT).description('请求正文模板(vision 用)'),\n })\n\nexport const Config: Schema<Config> = Schema.object({\n mainLLM: llmSchema('aka-comfy-agent.models.main', MAIN_SYSTEM_PROMPT, 'http://127.0.0.1:8000/v1').description('主模型(DS:判定+选择)'),\n visionLLM: llmSchema('aka-comfy-agent.models.vision', GLM_PROBE_PROMPT, 'http://127.0.0.1:8001/v1').description('视觉模型(GLM:资产清单 probe)'),\n comfy: Schema.object({\n baseUrl: Schema.string().default('http://127.0.0.1:8188').description('ComfyUI 地址'),\n timeoutMs: Schema.number().min(30000).max(600000).default(180000).description('超时(ms)'),\n }).description('ComfyUI'),\n defaultBackgroundColor: Schema.string().default('yellow').description('背景色兜底(DS 未给或非法时使用;英文颜色词)'),\n session: Schema.object({\n maxTurns: Schema.number().min(1).max(50).default(10).description('最大轮次'),\n idleTimeoutMs: Schema.number().min(60000).max(24 * 60 * 60 * 1000).default(10 * 60 * 1000).description('空闲销毁(ms)'),\n perUserInGroup: Schema.boolean().default(false).description('群内按人隔离'),\n maxDiagnoseRetries: Schema.number().min(0).max(5).default(2).description('报错重试上限'),\n }).description('会话规则'),\n confirmBeforeGenerate: Schema.object({\n command: Schema.boolean().default(true).description('comfy 命令路径:生成前先发送 GLM 资产清单,待用户确认/修正后才提交生成'),\n chatluna: Schema.boolean().default(false).description('ChatLuna 工具路径:开启确认闸(工具无交互确认能力,开启后工具生成前会卡住,一般保持关闭)'),\n }).description('生成前确认闸(仅对视觉分析轮生效)'),\n visionCrosscheckRounds: Schema.number().min(1).max(5).default(2).description('视觉对抗自校验轮数:1=仅一次分析;2=分析后做1次对抗复核(推荐);更多=多次复核更稳但更慢更贵'),\n chatlunaEnabled: Schema.boolean().default(false).description('接入 ChatLuna 工具(未装则静默禁用)'),\n chatlunaRoomLastGenerated: Schema.boolean().default(true).description('记录房间最近生成图,供 last_generated 引用'),\n})\n","/**\n * 骨架版 Prompt 默认值(可公开发布的安全版本)。\n *\n * 本文件不包含任何私有 prompt IP(完整 MAIN_SYSTEM_PROMPT / GLM 资产清单 schema /\n * 身份/光场/背景锚点原文 / 材质与灯光片段库原文 / 内网 IP / key)。\n * 完整 prompt 已固化到私有飞书文档:\n * 云盘 Mac-Kael-Doc / aka-comfy-agent-完整Prompt固化.md\n * (设计/架构见 Mac-Kael-Doc / aka-comfy-agent 设计文档.md)\n *\n * 本文件为占位默认值,保证代码可编译、逻辑完整可跑;均为通用占位措辞,不构成私有 IP。\n * 恢复完整能力时,部署端按私有文档把本文件各常量/函数替换为完整原文并重新构建,\n * 或经 koishi.yml 的 mainLLM.systemPrompt / visionLLM.systemPrompt / visionLLM.userText 覆盖。\n *\n * 导出签名(供 brain.ts / index.ts / config.ts import)与完整版保持一致,勿改。\n */\n\n/** 主 LLM(DS)系统提示 —— 占位版;完整原文见私有文档。 */\nexport const MAIN_SYSTEM_PROMPT = `You are the edit-intent author for an image re-render pipeline. The pipeline prepends fixed identity, backdrop and lighting anchors before your output. Describe only the user's requested look (material or optical adjustments; a background change or light override when explicitly asked) as one short natural English clause. Placeholder default; the full prompt is restored via config override from the private design doc.`\n\n/**\n * GLM 视觉 probe prompt —— 占位版;完整原文(含完整 JSON schema)见私有文档。\n */\nexport const GLM_PROBE_PROMPT = `You are an asset extractor for a character image. From the screenshot, list every visual detail that a re-render could drift, and output a structured English JSON inventory (species, per-part colors, accessories, body surface, tail, pose, background). Do not invent details; use \"none\" when absent. Placeholder default; full prompt (with exact schema) is in the private design doc.`\n\n/** 兼容旧字段位(config schema 引用);FLUX.2 路径实际使用 GLM_PROBE_PROMPT。 */\nexport const VISION_SYSTEM_PROMPT = GLM_PROBE_PROMPT\n\n/** 视觉请求的 user 侧文本(图片 URL 由调用方拼进多模态 content)。 */\nexport const VISION_USER_TEXT = '分析这张游戏角色截图,输出资产清单 JSON。'\n\n/** 视觉对抗复核的 user 侧文本模板;{prev} 会被上一轮报告文本替换。 */\nexport const VISION_CROSSCHECK_TEXT = `这是对同一张源图的【资产清单对抗复核】。\n\n【上一轮 GLM 输出】\n{prev}\n\n请基于源图逐项对抗验证上一轮的每一项字段,明确指出错误/遗漏/脑补,修正会导致重绘跑偏的项,保持英文枚举与同一 schema 不变;不确定时写 \"none\"。只输出修正后的权威 JSON。`\n\n/** 主 LLM 决策每轮 user 消息里对视觉报告的字段名标签。 */\nexport const VISION_REPORT_LABEL = '资产清单(GLM 修正后)'\n\n/** 程序级强锁定的「身份保持锚点」 —— 占位版;完整原文见私有文档。 */\nexport const IDENTITY_ANCHOR =\n `Preserve the subject, its pose, outfit, accessories and colors of the source image exactly, ` +\n `keeping the same composition and framing, at high resolution, with no UI and no watermark. ` +\n `(Generic placeholder; the full locked wording is in the private design doc.)`\n\n/**\n * 程序级强锁定的「三点布光」棚拍光场锚点 —— 占位版。\n * 只描述光打在角色上的效果,不出现灯具实体词。完整原文见私有文档。\n */\nexport const LIGHTFIELD_ANCHOR =\n `Evenly light the character's front with gentle volume, keep the shadows soft and the face readable, ` +\n `and add a subtle rim so the silhouette lifts off the backdrop with a soft contact shadow. ` +\n `(Generic placeholder; the full locked wording is in the private design doc.)`\n\n/**\n * 背景替换子句(程序级固定 + 可配置颜色) —— 占位版;完整原文见私有文档。\n */\nexport function buildBackdropClause(color: string): string {\n const c = (color || 'yellow').trim().toLowerCase()\n return `Place the character on a plain solid ${c} seamless studio backdrop that fully replaces the source background. (Generic placeholder; full wording in private doc.)`\n}\n\n/**\n * 背景覆盖子句(DS 经 BACKGROUND: 行给出颜色+元素/场景) —— 占位版;完整原文见私有文档。\n */\nexport function buildBackdropFromOverride(desc: string): string {\n const d = (desc || '').trim().replace(/\\s+/g, ' ')\n return `Place the character against ${d}, fully replacing the source background. (Generic placeholder; full wording in private doc.)`\n}\n\n/** A 档骨架原文 —— 占位版;完整原文见私有文档。 */\nexport const A_SKELETON_TEMPLATE = `Repaint the character in the source image as a clean studio render on a seamless colored background, keeping identity, pose, clothing, accessories and colors unchanged, with even front lighting and a soft contact shadow. (Generic placeholder; full wording in private doc.)`\n\n/** 材质片段库 —— 占位版;完整条目见私有文档。 */\nexport interface MaterialSnippet {\n zh_name: string\n probe_prompt: string\n render_snippet: string\n guardrail: string\n}\n\nexport const MATERIAL_SNIPPETS: Record<\n 'fur' | 'metal' | 'plastic' | 'translucent_glow' | 'fabric',\n MaterialSnippet\n> = {\n fur: { zh_name: '毛绒/毛发(占位)', probe_prompt: '(占位)表面是否蓬松?', render_snippet: '(Placeholder fur snippet; see private doc.)', guardrail: '(placeholder guardrail)' },\n metal: { zh_name: '金属(占位)', probe_prompt: '(占位)是否高反射?', render_snippet: '(Placeholder metal snippet; see private doc.)', guardrail: '(placeholder guardrail)' },\n plastic: { zh_name: '塑料/哑光硬质(占位)', probe_prompt: '(占位)是否光滑硬壳?', render_snippet: '(Placeholder plastic snippet; see private doc.)', guardrail: '(placeholder guardrail)' },\n translucent_glow: { zh_name: '半透明/发光(占位)', probe_prompt: '(占位)是否自发光?', render_snippet: '(Placeholder glow snippet; see private doc.)', guardrail: '(placeholder guardrail)' },\n fabric: { zh_name: '织物/布料(占位)', probe_prompt: '(占位)是否有编织纹理?', render_snippet: '(Placeholder fabric snippet; see private doc.)', guardrail: '(placeholder guardrail)' },\n}\n\n/** 灯光强指令 intro —— 占位版;完整原文见私有文档。 */\nexport const LIGHTING_STRENGTH_INTRO = \"IMPORTANT: RE-LIGHT THE CHARACTER FROM <LIGHT_POSITION>. (Placeholder; full wording in private doc.)\"\n\n/** 灯光片段库 —— 占位版;完整条目见私有文档。 */\nexport interface LightSnippet {\n zh_name: string\n verified: boolean\n render_snippet: string\n position?: string\n negative?: string\n}\n\nexport const LIGHT_SNIPPETS: Record<\n 'front_default' | 'upper-left' | 'right-front' | 'right-backlight' | 'warm' | 'cool' | 'top',\n LightSnippet\n> = {\n front_default: { zh_name: '正面灯箱光(默认)(占位)', verified: true, render_snippet: '(Placeholder front lighting snippet; see private doc.)' },\n 'upper-left': { zh_name: '左上侧光(占位)', verified: true, position: 'UPPER-LEFT', render_snippet: '(Placeholder upper-left lighting snippet; see private doc.)', negative: '(placeholder negative)' },\n 'right-front': { zh_name: '右前主光(占位)', verified: true, position: 'RIGHT-FRONT', render_snippet: '(Placeholder right-front lighting snippet; see private doc.)', negative: '(placeholder negative)' },\n 'right-backlight': { zh_name: '右侧逆光/轮廓光(占位)', verified: true, position: 'BEHIND-RIGHT', render_snippet: '(Placeholder right-backlight snippet; see private doc.)', negative: '(placeholder negative)' },\n warm: { zh_name: '暖光(色温)(占位)', verified: false, position: 'A WARM DIRECTION', render_snippet: '(Placeholder warm lighting snippet; see private doc.)', negative: '(placeholder negative)' },\n cool: { zh_name: '冷光(色温)(占位)', verified: false, position: 'A COOL DIRECTION', render_snippet: '(Placeholder cool lighting snippet; see private doc.)', negative: '(placeholder negative)' },\n top: { zh_name: '顶光(正上方)(占位)', verified: false, position: 'DIRECTLY ABOVE', render_snippet: '(Placeholder top lighting snippet; see private doc.)', negative: '(placeholder negative)' },\n}\n\n/** 光学补强模板 —— 占位版;完整条目见私有文档。 */\nexport const OPTICAL_BOOST_TEMPLATES: Record<string, string> = {\n glowing_eyes: '(Placeholder glowing-eyes optical boost; see private doc.)',\n}\n","import type { Context } from 'koishi'\nimport type { LlmProviderConfig } from '../config.js'\nimport type { LlmMessage } from '../types.js'\n\n/** OpenAI 兼容 chat/completions 极简客户端。main / vision 共用;vision 侧仅在 message.content 里传多模态段。 */\nexport class LlmClient {\n constructor(\n private ctx: Context,\n private config: LlmProviderConfig,\n /** 最终使用的 model(构造前由 models.ts 决定,可能来自 GET /v1/models) */\n public readonly model: string,\n private timeoutMs: number,\n ) {}\n\n get enabled(): boolean {\n return this.config.enabled && !!this.config.baseUrl && !!this.model\n }\n\n async chat(messages: LlmMessage[], opts?: { temperature?: number; jsonMode?: boolean }): Promise<string> {\n if (!this.enabled) throw new Error(`LLM 未启用或未解析 model(baseUrl=${this.config.baseUrl})`)\n const url = joinUrl(this.config.baseUrl, '/chat/completions')\n const body: Record<string, unknown> = {\n model: this.model,\n messages,\n temperature: opts?.temperature ?? 0.4,\n }\n if (opts?.jsonMode) body['response_format'] = { type: 'json_object' }\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.config.apiKey) headers['Authorization'] = `Bearer ${this.config.apiKey}`\n\n const res = await (this.ctx.http as unknown as {\n (u: string, c: Record<string, unknown>): Promise<any>\n })(url, {\n method: 'POST',\n data: body,\n headers,\n timeout: this.timeoutMs,\n })\n const data = extractResponseData(res)\n const content = data?.choices?.[0]?.message?.content\n if (typeof content !== 'string' || !content) {\n throw new Error(`LLM 响应无 content:${JSON.stringify(data).slice(0, 200)}`)\n }\n return content\n }\n}\n\n/** 一些兼容适配器:ctx.http() 可能直接返回数据,也可能返回 { data, headers } 结构。 */\nfunction extractResponseData(res: any): any {\n if (res && typeof res === 'object' && 'data' in res && (res as any).headers) return (res as any).data\n return res\n}\n\nexport function joinUrl(base: string, path: string): string {\n const b = base.replace(/\\/+$/, '')\n const p = path.startsWith('/') ? path : `/${path}`\n return `${b}${p}`\n}\n","import type { Context } from 'koishi'\nimport type { LlmProviderConfig } from '../config.js'\nimport { joinUrl } from './client.js'\n\n/** GET {baseUrl}/models → data[].id 列表;失败时抛出可读错误。 */\nexport async function listModels(ctx: Context, cfg: LlmProviderConfig, timeoutMs: number): Promise<string[]> {\n const url = joinUrl(cfg.baseUrl, '/models')\n const headers: Record<string, string> = {}\n if (cfg.apiKey) headers['Authorization'] = `Bearer ${cfg.apiKey}`\n const res = await ctx.http.get(url, { timeout: timeoutMs, headers })\n const payload = (res && typeof res === 'object' && 'data' in res && Array.isArray((res as any).data))\n ? res\n : { data: [] as any[] }\n const ids: string[] = []\n for (const item of payload.data as any[]) {\n if (item && typeof item.id === 'string') ids.push(item.id)\n }\n return ids\n}\n\n/** 解析最终 model:手动 > 自动拉取第一个 > 回退空串(调用方再决定禁用)。 */\nexport async function resolveModel(\n ctx: Context,\n cfg: LlmProviderConfig,\n timeoutMs: number,\n logger: { info: (...a: any[]) => void; warn: (...a: any[]) => void },\n label: string,\n): Promise<string> {\n if (cfg.model?.trim()) return cfg.model.trim()\n if (!cfg.enabled) return ''\n try {\n const ids = await listModels(ctx, cfg, timeoutMs)\n if (ids.length === 0) {\n logger.warn('%s: GET /models 返回空列表 baseUrl=%s,禁用该 LLM', label, cfg.baseUrl)\n return ''\n }\n const picked = ids[0]!\n logger.info('%s: 自动选取模型 %s(候选 %d 个) baseUrl=%s', label, picked, ids.length, cfg.baseUrl)\n return picked\n } catch (err) {\n logger.warn('%s: GET /models 失败,禁用该 LLM baseUrl=%s err=%s', label, cfg.baseUrl,\n err instanceof Error ? err.message : String(err))\n return ''\n }\n}\n","import type { Context } from 'koishi'\n\n/** ComfyUI 8188 API 极简客户端;只用无鉴权内网。 */\nexport class ComfyClient {\n constructor(\n private ctx: Context,\n public readonly baseUrl: string,\n private timeoutMs: number,\n ) {}\n\n /** POST /upload/image (multipart, field name = image)。返回 ComfyUI 用于 LoadImage.image 的 name。 */\n async uploadImage(buffer: Buffer, filename: string, mimeType = 'image/png'): Promise<string> {\n const form = new FormData()\n form.append('image', new Blob([new Uint8Array(buffer)], { type: mimeType }), filename)\n form.append('overwrite', 'true')\n const res = await (this.ctx.http as unknown as {\n (u: string, c: Record<string, unknown>): Promise<any>\n })(this.url('/upload/image'), {\n method: 'POST',\n data: form,\n timeout: this.timeoutMs,\n })\n const data = unwrap(res)\n if (!data || typeof data.name !== 'string') {\n throw new Error(`ComfyUI /upload/image 未返回 name:${JSON.stringify(data).slice(0, 200)}`)\n }\n return data.name\n }\n\n /** POST /prompt。body { prompt, client_id }。返回 prompt_id。 */\n async submitPrompt(workflow: Record<string, unknown>, clientId: string): Promise<string> {\n const res = await (this.ctx.http as unknown as {\n (u: string, c: Record<string, unknown>): Promise<any>\n })(this.url('/prompt'), {\n method: 'POST',\n data: { prompt: workflow, client_id: clientId },\n headers: { 'Content-Type': 'application/json' },\n timeout: this.timeoutMs,\n })\n const data = unwrap(res)\n if (!data || typeof data.prompt_id !== 'string') {\n // ComfyUI 校验失败时会返回 { error, node_errors }\n const err = data?.error ?? data?.node_errors ?? data\n throw new Error(`ComfyUI /prompt 拒绝:${safeStringify(err).slice(0, 500)}`)\n }\n return data.prompt_id\n }\n\n async getHistory(promptId: string): Promise<HistoryEntry | null> {\n const res = await this.ctx.http.get(this.url(`/history/${encodeURIComponent(promptId)}`), {\n timeout: this.timeoutMs,\n })\n const data = unwrap(res)\n if (!data || typeof data !== 'object') return null\n const entry = (data as Record<string, unknown>)[promptId]\n return (entry as HistoryEntry | undefined) ?? null\n }\n\n /** GET /view?filename&subfolder&type,返回 image buffer。 */\n async view(filename: string, subfolder = '', type: 'output' | 'input' | 'temp' = 'output'): Promise<Buffer> {\n const url = this.url(`/view?filename=${encodeURIComponent(filename)}&subfolder=${encodeURIComponent(subfolder)}&type=${type}`)\n const ab = await this.ctx.http.get(url, { responseType: 'arraybuffer', timeout: this.timeoutMs })\n return Buffer.from(ab as ArrayBuffer)\n }\n\n async queue(): Promise<QueueSnapshot> {\n const res = await this.ctx.http.get(this.url('/queue'), { timeout: this.timeoutMs })\n const data = unwrap(res) ?? {}\n const running = Array.isArray(data.queue_running) ? data.queue_running.length : 0\n const pending = Array.isArray(data.queue_pending) ? data.queue_pending.length : 0\n return { running, pending }\n }\n\n /** 轮询 history 到 success/error,或超时(timeoutMs 独立传入以覆盖首次模型加载) */\n async pollUntilDone(\n promptId: string,\n opts: { timeoutMs: number; intervalMs?: number; onTick?: (elapsed: number) => void },\n ): Promise<HistoryEntry> {\n const interval = opts.intervalMs ?? 1500\n const start = Date.now()\n let last: HistoryEntry | null = null\n while (Date.now() - start < opts.timeoutMs) {\n const entry = await this.getHistory(promptId).catch(() => null)\n if (entry) {\n last = entry\n const st = entry.status\n if (st?.completed === true) return entry\n // status_str \"error\" 或 messages 里含 execution_error 均视为失败\n if (st?.status_str === 'error') return entry\n if (Array.isArray(st?.messages)) {\n for (const m of st.messages) {\n if (Array.isArray(m) && m[0] === 'execution_error') return entry\n }\n }\n }\n if (opts.onTick) opts.onTick(Date.now() - start)\n await sleep(interval)\n }\n if (last) return last\n throw new Error(`ComfyUI 轮询超时(${opts.timeoutMs}ms),prompt_id=${promptId}`)\n }\n\n private url(p: string): string {\n const base = this.baseUrl.replace(/\\/+$/, '')\n return `${base}${p.startsWith('/') ? p : `/${p}`}`\n }\n}\n\nexport interface HistoryEntry {\n status?: {\n completed?: boolean\n status_str?: string\n messages?: unknown[]\n }\n outputs?: Record<string, {\n images?: Array<{ filename: string; subfolder?: string; type?: string }>\n }>\n}\n\nexport interface QueueSnapshot {\n running: number\n pending: number\n}\n\nfunction unwrap(res: any): any {\n if (res && typeof res === 'object' && 'data' in res && 'headers' in res) return (res as any).data\n return res\n}\n\nfunction safeStringify(v: unknown): string {\n try { return JSON.stringify(v) } catch { return String(v) }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms))\n}\n","/**\n * FLUX.2 固化 workflow(docs/flux2-pack-source.md §1)。\n * 固定:20 步 / euler / 1024² / FluxGuidance 4.0 / 无 Canny / 无负向。\n * 唯一可变输入:LoadImage.image / CLIPTextEncode.text / RandomNoise.noise_seed。\n */\nexport function buildFlux2Workflow(input: {\n imageName: string\n text: string\n seed: number\n}): Record<string, unknown> {\n const { imageName, text, seed } = input\n return {\n unet: {\n class_type: 'UNETLoader',\n inputs: { unet_name: 'flux2_dev_fp8mixed.safetensors', weight_dtype: 'default' },\n },\n clip: {\n class_type: 'CLIPLoader',\n inputs: { clip_name: 'mistral_3_small_flux2_bf16.safetensors', type: 'flux2' },\n },\n vae: {\n class_type: 'VAELoader',\n inputs: { vae_name: 'flux2-vae.safetensors' },\n },\n loadimg: {\n class_type: 'LoadImage',\n inputs: { image: imageName },\n },\n enc_img: {\n class_type: 'VAEEncode',\n inputs: { pixels: ['loadimg', 0], vae: ['vae', 0] },\n },\n sched: {\n class_type: 'Flux2Scheduler',\n inputs: { steps: 20, width: 1024, height: 1024 },\n },\n sel: {\n class_type: 'KSamplerSelect',\n inputs: { sampler_name: 'euler' },\n },\n noise: {\n class_type: 'RandomNoise',\n inputs: { noise_seed: seed },\n },\n enc: {\n class_type: 'CLIPTextEncode',\n inputs: { text, clip: ['clip', 0] },\n },\n guid: {\n class_type: 'FluxGuidance',\n inputs: { conditioning: ['enc', 0], guidance: 4.0 },\n },\n reflat: {\n class_type: 'ReferenceLatent',\n // 注意:本机 ReferenceLatent 节点仅收 conditioning+latent,无 strength 参数(node 定义经 /object_info 核实)\n inputs: { conditioning: ['guid', 0], latent: ['enc_img', 0] },\n },\n guider: {\n class_type: 'BasicGuider',\n inputs: { model: ['unet', 0], conditioning: ['reflat', 0] },\n },\n empty: {\n class_type: 'EmptyFlux2LatentImage',\n inputs: { width: 1024, height: 1024, batch_size: 1 },\n },\n sample: {\n class_type: 'SamplerCustomAdvanced',\n inputs: {\n noise: ['noise', 0],\n guider: ['guider', 0],\n sampler: ['sel', 0],\n sigmas: ['sched', 0],\n latent_image: ['empty', 0],\n },\n },\n dec: {\n class_type: 'VAEDecode',\n inputs: { samples: ['sample', 0], vae: ['vae', 0] },\n },\n save: {\n class_type: 'SaveImage',\n inputs: { images: ['dec', 0], filename_prefix: 'flux2_render' },\n },\n }\n}\n\n/** 32-bit 无符号随机种子。 */\nexport function randomSeed(): number {\n return Math.floor(Math.random() * 0xffffffff) >>> 0\n}\n","import type { DiagnoseHint } from '../types.js'\n\n/**\n * FLUX.2 workflow 参数固定;诊断只做人话提示 + 是否重试判定,不再打参数补丁。\n * 重试路径由 brain 换 seed 重提交。\n */\nexport function diagnose(rawError: string): DiagnoseHint {\n const msg = rawError.toLowerCase()\n if (msg.includes('vram') && msg.includes('grow')) {\n return {\n message: 'ComfyUI 报 VRAM grow failed(GPU 可能被占或模型加载失败)。等一下再试,别切卡。',\n retryable: false,\n }\n }\n if (msg.includes('out of memory') || msg.includes('cuda oom') || msg.includes('oom')) {\n return {\n message: '显存不足,稍后重试。',\n retryable: true,\n }\n }\n if (msg.includes('no module named matplotlib')) {\n return {\n message: 'comfyui_controlnet_aux 依赖警告(matplotlib 缺失),不影响本流程,忽略。',\n retryable: true,\n }\n }\n if (msg.includes('timeout') || msg.includes('running') || msg.includes('queue')) {\n return {\n message: '任务仍在 running(可能首次加载模型慢),等一下即可。',\n retryable: true,\n }\n }\n if (msg.includes('validation') || msg.includes('node_errors') || msg.includes('invalid')) {\n return {\n message: 'workflow 校验失败:参数已固定,换 seed 重试一次。',\n retryable: true,\n }\n }\n return {\n message: `未知报错:${rawError.slice(0, 200)}`,\n retryable: false,\n }\n}\n","import type { Logger } from 'koishi'\nimport type { ComfyClient } from '../comfy/client.js'\nimport { buildFlux2Workflow, randomSeed } from '../comfy/workflow.js'\nimport type { LlmClient } from '../llm/client.js'\nimport { buildBackdropClause, buildBackdropFromOverride, IDENTITY_ANCHOR, LIGHTFIELD_ANCHOR, VISION_REPORT_LABEL } from '../llm/prompts.js'\nimport type {\n BrainDecision,\n LlmMessage,\n LlmMultimodalContent,\n RenderResult,\n Session,\n} from '../types.js'\nimport { diagnose } from './diagnose.js'\nimport type { SerialLock } from './session.js'\n\nexport interface BrainDeps {\n main: LlmClient\n vision: LlmClient\n comfy: ComfyClient\n lock: SerialLock\n logger: Logger\n clientId: string\n comfyTimeoutMs: number\n maxDiagnoseRetries: number\n /** 主 LLM 系统提示(DS:判定+选择) */\n mainSystemPrompt: string\n /** 视觉 LLM 系统提示(GLM probe) */\n visionSystemPrompt: string\n /** 视觉请求正文模板 */\n visionUserText: string\n /** 视觉对抗复核:总轮数(≥1;>1 表示首轮分析后做 (rounds-1) 次对抗复核) */\n visionCrosscheckRounds: number\n /** 视觉对抗复核正文模板(含 {prev} 占位符) */\n visionCrosscheckText: string\n /** 背景色兜底(DS 未给或非法时使用) */\n defaultBackgroundColor: string\n /** Comfy 生成轮询期间的回调(elapsedMs);用于向用户反馈生成进度(可选) */\n onGenerateProgress?: (elapsedMs: number) => void\n}\n\n/** 单轮 agent loop:vision → main → workflow → 提交 → 轮询 → 取图;报错走 diagnose 有限重试 */\nexport async function runTurn(\n deps: BrainDeps,\n session: Session,\n userFeedback: string,\n opts: { runVision: boolean; sourceDataUrl?: string },\n): Promise<RenderResult> {\n const { vision, logger } = deps\n\n // 1. 视觉分层(新会话首轮或用户带新图时才做)\n if (opts.runVision && vision.enabled && opts.sourceDataUrl) {\n try {\n const analysis = await runAnalysisOnly(deps, opts.sourceDataUrl)\n session.materialAnalysis = analysis\n logger.info('vision analysis ok len=%d preview=%s', analysis.length, analysis.slice(0, 60).replace(/\\s+/g, ' '))\n } catch (err) {\n logger.warn('vision analysis failed: %s', errMsg(err))\n session.materialAnalysis = session.materialAnalysis || '(vision 分析失败)'\n }\n } else if (!vision.enabled && !session.materialAnalysis) {\n session.materialAnalysis = '(vision 未启用)'\n }\n\n return runGenerateOnly(deps, session, userFeedback)\n}\n\n/**\n * 确认闸/常规路径共用的视觉分析入口:多轮对抗自校验。\n * 首轮做 GLM probe 分析,随后 (rounds-1) 轮用【对抗复核】模板要求 GLM 逐项验证并修正上一轮,\n * 最终返回最后一轮的修正后资产清单 JSON 文本。\n */\nexport async function runAnalysisOnly(deps: BrainDeps, dataUrl: string): Promise<string> {\n const { vision } = deps\n if (!vision.enabled) return '(vision 未启用)'\n\n let report = await runVisionAnalysis(vision, dataUrl, deps.visionSystemPrompt, deps.visionUserText)\n const rounds = Math.max(1, Math.floor(deps.visionCrosscheckRounds) || 1)\n for (let i = 1; i < rounds; i++) {\n const crossText = (deps.visionCrosscheckText || '').replace('{prev}', report)\n report = await runVisionAnalysis(vision, dataUrl, deps.visionSystemPrompt, crossText)\n deps.logger.info('vision crosscheck round=%d/%d len=%d preview=%s',\n i + 1, rounds, report.length, report.slice(0, 60).replace(/\\s+/g, ' '))\n }\n return report\n}\n\nasync function runVisionAnalysis(\n vision: LlmClient,\n dataUrl: string,\n systemPrompt: string,\n userText: string,\n): Promise<string> {\n const messages: LlmMessage[] = [\n { role: 'system', content: systemPrompt },\n {\n role: 'user',\n content: [\n { type: 'text', text: userText },\n { type: 'image_url', image_url: { url: dataUrl } },\n ] satisfies LlmMultimodalContent[],\n },\n ]\n return (await vision.chat(messages, { temperature: 0.1, jsonMode: true })).trim()\n}\n\n/**\n * 确认闸阶段二:不做 vision,直接以 session.materialAnalysis 为准执行\n * DS 主模型包办 prompt → workflow 提交 → 轮询 → 取图。\n */\nexport async function runGenerateOnly(deps: BrainDeps, session: Session, userFeedback: string): Promise<RenderResult> {\n const { main, comfy, lock, logger } = deps\n\n // 2. DS 主模型:写出修改意图。最终 prompt = 身份锚点 + 可配置纯色背景替换句 + 三点布光场锚点 + DS 修改句。\n // 身份/构图/配色、背景替换、棚拍光影都是程序锁死;DS 只在用户显式要求时覆盖背景色/光位,\n // 其余写材质/光学。背景色默认取 config.defaultBackgroundColor(可配置)。\n const decision = await runMainLlm(main, session, userFeedback, deps.mainSystemPrompt)\n // 背景子句:DS 给了 BACKGROUND: 覆盖 → 用该描述(颜色+元素/场景)替换默认纯色;否则用 config.defaultBackgroundColor 纯色兜底\n const backdrop = decision.background\n ? buildBackdropFromOverride(decision.background)\n : buildBackdropClause(deps.defaultBackgroundColor)\n const promptText = `${IDENTITY_ANCHOR} ${backdrop} ${LIGHTFIELD_ANCHOR} ${decision.prompt}`\n logger.info('brain prompt len=%d ds=%d backdrop=%s lightfield=%d preview=%s', promptText.length, decision.prompt.length, decision.background?.slice(0, 40) || deps.defaultBackgroundColor || 'yellow', LIGHTFIELD_ANCHOR.length, promptText.slice(0, 160).replace(/\\s+/g, ' '))\n logger.info('brain note=%s', decision.note ?? '')\n\n // 4. seed\n let seed = session.params.seed\n if (!Number.isFinite(seed) || seed < 0) seed = randomSeed()\n\n // 5. 提交/轮询/取图,报错走 diagnose 重试(换 seed 重提交)\n let attempt = 0\n const maxAttempts = 1 + Math.max(0, deps.maxDiagnoseRetries)\n let lastError = ''\n\n while (attempt < maxAttempts) {\n attempt++\n try {\n const result = await lock.run(async () => {\n const workflow = buildFlux2Workflow({\n imageName: session.source.name,\n text: promptText,\n seed,\n })\n const promptId = await comfy.submitPrompt(workflow, deps.clientId)\n logger.info('comfy submit ok prompt_id=%s attempt=%d seed=%d', promptId, attempt, seed)\n const history = await comfy.pollUntilDone(promptId, {\n timeoutMs: deps.comfyTimeoutMs,\n onTick: (t) => {\n deps.onGenerateProgress?.(t)\n if (t % 15000 < 1600) logger.info('poll elapsed=%dms prompt_id=%s', t, promptId)\n },\n })\n return { promptId, history }\n })\n\n const outputImage = pickOutputImage(result.history)\n if (!outputImage) {\n const errMsgText = extractHistoryError(result.history) || '未生成输出图(未知)'\n throw new Error(errMsgText)\n }\n\n const buf = await comfy.view(outputImage.filename, outputImage.subfolder ?? '', 'output')\n // 成功:把本轮 prompt 塞进 LLM 上下文,便于下一轮参考\n session.params = { seed }\n return {\n imageBuffer: buf,\n filename: outputImage.filename,\n params: session.params,\n promptId: result.promptId,\n note: decision.note,\n material: decision.material,\n lighting: decision.lighting,\n background: decision.background,\n }\n } catch (err) {\n lastError = errMsg(err)\n session.errorHistory.push({ at: Date.now(), message: lastError })\n const hint = diagnose(lastError)\n logger.warn('brain attempt=%d failed: %s → hint: %s retryable=%s',\n attempt, lastError.slice(0, 200), hint.message, hint.retryable)\n session.errorHistory[session.errorHistory.length - 1]!.hint = hint.message\n if (!hint.retryable || attempt >= maxAttempts) {\n throw new Error(`${lastError}\\n诊断:${hint.message}`)\n }\n seed = randomSeed()\n }\n }\n\n throw new Error(`brain 重试耗尽(${maxAttempts} 次):${lastError}`)\n}\n\nasync function runMainLlm(\n main: LlmClient,\n session: Session,\n feedback: string,\n systemPrompt: string,\n): Promise<BrainDecision> {\n const contextMessages: LlmMessage[] = []\n contextMessages.push({ role: 'system', content: systemPrompt })\n for (const m of session.llmContext) contextMessages.push(m)\n const userText = [\n `[本轮反馈]\\n${feedback || '(无新反馈,请按当前状态生成或微调一次)'}`,\n `[当前 params 快照]\\n${JSON.stringify(session.params)}`,\n `[${VISION_REPORT_LABEL}]\\n${session.materialAnalysis}`,\n session.errorHistory.length\n ? `[错误历史(最近3条)]\\n${session.errorHistory.slice(-3).map((e) => `- ${e.message}${e.hint ? `|诊断:${e.hint}` : ''}`).join('\\n')}`\n : '',\n '请直接输出最终的 FLUX.2 prompt(英文自然语言一段),如需可另起一行以 NOTE: 开头给一句中文备注。',\n ].filter(Boolean).join('\\n\\n')\n const userMsg: LlmMessage = { role: 'user', content: userText }\n contextMessages.push(userMsg)\n\n const raw = await main.chat(contextMessages, { temperature: 0.7 })\n const parsed = parsePrompt(raw)\n\n session.llmContext.push(userMsg)\n session.llmContext.push({ role: 'assistant', content: parsed.prompt })\n trimContext(session)\n return parsed\n}\n\n/** 解析主 LLM 的一整段 prompt 输出;识别可选 BACKGROUND: 行作背景覆盖、可选 NOTE: 行作展示备注。 */\nfunction parsePrompt(raw: string): BrainDecision {\n let text = raw.trim()\n .replace(/^```(?:text|prompt|en)?\\s*/i, '')\n .replace(/```$/i, '')\n .trim()\n if (!text) throw new Error('主 LLM 输出为空')\n\n // NOTE: 行拆出来作 note(通常在末尾),剩余作为主句\n let note: string | undefined\n const noteLine = text.match(/\\n?:?\\s*NOTE:\\s*([\\s\\S]*)$/i)\n if (noteLine && noteLine.index !== undefined && noteLine[1]) {\n note = noteLine[1].trim()\n text = text.slice(0, noteLine.index).trim()\n }\n\n // BACKGROUND: 独立行拆出来作背景覆盖(颜色+元素/场景),并从主句中剔除避免重复\n let background: string | undefined\n const bgLine = text.match(/^BACKGROUND:\\s*(.+)$/im)\n if (bgLine && bgLine[1]) {\n background = bgLine[1].trim()\n text = text.replace(/^BACKGROUND:[^\\n]*\\n?/im, '').trim()\n }\n\n // 背景覆盖已被剥离:若主句为空(用户只改背景)则保留空串,不回退拼入原始含 BACKGROUND 的文本\n const prompt = text || (background ? '' : raw.trim())\n return { prompt, note, background }\n}\n\nfunction pickOutputImage(history: { outputs?: Record<string, { images?: Array<{ filename: string; subfolder?: string; type?: string }> }> })\n : { filename: string; subfolder?: string } | null {\n const outs = history?.outputs\n if (!outs) return null\n const preferred = outs['save']\n const fromPreferred = preferred?.images?.[0]\n if (fromPreferred) return fromPreferred\n for (const key of Object.keys(outs)) {\n const img = outs[key]?.images?.[0]\n if (img) return img\n }\n return null\n}\n\nfunction extractHistoryError(history: { status?: { messages?: unknown[] } }): string {\n const msgs = history?.status?.messages\n if (!Array.isArray(msgs)) return ''\n const parts: string[] = []\n for (const m of msgs) {\n if (!Array.isArray(m)) continue\n if (m[0] === 'execution_error' || m[0] === 'execution_interrupted' || m[0] === 'execution_cached') {\n parts.push(JSON.stringify(m).slice(0, 300))\n }\n }\n return parts.join(' | ')\n}\n\nfunction trimContext(session: Session, maxMessages = 20): void {\n if (session.llmContext.length > maxMessages) {\n session.llmContext.splice(0, session.llmContext.length - maxMessages)\n }\n}\n\nfunction errMsg(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n","import type { RenderParams, Session } from '../types.js'\n\n/** 会话存储;键设计支持 perUserInGroup 切换。挂到模块级,热重载后 apply 重新引用同一 Map。 */\nexport interface SessionKeyInput {\n channelId: string\n userId: string\n guildId?: string\n perUserInGroup: boolean\n}\n\nexport class SessionStore {\n private map = new Map<string, Session>()\n private sweepTimer: ReturnType<typeof setInterval> | null = null\n /** 记录最近被空闲清除的会话 key → 清除时刻 ms(供“会话已过期”提示,区分“从未有会话”) */\n private recentlyExpired = new Map<string, number>()\n\n constructor(private idleTimeoutMs: number) {}\n\n key(input: SessionKeyInput): string {\n // 私聊没有 guildId → 按 userId 独立;群内看 perUserInGroup 开关。\n if (!input.guildId) return `dm:${input.userId}`\n if (input.perUserInGroup) return `g:${input.channelId}:${input.userId}`\n return `g:${input.channelId}`\n }\n\n get(key: string): Session | undefined {\n const s = this.map.get(key)\n if (!s) return undefined\n if (Date.now() - s.lastUsedAt > this.idleTimeoutMs) {\n this.map.delete(key)\n this.recentlyExpired.set(key, Date.now())\n return undefined\n }\n return s\n }\n\n /** 该 key 是否有“刚过期”的会话(用于会话已过期提示;expiryWindowMs 内有效) */\n wasRecentlyExpired(key: string, expiryWindowMs: number): boolean {\n const t = this.recentlyExpired.get(key)\n if (t === undefined) return false\n if (Date.now() - t > expiryWindowMs) {\n this.recentlyExpired.delete(key)\n return false\n }\n return true\n }\n\n /** 销毁后显式记录过期(用户 + 空清空 / 换图重建时),保证后续提示一致 */\n markExpired(key: string): void {\n this.recentlyExpired.set(key, Date.now())\n }\n\n set(key: string, session: Session): void {\n this.map.set(key, session)\n }\n\n delete(key: string): boolean {\n return this.map.delete(key)\n }\n\n size(): number {\n return this.map.size\n }\n\n touch(session: Session): void {\n session.lastUsedAt = Date.now()\n }\n\n startSweep(intervalMs = 5 * 60 * 1000): void {\n if (this.sweepTimer) return\n this.sweepTimer = setInterval(() => {\n const now = Date.now()\n for (const [k, s] of this.map) {\n if (now - s.lastUsedAt > this.idleTimeoutMs) {\n this.map.delete(k)\n this.recentlyExpired.set(k, now)\n }\n }\n }, intervalMs)\n // 允许进程退出\n if (typeof this.sweepTimer === 'object' && this.sweepTimer && 'unref' in this.sweepTimer) {\n ;(this.sweepTimer as { unref: () => void }).unref()\n }\n }\n\n stopSweep(): void {\n if (this.sweepTimer) {\n clearInterval(this.sweepTimer)\n this.sweepTimer = null\n }\n }\n}\n\n/** 简单串行锁:全局 GPU 串行 —— 同一时刻只有一个渲染任务提交 ComfyUI。 */\nexport class SerialLock {\n private chain: Promise<void> = Promise.resolve()\n\n run<T>(task: () => Promise<T>): Promise<T> {\n const next = this.chain.then(task, task)\n // 将链条推进到 next 完成之后(无论成败),但不吞掉调用者拿到的错误。\n this.chain = next.then(\n () => undefined,\n () => undefined,\n )\n return next\n }\n}\n\nexport function initialParams(seed: number): RenderParams {\n return { seed }\n}\n","import type { Context, Logger } from 'koishi'\nimport type { ComfyClient } from '../comfy/client.js'\nimport { randomSeed } from '../comfy/workflow.js'\nimport type { LlmClient } from '../llm/client.js'\nimport type { RenderParams, RenderResult, Session, UploadedImage } from '../types.js'\nimport { runAnalysisOnly, runTurn } from './brain.js'\nimport { SerialLock, initialParams } from './session.js'\n\nexport interface RenderFacadeDeps {\n ctx: Context\n logger: Logger\n comfy: ComfyClient\n getMainClient: () => LlmClient\n getVisionClient: () => LlmClient\n lock: SerialLock\n clientId: string\n comfyTimeoutMs: number\n maxDiagnoseRetries: number\n mainSystemPrompt: string\n visionSystemPrompt: string\n visionUserText: string\n visionCrosscheckRounds: number\n visionCrosscheckText: string\n defaultBackgroundColor: string\n}\n\nexport interface RenderOneShotInput {\n imageUrl: string\n instruction: string\n paramsOverride?: Partial<RenderParams>\n userId: string\n channelId?: string\n guildId?: string\n /** true = 生成前确认闸:仅跑视觉分析并返回报告,不提交生成(工具转述后等待用户确认)。 */\n confirmBrake?: boolean\n}\n\nexport interface RenderOneShotResult {\n buffer: Buffer\n filename: string\n note?: string\n params: RenderParams\n comfyViewUrl: string\n /** confirmBrake 路径下非空 = 处于待确认状态,buffer/filename 为空 */\n awaitingAnalysis?: string\n material?: string\n lighting?: string\n background?: string\n}\n\n/**\n * 轻量渲染门面:一次调用 = 一次 FLUX.2 渲染。\n * 复用现有 fetchImage / uploadImage / runTurn(含 vision + 主 LLM + workflow)。\n * 不入 SessionStore、不参与多轮迭代 —— 多轮由 ChatLuna 会话层承载。\n */\nexport function createRenderFacade(deps: RenderFacadeDeps) {\n return {\n async renderOneShot(input: RenderOneShotInput): Promise<RenderOneShotResult> {\n const main = deps.getMainClient()\n if (!main.enabled) {\n throw new Error('主 LLM 未启用或未解析到 model,请在配置里补 baseUrl/apiKey/model 再试。')\n }\n const downloaded = await fetchImage(deps.ctx, input.imageUrl, deps.comfyTimeoutMs)\n const filename = `input_${Date.now()}.${extForMime(downloaded.mimeType)}`\n const name = await deps.comfy.uploadImage(downloaded.buffer, filename, downloaded.mimeType)\n const uploaded: UploadedImage = { name, mimeType: downloaded.mimeType, bytes: downloaded.buffer.length }\n deps.logger.info('facade upload ok name=%s bytes=%d', name, downloaded.buffer.length)\n\n const baseParams = initialParams(randomSeed())\n const merged: RenderParams = { ...baseParams, ...(input.paramsOverride ?? {}) }\n if (!Number.isFinite(merged.seed) || merged.seed < 0) merged.seed = randomSeed()\n\n // 确认闸:仅做视觉分析,返回报告 + 空 buffer(调用方负责转述并等待用户确认/修正)\n if (input.confirmBrake) {\n let analysis = ''\n try {\n const vision = deps.getVisionClient()\n if (vision.enabled) {\n analysis = await runAnalysisOnly(\n {\n main,\n vision,\n comfy: deps.comfy,\n lock: deps.lock,\n logger: deps.logger,\n clientId: deps.clientId,\n comfyTimeoutMs: deps.comfyTimeoutMs,\n maxDiagnoseRetries: deps.maxDiagnoseRetries,\n mainSystemPrompt: deps.mainSystemPrompt,\n visionSystemPrompt: deps.visionSystemPrompt,\n visionUserText: deps.visionUserText,\n visionCrosscheckRounds: deps.visionCrosscheckRounds,\n visionCrosscheckText: deps.visionCrosscheckText,\n defaultBackgroundColor: deps.defaultBackgroundColor,\n },\n downloaded.dataUrl,\n )\n } else {\n analysis = '(vision 未启用)'\n }\n deps.logger.info('facade confirmBrake analysis len=%d preview=%s', analysis.length, analysis.slice(0, 60).replace(/\\s+/g, ' '))\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n throw new Error(`视觉分析失败:${msg}`)\n }\n return {\n buffer: Buffer.alloc(0),\n filename: '',\n params: merged,\n comfyViewUrl: '',\n awaitingAnalysis: analysis,\n }\n }\n\n const session: Session = {\n key: `chatluna:${input.userId}:${Date.now()}`,\n channelId: input.channelId ?? '',\n userId: input.userId,\n guildId: input.guildId,\n source: uploaded,\n materialAnalysis: '',\n turns: 0,\n params: merged,\n llmContext: [],\n errorHistory: [],\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n busy: false,\n }\n\n const result: RenderResult = await runTurn(\n {\n main,\n vision: deps.getVisionClient(),\n comfy: deps.comfy,\n lock: deps.lock,\n logger: deps.logger,\n clientId: deps.clientId,\n comfyTimeoutMs: deps.comfyTimeoutMs,\n maxDiagnoseRetries: deps.maxDiagnoseRetries,\n mainSystemPrompt: deps.mainSystemPrompt,\n visionSystemPrompt: deps.visionSystemPrompt,\n visionUserText: deps.visionUserText,\n visionCrosscheckRounds: deps.visionCrosscheckRounds,\n visionCrosscheckText: deps.visionCrosscheckText,\n defaultBackgroundColor: deps.defaultBackgroundColor,\n },\n session,\n input.instruction,\n { runVision: true, sourceDataUrl: downloaded.dataUrl },\n )\n\n const comfyViewUrl = buildComfyViewUrl(deps.comfy.baseUrl, result.filename)\n return {\n buffer: result.imageBuffer,\n filename: result.filename,\n note: result.note,\n params: result.params,\n comfyViewUrl,\n material: result.material,\n lighting: result.lighting,\n background: result.background,\n }\n },\n }\n}\n\nexport type RenderFacade = ReturnType<typeof createRenderFacade>\n\nasync function fetchImage(\n ctx: Context,\n url: string,\n timeoutMs: number,\n): Promise<{ buffer: Buffer; mimeType: string; dataUrl: string }> {\n let buffer: Buffer\n let mimeType = 'image/png'\n if (url.startsWith('internal:') || url.startsWith('file:') || url.startsWith('data:')) {\n const file = await ctx.http.file(url, { timeout: timeoutMs })\n buffer = Buffer.from(file.data)\n if (typeof file.type === 'string' && file.type) mimeType = file.type\n } else {\n const ab = await ctx.http.get(url, { responseType: 'arraybuffer', timeout: timeoutMs })\n buffer = Buffer.from(ab as ArrayBuffer)\n mimeType = sniffMime(buffer) || mimeType\n }\n const dataUrl = `data:${mimeType};base64,${buffer.toString('base64')}`\n return { buffer, mimeType, dataUrl }\n}\n\nfunction sniffMime(buffer: Buffer): string | null {\n if (buffer.length < 4) return null\n if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4e && buffer[3] === 0x47) return 'image/png'\n if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return 'image/jpeg'\n if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) return 'image/gif'\n if (buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46) return 'image/webp'\n return null\n}\n\nfunction extForMime(mime: string): string {\n if (mime === 'image/png') return 'png'\n if (mime === 'image/jpeg') return 'jpg'\n if (mime === 'image/gif') return 'gif'\n if (mime === 'image/webp') return 'webp'\n return 'png'\n}\n\nfunction buildComfyViewUrl(baseUrl: string, filename: string, subfolder = '', type: 'output' | 'input' | 'temp' = 'output'): string {\n const base = baseUrl.replace(/\\/+$/, '')\n return `${base}/view?filename=${encodeURIComponent(filename)}&subfolder=${encodeURIComponent(subfolder)}&type=${type}`\n}\n","import { createRequire } from 'node:module'\nimport type { Context } from 'koishi'\n\nexport interface ChatLunaServiceLike {\n installPlugin(plugin: unknown): Promise<void> | void\n uninstallPlugin(plugin: unknown): void\n getPlugin?(name: string): unknown\n}\n\nexport interface ChatLunaPluginLike {\n registerTool(name: string, tool: unknown): void\n dispose?(): Promise<void> | void\n}\n\nexport type ChatLunaPluginConstructor = new (\n ctx: Context,\n config: Record<string, never>,\n platformName: string,\n createConfigPool: boolean,\n) => ChatLunaPluginLike\n\nexport type StructuredToolConstructor = new (...args: any[]) => {\n name?: string\n description?: string\n schema?: unknown\n _call(input: Record<string, unknown>, runManager?: unknown, config?: { configurable?: { session?: unknown } }): Promise<string>\n}\n\nconst runtimeRequire = createRequire(`${process.cwd()}/package.json`)\n\nexport async function loadChatLunaRuntime(): Promise<{\n ChatLunaPlugin: ChatLunaPluginConstructor\n StructuredTool: StructuredToolConstructor\n}> {\n const [chatlunaModule, langchainToolsModule] = await Promise.all([\n loadRuntimeModule('koishi-plugin-chatluna/services/chat'),\n loadRuntimeModule('@langchain/core/tools'),\n ])\n\n const ChatLunaPlugin = chatlunaModule.ChatLunaPlugin as ChatLunaPluginConstructor | undefined\n const StructuredTool = langchainToolsModule.StructuredTool as StructuredToolConstructor | undefined\n\n if (!ChatLunaPlugin) {\n throw new Error('ChatLunaPlugin export not found from koishi-plugin-chatluna/services/chat.')\n }\n if (!StructuredTool) {\n throw new Error('StructuredTool export not found from @langchain/core/tools.')\n }\n\n return { ChatLunaPlugin, StructuredTool }\n}\n\nasync function loadRuntimeModule(specifier: string): Promise<Record<string, any>> {\n try {\n return runtimeRequire(specifier) as Record<string, any>\n } catch (requireError) {\n const dynamicImport = new Function('s', 'return import(s)') as (target: string) => Promise<Record<string, any>>\n return dynamicImport(specifier).catch((importError) => {\n const requireMessage = requireError instanceof Error ? requireError.message : String(requireError)\n const importMessage = importError instanceof Error ? importError.message : String(importError)\n throw new Error(`failed to load runtime module \"${specifier}\"; require: ${requireMessage}; import: ${importMessage}`)\n })\n }\n}\n","import { h } from 'koishi'\nimport type { RenderFacade } from '../../agent/render-facade.js'\nimport type { RenderParams } from '../../types.js'\nimport type { StructuredToolConstructor } from './runtime.js'\nimport type { ChatLunaConfigAccessor, ChatLunaSessionLike, LastGeneratedStore } from './types.js'\n\nconst TOOL_NAME = 'comfy_render'\n\nconst TOOL_DESCRIPTION = 'Run one ComfyUI FLUX.2 image-to-image render through the aka-comfy-agent internal agent (GLM asset probe + DS material/lighting/background judgment + fixed snippet assembly + workflow submit/poll/fetch). Prefer this tool when the user wants to modify or continue iterating an existing image with natural language.'\n\nconst TOOL_USAGE = 'Provide the user instruction plus a referenceMode telling the tool where to load the source image from. The tool sends the rendered image back into the current session and returns { ok, imageUrl, note?, params? } as JSON.'\n\nconst COMFY_USAGE_GUIDE = (\n '使用准则(务必遵守):\\n' +\n '1. 源图获取(referenceMode):优先让用户\"引用(quote)\"要修改的那张图片,然后用 quoted_message 模式自动取图;若引用本会话前一张图或\"上次生成的图\",用 last_generated;若用户给了图片 URL,用 explicit。仅在\"当前这条用户消息本身直接带有图片\"时才用 current_message——在 ChatLuna 里 current_message 通常拿不到上一条消息中的图。\\n' +\n '2. 拿不到源图时:不要长篇技术解释,也不许自己编一张图。直接引导用户:引用那张图片再发一次,或提供图片链接,或用文字描述画面。\\n' +\n '3. instruction 必须忠实转述用户的原话意图,禁止自行扩充或改写生成目标。\\n' +\n '4. 若工具的返回带 awaitingConfirmation / awaitingAnalysis,表示已进入确认闸:把图像分析报告转述给用户并等待其确认/修正,确认后再调用本工具真正生成,不要跳过确认直接出图。'\n)\n\nconst TOOL_INPUT_SCHEMA = {\n type: 'object',\n properties: {\n instruction: {\n type: 'string',\n description: 'Natural-language instruction describing the desired modification / generation intent.',\n },\n referenceMode: {\n type: 'string',\n enum: ['current_message', 'quoted_message', 'explicit', 'last_generated'],\n description: 'Where to load the source image from. Defaults to current_message.',\n },\n imageUrls: {\n type: 'array',\n items: { type: 'string' },\n description: 'Explicit image URLs when referenceMode is explicit.',\n },\n params: {\n type: 'object',\n description: 'Optional overrides for FLUX.2 render parameters (only seed is configurable; all other workflow params are fixed).',\n properties: {\n seed: { type: 'number' },\n },\n additionalProperties: false,\n },\n },\n required: ['instruction'],\n additionalProperties: false,\n}\n\nexport interface ComfyRenderToolDeps {\n render: RenderFacade\n getConfig: ChatLunaConfigAccessor\n lastGenerated: LastGeneratedStore\n userTaskLocks: Set<string>\n logger?: { info(msg: string, ...args: unknown[]): void; warn?(msg: string, ...args: unknown[]): void }\n}\n\nexport function createComfyRenderToolInstance(\n StructuredTool: StructuredToolConstructor,\n deps: ComfyRenderToolDeps,\n) {\n return new class extends StructuredTool {\n name = TOOL_NAME\n description = [\n TOOL_DESCRIPTION,\n `Usage: ${TOOL_USAGE}`,\n COMFY_USAGE_GUIDE,\n `Input JSON schema: ${JSON.stringify(TOOL_INPUT_SCHEMA)}`,\n ].join('\\n')\n schema = TOOL_INPUT_SCHEMA\n\n constructor() {\n super({ verboseParsingErrors: true })\n }\n\n async _call(\n input: Record<string, unknown>,\n _runManager?: unknown,\n runtimeConfig?: { configurable?: { session?: ChatLunaSessionLike } },\n ) {\n const session = runtimeConfig?.configurable?.session\n if (!session?.userId) {\n return formatToolError('会话无效,无法识别用户。')\n }\n try {\n const cfg = runtimeConfig?.configurable ?? {}\n const ac = (cfg as any).agentContext\n const safe = (v: unknown, n = 120): string => {\n const s = typeof v === 'string' ? v : JSON.stringify(v)\n return !s ? '' : (s.length > n ? s.slice(0, n) + '…' : s)\n }\n deps.logger?.info('[comfy_render] diag-config keys=%j', Object.keys(cfg))\n deps.logger?.info('[comfy_render] diag-agentContext keys=%j conversationId=%s messages=%s',\n ac ? Object.keys(ac) : '(none)',\n ac?.conversationId ?? ac?.conversation_id ?? ac?.roomId ?? ac?.room_id ?? '',\n Array.isArray(ac?.messages) ? `array(${ac.messages.length})` : typeof ac?.messages,\n )\n if (Array.isArray(ac?.messages)) {\n const lastImg = [...ac.messages].reverse().find((m: any) => {\n const c = m?.content\n const s = typeof c === 'string' ? c : JSON.stringify(c ?? '')\n return /<img|image_url|data:image|internal:|img src/i.test(s)\n })\n deps.logger?.info('[comfy_render] diag-agentContext lastImageMsg role=%s contentHead=%s',\n lastImg?.role ?? '', safe(typeof lastImg?.content === 'string' ? lastImg.content : JSON.stringify(lastImg?.content ?? ''), 200))\n }\n deps.logger?.info('[comfy_render] diag-session2 elements=%s eventKeys=%j',\n Array.isArray(session.elements) ? safe(session.elements.map((e: any) => {\n try { return (e && typeof e.toString === 'function') ? e.toString() : String(e) } catch { return String(e) }\n }).join(' | ').slice(0, 200)) : typeof session.elements,\n (session as any).event ? Object.keys((session as any).event) : '(none)',\n )\n return await runComfyRender(input, session, deps)\n } catch (error) {\n return formatToolError(error instanceof Error ? error.message : String(error))\n }\n }\n }()\n}\n\nasync function runComfyRender(\n input: Record<string, unknown>,\n session: ChatLunaSessionLike,\n deps: ComfyRenderToolDeps,\n): Promise<string> {\n const userId = session.userId!\n if (deps.userTaskLocks.has(userId)) {\n return formatToolError('您有一个 comfy 渲染任务正在进行中,请等待完成。')\n }\n deps.userTaskLocks.add(userId)\n try {\n const instruction = typeof input.instruction === 'string' ? input.instruction.trim() : ''\n if (!instruction) return formatToolError('instruction 必填。')\n\n const referenceMode = typeof input.referenceMode === 'string' ? input.referenceMode : 'current_message'\n const imageUrls = resolveReferenceImages(referenceMode, input, session, deps.lastGenerated)\n const sourceUrl = imageUrls[0]\n if (!sourceUrl) {\n return formatToolError(\n `未能解析到源图(referenceMode=${referenceMode})。` +\n `请按下面任一方式引导用户把源图提供给我,不要长篇技术解释,也不许自创一张图:` +\n `(a) 让用户【引用(quote)】那张要修改的图片后再发一次 → 用 referenceMode=quoted_message;` +\n `(b) 若是引用本会话刚生成/前一张图 → 用 referenceMode=last_generated;` +\n `(c) 若用户能提供图片 URL → 用 referenceMode=explicit 并填 imageUrls。`\n )\n }\n\n const paramsOverride = normalizeParamsOverride(input.params)\n const confirmBrake = deps.getConfig().confirmBeforeGenerate?.chatluna === true\n\n const result = await deps.render.renderOneShot({\n imageUrl: sourceUrl,\n instruction,\n paramsOverride,\n userId,\n channelId: session.channelId,\n guildId: session.guildId,\n confirmBrake,\n })\n\n // 确认闸开启:工具只拿到分析报告(无图),转述给 ChatLuna,由它继续向用户征求确认/修正\n if (result.awaitingAnalysis) {\n return formatToolJson({\n ok: true,\n awaitingConfirmation: true,\n analysis: result.awaitingAnalysis,\n instruction,\n hint: '已将图像分析报告返回给模型。模型应向用户展示报告并征询确认/修正;用户确认后再次调用本工具生成(可附上修正后的 instruction)。',\n })\n }\n\n if (typeof session.send === 'function') {\n try {\n await Promise.resolve(session.send(h.image(result.comfyViewUrl)))\n } catch {\n // 忽略推送失败:仍把 imageUrl 返给模型\n }\n }\n\n const config = deps.getConfig()\n if (config.chatlunaRoomLastGenerated) {\n const conversationId = resolveConversationId(session)\n if (conversationId) {\n deps.lastGenerated.set(conversationId, { imageUrl: result.comfyViewUrl, at: Date.now() })\n }\n }\n\n return formatToolJson({\n ok: true,\n imageUrl: result.comfyViewUrl,\n note: result.note,\n params: result.params,\n })\n } finally {\n deps.userTaskLocks.delete(userId)\n }\n}\n\nfunction resolveReferenceImages(\n referenceMode: string,\n input: Record<string, unknown>,\n session: ChatLunaSessionLike,\n lastGenerated: LastGeneratedStore,\n): string[] {\n if (referenceMode === 'explicit') {\n return normalizeImageUrls(Array.isArray(input.imageUrls)\n ? (input.imageUrls as unknown[])\n : [])\n }\n if (referenceMode === 'last_generated') {\n const conversationId = resolveConversationId(session)\n if (!conversationId) return []\n const rec = lastGenerated.get(conversationId)\n return rec ? [rec.imageUrl] : []\n }\n if (referenceMode === 'current_message') {\n return parseImagesFromMessageContent(session.content)\n }\n if (referenceMode === 'quoted_message') {\n return normalizeImageUrls([\n ...parseImagesFromMessageContent(session.quote?.content),\n ...(Array.isArray(session.quote?.elements)\n ? h.select(session.quote!.elements as any[], 'img').map((img: any) => img.attrs?.src)\n : []),\n ])\n }\n return []\n}\n\nconst IMAGE_PLACEHOLDER_RE = /\\[image:([^\\]]+)\\]/gi\n\nfunction extractImagePlaceholders(text: string): string[] {\n const urls: string[] = []\n const re = new RegExp(IMAGE_PLACEHOLDER_RE.source, IMAGE_PLACEHOLDER_RE.flags)\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n const u = (m[1] ?? '').trim()\n if (u) urls.push(u)\n }\n return urls\n}\n\nfunction parseImagesFromMessageContent(content: unknown): string[] {\n if (typeof content === 'string' && content.trim()) {\n return normalizeImageUrls([\n ...h.select(h.parse(content), 'img').map((img: any) => img.attrs?.src),\n ...extractImagePlaceholders(content),\n ])\n }\n if (Array.isArray(content)) {\n const urls: string[] = []\n for (const el of content as any[]) {\n if (!el || typeof el !== 'object') continue\n const type = (el as any).type\n if (type === 'img' && (el as any).attrs?.src) {\n urls.push((el as any).attrs.src)\n } else if (type === 'text' && (el as any).attrs?.content) {\n urls.push(...extractImagePlaceholders(String((el as any).attrs.content)))\n }\n }\n return normalizeImageUrls(urls)\n }\n return []\n}\n\nfunction normalizeImageUrls(items: unknown[]): string[] {\n return items\n .filter((item): item is string => typeof item === 'string' && item.trim().length > 0)\n .map((item) => item.trim())\n}\n\nfunction resolveConversationId(session: ChatLunaSessionLike): string | undefined {\n const cid = session.conversationId ?? session.conversation_id ?? session.roomId ?? session.room_id\n if (typeof cid === 'string' && cid.trim()) return cid.trim()\n return undefined\n}\n\nfunction normalizeParamsOverride(v: unknown): Partial<RenderParams> | undefined {\n if (!v || typeof v !== 'object') return undefined\n const obj = v as Record<string, unknown>\n const patch: Partial<RenderParams> = {}\n const raw = obj['seed']\n if (typeof raw === 'number' && Number.isFinite(raw)) patch.seed = raw\n else if (typeof raw === 'string' && raw.trim() && Number.isFinite(Number(raw))) patch.seed = Number(raw)\n return Object.keys(patch).length ? patch : undefined\n}\n\nfunction formatToolError(message: string): string {\n return formatToolJson({ ok: false, error: message })\n}\n\nfunction formatToolJson(value: unknown): string {\n return JSON.stringify(value, null, 2)\n}\n\n/**\n * ChatLuna 工具激活判定(selector)。只在「明确想用 comfy 处理图像」的消息里暴露 comfy_render:\n * 消息含图片标签、或显式提到 comfy/v2、或包含强图像编辑信号。普通闲聊(天气/问候/寒暄)时工具隐藏,\n * 从而与命令流程保持互斥——用户想用 comfy 生成就走命令或显式说,不会被聊天语境误触发。\n */\nconst TOOL_TRIGGER_RE = /comfy|v2|改图|重绘|渲染|换成|生成图像|把.{0,10}?(背景|颜色|风格|材质)|背景|配色|材质|毛绒|编辑.{0,4}图/i\n\nfunction extractMessageText(content: unknown): string {\n if (typeof content === 'string') return content\n if (Array.isArray(content)) {\n return content\n .map((block) => {\n if (typeof block === 'string') return block\n if (block && typeof block === 'object') {\n const anyBlock = block as { text?: unknown; content_url?: unknown; image_url?: unknown; url?: unknown }\n return [anyBlock.text, anyBlock.content_url, anyBlock.image_url, anyBlock.url]\n .filter((v): v is string => typeof v === 'string')\n .join(' ')\n }\n return ''\n })\n .filter(Boolean)\n .join(' ')\n }\n return ''\n}\n\nexport function shouldActivateComfyTool(messages: unknown): boolean {\n const arr = Array.isArray(messages) ? messages : []\n const last = arr[arr.length - 1] as { content?: unknown; images?: unknown; elements?: unknown } | undefined\n if (!last) return false\n const text = extractMessageText(last.content).trim()\n if (!text) return Boolean(last.images || last.elements) // 只有图片没有文字 → 保守激活\n // ChatLuna 会把纯图/内联图转成 [image:URL] 文本占位符;只要出现占位符即视为图像意图,激活 comfy 工具\n if (/\\[image:/i.test(text)) return true\n return TOOL_TRIGGER_RE.test(text) || /<img|!\\[[^\\]]*\\]\\(https?/.test(text)\n}\n","import type { Context } from 'koishi'\nimport type { Config } from '../../config.js'\nimport type { RenderFacade } from '../../agent/render-facade.js'\nimport { loadChatLunaRuntime } from './runtime.js'\nimport type { ChatLunaPluginLike, ChatLunaServiceLike, StructuredToolConstructor } from './runtime.js'\nimport { createComfyRenderToolInstance, shouldActivateComfyTool } from './tool-runtime.js'\nimport type { ChatLunaSessionLike, LastGeneratedStore } from './types.js'\n\nexport const CHATLUNA_BRIDGE_PLATFORM_NAME = 'aka-comfy-agent-chatluna-bridge'\nconst COMFY_RENDER_TOOL_NAME = 'comfy_render'\n\nexport interface ChatLunaBridgeManagerDeps {\n render: RenderFacade\n lastGenerated: LastGeneratedStore\n userTaskLocks: Set<string>\n}\n\nexport class ChatLunaBridgeManager {\n private chatLunaPlugin?: ChatLunaPluginLike\n private warnedUnavailable = false\n private syncQueue: Promise<void> = Promise.resolve()\n private readonly getConfig = () => this.config\n\n constructor(\n private readonly ctx: Context,\n private readonly deps: ChatLunaBridgeManagerDeps,\n private config: Config,\n private readonly logger: ReturnType<Context['logger']>,\n ) {}\n\n updateConfig(config: Config): void {\n this.config = config\n }\n\n sync(enabled: boolean): Promise<void> {\n this.syncQueue = this.syncQueue\n .catch(() => {})\n .then(async () => {\n if (enabled) await this.enable()\n else await this.disable()\n })\n return this.syncQueue\n }\n\n async dispose(): Promise<void> {\n await this.disable()\n }\n\n private async enable(): Promise<void> {\n const chatlunaService = this.getChatLunaService()\n if (!chatlunaService) {\n if (!this.warnedUnavailable) {\n this.logger.warn('ChatLuna bridge enabled, but ChatLuna service is not available. Install and enable ChatLuna first.')\n this.warnedUnavailable = true\n }\n return\n }\n\n if (this.chatLunaPlugin) {\n this.warnedUnavailable = false\n return\n }\n\n const runtime = await loadChatLunaRuntime().catch((error) => {\n if (!this.warnedUnavailable) {\n const message = error instanceof Error ? error.message : String(error)\n this.logger.warn('ChatLuna bridge enabled but runtime modules are unavailable: %s', message)\n this.warnedUnavailable = true\n }\n return null\n })\n if (!runtime) return\n\n const existing = chatlunaService.getPlugin?.(CHATLUNA_BRIDGE_PLATFORM_NAME)\n if (existing) {\n this.logger.warn('ChatLuna plugin name \"%s\" is already in use, skip built-in bridge registration.', CHATLUNA_BRIDGE_PLATFORM_NAME)\n this.warnedUnavailable = true\n return\n }\n\n const plugin = new runtime.ChatLunaPlugin(this.ctx, {}, CHATLUNA_BRIDGE_PLATFORM_NAME, false)\n this.registerTools(plugin, runtime.StructuredTool)\n\n if (!chatlunaService.getPlugin?.(CHATLUNA_BRIDGE_PLATFORM_NAME)) {\n await Promise.resolve(chatlunaService.installPlugin(plugin))\n }\n\n this.chatLunaPlugin = plugin\n this.warnedUnavailable = false\n this.logger.info('ChatLuna bridge enabled (tool: %s).', COMFY_RENDER_TOOL_NAME)\n }\n\n private async disable(): Promise<void> {\n if (!this.chatLunaPlugin) return\n const plugin = this.chatLunaPlugin\n this.chatLunaPlugin = undefined\n\n const chatlunaService = this.getChatLunaService()\n try {\n chatlunaService?.uninstallPlugin(plugin)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.logger.warn('failed to uninstall ChatLuna bridge plugin: %s', message)\n }\n\n try {\n await Promise.resolve(plugin.dispose?.())\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.logger.warn('failed to dispose ChatLuna bridge plugin: %s', message)\n }\n\n this.logger.info('ChatLuna bridge disabled.')\n }\n\n private registerTools(plugin: ChatLunaPluginLike, StructuredTool: StructuredToolConstructor): void {\n const deps = this.deps\n const getConfig = this.getConfig\n const logger = this.logger\n plugin.registerTool(COMFY_RENDER_TOOL_NAME, {\n selector: (messages: unknown) => {\n const arr = Array.isArray(messages) ? messages : []\n const last = arr[arr.length - 1] as { content?: unknown; role?: unknown } | undefined\n const name = (last && (last as { name?: unknown }).name) || (last && (last as { _name?: unknown })._name) || ''\n const active = shouldActivateComfyTool(messages)\n logger.info('[comfy_render] selector active=%s nmsg=%d lastRole=%s lastName=%s content=%j (head300=%s)',\n active, arr.length, last?.role ?? '', name, arr.length ? JSON.stringify(last?.content).slice(0, 600) : '(no msg)',\n typeof last?.content === 'string' ? last.content.slice(0, 300) : '(non-string)')\n return active\n },\n authorization(session: ChatLunaSessionLike) {\n if (session?.userId) {\n try {\n logger.info('[comfy_render] diag-session keys=%j contentType=%s contentHead=%s elements=%s quote=%s msg=%s',\n Object.keys(session),\n typeof session.content,\n typeof session.content === 'string' ? '<' + session.content.slice(0, 220).replace(/data:[^,]{0,40}/g, 'data:[DATA]') + '>' : '(non-string)',\n Array.isArray(session.elements) ? `array(${session.elements.length})` : typeof session.elements,\n session.quote ? JSON.stringify(Object.keys(session.quote as object)) : 'no',\n typeof (session as any).message === 'object' ? JSON.stringify(Object.keys((session as any).message)) : typeof (session as any).message,\n )\n } catch (e) { /* diag only */ }\n }\n return Boolean(session?.userId)\n },\n createTool() {\n return createComfyRenderToolInstance(StructuredTool, {\n render: deps.render,\n getConfig,\n lastGenerated: deps.lastGenerated,\n userTaskLocks: deps.userTaskLocks,\n logger,\n })\n },\n })\n }\n\n private getChatLunaService(): ChatLunaServiceLike | null {\n const service = (this.ctx as Context & {\n chatluna?: ChatLunaServiceLike\n }).chatluna\n if (!service) return null\n if (typeof service.installPlugin !== 'function' || typeof service.uninstallPlugin !== 'function') return null\n return service\n }\n}\n"],"mappings":"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","names":["import_koishi","name","import_koishi","runtime","name","h","name","errMsg","fetchImage","sniffMime","extForMime"]}
|