From 36a0d1012ca2f97d370154cbbf09fdbdb393ca44 Mon Sep 17 00:00:00 2001 From: Drac Date: Mon, 16 Jun 2025 18:22:25 +0200 Subject: [PATCH] feat: dynamically load character slugs --- __init__.py | 2 +- js/nodespawn.js | 77 ++++++++++++++++------------ nodes/gen_text_venice_params_node.py | 6 +-- pyserver/update_characters.py | 14 +++-- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/__init__.py b/__init__.py index 356bdf7..d6eec3f 100644 --- a/__init__.py +++ b/__init__.py @@ -3,7 +3,7 @@ import importlib.util from .pyserver import ( get_key_from_jssetting, # noqa: F401 - # update_characters, + update_characters, update_models, # noqa: F401 update_styles, # noqa: F401 ) diff --git a/js/nodespawn.js b/js/nodespawn.js index 23efae4..6a679c1 100644 --- a/js/nodespawn.js +++ b/js/nodespawn.js @@ -115,41 +115,51 @@ app.registerExtension({ alert(`(VeniceAI.NodeSpawn) Failed to fetch text models:\n${error}`); } } - - // Find the character_slug widget // TODO: move this to a separate node depending on how character slugs are used (if model dependent eg) - // const characterSlugWidget = this.widgets.find(w => w.name === "model"); - // if (characterSlugWidget) { - // try { - // console.log("(VeniceAI.NodeSpawn) Trying to fetch character slugs..."); - // const response = await api.fetchApi("/veniceai/get_characters_list"); - - // if (!response.ok) { - // throw new Error(`HTTP error: ${response.status} ${response.statusText}`); - // } - - // const rawText = await response.text(); - - // let data; - // try { - // data = JSON.parse(rawText); - // } catch (jsonError) { - // throw new Error(`Failed to parse JSON: ${jsonError.message}. Raw response: ${rawText}`); - // } - - // characterSlugWidget.options.values = data.characters; // todo: implement on python side - // if (characterSlugWidget.onChange) { - // characterSlugWidget.onChange(); - // } - - // this.setDirtyCanvas(true); - // } catch (error) { - // console.error("(VeniceAI.NodeSpawn) Failed to fetch character slugs:", error); - // alert(`(VeniceAI.NodeSpawn) Failed to fetch character slugs:\n${error}`); - // } - // } }; } + if (nodeData.name === "GenerateTextVeniceParameters_VENICE") { + const originalOnNodeCreated = nodeType.prototype.onNodeCreated; + nodeType.prototype.onNodeCreated = async function () { + if (originalOnNodeCreated) { + originalOnNodeCreated.apply(this); + } + + // Find the widget + const characterSlugWidget = this.widgets.find(w => w.name === "character_slug"); + if (characterSlugWidget) { + try { + console.log("(VeniceAI.NodeSpawn) Trying to fetch character slugs..."); + const response = await api.fetchApi("/veniceai/get_characters_list"); + + if (!response.ok) { + throw new Error(`HTTP error: ${response.status} ${response.statusText}`); + } + + const rawText = await response.text(); + + let data; + try { + data = JSON.parse(rawText); + } catch (jsonError) { + throw new Error(`Failed to parse JSON: ${jsonError.message}. Raw response: ${rawText}`); + } + console.log("(VeniceAI.NodeSpawn) Fetched character slugs:", data.characters); + characterSlugWidget.options.values = data.characters; + if (characterSlugWidget.onChange) { + characterSlugWidget.onChange(); + } + + this.setDirtyCanvas(true); + } catch (error) { + console.error("(VeniceAI.NodeSpawn) Failed to fetch character slugs:", error); + alert(`(VeniceAI.NodeSpawn) Failed to fetch character slugs:\n${error}`); + } + } + }; + } + + if (nodeData.name === "GenerateSpeech_VENICE") { const originalOnNodeCreated = nodeType.prototype.onNodeCreated; nodeType.prototype.onNodeCreated = async function () { @@ -177,6 +187,7 @@ app.registerExtension({ throw new Error(`Failed to parse JSON: ${jsonError.message}. Raw response: ${rawText}`); } + console.log("(VeniceAI.NodeSpawn) Fetched tts models:", data.tts_models); modelWidget.options.values = data.tts_models; if (modelWidget.onChange) { modelWidget.onChange(); @@ -220,5 +231,7 @@ app.registerExtension({ } } } + } }); + diff --git a/nodes/gen_text_venice_params_node.py b/nodes/gen_text_venice_params_node.py index 5456302..9483350 100644 --- a/nodes/gen_text_venice_params_node.py +++ b/nodes/gen_text_venice_params_node.py @@ -15,9 +15,9 @@ class GenerateTextVeniceParameters: return { "required": { "character_slug": ( - "STRING", + "COMBO", { - "default": "cslug", + "default": "strawberry-the-cat", "tooltip": "", }, ), @@ -67,7 +67,7 @@ class GenerateTextVeniceParameters: } RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("response",) + RETURN_NAMES = ("venice_parameters",) FUNCTION = "pass_on_params" CATEGORY = "venice.ai" diff --git a/pyserver/update_characters.py b/pyserver/update_characters.py index 49c10b5..d29858f 100644 --- a/pyserver/update_characters.py +++ b/pyserver/update_characters.py @@ -5,7 +5,7 @@ from pathlib import Path import requests from aiohttp import web -from server import PromptServer # type: ignore +from server import PromptServer # type: ignore from ..globals import API_ENDPOINTS, VENICEAI_BASE_URL @@ -33,8 +33,7 @@ async def update_characters_list_server(request): # remove "object" key from response_data response_data.pop("object", None) - response_data["data"] = sorted(response_data.get("data", []), key=lambda item: item.get("name", "")) - response_data["data"].insert(0, "none") + response_data["data"] = sorted(response_data.get("data", []), key=lambda item: item.get("slug", "")) with open(characters_list_path, "w") as f: json.dump(response_data, f, indent=4) @@ -47,8 +46,13 @@ async def update_characters_list_server(request): @routes.get("/veniceai/get_characters_list") -async def get_local_characters_list(requests): +async def get_local_characters_list(request): with open(characters_list_path, "r") as f: characters_list_json = json.load(f) - return web.json_response(characters_list_json) + characters = [] + for item in characters_list_json.get("data", []): + if isinstance(item, dict) and item.get("slug"): + characters.append(item.get("slug")) + + return web.json_response({"characters": characters})