diff --git a/_xbr_pro_block.js b/_xbr_pro_block.js
index 2023b34..74ac741 100644
--- a/_xbr_pro_block.js
+++ b/_xbr_pro_block.js
@@ -53,7 +53,7 @@ const XBR_INFERENCE_MODES_ = XBR_INFERENCE_MODES;
function xbrLlmDefaults() {
return {
model: { model: "", mmproj: "None", chat_handler: "None", n_ctx: 8192, vram_limit: -1, image_min_tokens: 0, image_max_tokens: 0 },
- run: { inference_mode: "one by one", max_frames: 24, max_size: 256, seed: 0, seed_control: "randomize", force_offload: false, save_states: false },
+ run: { inference_mode: "one by one", max_frames: 24, max_size: 256, seed: 0, seed_control: "randomize", force_offload: false, save_states: false, strip_thinking: true },
params: {
max_tokens: 6144, top_k: 40, top_p: 0.9, min_p: 0.05, typical_p: 1.0, temperature: 0.6,
repeat_penalty: 1.12, frequency_penalty: 0.0, present_penalty: 0.0,
@@ -83,6 +83,7 @@ function xbrParseLlm(raw) {
cfg.run.seed_control = xbrPick(r.seed_control, XBR_SEED_MODES, cfg.run.seed_control);
cfg.run.force_offload = xbrFlag(r.force_offload, cfg.run.force_offload);
cfg.run.save_states = xbrFlag(r.save_states, cfg.run.save_states);
+ cfg.run.strip_thinking = xbrFlag(r.strip_thinking, cfg.run.strip_thinking); // 模型输出思考过程时自动过滤
const p = (d.params && typeof d.params === "object") ? d.params : {};
cfg.params.max_tokens = xbrInt(p.max_tokens, cfg.params.max_tokens, 0, 262144);
@@ -131,7 +132,7 @@ function xbrSaveJson(node, patch) {
return cur;
}
-/* ── 预设设定词(三视图 / 四视图 / 背景纯透明的设定文本)─────────────
+/* ── 预设设定词(三视图 / 四视图 / 五视图 / 背景纯透明的设定文本)─────────────
* · 用户改过的按「模式|语言」存进节点(基础面板的 preset_texts 存档);
* · 弹窗读「存档 → 默认」,提交时同步写 widget + 存档。 */
function xbrPresetStore(node) {
@@ -142,6 +143,28 @@ function xbrPresetTextFor(node, mode, lang) {
return (t && String(t).trim()) ? t : (defaultPresetOf(mode, lang) || "");
}
+/* ── 预设选项按「输出语言」过滤 ───────────────────────────────
+ * 预设名的语言标签形如 "Z-Image Turbo [ZH]" / "Normal - 描述 [EN]";
+ * 输出语言选中文 → 增强预设 / 反推预设 只列 [ZH] 项,选英文 → 只列 [EN] 项。 */
+function xbrLangTagOf(v) {
+ const m = String(v == null ? "" : v).match(/[\[[](ZH|EN)[\]]]\s*$/i);
+ return m ? m[1].toUpperCase() : "";
+}
+function xbrFilterByLang(opts, lang) {
+ const want = xbrLangTagOf(lang);
+ if (!want) return opts.slice();
+ const hit = opts.filter((o) => xbrLangTagOf(o) === want);
+ return hit.length ? hit : opts.slice(); // 全无语言标签(老预设)→ 全量兜底
+}
+/** 语言变了:优先换到「同名的另一语言项」,否则取该语言第一项 */
+function xbrSnapPreset(opts, cur, lang) {
+ const list = xbrFilterByLang(opts, lang);
+ if (list.includes(cur)) return cur;
+ const base = String(cur == null ? "" : cur).replace(/\s*[\[[](ZH|EN)[\]]]\s*$/i, "").trim();
+ const same = list.find((o) => String(o).replace(/\s*[\[[](ZH|EN)[\]]]\s*$/i, "").trim() === base);
+ return same || list[0] || cur;
+}
+
/* ── 在线 API(存 ComfyUI user 目录,Key 不进工作流)──────── */
async function xbrApiGet() {
const resp = await api.fetchApi(XBR_API_ENDPOINT);
@@ -338,8 +361,11 @@ function xbrRenderLlm(body, node, draft, ctx) {
draft.lang = v;
// 换语言 → 未在弹窗里改过设定词时,跟着取该语言的「存档 → 默认」
if (!draft.presetTouched) draft.presetText = xbrPresetTextFor(node, draft.mode, v);
+ // 语言决定预设可选范围:增强预设 / 反推预设 自动换到同语言的同名项(没有则取该语言第一项)
+ draft.preset = xbrSnapPreset(xbrWidgetOptions(node, "preset"), draft.preset, v);
+ draft.task_preset = xbrSnapPreset(xbrWidgetOptions(node, "task_preset"), draft.task_preset, v);
})));
- body.append(xbrHint("本项是唯一的语言设置:① 元素词表 / 预设句按哪种语言加载 ② LLM 反推最终输出的提示词是中文还是英文。其他地方不再有语言选项。"));
+ body.append(xbrHint("本项是唯一的语言设置:① 词表 / 设定词 / 预设句按哪种语言加载 ② 增强预设与反推预设只列该语言的选项 ③ 最终输出的提示词语言。"));
body.append(makeSectionTitle("提示词增强反推(总开关在节点表面的「启用 LLM 反推」)"));
@@ -352,7 +378,7 @@ function xbrRenderLlm(body, node, draft, ctx) {
const llm = s.model;
const models = lists.model.length ? lists.model : (llm.model ? [llm.model] : []);
if (!llm.model && models.length) llm.model = models[0];
- body.append(field("强制卸载", checkboxControl(s.run.force_offload, "LLM 用完即卸载(释放显存)", (v) => { s.run.force_offload = v; })));
+ body.append(field("强制卸载", checkboxControl(s.run.force_offload, "LLM 用完即卸载,释放显存", (v) => { s.run.force_offload = v; })));
if (models.length) body.append(field("模型", selectControl(models, llm.model, (v) => { llm.model = v; })));
else body.append(field("模型", xbrTextControl(llm.model, "未找到本地 LLM 模型(.gguf 放到 models/LLM)", (v) => { llm.model = v; })));
body.append(field("视觉模块 mmproj", selectControl(lists.mmproj.length ? lists.mmproj : ["None"], llm.mmproj, (v) => { llm.mmproj = v; })));
@@ -382,7 +408,7 @@ function xbrRenderLlm(body, node, draft, ctx) {
const a = draft.api || (draft.api = { ...XBR_API_DEFAULTS });
body.append(field("服务商", selectControl(ctx.providers, a.provider, (v) => { a.provider = v; })));
body.append(field("模型", xbrTextControl(a.model, XBR_API_DEFAULTS.model, (v) => { a.model = v; })));
- body.append(field("API Key", xbrPasswordControl(a.api_key, "sk-…(保存在本地,不写入工作流)", (v) => { a.api_key = v; })));
+ body.append(field("API Key", xbrPasswordControl(a.api_key, "sk-… 保存在本地,不写入工作流", (v) => { a.api_key = v; })));
body.append(field("Base URL", xbrTextControl(a.base_url, XBR_API_DEFAULTS.base_url, (v) => { a.base_url = v; })));
body.append(field("temperature", xbrNumberControl(a.temperature, { min: 0, max: 2, step: 0.01 }, (v) => { a.temperature = v; })));
body.append(field("max_tokens", xbrNumberControl(a.max_tokens, { min: 1, max: 262144, step: 1 }, (v) => { a.max_tokens = Math.round(v); })));
@@ -398,8 +424,13 @@ function xbrRenderLlm(body, node, draft, ctx) {
body.append(field("推理模式", selectControl(XBR_INFERENCE_MODES, s.run.inference_mode, (v) => { s.run.inference_mode = v; })));
body.append(field("最大帧数", xbrNumberControl(s.run.max_frames, { min: 2, max: 1024, step: 1 }, (v) => { s.run.max_frames = Math.round(v); })));
body.append(field("最大尺寸", xbrNumberControl(s.run.max_size, { min: 128, max: 16384, step: 64 }, (v) => { s.run.max_size = Math.round(v); })));
- body.append(field("保存对话状态", checkboxControl(s.run.save_states, "在内存中保留本次对话上下文(多轮连续反推)", (v) => { s.run.save_states = v; })));
+ body.append(field("保存对话状态", checkboxControl(s.run.save_states, "在内存中保留本次对话上下文,多轮连续反推", (v) => { s.run.save_states = v; })));
body.append(field("状态 UID", xbrNumberControl(s.params.state_uid, { min: -1, max: 999999, step: 1 }, (v) => { s.params.state_uid = Math.round(v); })));
+ body.append(field("🧠 过滤思考过程", checkboxControl(s.run.strip_thinking,
+ "模型把「思考过程 / 推理段 / 工作流程」一起输出时,自动只保留最终提示词", (v) => { s.run.strip_thinking = v; })));
+ body.append(el("div", "font-size:11px;color:#888;line-height:1.65;margin:2px 0 8px;",
+ "开启后=① 系统提示词末尾追加「只输出最终提示词」硬规则 ② 仍漏出思考时自动剔除思考标签、"
+ + "Final Output / 最终输出 标记之前的推理段与代码围栏(拿不到内容则原文保留)。"));
body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "提示词正文在节点上的提示词框里编辑;「📝 提示词」端口接线后该框锁定。"));
}
@@ -417,7 +448,7 @@ function xbrRenderPreset(body, node, draft, ctx) {
draft.presetText = xbrPresetTextFor(node, v, draft.lang);
ctx.rerender(); // 有/无设定词的模式之间切换 → 重画「设定词」区
})));
- body.append(xbrHint("常规文生图 = 只输出正文;人物三视图 / 人物四视图 / 背景纯透明 = 最终提示词最顶端自动加上该模式的设定词。"));
+ body.append(xbrHint("常规文生图 = 只输出正文;人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 = 最终提示词最顶端自动加上该模式的设定词。"));
// ── 设定词(预设模式对应的设定文本;只在弹窗里编辑,节点表面不显示)──
const modeDef = defaultPresetOf(draft.mode, draft.lang);
@@ -426,7 +457,7 @@ function xbrRenderPreset(body, node, draft, ctx) {
if (!String(draft.presetText || "").trim()) draft.presetText = modeDef;
const taP = textareaControl(draft.presetText, (v) => { draft.presetText = v; draft.presetTouched = true; },
"width:100%;box-sizing:border-box;min-height:180px;resize:vertical;");
- taP.placeholder = "该模式的设定词(改过的按「模式 + 语言」记进节点,换模式 / 换语言都不丢)";
+ taP.placeholder = "该模式的设定词;改过的按「模式 + 语言」记进节点,换模式 / 换语言都不丢";
taP.spellcheck = false;
body.append(taP);
const taRow = el("div", "display:flex;align-items:center;gap:10px;margin:6px 0 4px;");
@@ -445,19 +476,20 @@ function xbrRenderPreset(body, node, draft, ctx) {
}
body.append(makeSectionTitle("增强预设(= 提示词设定 / system prompt)"));
- const presetOpts = xbrWidgetOptions(node, "preset");
+ const presetOpts = xbrFilterByLang(xbrWidgetOptions(node, "preset"), draft.lang);
if (presetOpts.length) body.append(field("增强预设", selectControl(presetOpts, draft.preset, (v) => { draft.preset = v; })));
- body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "该预设内容即「🧩 提示词设定」输出的正文,同时作为 LLM 反推的系统提示词。"));
+ body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;",
+ `该预设内容即「🧩 提示词设定」输出的正文,同时作为 LLM 反推的系统提示词;只列与输出语言 ${draft.lang} 相符的预设。`));
body.append(makeSectionTitle("反推预设"));
- const taskOpts = xbrWidgetOptions(node, "task_preset");
+ const taskOpts = xbrFilterByLang(xbrWidgetOptions(node, "task_preset"), draft.lang);
if (taskOpts.length) body.append(field("反推预设", selectControl(taskOpts, draft.task_preset, (v) => { draft.task_preset = v; })));
body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "带 * 的预设里 * 是必填占位符,由节点上的提示词框或外接「📝 提示词」填入。"));
body.append(makeSectionTitle("追加设定(可留空)"));
const ta = textareaControl(s.extra_system, (v) => { s.extra_system = v; },
"width:100%;box-sizing:border-box;min-height:200px;resize:vertical;");
- ta.placeholder = "追加在增强预设之后的额外要求(例:只输出一行、不要解释过程…)";
+ ta.placeholder = "追加在增强预设之后的额外要求,例:只输出一行、不要解释过程…";
ta.spellcheck = false;
body.append(ta);
}
@@ -472,22 +504,26 @@ function xbrRenderHelp(body) {
" · 🖼️ 图像:外接图像 / 视频帧(LLM 反推:图 → 提示词)",
"",
"【输出端口】",
- " · 📝 提示词:最终提示词(未启用 LLM = 拼装好的提示词;启用 LLM = 设定词原样置顶 + LLM 增强后的正文)",
+ " · 📝 提示词:最终提示词。未启用 LLM = 拼装好的提示词;启用 LLM = 设定词原封不动置顶 + LLM 增强后的正文",
+ " · 🖼️ 空latent:第二位。按「空latent类型」生成,官方 latent_format 逐字段对齐",
" · 📋 提示词列表:按行拆分",
- " · 🧩 提示词设定:本次的提示词设定(增强预设 + 输出语言 + 追加设定)",
- " · 🖼️ 空latent:按「空latent类型」生成(官方 latent_format 逐字段对齐)",
+ " · 🧩 提示词设定:本次的提示词设定,即增强预设 + 输出语言 + 追加设定",
"",
"【节点表面】",
" · 第 1 行按钮:🤖 LLM设置(唯一语言设置 / 后端 / 采样参数)| ✨ 增强预设(空latent类型 / 预设模式 / 增强预设 / 反推预设 / 追加设定)| 📖 使用说明",
" · 第 2 行按钮:8 个元素分类(点选项行加入/移除提示词,【添加详细描述】写补充说明)",
" · ✅ 启用 LLM 反推:关(默认)= 原「生图提示词预设」行为;开 = 图/文 → 提示词",
- " · 设定词:预设模式(三视图 / 四视图 / 背景纯透明)的设定文本,在「✨ 增强预设」弹窗里编辑;"
+ " · 设定词:预设模式(三视图 / 四视图 / 五视图 / 背景纯透明)的设定文本,在「✨ 增强预设」弹窗里编辑;"
+ "改过的随节点保存,换模式 / 换语言不丢;输出时原封不动加在增强结果的顶端,也作为 LLM 的增强参考",
" · 提示词框右上角「📋 复制」;下方「参数设定显示」实时显示当前配置",
"",
"【保存】",
" · 弹窗底部:☑ 自动保存(默认开)+ 字号 + 界面缩放;「取消」丢弃未保存改动",
" · 配置随工作流保存;在线 API 的 Key 存在 ComfyUI user 目录,不进工作流",
+ "",
+ "【语言 / 思考过程】",
+ " · 输出语言(🤖 LLM设置)是唯一语言设置:决定词表与设定词按哪种语言加载、增强预设与反推预设只列哪种语言的选项、最终提示词的语言",
+ " · 🧠 过滤思考过程(🤖 LLM设置 → 指令推理,默认开):系统提示词末尾追加「只输出最终提示词」硬规则;模型仍漏出思考过程时,自动剔掉思考标签、Final Output / 最终输出 之前的推理段与代码围栏",
];
body.append(el("div", "font-size:12px;color:#bbb;line-height:1.9;white-space:pre-wrap;font-family:ui-monospace,Consolas,monospace;", lines.join("\n")));
}
@@ -507,10 +543,13 @@ async function xbrOpenModal(node, panelId) {
lang: xbrPick(String(xbrWidgetVal(node, "output_lang") ?? ""), LANGS, LANGS[0]),
kind: xbrPick(String(xbrWidgetVal(node, "latent_kind") ?? ""), LATENT_KINDS, LATENT_KINDS[0]),
mode: xbrPick(String(xbrWidgetVal(node, "preset_mode") ?? ""), MODES, MODES[0]),
- // 设定词(三视图/四视图/背景纯透明):弹窗内草稿 + 「用户是否改过」标记
+ // 设定词(三视图/四视图/五视图/背景纯透明):弹窗内草稿 + 「用户是否改过」标记
presetText: String(xbrWidgetVal(node, "three_view_text") ?? ""),
presetTouched: false,
};
+ // 预设选项与输出语言对齐(旧工作流可能存着异语言的预设名)
+ draft.preset = xbrSnapPreset(xbrWidgetOptions(node, "preset"), draft.preset, draft.lang);
+ draft.task_preset = xbrSnapPreset(xbrWidgetOptions(node, "task_preset"), draft.task_preset, draft.lang);
let apiSaved = null, providers = [XBR_DEFAULT_PROVIDER];
if (panelId === "llm") {
diff --git a/js/xb_image_prompt_preset.js b/js/xb_image_prompt_preset.js
index 3537da7..9364f83 100644
--- a/js/xb_image_prompt_preset.js
+++ b/js/xb_image_prompt_preset.js
@@ -2,7 +2,7 @@
* XB-BOX - 🖼️ 生图提示词预设 — 前端面板
* ============================================================
* 节点:XB_ImagePromptPreset(后端 nodes_image_prompt_preset.py,V1 经典 API)
- * 表面:空latent类型 / 输出语言 / 预设模式 / (三视图预设句)/ 画幅比例 / 宽度 / 高度 / 生成数量
+ * 表面:空latent类型 / 输出语言 / 预设模式 / (三视图/四视图/五视图预设句)/ 画幅比例 / 宽度 / 高度 / 生成数量
* + 节点提示词框 + 8 个分类按钮(4 列 × 2 行):🏷️ 风格 | 🎥 视角 | 👤 主体 | 🎬 姿态 | 👚 装扮 | 🎒 道具 | 💡 光影 | 🏞️ 背景
* 每个按钮一个面板,面板顶部「分类签」切子类;选项行整行可点(加入 → 变蓝 ✔ / 再点移除)
* 道具类点行会先弹「与主体的关系」多宫格标签窗(30 个关系词)
@@ -32,8 +32,9 @@ const LANGS = [LANG_ZH, LANG_EN];
const MODE_TP = "常规文生图";
const MODE_3V = "人物三视图";
const MODE_4V = "人物四视图";
+const MODE_5V = "人物五视图";
const MODE_RGBA = "背景纯透明";
-const MODES = [MODE_TP, MODE_3V, MODE_4V, MODE_RGBA];
+const MODES = [MODE_TP, MODE_3V, MODE_4V, MODE_5V, MODE_RGBA];
const ASPECT_MAP = { "1:1": 1, "16:9": 16 / 9, "9:16": 9 / 16, "4:3": 4 / 3, "3:4": 3 / 4, "21:9": 21 / 9 };
const SIZE_STEP = 16; // width/height 的兜底步长(实际以「空latent类型」的官方最小步长为准)
@@ -71,6 +72,10 @@ const PRESET_TEXT_DEFAULT = {
[LANG_ZH]: "生成四宫格排列的角色概念设计图,画面左上角面板是角色面部的精细特写肖像,画面右上角面板是角色面部侧面的的精细特写肖像,画面左下角面板是无头部人物正面衣着展示图,画面右下角面板是人物背面全身站姿。",
[LANG_EN]: "Generate a character concept design sheet arranged in a 2x2 grid, the top-left panel is a finely detailed close-up portrait of the character's face, the top-right panel is a finely detailed close-up profile portrait of the character's face, the bottom-left panel is a headless front-facing outfit display view, the bottom-right panel is a full-body back standing pose.",
},
+ [MODE_5V]: {
+ [LANG_ZH]: "生成五宫格排列的角色概念设计图,画面左上角面板是角色面部的精细特写肖像,画面右上角面板是角色面部侧面的的精细特写肖像,画面下方左侧面板是无头部人物正面衣着展示图,画面下方中间面板是无头部人物侧面衣着展示图,画面下方右侧面板是人物背面全身站姿。",
+ [LANG_EN]: "Generate a character concept design sheet arranged in five panels, the top-left panel is a finely detailed close-up portrait of the character's face, the top-right panel is a finely detailed close-up profile portrait of the character's face, the bottom-left panel is a headless front-facing outfit display view, the bottom-middle panel is a headless side-facing outfit display view, the bottom-right panel is a full-body back standing pose.",
+ },
[MODE_RGBA]: {
[LANG_ZH]: "生成一张具有透明度的 RGBA 格式图像,包含 Alpha 通道,背景为纯透明。",
[LANG_EN]: "Generate an RGBA image with an alpha channel and a fully transparent background.",
@@ -83,7 +88,7 @@ const THREE_VIEW_DEFAULT = PRESET_TEXT_DEFAULT[MODE_3V];
/**
* ── 预设句(设定词)档案 ─────────────────────────────────────────────
- * 需求:预设模式(三视图 / 四视图 / 背景纯透明)的设定词可编辑,且**改过就存进节点、
+ * 需求:预设模式(三视图 / 四视图 / 五视图 / 背景纯透明)的设定词可编辑,且**改过就存进节点、
* 换模式 / 换语言都不丢**(切换时取「存档 → 默认」,绝不把用户改过的冲掉)。
* 存档位置 = manager_settings.preset_texts(JSON 字符串随工作流一起保存):
* { "人物三视图|中文 [ZH]": "用户改过的设定词", … } ← 只存与默认不同的那条
@@ -2677,7 +2682,7 @@ function setupNode(node) {
try { node.setDirtyCanvas?.(true, true); } catch (_) {}
};
- // 预设句输入框:常规文生图以外的 3 个模式(三视图/四视图/背景纯透明)都显示(一次性隐藏/显示,不做任何 setSize,不会抖动)
+ // 预设句输入框:常规文生图以外的 4 个模式(三视图/四视图/五视图/背景纯透明)都显示(一次性隐藏/显示,不做任何 setSize,不会抖动)
const applyPresetTextVisibility = () => {
try {
if (PRESET_TEXT_DEFAULT[surfaceOf().mode]) { showWidget(w3v); stylePresetTextBox(); } else hideWidget(w3v);
diff --git a/js/xb_image_prompt_preset_pro.js b/js/xb_image_prompt_preset_pro.js
index b05e874..a4e0651 100644
--- a/js/xb_image_prompt_preset_pro.js
+++ b/js/xb_image_prompt_preset_pro.js
@@ -2,7 +2,7 @@
* XB-BOX - 🖼️ 生图提示词预设 — 前端面板
* ============================================================
* 节点:XB_ImagePromptPreset(后端 nodes_image_prompt_preset.py,V1 经典 API)
- * 表面:空latent类型 / 输出语言 / 预设模式 / (三视图预设句)/ 画幅比例 / 宽度 / 高度 / 生成数量
+ * 表面:空latent类型 / 输出语言 / 预设模式 / (三视图/四视图/五视图预设句)/ 画幅比例 / 宽度 / 高度 / 生成数量
* + 节点提示词框 + 8 个分类按钮(4 列 × 2 行):🏷️ 风格 | 🎥 视角 | 👤 主体 | 🎬 姿态 | 👚 装扮 | 🎒 道具 | 💡 光影 | 🏞️ 背景
* 每个按钮一个面板,面板顶部「分类签」切子类;选项行整行可点(加入 → 变蓝 ✔ / 再点移除)
* 道具类点行会先弹「与主体的关系」多宫格标签窗(30 个关系词)
@@ -33,8 +33,9 @@ const LANGS = [LANG_ZH, LANG_EN];
const MODE_TP = "常规文生图";
const MODE_3V = "人物三视图";
const MODE_4V = "人物四视图";
+const MODE_5V = "人物五视图";
const MODE_RGBA = "背景纯透明";
-const MODES = [MODE_TP, MODE_3V, MODE_4V, MODE_RGBA];
+const MODES = [MODE_TP, MODE_3V, MODE_4V, MODE_5V, MODE_RGBA];
const ASPECT_MAP = { "1:1": 1, "16:9": 16 / 9, "9:16": 9 / 16, "4:3": 4 / 3, "3:4": 3 / 4, "21:9": 21 / 9 };
const SIZE_STEP = 16; // width/height 的兜底步长(实际以「空latent类型」的官方最小步长为准)
@@ -72,6 +73,10 @@ const PRESET_TEXT_DEFAULT = {
[LANG_ZH]: "生成四宫格排列的角色概念设计图,画面左上角面板是角色面部的精细特写肖像,画面右上角面板是角色面部侧面的的精细特写肖像,画面左下角面板是无头部人物正面衣着展示图,画面右下角面板是人物背面全身站姿。",
[LANG_EN]: "Generate a character concept design sheet arranged in a 2x2 grid, the top-left panel is a finely detailed close-up portrait of the character's face, the top-right panel is a finely detailed close-up profile portrait of the character's face, the bottom-left panel is a headless front-facing outfit display view, the bottom-right panel is a full-body back standing pose.",
},
+ [MODE_5V]: {
+ [LANG_ZH]: "生成五宫格排列的角色概念设计图,画面左上角面板是角色面部的精细特写肖像,画面右上角面板是角色面部侧面的的精细特写肖像,画面下方左侧面板是无头部人物正面衣着展示图,画面下方中间面板是无头部人物侧面衣着展示图,画面下方右侧面板是人物背面全身站姿。",
+ [LANG_EN]: "Generate a character concept design sheet arranged in five panels, the top-left panel is a finely detailed close-up portrait of the character's face, the top-right panel is a finely detailed close-up profile portrait of the character's face, the bottom-left panel is a headless front-facing outfit display view, the bottom-middle panel is a headless side-facing outfit display view, the bottom-right panel is a full-body back standing pose.",
+ },
[MODE_RGBA]: {
[LANG_ZH]: "生成一张具有透明度的 RGBA 格式图像,包含 Alpha 通道,背景为纯透明。",
[LANG_EN]: "Generate an RGBA image with an alpha channel and a fully transparent background.",
@@ -84,7 +89,7 @@ const THREE_VIEW_DEFAULT = PRESET_TEXT_DEFAULT[MODE_3V];
/**
* ── 预设句(设定词)档案 ─────────────────────────────────────────────
- * 需求:预设模式(三视图 / 四视图 / 背景纯透明)的设定词可编辑,且**改过就存进节点、
+ * 需求:预设模式(三视图 / 四视图 / 五视图 / 背景纯透明)的设定词可编辑,且**改过就存进节点、
* 换模式 / 换语言都不丢**(切换时取「存档 → 默认」,绝不把用户改过的冲掉)。
* 存档位置 = manager_settings.preset_texts(JSON 字符串随工作流一起保存):
* { "人物三视图|中文 [ZH]": "用户改过的设定词", … } ← 只存与默认不同的那条
@@ -2693,7 +2698,7 @@ function setupNode(node) {
try { node.setDirtyCanvas?.(true, true); } catch (_) {}
};
- // 预设句输入框:常规文生图以外的 3 个模式(三视图/四视图/背景纯透明)都显示(一次性隐藏/显示,不做任何 setSize,不会抖动)
+ // 预设句输入框:常规文生图以外的 4 个模式(三视图/四视图/五视图/背景纯透明)都显示(一次性隐藏/显示,不做任何 setSize,不会抖动)
const applyPresetTextVisibility = () => {
try {
// Pro:设定词只在「✨ 增强预设」弹窗里编辑 → 节点表面始终隐藏(不把选项参数暴露在外面)
@@ -3057,7 +3062,7 @@ const XBR_INFERENCE_MODES_ = XBR_INFERENCE_MODES;
function xbrLlmDefaults() {
return {
model: { model: "", mmproj: "None", chat_handler: "None", n_ctx: 8192, vram_limit: -1, image_min_tokens: 0, image_max_tokens: 0 },
- run: { inference_mode: "one by one", max_frames: 24, max_size: 256, seed: 0, seed_control: "randomize", force_offload: false, save_states: false },
+ run: { inference_mode: "one by one", max_frames: 24, max_size: 256, seed: 0, seed_control: "randomize", force_offload: false, save_states: false, strip_thinking: true },
params: {
max_tokens: 6144, top_k: 40, top_p: 0.9, min_p: 0.05, typical_p: 1.0, temperature: 0.6,
repeat_penalty: 1.12, frequency_penalty: 0.0, present_penalty: 0.0,
@@ -3087,6 +3092,7 @@ function xbrParseLlm(raw) {
cfg.run.seed_control = xbrPick(r.seed_control, XBR_SEED_MODES, cfg.run.seed_control);
cfg.run.force_offload = xbrFlag(r.force_offload, cfg.run.force_offload);
cfg.run.save_states = xbrFlag(r.save_states, cfg.run.save_states);
+ cfg.run.strip_thinking = xbrFlag(r.strip_thinking, cfg.run.strip_thinking); // 模型输出思考过程时自动过滤
const p = (d.params && typeof d.params === "object") ? d.params : {};
cfg.params.max_tokens = xbrInt(p.max_tokens, cfg.params.max_tokens, 0, 262144);
@@ -3135,7 +3141,7 @@ function xbrSaveJson(node, patch) {
return cur;
}
-/* ── 预设设定词(三视图 / 四视图 / 背景纯透明的设定文本)─────────────
+/* ── 预设设定词(三视图 / 四视图 / 五视图 / 背景纯透明的设定文本)─────────────
* · 用户改过的按「模式|语言」存进节点(基础面板的 preset_texts 存档);
* · 弹窗读「存档 → 默认」,提交时同步写 widget + 存档。 */
function xbrPresetStore(node) {
@@ -3146,6 +3152,28 @@ function xbrPresetTextFor(node, mode, lang) {
return (t && String(t).trim()) ? t : (defaultPresetOf(mode, lang) || "");
}
+/* ── 预设选项按「输出语言」过滤 ───────────────────────────────
+ * 预设名的语言标签形如 "Z-Image Turbo [ZH]" / "Normal - 描述 [EN]";
+ * 输出语言选中文 → 增强预设 / 反推预设 只列 [ZH] 项,选英文 → 只列 [EN] 项。 */
+function xbrLangTagOf(v) {
+ const m = String(v == null ? "" : v).match(/[\[[](ZH|EN)[\]]]\s*$/i);
+ return m ? m[1].toUpperCase() : "";
+}
+function xbrFilterByLang(opts, lang) {
+ const want = xbrLangTagOf(lang);
+ if (!want) return opts.slice();
+ const hit = opts.filter((o) => xbrLangTagOf(o) === want);
+ return hit.length ? hit : opts.slice(); // 全无语言标签(老预设)→ 全量兜底
+}
+/** 语言变了:优先换到「同名的另一语言项」,否则取该语言第一项 */
+function xbrSnapPreset(opts, cur, lang) {
+ const list = xbrFilterByLang(opts, lang);
+ if (list.includes(cur)) return cur;
+ const base = String(cur == null ? "" : cur).replace(/\s*[\[[](ZH|EN)[\]]]\s*$/i, "").trim();
+ const same = list.find((o) => String(o).replace(/\s*[\[[](ZH|EN)[\]]]\s*$/i, "").trim() === base);
+ return same || list[0] || cur;
+}
+
/* ── 在线 API(存 ComfyUI user 目录,Key 不进工作流)──────── */
async function xbrApiGet() {
const resp = await api.fetchApi(XBR_API_ENDPOINT);
@@ -3342,8 +3370,11 @@ function xbrRenderLlm(body, node, draft, ctx) {
draft.lang = v;
// 换语言 → 未在弹窗里改过设定词时,跟着取该语言的「存档 → 默认」
if (!draft.presetTouched) draft.presetText = xbrPresetTextFor(node, draft.mode, v);
+ // 语言决定预设可选范围:增强预设 / 反推预设 自动换到同语言的同名项(没有则取该语言第一项)
+ draft.preset = xbrSnapPreset(xbrWidgetOptions(node, "preset"), draft.preset, v);
+ draft.task_preset = xbrSnapPreset(xbrWidgetOptions(node, "task_preset"), draft.task_preset, v);
})));
- body.append(xbrHint("本项是唯一的语言设置:① 元素词表 / 预设句按哪种语言加载 ② LLM 反推最终输出的提示词是中文还是英文。其他地方不再有语言选项。"));
+ body.append(xbrHint("本项是唯一的语言设置:① 词表 / 设定词 / 预设句按哪种语言加载 ② 增强预设与反推预设只列该语言的选项 ③ 最终输出的提示词语言。"));
body.append(makeSectionTitle("提示词增强反推(总开关在节点表面的「启用 LLM 反推」)"));
@@ -3356,7 +3387,7 @@ function xbrRenderLlm(body, node, draft, ctx) {
const llm = s.model;
const models = lists.model.length ? lists.model : (llm.model ? [llm.model] : []);
if (!llm.model && models.length) llm.model = models[0];
- body.append(field("强制卸载", checkboxControl(s.run.force_offload, "LLM 用完即卸载(释放显存)", (v) => { s.run.force_offload = v; })));
+ body.append(field("强制卸载", checkboxControl(s.run.force_offload, "LLM 用完即卸载,释放显存", (v) => { s.run.force_offload = v; })));
if (models.length) body.append(field("模型", selectControl(models, llm.model, (v) => { llm.model = v; })));
else body.append(field("模型", xbrTextControl(llm.model, "未找到本地 LLM 模型(.gguf 放到 models/LLM)", (v) => { llm.model = v; })));
body.append(field("视觉模块 mmproj", selectControl(lists.mmproj.length ? lists.mmproj : ["None"], llm.mmproj, (v) => { llm.mmproj = v; })));
@@ -3386,7 +3417,7 @@ function xbrRenderLlm(body, node, draft, ctx) {
const a = draft.api || (draft.api = { ...XBR_API_DEFAULTS });
body.append(field("服务商", selectControl(ctx.providers, a.provider, (v) => { a.provider = v; })));
body.append(field("模型", xbrTextControl(a.model, XBR_API_DEFAULTS.model, (v) => { a.model = v; })));
- body.append(field("API Key", xbrPasswordControl(a.api_key, "sk-…(保存在本地,不写入工作流)", (v) => { a.api_key = v; })));
+ body.append(field("API Key", xbrPasswordControl(a.api_key, "sk-… 保存在本地,不写入工作流", (v) => { a.api_key = v; })));
body.append(field("Base URL", xbrTextControl(a.base_url, XBR_API_DEFAULTS.base_url, (v) => { a.base_url = v; })));
body.append(field("temperature", xbrNumberControl(a.temperature, { min: 0, max: 2, step: 0.01 }, (v) => { a.temperature = v; })));
body.append(field("max_tokens", xbrNumberControl(a.max_tokens, { min: 1, max: 262144, step: 1 }, (v) => { a.max_tokens = Math.round(v); })));
@@ -3402,8 +3433,13 @@ function xbrRenderLlm(body, node, draft, ctx) {
body.append(field("推理模式", selectControl(XBR_INFERENCE_MODES, s.run.inference_mode, (v) => { s.run.inference_mode = v; })));
body.append(field("最大帧数", xbrNumberControl(s.run.max_frames, { min: 2, max: 1024, step: 1 }, (v) => { s.run.max_frames = Math.round(v); })));
body.append(field("最大尺寸", xbrNumberControl(s.run.max_size, { min: 128, max: 16384, step: 64 }, (v) => { s.run.max_size = Math.round(v); })));
- body.append(field("保存对话状态", checkboxControl(s.run.save_states, "在内存中保留本次对话上下文(多轮连续反推)", (v) => { s.run.save_states = v; })));
+ body.append(field("保存对话状态", checkboxControl(s.run.save_states, "在内存中保留本次对话上下文,多轮连续反推", (v) => { s.run.save_states = v; })));
body.append(field("状态 UID", xbrNumberControl(s.params.state_uid, { min: -1, max: 999999, step: 1 }, (v) => { s.params.state_uid = Math.round(v); })));
+ body.append(field("🧠 过滤思考过程", checkboxControl(s.run.strip_thinking,
+ "模型把「思考过程 / 推理段 / 工作流程」一起输出时,自动只保留最终提示词", (v) => { s.run.strip_thinking = v; })));
+ body.append(el("div", "font-size:11px;color:#888;line-height:1.65;margin:2px 0 8px;",
+ "开启后=① 系统提示词末尾追加「只输出最终提示词」硬规则 ② 仍漏出思考时自动剔除思考标签、"
+ + "Final Output / 最终输出 标记之前的推理段与代码围栏(拿不到内容则原文保留)。"));
body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "提示词正文在节点上的提示词框里编辑;「📝 提示词」端口接线后该框锁定。"));
}
@@ -3421,7 +3457,7 @@ function xbrRenderPreset(body, node, draft, ctx) {
draft.presetText = xbrPresetTextFor(node, v, draft.lang);
ctx.rerender(); // 有/无设定词的模式之间切换 → 重画「设定词」区
})));
- body.append(xbrHint("常规文生图 = 只输出正文;人物三视图 / 人物四视图 / 背景纯透明 = 最终提示词最顶端自动加上该模式的设定词。"));
+ body.append(xbrHint("常规文生图 = 只输出正文;人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 = 最终提示词最顶端自动加上该模式的设定词。"));
// ── 设定词(预设模式对应的设定文本;只在弹窗里编辑,节点表面不显示)──
const modeDef = defaultPresetOf(draft.mode, draft.lang);
@@ -3430,7 +3466,7 @@ function xbrRenderPreset(body, node, draft, ctx) {
if (!String(draft.presetText || "").trim()) draft.presetText = modeDef;
const taP = textareaControl(draft.presetText, (v) => { draft.presetText = v; draft.presetTouched = true; },
"width:100%;box-sizing:border-box;min-height:180px;resize:vertical;");
- taP.placeholder = "该模式的设定词(改过的按「模式 + 语言」记进节点,换模式 / 换语言都不丢)";
+ taP.placeholder = "该模式的设定词;改过的按「模式 + 语言」记进节点,换模式 / 换语言都不丢";
taP.spellcheck = false;
body.append(taP);
const taRow = el("div", "display:flex;align-items:center;gap:10px;margin:6px 0 4px;");
@@ -3449,19 +3485,20 @@ function xbrRenderPreset(body, node, draft, ctx) {
}
body.append(makeSectionTitle("增强预设(= 提示词设定 / system prompt)"));
- const presetOpts = xbrWidgetOptions(node, "preset");
+ const presetOpts = xbrFilterByLang(xbrWidgetOptions(node, "preset"), draft.lang);
if (presetOpts.length) body.append(field("增强预设", selectControl(presetOpts, draft.preset, (v) => { draft.preset = v; })));
- body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "该预设内容即「🧩 提示词设定」输出的正文,同时作为 LLM 反推的系统提示词。"));
+ body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;",
+ `该预设内容即「🧩 提示词设定」输出的正文,同时作为 LLM 反推的系统提示词;只列与输出语言 ${draft.lang} 相符的预设。`));
body.append(makeSectionTitle("反推预设"));
- const taskOpts = xbrWidgetOptions(node, "task_preset");
+ const taskOpts = xbrFilterByLang(xbrWidgetOptions(node, "task_preset"), draft.lang);
if (taskOpts.length) body.append(field("反推预设", selectControl(taskOpts, draft.task_preset, (v) => { draft.task_preset = v; })));
body.append(el("div", "font-size:11px;color:#888;line-height:1.6;margin:2px 0 8px;", "带 * 的预设里 * 是必填占位符,由节点上的提示词框或外接「📝 提示词」填入。"));
body.append(makeSectionTitle("追加设定(可留空)"));
const ta = textareaControl(s.extra_system, (v) => { s.extra_system = v; },
"width:100%;box-sizing:border-box;min-height:200px;resize:vertical;");
- ta.placeholder = "追加在增强预设之后的额外要求(例:只输出一行、不要解释过程…)";
+ ta.placeholder = "追加在增强预设之后的额外要求,例:只输出一行、不要解释过程…";
ta.spellcheck = false;
body.append(ta);
}
@@ -3476,22 +3513,26 @@ function xbrRenderHelp(body) {
" · 🖼️ 图像:外接图像 / 视频帧(LLM 反推:图 → 提示词)",
"",
"【输出端口】",
- " · 📝 提示词:最终提示词(未启用 LLM = 拼装好的提示词;启用 LLM = 设定词原样置顶 + LLM 增强后的正文)",
+ " · 📝 提示词:最终提示词。未启用 LLM = 拼装好的提示词;启用 LLM = 设定词原封不动置顶 + LLM 增强后的正文",
+ " · 🖼️ 空latent:第二位。按「空latent类型」生成,官方 latent_format 逐字段对齐",
" · 📋 提示词列表:按行拆分",
- " · 🧩 提示词设定:本次的提示词设定(增强预设 + 输出语言 + 追加设定)",
- " · 🖼️ 空latent:按「空latent类型」生成(官方 latent_format 逐字段对齐)",
+ " · 🧩 提示词设定:本次的提示词设定,即增强预设 + 输出语言 + 追加设定",
"",
"【节点表面】",
" · 第 1 行按钮:🤖 LLM设置(唯一语言设置 / 后端 / 采样参数)| ✨ 增强预设(空latent类型 / 预设模式 / 增强预设 / 反推预设 / 追加设定)| 📖 使用说明",
" · 第 2 行按钮:8 个元素分类(点选项行加入/移除提示词,【添加详细描述】写补充说明)",
" · ✅ 启用 LLM 反推:关(默认)= 原「生图提示词预设」行为;开 = 图/文 → 提示词",
- " · 设定词:预设模式(三视图 / 四视图 / 背景纯透明)的设定文本,在「✨ 增强预设」弹窗里编辑;"
+ " · 设定词:预设模式(三视图 / 四视图 / 五视图 / 背景纯透明)的设定文本,在「✨ 增强预设」弹窗里编辑;"
+ "改过的随节点保存,换模式 / 换语言不丢;输出时原封不动加在增强结果的顶端,也作为 LLM 的增强参考",
" · 提示词框右上角「📋 复制」;下方「参数设定显示」实时显示当前配置",
"",
"【保存】",
" · 弹窗底部:☑ 自动保存(默认开)+ 字号 + 界面缩放;「取消」丢弃未保存改动",
" · 配置随工作流保存;在线 API 的 Key 存在 ComfyUI user 目录,不进工作流",
+ "",
+ "【语言 / 思考过程】",
+ " · 输出语言(🤖 LLM设置)是唯一语言设置:决定词表与设定词按哪种语言加载、增强预设与反推预设只列哪种语言的选项、最终提示词的语言",
+ " · 🧠 过滤思考过程(🤖 LLM设置 → 指令推理,默认开):系统提示词末尾追加「只输出最终提示词」硬规则;模型仍漏出思考过程时,自动剔掉思考标签、Final Output / 最终输出 之前的推理段与代码围栏",
];
body.append(el("div", "font-size:12px;color:#bbb;line-height:1.9;white-space:pre-wrap;font-family:ui-monospace,Consolas,monospace;", lines.join("\n")));
}
@@ -3511,10 +3552,13 @@ async function xbrOpenModal(node, panelId) {
lang: xbrPick(String(xbrWidgetVal(node, "output_lang") ?? ""), LANGS, LANGS[0]),
kind: xbrPick(String(xbrWidgetVal(node, "latent_kind") ?? ""), LATENT_KINDS, LATENT_KINDS[0]),
mode: xbrPick(String(xbrWidgetVal(node, "preset_mode") ?? ""), MODES, MODES[0]),
- // 设定词(三视图/四视图/背景纯透明):弹窗内草稿 + 「用户是否改过」标记
+ // 设定词(三视图/四视图/五视图/背景纯透明):弹窗内草稿 + 「用户是否改过」标记
presetText: String(xbrWidgetVal(node, "three_view_text") ?? ""),
presetTouched: false,
};
+ // 预设选项与输出语言对齐(旧工作流可能存着异语言的预设名)
+ draft.preset = xbrSnapPreset(xbrWidgetOptions(node, "preset"), draft.preset, draft.lang);
+ draft.task_preset = xbrSnapPreset(xbrWidgetOptions(node, "task_preset"), draft.task_preset, draft.lang);
let apiSaved = null, providers = [XBR_DEFAULT_PROVIDER];
if (panelId === "llm") {
diff --git a/locales/en/nodeDefs.json b/locales/en/nodeDefs.json
index 758c66d..cb5ade0 100644
--- a/locales/en/nodeDefs.json
+++ b/locales/en/nodeDefs.json
@@ -357,7 +357,30 @@
}
},
"XB_ImagePromptPresetPro": {
- "display_name": "XB-BOX - 🖼️ Image Prompt Preset Pro"
+ "display_name": "XB-BOX - 🖼️ Image Prompt Preset Pro",
+ "description": "Image Prompt Preset + Prompt Enhance/Reverse fused: empty latent type + aspect params + 8-category element panel; optional LLM enhance/reverse (local model or online API, enhance preset / task preset / extra settings / sampling params). Outputs: prompt / prompt list / prompt settings / empty latent.",
+ "inputs": {
+ "latent_kind": { "name": "Empty Latent Type" },
+ "output_lang": { "name": "Output Language" },
+ "preset_mode": { "name": "Preset Mode" },
+ "three_view_text": { "name": "Preset Text (3/4/5-view / transparent)" },
+ "aspect_ratio": { "name": "Aspect Ratio" },
+ "width": { "name": "Width" },
+ "height": { "name": "Height" },
+ "batch_size": { "name": "Batch Size" },
+ "use_llm": { "name": "✅ Enable LLM Enhance" },
+ "backend": { "name": "LLM Backend" },
+ "preset": { "name": "Enhance Preset" },
+ "task_preset": { "name": "Reverse Preset" },
+ "text": { "name": "📝 Prompt" },
+ "images": { "name": "🖼️ Image" }
+ },
+ "outputs": {
+ "0": { "name": "📝 Prompt" },
+ "1": { "name": "�️ Empty Latent" },
+ "2": { "name": "📋 Prompt List" },
+ "3": { "name": "🧩 Prompt Settings" }
+ }
},
"XB_ImagePromptPreset": {
"display_name": "XB-BOX - 🖼️ Image Prompt Preset",
@@ -365,7 +388,7 @@
"output_lang": { "name": "Output Language" },
"preset_mode": { "name": "Preset Mode" },
"latent_kind": { "name": "Empty Latent Type" },
- "three_view_text": { "name": "Preset Text (3-view/4-view/transparent)" },
+ "three_view_text": { "name": "Preset Text (3/4/5-view / transparent)" },
"aspect_ratio": { "name": "Aspect Ratio" },
"width": { "name": "Width" },
"height": { "name": "Height" },
diff --git a/locales/zh/nodeDefs.json b/locales/zh/nodeDefs.json
index 3d783cf..8a180f6 100644
--- a/locales/zh/nodeDefs.json
+++ b/locales/zh/nodeDefs.json
@@ -5888,12 +5888,12 @@
},
"XB_ImagePromptPresetPro": {
"display_name": "XB-BOX - 🖼️ 生图提示词预设Pro",
- "description": "生图提示词预设 + 提示词增强反推 融合:空latent类型/画幅参数 + 8 大类元素面板拼装提示词;可选 LLM 反推(本地模型或在线 API,增强预设 / 反推预设 / 追加设定 / 采样参数 / 生成后控制)。输出:提示词 / 提示词列表 / 提示词设定 / 空latent。",
+ "description": "生图提示词预设 + 提示词增强反推 融合:空latent类型/画幅参数 + 8 大类元素面板拼装提示词;可选 LLM 反推(本地模型或在线 API,增强预设 / 反推预设 / 追加设定 / 采样参数 / 生成后控制)。输出:提示词 / 空latent / 提示词列表 / 提示词设定。",
"inputs": {
"latent_kind": { "name": "空latent类型" },
"output_lang": { "name": "输出语言" },
"preset_mode": { "name": "预设模式" },
- "three_view_text": { "name": "预设句(三视图/四视图/透明背景)" },
+ "three_view_text": { "name": "预设句(三/四/五视图·透明背景)" },
"aspect_ratio": { "name": "画幅比例" },
"width": { "name": "宽度" },
"height": { "name": "高度" },
@@ -5907,9 +5907,9 @@
},
"outputs": {
"0": { "name": "📝 提示词" },
- "1": { "name": "📋 提示词列表" },
- "2": { "name": "🧩 提示词设定" },
- "3": { "name": "🖼️ 空latent" }
+ "1": { "name": "�️ 空latent" },
+ "2": { "name": "📋 提示词列表" },
+ "3": { "name": "🧩 提示词设定" }
}
},
"XB_ImagePromptPreset": {
@@ -5918,7 +5918,7 @@
"output_lang": { "name": "输出语言" },
"preset_mode": { "name": "预设模式" },
"latent_kind": { "name": "空latent类型" },
- "three_view_text": { "name": "预设句(三视图/四视图/透明背景)" },
+ "three_view_text": { "name": "预设句(三/四/五视图·透明背景)" },
"aspect_ratio": { "name": "画幅比例" },
"width": { "name": "宽度" },
"height": { "name": "高度" },
diff --git a/nodes_image_prompt_preset.py b/nodes_image_prompt_preset.py
index 9526bb3..cf44c86 100644
--- a/nodes_image_prompt_preset.py
+++ b/nodes_image_prompt_preset.py
@@ -3,7 +3,7 @@
一个节点顶替「提示词输入框 + 画幅比例节点 + 官方空 latent」三件套:
· 表面参数(顺序 = 节点上从上到下的显示顺序):
- 空latent类型 / 输出语言 / 预设模式 / 预设句(三视图·四视图·背景纯透明 三个模式显示)
+ 空latent类型 / 输出语言 / 预设模式 / 预设句(三视图·四视图·五视图·背景纯透明 四个模式显示)
/ 画幅比例 / 宽度 / 高度 / 生成数量
—— 画幅比例 + 步长锁定 + 比例联动 100% 复刻本包「XB-BOX - 🖼️ 图片参数大全」
(去掉强度(小数)/强度(整数)/缩放尺寸三项)
@@ -11,7 +11,7 @@
🏷️ 风格 | 🎥 视角 | 👤 主体 | 🎬 姿态 | 👚 装扮 | 🎒 道具 | 💡 光影 | 🏞️ 背景
每个按钮打开**该分类**的独立面板;面板顶部「分类签」切换子类
(例:风格 → 真人/动画/3D/绘画插画/特殊风格),选项行 = ➕ ➖ 选项名 添加详细描述
-· 输出①「提示词」= 预设句(人物三视图 / 人物四视图 / 背景纯透明;常规文生图无预设句)+ 正文(节点提示词框 ↔ 面板预览框双向同步)
+· 输出①「提示词」= 预设句(人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明;常规文生图无预设句)+ 正文(节点提示词框 ↔ 面板预览框双向同步)
· 输出②「空latent」= 主流模型直接可用的空 latent(选项名 = 模型名,规格按官方 latent_format;
通道数 / 下采样除数 / 步长 / 上下限 / batch 上限 / downscale 元数据逐项对齐):
· Z-image 16 通道 · /8 · 步长 16 (官方 latent_format = Flux)
@@ -52,20 +52,23 @@ MAX_RESOLUTION = int(getattr(nodes, "MAX_RESOLUTION", 16384) or 16384)
# ── 面板枚举(字符串即存储值,前端面板与后端按同一份字符串对齐) ────────────
OUTPUT_LANGS = ["中文 [ZH]", "英文 [EN]"]
-PRESET_MODES = ["常规文生图", "人物三视图", "人物四视图", "背景纯透明"]
+PRESET_MODES = ["常规文生图", "人物三视图", "人物四视图", "人物五视图", "背景纯透明"]
LANG_ZH = OUTPUT_LANGS[0]
MODE_TEXT2IMG = PRESET_MODES[0]
MODE_THREE_VIEW = PRESET_MODES[1]
MODE_FOUR_VIEW = PRESET_MODES[2]
-MODE_TRANSPARENT = PRESET_MODES[3]
+MODE_FIVE_VIEW = PRESET_MODES[3]
+MODE_TRANSPARENT = PRESET_MODES[4]
# ── 主流模型的空 latent 规格表(逐项抄自 ComfyUI:comfy/latent_formats.py + 官方空 latent 节点)──
# 选项文字 = 模型名(干净,不带专业参数;细节放 tooltip 与日志);规格仍完整保留以保证对得上模型:
-# 每行: (选项名, 通道数, 下采样除数_h, _w, 尺寸步长, 最小, 最大, batch上限, downscale_ratio_spacial)# · 尺寸步长 = 各模型**官方最小步长**(= 下采样 × patch2):
+# 每行: (选项名, 通道数, 下采样除数_h, _w, 尺寸步长, 最小, 最大, batch上限, downscale_ratio_spacial)
+# · 尺寸步长 = 各模型**官方最小步长**(= 下采样 × patch2):
# Anima/Boogu/SDXL = 8(/8)、Flux2 = 16(/16)、Krea2/SD3/Z-image = 16(/8×patch2)、
# Qwen-image = 32(用户指定:Qwen 系工作流习惯锁 32)、Hunyuan = 32(/32)。
-# 不做全局统一的 32,避免把 1080 这类尺寸无谓地顶到 1088。# · downscale_ratio_spacial = 官方 EmptyLatentImage/EmptySD3 带回的元数据(供
+# 不做全局统一的 32,避免把 1080 这类尺寸无谓地顶到 1088。
+# · downscale_ratio_spacial = 官方 EmptyLatentImage/EmptySD3 带回的元数据(供
# comfy/sample.py:fix_empty_latent_channels 判断是否需要缩放尺寸)
_OFFICIAL_LATENT_ROWS = [
("Anima", 16, 8, 8, 8, 16, MAX_RESOLUTION, 4096, 8),
@@ -92,7 +95,7 @@ LATENT_KIND_SPEC = {
DEFAULT_LATENT_KIND = "Z-image"
# 各预设模式的默认预设句(可编辑字段 three_view_text 的默认值):按「预设模式 × 输出语言」给。
# · 常规文生图 = 无预设句(原样输出正文,用户完全可控)
-# · 人物三视图 / 人物四视图 / 背景纯透明 = 成句时自动把预设句拼在正文前(中文「。」/英文 ". ")
+# · 人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 = 成句时自动把预设句拼在正文前(中文「。」/英文 ". ")
PRESET_TEXT = {
MODE_THREE_VIEW: {
LANG_ZH: "生成平行排列的角色概念设计图,画面从左到右由四个独立面板组成:"
@@ -115,11 +118,24 @@ PRESET_TEXT = {
"the bottom-left panel is a headless front-facing outfit display view, "
"the bottom-right panel is a full-body back standing pose.",
},
+ MODE_FIVE_VIEW: {
+ LANG_ZH: "生成五宫格排列的角色概念设计图,画面左上角面板是角色面部的精细特写肖像,"
+ "画面右上角面板是角色面部侧面的的精细特写肖像,"
+ "画面下方左侧面板是无头部人物正面衣着展示图,画面下方中间面板是无头部人物侧面衣着展示图,"
+ "画面下方右侧面板是人物背面全身站姿。",
+ OUTPUT_LANGS[1]: "Generate a character concept design sheet arranged in five panels, "
+ "the top-left panel is a finely detailed close-up portrait of the character's face, "
+ "the top-right panel is a finely detailed close-up profile portrait of the character's face, "
+ "the bottom-left panel is a headless front-facing outfit display view, "
+ "the bottom-middle panel is a headless side-facing outfit display view, "
+ "the bottom-right panel is a full-body back standing pose.",
+ },
MODE_TRANSPARENT: {
LANG_ZH: "生成一张具有透明度的 RGBA 格式图像,包含 Alpha 通道,背景为纯透明。",
OUTPUT_LANGS[1]: "Generate an RGBA image with an alpha channel and a fully transparent background.",
},
}
+
# 兼容旧引用(三视图预设句)
THREE_VIEW_TEXT = PRESET_TEXT[MODE_THREE_VIEW]
@@ -322,7 +338,7 @@ def build_empty_latent(latent_kind, width, height, batch_size=1):
# 提示词拼装
# ============================================================================
def build_prompt(lang, preset_mode, three_view_text, body):
- """预设句(人物三视图 / 人物四视图 / 背景纯透明) + 正文,按输出语言使用合适的分句符。
+ """预设句(人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明) + 正文,按输出语言使用合适的分句符。
正文 = 节点表面提示词框(也等于面板预览框,两者双向同步)。
常规文生图:预设句不参与 → 原样输出正文(保持用户完全可控)。
@@ -347,7 +363,7 @@ def build_prompt(lang, preset_mode, three_view_text, body):
class XB_ImagePromptPreset:
"""XB-BOX - 🖼️ 生图提示词预设:画幅参数 + 提示词拼装 + 官方空 latent 一体化。
- · 表面参数顺序 = 输出语言 / 预设模式 / 空latent类型 / 预设句(三视图·四视图·背景纯透明 显示)
+ · 表面参数顺序 = 输出语言 / 预设模式 / 空latent类型 / 预设句(三视图·四视图·五视图·背景纯透明 显示)
/ 画幅比例 / 宽度 / 高度 / 生成数量;
· 「提示词」输出 = 预设句(人物三视图)+ 节点提示词框正文;
· 「空latent」输出 = 官方空 latent(见 LATENT_KINDS,共 9 种,逐字段对齐,选项按首字母排列)。
@@ -374,12 +390,12 @@ class XB_ImagePromptPreset:
"preset_mode": (list(PRESET_MODES), {
"default": PRESET_MODES[0],
"tooltip": "预设模式:常规文生图=只输出正文(完全可控);"
- "人物三视图 / 人物四视图 / 背景纯透明 = 成句自动在正文前加对应预设句"
+ "人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 = 成句自动在正文前加对应预设句"
"(下方预设句框仅这三个模式显示,切模式时未改过的默认句会自动跟随)",
}),
"three_view_text": ("STRING", {
"default": THREE_VIEW_TEXT[LANG_ZH], "multiline": True,
- "tooltip": "预设句 / 设定词(人物三视图 / 人物四视图 / 背景纯透明 三个模式生效;常规文生图时本框自动隐藏)\n"
+ "tooltip": "预设句 / 设定词(人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 四个模式生效;常规文生图时本框自动隐藏)\n"
"· 用户改过的设定词按「模式 + 语言」存进本节点(换模式 / 换语言都不会丢);\n"
"· 最终提示词输出时它会被原封不动加在正文最顶端",
}),
@@ -407,7 +423,7 @@ class XB_ImagePromptPreset:
"internal_prompt": ("STRING", {
"default": "", "multiline": True,
"tooltip": "节点内编辑的提示词正文(与元素面板的预览框双向同步,随工作流保存)。"
- "人物三视图 / 人物四视图 / 背景纯透明模式下,本字段前面会自动拼上对应预设句",
+ "人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明模式下,本字段前面会自动拼上对应预设句",
}),
"manager_settings": ("STRING", {
"default": "", "multiline": True,
@@ -465,7 +481,7 @@ class XB_ImagePromptPreset:
__all__ = [
"XB_ImagePromptPreset",
"ASPECT_RATIO_OPTIONS", "ASPECT_RATIO_MAP", "SIZE_STEP", "SIZE_MIN", "MAX_RESOLUTION",
- "BATCH_MAX", "OUTPUT_LANGS", "PRESET_MODES", "PRESET_TEXT", "THREE_VIEW_TEXT",
+ "BATCH_MAX", "MODE_FIVE_VIEW", "OUTPUT_LANGS", "PRESET_MODES", "PRESET_TEXT", "THREE_VIEW_TEXT",
"LATENT_KINDS", "LATENT_KIND_SPEC", "DEFAULT_LATENT_KIND", "ELEMENT_CATEGORIES",
"default_settings", "parse_settings", "round_step", "normalize_size",
"latent_kind_spec", "latent_step_of", "latent_shape_of",
diff --git a/nodes_image_prompt_preset_pro.py b/nodes_image_prompt_preset_pro.py
index 6451cd8..dfd51ae 100644
--- a/nodes_image_prompt_preset_pro.py
+++ b/nodes_image_prompt_preset_pro.py
@@ -5,7 +5,7 @@ XB-BOX - 🖼️ 生图提示词预设Pro
① 生图预设(原节点全部能力)
· 空latent类型(9 种主流模型,逐字段对齐官方 latent_format)
- · 输出语言 / 预设模式 / 设定词(三视图·四视图·背景纯透明)
+ · 输出语言 / 预设模式 / 设定词(三视图·四视图·五视图·背景纯透明)
- 设定词既作为 LLM 的**增强参考**,也(启用 LLM 时)被**原封不动**加在最终提示词最顶端;
- 用户改过的设定词按「模式 + 语言」存在本节点里,换模式 / 换语言都不会丢。
· 画幅比例 / 宽度 / 高度 / 生成数量(步长按模型官方最小步长 8/16/32 + 比例联动)
@@ -22,7 +22,7 @@ XB-BOX - 🖼️ 生图提示词预设Pro
· 关(默认)= 原「生图提示词预设」行为:只输出拼装好的提示词 + 空latent(不加载模型、不调 API)
· 开 = 把(外接「提示词」端口 或 节点上拼装好的提示词)+ 外接「图像」交给 LLM 反推
-输出:提示词(最终)/ 提示词列表 / 提示词设定 / 空latent
+输出:提示词(最终)/ 空latent / 提示词列表 / 提示词设定
(两个旧节点全部保留,本节点只是融合超集)
CATEGORY: XB_ToolBox/Image_Params
@@ -38,11 +38,13 @@ from .nodes_llama import (
preset_tags,
)
from .nodes_llama_prompt_reverse import (
+ OUTPUT_FORMAT_HINT,
_LANG_HINT,
_OUTPUT_LANGS,
_SEED_MAX,
build_api_config,
parse_settings as parse_llm_settings,
+ strip_reasoning,
)
from .nodes_image_prompt_preset import (
ASPECT_RATIO_OPTIONS,
@@ -102,10 +104,11 @@ def _next_seed(seed, mode):
def _build_system_prompt(preset, extra, output_lang, preset_text=""):
- """提示词设定 = 增强预设 + 输出语言 + 设定词参考 + 追加设定
+ """提示词设定 = 增强预设 + 输出语言 + 设定词参考 + 追加设定 + 只输出最终提示词的硬规则
`preset_text`(预设模式的设定词)只作为**增强参考**告诉 LLM:它会被节点原样置顶到最终提示词,
禁止在正文里重复 / 改写它。
+ 最后追加 `OUTPUT_FORMAT_HINT`(最高优先级)——专治模型把思考过程 / 步骤 / 说明一起输出。
"""
parts = [XB_llamaPromptEnhancer().main(preset)[0] or ""]
parts.append(_LANG_HINT.get(output_lang, _LANG_HINT[_OUTPUT_LANGS[0]]))
@@ -115,6 +118,7 @@ def _build_system_prompt(preset, extra, output_lang, preset_text=""):
extra = (extra or "").strip()
if extra:
parts.append(extra)
+ parts.append(OUTPUT_FORMAT_HINT) # 放最后 = 最显眼(用户要求:不要思考过程)
return "\n\n".join([p for p in parts if p])
@@ -154,11 +158,11 @@ class XB_ImagePromptPresetPro:
}),
"preset_mode": (list(PRESET_MODES), {
"default": PRESET_MODES[0],
- "tooltip": "预设模式:常规文生图=只输出正文;人物三视图 / 人物四视图 / 背景纯透明 = 自动在正文前加预设句",
+ "tooltip": "预设模式:常规文生图=只输出正文;人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 = 自动在正文前加预设句",
}),
"three_view_text": ("STRING", {
"default": THREE_VIEW_TEXT[DEFAULT_LANG], "multiline": True,
- "tooltip": "预设模式的设定词(人物三视图 / 人物四视图 / 背景纯透明 三个模式生效;常规文生图时自动隐藏)\n"
+ "tooltip": "预设模式的设定词(人物三视图 / 人物四视图 / 人物五视图 / 背景纯透明 四个模式生效;常规文生图时自动隐藏)\n"
"· 最终提示词输出时它会被【原封不动】加在最顶端;\n"
"· 用户改过的设定词按「模式 + 语言」存进本节点(换模式 / 换语言都不会丢);\n"
"· 启用 LLM 时它作为增强参考交给 LLM(LLM 只增强正文,不改写它)",
@@ -227,14 +231,14 @@ class XB_ImagePromptPresetPro:
},
}
- RETURN_TYPES = ("STRING", "STRING", "STRING", "LATENT")
- RETURN_NAMES = ("提示词", "提示词列表", "提示词设定", "空latent")
- OUTPUT_IS_LIST = (False, True, False, False)
+ RETURN_TYPES = ("STRING", "LATENT", "STRING", "STRING")
+ RETURN_NAMES = ("提示词", "空latent", "提示词列表", "提示词设定")
+ OUTPUT_IS_LIST = (False, False, True, False)
FUNCTION = "generate"
CATEGORY = "XB_ToolBox/Image_Params"
DESCRIPTION = ("生图提示词预设Pro:生图预设(空latent类型/输出语言/预设模式/画幅参数 + 8 大类元素面板提示词拼装)"
"+ LLM 提示词增强反推(本地模型或在线 API、增强预设、反推预设、追加设定、采样参数、生成后控制)。"
- "「启用 LLM 反推」关 = 原预设行为;开 = 图/文 → 提示词。输出:提示词 / 提示词列表 / 提示词设定 / 空latent。")
+ "「启用 LLM 反推」关 = 原预设行为;开 = 图/文 → 提示词。输出:提示词 / 空latent / 提示词列表 / 提示词设定。")
@classmethod
def IS_CHANGED(cls, manager_settings="", **kwargs):
@@ -313,8 +317,13 @@ class XB_ImagePromptPresetPro:
images=images,
queue_handler=None,
)
- # ④c 设定词**原封不动**加在增强结果最顶端(与未启用 LLM 时的成句规则完全一致)
- final_prompt = build_prompt(output_lang, preset_mode, three_view_text, out1 or "")
+ # ④c 思考过程过滤(模型无视规则、把推理段一起输出时只留最终提示词)
+ raw_out = out1 or ""
+ body_out = strip_reasoning(raw_out) if run.get("strip_thinking", True) else raw_out
+ if body_out != raw_out.strip():
+ print(f"[XB-生图预设Pro] 🧠 已过滤思考过程:{len(raw_out)} 字 → {len(body_out)} 字")
+ # ④d 设定词**原封不动**加在增强结果最顶端(与未启用 LLM 时的成句规则完全一致)
+ final_prompt = build_prompt(output_lang, preset_mode, three_view_text, body_out)
else:
# 原「🖼️ 生图提示词预设」行为:直接输出拼装好的提示词
final_prompt = base_prompt
@@ -338,7 +347,7 @@ class XB_ImagePromptPresetPro:
}
if use_llm and next_seed is not None:
ui["seed"] = [next_seed] # 前端回写到 manager_settings.llm.run.seed(仅 LLM 采样时才推进种子)
- return {"ui": ui, "result": (final_prompt, prompt_list, full_system or "", latent)}
+ return {"ui": ui, "result": (final_prompt, latent, prompt_list, full_system or "")}
NODE_CLASS_MAPPINGS = {
diff --git a/nodes_llama_prompt_reverse.py b/nodes_llama_prompt_reverse.py
index 8c77733..953840b 100644
--- a/nodes_llama_prompt_reverse.py
+++ b/nodes_llama_prompt_reverse.py
@@ -23,6 +23,7 @@ CATEGORY: XB-llama
import json
import os
import random
+import re
import folder_paths
from aiohttp import web
@@ -141,6 +142,7 @@ DEFAULT_SETTINGS = {
"run": {
"inference_mode": "one by one", "max_frames": 24, "max_size": 256,
"seed": 0, "seed_control": "randomize", "force_offload": False, "save_states": False,
+ "strip_thinking": True, # 模型把「思考过程 / 推理段」一起输出时,只保留最终提示词
},
# 推理参数(原「⚙️ 推理参数」)
"params": {
@@ -166,6 +168,88 @@ _LANG_HINT = {
"英文 [EN]": "【Output language】Always respond in English.",
}
+# 「只输出最终提示词」硬规则:拼在系统提示词最后,专治模型把思考过程/步骤/说明一起输出
+OUTPUT_FORMAT_HINT = (
+ "【输出格式(最高优先级,凌驾于以上所有说明)】\n"
+ "直接输出最终提示词本身,不要输出思考过程、推理步骤、分析、工作流程、小标题、编号列表、"
+ "对以上说明的复述、代码块围栏(```),也不要任何开场白、总结或解释。"
+)
+
+# ── 思考过程过滤(模型无视上面的规则、把推理段一起吐出来时的兜底)──────────────
+_THINK_BLOCK_RE = re.compile(r"<(thinking|think|analysis|reasoning)\b[^>]*>.*?\1>", re.S | re.I)
+_THINK_CLOSE_RE = re.compile(r"(thinking|think|analysis|reasoning)>", re.I)
+_THINK_HEADERS = ("thinking process", "think process", "reasoning process", "chain of thought",
+ "思考过程", "推理过程", "分析过程", "思维过程", "思路如下", "先分析")
+_ANSWER_MARKERS = ("final output", "final answer", "final prompt",
+ "最终输出", "最终答案", "最终提示词", "最终结果", "成品提示词", "输出结果")
+
+
+def _looks_like_reasoning(block):
+ """判断一个段落块像「推理 / 草稿 / 清单」而不是成品提示词"""
+ b = block.strip()
+ if not b:
+ return False
+ if b.startswith(("*", "_", "-", ">", "#", "```", "|")):
+ return True
+ if re.match(r"^\**(?:\d+[\.\、\))]|[-*•])\s*\**", b):
+ return True
+ if "**" in b and len(b) < 400:
+ return True
+ if re.match(r"^\**[^。!?\n]{0,30}[::]\**\s*$", b):
+ return True
+ return False
+
+
+def strip_reasoning(text):
+ """丢掉模型输出里的「思考过程」,只留最终提示词。
+
+ ① 显式思考块 … / … → 删;只有闭合标签 → 取其后的内容
+ ② 显式「最终输出 / Final Output」标记 → 取最后一个标记之后的正文
+ ③ 开头是思考标题(Thinking Process / 思考过程…)→ 从尾部往回收集「成品段落」,丢掉前面的推理段
+ ④ 去掉整段代码围栏,并去掉首尾空白
+ 任何一步拿不到内容都回退原文(绝不把结果清空)。
+ """
+ raw = text if isinstance(text, str) else ""
+ s = raw.strip()
+ if not s:
+ return raw
+ original = s
+
+ s2 = _THINK_BLOCK_RE.sub("", s) # ①
+ closes = list(_THINK_CLOSE_RE.finditer(s2))
+ if closes:
+ s2 = s2[closes[-1].end():]
+ s = s2.strip() or s
+
+ low = s.lower() # ②
+ best, hit_len = -1, 0
+ for marker in _ANSWER_MARKERS:
+ i = low.rfind(marker)
+ if i > best:
+ best, hit_len = i, len(marker)
+ if best >= 0:
+ tail = s[best + hit_len:]
+ nl = tail.find("\n")
+ if nl >= 0:
+ tail = tail[nl + 1:]
+ tail = tail.lstrip(":: \t-—").strip() # 支持「最终输出:xxx」同行写法
+ if tail:
+ s = tail
+
+ head = s[:400].lower() # ③
+ if any(h in head for h in _THINK_HEADERS):
+ blocks = [b for b in re.split(r"\n\s*\n", s) if b.strip()]
+ keep = []
+ for b in reversed(blocks):
+ if _looks_like_reasoning(b):
+ break
+ keep.append(b)
+ if keep:
+ s = "\n\n".join(reversed(keep)).strip()
+
+ s = re.sub(r"^```[a-zA-Z]*\s*|\s*```$", "", s.strip()).strip() # ④
+ return s or original
+
def _num(value, default, lo=None, hi=None):
try:
@@ -237,6 +321,7 @@ def parse_settings(raw):
rd["seed_control"] = _pick(r.get("seed_control"), _SEED_MODES, rd["seed_control"])
rd["force_offload"] = _bool(r.get("force_offload"), rd["force_offload"])
rd["save_states"] = _bool(r.get("save_states"), rd["save_states"])
+ rd["strip_thinking"] = _bool(r.get("strip_thinking"), rd["strip_thinking"])
p = data.get("params") if isinstance(data.get("params"), dict) else {}
pd = out["params"]