From 6b1f89a22947f330d00edcaa2b9c34df5ccfd7da Mon Sep 17 00:00:00 2001 From: sakura1bgx Date: Sat, 5 Jul 2025 09:55:43 +0900 Subject: [PATCH] Update flipstreamviewer.py - Align MessageBox layout - Add FlipStreamLogBox - Clear old params when loading presets - Add response_format to FlipStreamChat - Change max_history behavior in FlipStreamChat - Add FlipStreamParseJson --- flipstreamviewer.py | 126 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 24 deletions(-) diff --git a/flipstreamviewer.py b/flipstreamviewer.py index a9fcbe2..6428d02 100644 --- a/flipstreamviewer.py +++ b/flipstreamviewer.py @@ -60,6 +60,8 @@ def btoa_utf8(value): return base64.b64encode(value.encode("utf-8")).decode() def atob_utf8(value): + if not value: + return "" return base64.b64decode(value.encode()).decode("utf-8") STREAM_COMPRESSION = 0 @@ -67,8 +69,9 @@ UPDATE_DELAY = 1.0 allowed_ips = ["127.0.0.1"] setparam = {} setstate = {} -param = {"lora": "", "_capture_offsetX": 0, "_capture_offsetY": 0, "_capture_scale": 100} -state = {"message": btoa_utf8(""), "update_and_reload": False, "presetTitle": time.strftime("%Y%m%d-%H%M"), "presetFolder": "", "presetFile": "", "loraRate": "1", "loraRank": "0", "loraMode": "", "loraFolder": "", "loraFile": "", "loraTagOptions": "[]", "loraTag": "", "loraLinkHref": "", "loraPreviewSrc": "", "darker": 0.0, "wd14th": 0.35, "wd14cth": 0.85} +default_param = {"lora": "", "_capture_offsetX": 0, "_capture_offsetY": 0, "_capture_scale": 100} +param = default_param.copy() +state = {"message": "", "update_and_reload": False, "presetTitle": time.strftime("%Y%m%d-%H%M"), "presetFolder": "", "presetFile": "", "loraRate": "1", "loraRank": "0", "loraMode": "", "loraFolder": "", "loraFile": "", "loraTagOptions": "[]", "loraTag": "", "loraLinkHref": "", "loraPreviewSrc": "", "darker": 0.0, "wd14th": 0.35, "wd14cth": 0.85} frame_updating = None frame_buffer = None frame_mtime = 0 @@ -243,18 +246,14 @@ div.row { width: 50%; } -#toggleViewButton { - width: 100%; - height: 85%; - border: none; - background: transparent; -} - #messageBox { width: 100%; - height: 15%; + height: 100%; border: none; + padding: 10px; background: transparent; + text-align: left; + user-select: none; font-size: 1.5rem; text-shadow: black 2px 0px, black -2px 0px, @@ -293,6 +292,9 @@ function btoa_utf8(str) { } function atob_utf8(str) { + if (!str) { + return ""; + } return decodeURIComponent(escape(atob(str))); } @@ -800,6 +802,7 @@ function toggleView() { function hideView() { const leftPanel = document.getElementById("leftPanel"); + const centerPanel = document.getElementById("centerPanel"); const rightPanel = document.getElementById("rightPanel"); const presetLeftPanel = document.getElementById("presetLeftPanel"); const presetRightPanel = document.getElementById("presetRightPanel"); @@ -810,6 +813,7 @@ function hideView() { document.getElementById("loraPreview").src = ""; document.querySelectorAll('.FlipStreamPreviewBox').forEach(x => x.src = ""); leftPanel.style.visibility = "hidden"; + centerPanel.style.visibility = "hidden"; rightPanel.style.visibility = "hidden"; presetLeftPanel.style.visibility = "hidden"; presetRightPanel.style.visibility = "hidden"; @@ -821,7 +825,14 @@ async function refreshView() { const data = await fetch("/flipstreamviewer/refresh_view") .then(r => r.json()).catch(e => ({ status: "Fails to refresh view: " + e })); document.getElementById("statusInfo").textContent = data.status_info || "Empty"; - document.getElementById("messageBox").textContent = atob_utf8(data.message) || ""; + document.getElementById("messageBox").innerText = atob_utf8(data.message) || ""; + document.querySelectorAll('.FlipStreamLogBox').forEach(async x => { + if (x.id in data.log) { + x.innerText = atob_utf8(data.log[x.id]) || ""; + } else { + x.innerText = ""; + } + }); if (document.body.style.backgroundImage != "none") { document.body.style.backgroundImage = `url('/flipstreamviewer/stream?mtime=${data.stream_mtime || 0}')`; document.querySelectorAll('.FlipStreamPreviewBox').forEach(async x => { @@ -1003,7 +1014,7 @@ async def viewer(request): def add_textbox(title, label, default, rows): if not label.isidentifier(): raise RuntimeError(f"{title}: label must contain only valid identifier characters.") - param.setdefault(label, default) + param.setdefault(label, btoa_utf8(default)) block[f"{title}_{label}"] = f""" """ @@ -1119,6 +1130,13 @@ async def viewer(request): """ + + def add_logbox(title, label, log, rows): + if not label.isidentifier(): + raise RuntimeError(f"{title}: label must contain only valid identifier characters.") + if (label + "LogBox") in state: + block[f"{title}_{label}"] = f""" + """ hist = server.PromptServer.instance.prompt_queue.get_history(max_items=1) nodedict = next(iter(hist.values()))["prompt"][2] if hist else None @@ -1141,6 +1159,8 @@ async def viewer(request): add_fileselect(title, **inputs) if class_type == "FlipStreamPreviewBox": add_previewbox(title, **inputs) + if class_type == "FlipStreamLogBox": + add_logbox(title, **inputs) text_html = f"""{HEAD}
@@ -1150,9 +1170,8 @@ async def viewer(request):
{"".join([x[1] for x in sorted(block.items())])} -
- -
+
+
Status
@@ -1316,9 +1335,8 @@ async def viewer(request):
-
- -
+
+
@@ -1375,6 +1393,7 @@ async def get_status(request): data["status_info"] = status_info data["stream_mtime"] = frame_mtime data["preview_mtime"] = {key: state[key][0] for key in state if key.endswith("PreviewBox")} + data["log"] = {key: state[key] for key in state if key.endswith("LogBox")} data["message"] = state["message"] data["update_and_reload"] = state["update_and_reload"] state["update_and_reload"] = False @@ -1514,6 +1533,8 @@ async def load_preset(request): buf = {"lora": buf.get("lora", "")} state.update(stt) + param.clear() + param.update(default_param) param.update(buf) state["presetTitle"] = Path(state["presetFile"]).stem time.sleep(UPDATE_DELAY) @@ -1606,7 +1627,7 @@ class FlipStreamTextBox: def run(self, label, default, **kwargs): global frame_updating frame_updating = time.time() - param.setdefault(label, btoa_utf8(default)) + param.setdefault(label, btoa_utf8(default)) return (atob_utf8(param[label]),) @@ -1807,6 +1828,27 @@ class FlipStreamPreviewBox: return () +class FlipStreamLogBox: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "label": ("STRING", {"default": "empty"}), + "log": ("STRING",), + "rows": ("INT", {"default": 3}), + } + } + + RETURN_TYPES = () + OUTPUT_NODE = True + FUNCTION = "run" + CATEGORY = "FlipStreamViewer" + + def run(self, label, log, **kwargs): + state[label + "LogBox"] = btoa_utf8(log) + return () + + class FlipStreamSetUpdateAndReload: @classmethod def INPUT_TYPES(s): @@ -2377,8 +2419,9 @@ class FlipStreamChat: "seed": ("INT", {"default": -1}), "max_tokens": ("INT", {"default": 128, "min": 0}), "presence_penalty": ("FLOAT", {"default": 0, "min": 0}), - "frequency_penalty": ("FLOAT", {"default": 0, "min": 0}), - "repeat_penalty": ("FLOAT", {"default": 1.0, "min": 0}) + "frequency_penalty": ("FLOAT", {"default": 0.5, "min": 0}), + "repeat_penalty": ("FLOAT", {"default": 1.0, "min": 0}), + "response_format": ("STRING", {"default": "", "multiline": True}) }, "optional": { "chat_model": ("CHAT_MODEL",), @@ -2412,7 +2455,7 @@ class FlipStreamChat: self.model.close() self.model._FlipStreamChat_is_closed = True - def chat(self, system, user, stop, messages, **kwargs): + def chat(self, system, user, stop, messages, response_format, **kwargs): if system != self.system: self.system = system messages.clear() @@ -2422,7 +2465,11 @@ class FlipStreamChat: messages[0] = dict(role="system", content=system) if user: messages.append(dict(role="user", content=user)) - return self.model.create_chat_completion(messages, stop=list(filter(str.strip, stop.split(","))), **kwargs)["choices"][0]["message"] + if response_format: + response_format = json.loads(response_format) + else: + response_format = None + return self.model.create_chat_completion(messages, stop=list(filter(str.strip, stop.split(","))), response_format=response_format, **kwargs)["choices"][0]["message"] def run(self, model_file, n_ctx, n_gpu_layers, unload_other_models, close_after_use, system, user, instant, max_history, stop, chat_model=None, messages=None, **kwargs): if unload_other_models: @@ -2441,7 +2488,7 @@ class FlipStreamChat: if max_history == 0: messages.clear() elif max_history >= 1: - messages[:] = messages[-max_history:] + messages[:] = messages[:max_history] self.load_model(model_file, n_ctx, n_gpu_layers) if instant: res = self.chat(system, user, stop, messages.copy(), **kwargs) @@ -2457,6 +2504,33 @@ class FlipStreamChat: return (self.model, output, messages) +class FlipStreamParseJson: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "json_input": ("STRING", {"default": "", "multiline": True}), + "keys": ("STRING", {"default": "", "multiline": True}), + "joinstr": ("STRING", {"default": ",", "multiline": True}), + "ignore_error": ("BOOLEAN", {"default": True}), + } + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "run" + CATEGORY = "FlipStreamViewer" + + def run(self, json_input, keys, joinstr, ignore_error): + value = [] + try: + for key in keys.split(","): + value.append(json.loads(json_input)[key.strip()]) + except Exception as e: + if not ignore_error: + raise RuntimeError(f"FlipStreamParseJsonItem: Invalid JSON input: {e}: {json_input}") + return (joinstr.join(value),) + + class FlipStreamBatchPrompt: @classmethod def INPUT_TYPES(s): @@ -2614,6 +2688,7 @@ NODE_CLASS_MAPPINGS = { "FlipStreamFileSelect_Input": FlipStreamFileSelect_Input, "FlipStreamFileSelect_Output": FlipStreamFileSelect_Output, "FlipStreamPreviewBox": FlipStreamPreviewBox, + "FlipStreamLogBox": FlipStreamLogBox, "FlipStreamSetUpdateAndReload": FlipStreamSetUpdateAndReload, "FlipStreamSetMessage": FlipStreamSetMessage, "FlipStreamSetParam": FlipStreamSetParam, @@ -2631,6 +2706,7 @@ NODE_CLASS_MAPPINGS = { "FlipStreamRembg": FlipStreamRembg, "FlipStreamSegMask": FlipStreamSegMask, "FlipStreamChat": FlipStreamChat, + "FlipStreamParseJson": FlipStreamParseJson, "FlipStreamBatchPrompt": FlipStreamBatchPrompt, "FlipStreamFilmVfi": FlipStreamFilmVfi, "FlipStreamViewer": FlipStreamViewer, @@ -2651,6 +2727,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "FlipStreamFileSelect_Input": "FlipStreamFileSelect_Input", "FlipStreamFileSelect_Output": "FlipStreamFileSelect_Output", "FlipStreamPreviewBox": "FlipStreamPreviewBox", + "FlipStreamLogBox": "FlipStreamLogBox", "FlipStreamSetUpdateAndReload": "FlipStreamSetUpdateAndReload", "FlipStreamSetMessage": "FlipStreamSetMessage", "FlipStreamSetParam": "FlipStreamSetParam", @@ -2668,6 +2745,7 @@ NODE_DISPLAY_NAME_MAPPINGS = { "FlipStreamRembg": "FlipStreamRembg", "FlipStreamSegMask": "FlipStreamSegMask", "FlipStreamChat": "FlipStreamChat", + "FlipStreamParseJson": "FlipStreamParseJson", "FlipStreamBatchPrompt": "FlipStreamBatchPrompt", "FlipStreamFilmVfi": "FlipStreamFilmVfi", "FlipStreamViewer": "FlipStreamViewer",