V 2.0.0 - Universal api #74 - user files
This commit is contained in:
@@ -645,6 +645,73 @@ class PrimereModelConceptSelector:
|
||||
zimage_model, zimage_clip, zimage_vae
|
||||
)
|
||||
|
||||
class PrimereControlledSamplersSteps:
|
||||
CATEGORY = TREE_DASHBOARD
|
||||
RETURN_TYPES = ("STRING", comfy.samplers.KSampler.SAMPLERS, comfy.samplers.KSampler.SCHEDULERS, "INT", "FLOAT")
|
||||
RETURN_NAMES = ("MODEL_CONCEPT", "SAMPLER_NAME", "SCHEDULER_NAME", "STEPS", "CFG")
|
||||
FUNCTION = "get_controlledsampler_step"
|
||||
|
||||
kolors_schedulers = ["EulerDiscreteScheduler", "EulerAncestralDiscreteScheduler", "DPMSolverMultistepScheduler", "DPMSolverMultistepScheduler_SDE_karras", "UniPCMultistepScheduler", "DEISMultistepScheduler"]
|
||||
sana_schedulers = ['flow_dpm-solver']
|
||||
|
||||
UNETLIST = PrimereModelConceptSelector.UNETLIST
|
||||
DIFFUSIONLIST = PrimereModelConceptSelector.DIFFUSIONLIST
|
||||
TEXT_ENCODERS = PrimereModelConceptSelector.TEXT_ENCODERS
|
||||
GGUFLIST = PrimereModelConceptSelector.GGUFLIST
|
||||
VAELIST = PrimereModelConceptSelector.VAELIST
|
||||
CLIPLIST = PrimereModelConceptSelector.CLIPLIST
|
||||
MODELLIST = PrimereModelConceptSelector.MODELLIST
|
||||
TEXT_ENCODERS_PATHS = PrimereModelConceptSelector.TEXT_ENCODERS_PATHS
|
||||
|
||||
CONCEPT_LIST = PrimereModelConceptSelector.CONCEPT_LIST
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_concept": ("STRING", {"default": None, "forceInput": True}),
|
||||
"concepts": (["Auto"] + cls.CONCEPT_LIST,),
|
||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
|
||||
"scheduler_name": (cls.sana_schedulers + cls.kolors_schedulers + comfy.samplers.KSampler.SCHEDULERS,),
|
||||
"steps": ("INT", {"default": 12, "min": 1, "max": 1000, "step": 1}),
|
||||
"cfg": ("FLOAT", {"default": 7, "min": 0.1, "max": 100, "step": 0.01}),
|
||||
"vae": (["None"] + cls.VAELIST,),
|
||||
"encoder_1": (["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.TEXT_ENCODERS_PATHS,),
|
||||
"encoder_2": (["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.TEXT_ENCODERS_PATHS,),
|
||||
"encoder_3": (["None"] + cls.TEXT_ENCODERS + cls.CLIPLIST + cls.TEXT_ENCODERS_PATHS,),
|
||||
"sampler": (["None"] + ["custom_advanced", "ksampler"], {"default": "ksampler"}),
|
||||
"guidance": ('FLOAT', {"default": 3.5, "min": 0.0, "max": 100.0, "step": 0.1}),
|
||||
"weight_dtype": (["None"] + ["Auto", "default", "fp16", "bf16", "fp32", "fp8_e4m3fn", "fp8_e5m2"], {"default": "default"}),
|
||||
"precision": (["None"] + ['fp32', 'fp16', 'quant8', 'quant4'], {"default": "fp16"}),
|
||||
"use_speed_lora": ("BOOLEAN", {"default": False, "label_on": "Seed lora ON", "label_off": "Seed lora OFF"}),
|
||||
"speed_lora": (["None"],),
|
||||
"speed_lora_version": ([1.0, 1.1, 2.0], {"default": 2.0}),
|
||||
"speed_lora_precision": ("BOOLEAN", {"default": True, "label_on": "FP32", "label_off": "BF16"}),
|
||||
"speed_lora_step": ([4, 6, 8, 10, 12, 16], {"default": 8}),
|
||||
"speed_lora_strength": ("FLOAT", {"default": 1.00, "min": -20.00, "max": 20.00, "step": 0.01}),
|
||||
"use_srpo_lora": ("BOOLEAN", {"default": False, "label_on": "Use SRPO Lora", "label_off": "Ignore SRPO Lora"}),
|
||||
"use_srpo_svdq_lora": ("BOOLEAN", {"default": False, "label_on": "Use SRPO-NUNCHAKU Lora", "label_off": "Ignore SRPO-NUNCHAKU Lora"}),
|
||||
"srpo_lora_type": (["R&Q", "RockerBOO", "oficial", "adaptive"], {"default": "oficial"}),
|
||||
"srpo_lora_rank": ([8, 16, 32, 64, 128, 256], {"default": 8}),
|
||||
"srpo_lora_strength": ("FLOAT", {"default": 1, "min": -20.000, "max": 20.000, "step": 0.001}),
|
||||
"use_nunchaku_lora": ("BOOLEAN", {"default": False, "label_on": "Use nunchaku Lora", "label_off": "Ignore nunchaku Lora"}),
|
||||
"nunchaku_lora_type": (["kontext_deblur", "kontext_face_detailer", "anything_extracted"], {"default": "anything_extracted"}),
|
||||
"nunchaku_lora_rank": ([64, 256], {"default": 64}),
|
||||
"nunchaku_lora_strength": ("FLOAT", {"default": 1, "min": -20.000, "max": 20.000, "step": 0.001}),
|
||||
"pixart_refiner_model": (["None"] + cls.MODELLIST,),
|
||||
"refiner_sampler": (comfy.samplers.KSampler.SAMPLERS, {"default": "dpmpp_2m"}),
|
||||
"refiner_scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "normal"}),
|
||||
"refiner_cfg": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 100, "step": 0.01}),
|
||||
"refiner_steps": ("INT", {"default": 22, "min": 10, "max": 30, "step": 1}),
|
||||
"refiner_start": ("INT", {"default": 12, "min": 1, "max": 1000, "step": 1}),
|
||||
"refiner_denoise": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"refiner_ignore_prompt": ("BOOLEAN", {"default": False, "label_on": "Send prompt to refiner", "label_off": "Ignore prompt"}),
|
||||
}
|
||||
}
|
||||
|
||||
def get_controlledsampler_step(self, model_concept, sampler_name, scheduler_name, steps=12, cfg=7, **kwargs):
|
||||
return model_concept, sampler_name, scheduler_name, steps, round(cfg, 2)
|
||||
|
||||
class PrimereConceptDataTuple:
|
||||
RETURN_TYPES = ("TUPLE",)
|
||||
RETURN_NAMES = ("CONCEPT_DATA",)
|
||||
|
||||
+3
-1
@@ -33,7 +33,9 @@ class PrimereApiProcessor:
|
||||
FUNCTION = "process_uniapi"
|
||||
|
||||
API_RESULT = api_helper.get_api_config("apiconfig.json")
|
||||
API_SCHEMAS_RAW = utility.json2tuple(os.path.join(PRIMERE_ROOT, 'front_end', 'api_schemas.json'))
|
||||
_schema_file = os.path.join(PRIMERE_ROOT, 'front_end', 'api_schemas.json')
|
||||
_schema_example = os.path.join(PRIMERE_ROOT, 'front_end', 'api_schemas.example.json')
|
||||
API_SCHEMAS_RAW = utility.json2tuple(_schema_file if Path(_schema_file).is_file() else _schema_example)
|
||||
API_SCHEMA_REGISTRY = api_schema_registry.normalize_registry(API_SCHEMAS_RAW)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -70,6 +70,7 @@ for subdirs in valid_FElist:
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"PrimereSamplersSteps": Dashboard.PrimereSamplersSteps,
|
||||
"PrimereControlledSamplersSteps": Dashboard.PrimereControlledSamplersSteps,
|
||||
"PrimereVAE": Dashboard.PrimereVAE,
|
||||
"PrimereCKPT": Dashboard.PrimereCKPT,
|
||||
"PrimereVAELoader": Dashboard.PrimereVAELoader,
|
||||
@@ -148,6 +149,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"PrimereSamplersSteps": "Primere Samplers & Steps & Cfg",
|
||||
"PrimereControlledSamplersSteps": "Primere Controlled Sampler Setting",
|
||||
"PrimereVAE": "Primere VAE Selector",
|
||||
"PrimereCKPT": "Primere CKPT Selector",
|
||||
"PrimereVAELoader": "Primere VAE Loader",
|
||||
|
||||
@@ -7,6 +7,9 @@ def get_api_config(name: str) -> dict:
|
||||
fp = os.path.join(path, name)
|
||||
config_json = utility.json2tuple(fp)
|
||||
|
||||
if not config_json:
|
||||
return {}
|
||||
|
||||
for k, v in config_json.items():
|
||||
match k:
|
||||
case "OpenAI":
|
||||
|
||||
@@ -521,4 +521,10 @@ async def primere_prompt_saver(request):
|
||||
PromptServer.instance.send_sync("PromptDataSaveResponse", False)
|
||||
else:
|
||||
PromptServer.instance.send_sync("PromptDataSaveResponse", False)
|
||||
return web.json_response({})
|
||||
return web.json_response({})
|
||||
|
||||
routes17 = PromptServer.instance.routes
|
||||
@routes17.get('/primere_apiconfig_check')
|
||||
async def primere_apiconfig_check(request):
|
||||
config_path = os.path.join(PRIMERE_ROOT, 'json', 'apiconfig.json')
|
||||
return web.json_response({"exists": os.path.isfile(config_path)})
|
||||
@@ -82,7 +82,7 @@ export function showToast(status, message) {
|
||||
|
||||
const textEl = document.createElement("span");
|
||||
textEl.textContent = message;
|
||||
Object.assign(textEl.style, { flex: "1", lineHeight: "1.5", wordBreak: "break-all" });
|
||||
Object.assign(textEl.style, { flex: "1", lineHeight: "1.5", wordBreak: "normal", overflowWrap: "break-word" });
|
||||
|
||||
const closeBtn = document.createElement("button");
|
||||
closeBtn.textContent = "✕";
|
||||
|
||||
@@ -3,9 +3,11 @@ import { applyPrimereButtonStyle, showToast } from "./frontend_helper.js";
|
||||
|
||||
const TARGET_NODE_NAME = "PrimereApiProcessor";
|
||||
const SCHEMA_URL = new URL("/extensions/ComfyUI_Primere_Nodes/api_schemas.json", import.meta.url).href;
|
||||
const SCHEMA_EXAMPLE_URL = new URL("/extensions/ComfyUI_Primere_Nodes/api_schemas.example.json", import.meta.url).href;
|
||||
|
||||
let schemaCache = null;
|
||||
let schemaPromise = null;
|
||||
let apiconfigChecked = false;
|
||||
|
||||
async function loadSchemas() {
|
||||
if (schemaCache) {
|
||||
@@ -16,7 +18,12 @@ async function loadSchemas() {
|
||||
schemaPromise = fetch(SCHEMA_URL)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Cannot load schema file (${response.status})`);
|
||||
return fetch(SCHEMA_EXAMPLE_URL).then((fallback) => {
|
||||
if (!fallback.ok) {
|
||||
throw new Error(`Cannot load schema file (${fallback.status})`);
|
||||
}
|
||||
return fallback.json();
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
@@ -25,7 +32,7 @@ async function loadSchemas() {
|
||||
return schemaCache;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("[Primere UniApi] Failed to load json/api_schemas.json", error);
|
||||
console.error("[Primere UniApi] Failed to load api_schemas.json and api_schemas.example.json", error);
|
||||
return {};
|
||||
});
|
||||
}
|
||||
@@ -301,6 +308,15 @@ async function initializeUniApiNode(node) {
|
||||
const refreshBtn = node.addWidget("button", "↺ Reload API Schema", null, async () => {
|
||||
schemaCache = null;
|
||||
schemaPromise = null;
|
||||
let apiconfigMissing = false;
|
||||
try {
|
||||
const r = await fetch("/primere_apiconfig_check");
|
||||
const data = r.ok ? await r.json() : null;
|
||||
if (data && !data.exists) {
|
||||
apiconfigMissing = true;
|
||||
showToast("error", "API config file (json/apiconfig.json) not found. Create it with your API keys before using this node. See the manual for details.");
|
||||
}
|
||||
} catch (_) {}
|
||||
try {
|
||||
const freshRegistry = await loadSchemas();
|
||||
if (!freshRegistry || Object.keys(freshRegistry).length === 0) {
|
||||
@@ -309,9 +325,11 @@ async function initializeUniApiNode(node) {
|
||||
}
|
||||
updateServiceWidget(node, freshRegistry);
|
||||
updateParameterWidgets(node, freshRegistry);
|
||||
const providerCount = Object.keys(freshRegistry).length;
|
||||
const serviceCount = Object.values(freshRegistry).reduce((sum, p) => sum + Object.keys(p).length, 0);
|
||||
showToast("success", `API Schema reloaded successfully. ${providerCount} provider(s), ${serviceCount} service(s) loaded.`);
|
||||
if (!apiconfigMissing) {
|
||||
const providerCount = Object.keys(freshRegistry).length;
|
||||
const serviceCount = Object.values(freshRegistry).reduce((sum, p) => sum + Object.keys(p).length, 0);
|
||||
showToast("success", `API Schema reloaded successfully. ${providerCount} provider(s), ${serviceCount} service(s) loaded.`);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast("error", `API Schema reload failed. ${error.message}`);
|
||||
}
|
||||
@@ -350,6 +368,18 @@ async function initializeUniApiNode(node) {
|
||||
};
|
||||
}
|
||||
|
||||
if (!apiconfigChecked) {
|
||||
apiconfigChecked = true;
|
||||
fetch("/primere_apiconfig_check")
|
||||
.then((r) => r.ok ? r.json() : null)
|
||||
.then((data) => {
|
||||
if (data && !data.exists) {
|
||||
showToast("error", "API config file (json/apiconfig.json) not found. Create it with your API keys before using this node. See the manual for details.");
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
const schemaRegistry = await loadSchemas();
|
||||
updateServiceWidget(node, schemaRegistry);
|
||||
updateParameterWidgets(node, schemaRegistry);
|
||||
|
||||
Reference in New Issue
Block a user