Files
kunail0804-ComfyUI-Scene-Co…/nodes/configuration_node.py
T
bb846c8aa0 feat(kb): versioned Knowledge Base datasets with manifest + migration (#36) (#138)
Closes #127, #128, #129.

- manifest.json (new knowledge_base_manifest schema) records a dataset version,
  deterministic content_hash, source_vocab digest, optional entry_schema_version.
  Loader reads/skips it; regeneration stamps it deterministically.
- resolver.knowledge_base_version config option (Configuration + KB Loader nodes)
  pins a version; unavailable version -> clear SC0004.
- In-memory entry migration adapts older datasets to the current entry schema at
  load time without rewriting pinned files; too-old versions error clearly.

Co-authored-by: kunail0804 <kunail0804@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:11:23 +02:00

118 lines
4.7 KiB
Python

"""ComfyUI node for the compiler Configuration (MASTER_SPEC §20, §23).
A thin interface: it collects the configuration options as node inputs and emits
a single COMPILER_CONFIG that the other nodes consume, so behaviour can change
without editing the workflow. No compiler logic; it never imports ComfyUI.
"""
from __future__ import annotations
from typing import Any
from compiler.common.config import Config, ConfigError
from .adapters import format_messages
_DEBUG_LEVELS = ["none", "basic", "verbose", "developer"]
class ConfigurationNode:
"""Centralizes compiler configuration and emits it for the other nodes."""
CATEGORY = "Scene Compiler"
FUNCTION = "run"
RETURN_TYPES = ("COMPILER_CONFIG", "STRING")
RETURN_NAMES = ("config", "errors")
@classmethod
def INPUT_TYPES(cls) -> dict[str, Any]:
return {
"required": {
"analyzer_model": ("STRING", {"default": "llama3"}),
"analyzer_temperature": (
"FLOAT",
{"default": 0.0, "min": 0.0, "max": 2.0, "step": 0.1},
),
"analyzer_max_retries": ("INT", {"default": 3, "min": 0, "max": 10}),
"analyzer_timeout": ("INT", {"default": 300, "min": 1, "max": 3600}),
"knowledge_base": ("STRING", {"default": "knowledge_base/"}),
"resolver_strict_mode": ("BOOLEAN", {"default": True}),
"resolver_allow_aliases": ("BOOLEAN", {"default": True}),
"resolver_expansion_enabled": ("BOOLEAN", {"default": True}),
"resolver_max_expansion_depth": ("INT", {"default": 8, "min": 1, "max": 32}),
"resolver_include_nsfw": ("BOOLEAN", {"default": False}),
"validator_allow_unknown_fields": ("BOOLEAN", {"default": False}),
"prompt_target": ("STRING", {"default": "easy_illustrious"}),
"prompt_separator": ("STRING", {"default": ","}),
"prompt_remove_duplicate_tags": ("BOOLEAN", {"default": True}),
"debug_enabled": ("BOOLEAN", {"default": False}),
"debug_level": (_DEBUG_LEVELS, {"default": "basic"}),
},
# Appended at the END so saved workflows keep their positional widget
# values (ComfyUI stores widget values positionally).
"optional": {
"analyzer_system_prompt": ("STRING", {"multiline": True, "default": ""}),
"resolver_knowledge_base_version": ("STRING", {"default": ""}),
},
}
def run(
self,
analyzer_model: str,
analyzer_temperature: float,
analyzer_max_retries: int,
analyzer_timeout: int,
knowledge_base: str,
resolver_strict_mode: bool,
resolver_allow_aliases: bool,
resolver_expansion_enabled: bool,
resolver_max_expansion_depth: int,
resolver_include_nsfw: bool,
validator_allow_unknown_fields: bool,
prompt_target: str,
prompt_separator: str,
prompt_remove_duplicate_tags: bool,
debug_enabled: bool,
debug_level: str,
analyzer_system_prompt: str = "",
resolver_knowledge_base_version: str = "",
) -> tuple[Any, str]:
analyzer: dict[str, Any] = {
"model": analyzer_model,
"temperature": analyzer_temperature,
"max_retries": analyzer_max_retries,
"timeout": analyzer_timeout,
}
if analyzer_system_prompt:
analyzer["system_prompt"] = analyzer_system_prompt
document = {
"analyzer": analyzer,
"resolver": {
"knowledge_base": knowledge_base,
"strict_mode": resolver_strict_mode,
"allow_aliases": resolver_allow_aliases,
"expansion_enabled": resolver_expansion_enabled,
"max_expansion_depth": resolver_max_expansion_depth,
"include_nsfw": resolver_include_nsfw,
**(
{"knowledge_base_version": resolver_knowledge_base_version}
if resolver_knowledge_base_version
else {}
),
},
"validator": {"allow_unknown_fields": validator_allow_unknown_fields},
"prompt_builder": {
"target": prompt_target,
"separator": prompt_separator,
"remove_duplicate_tags": prompt_remove_duplicate_tags,
},
"debug": {"enabled": debug_enabled, "level": debug_level},
}
try:
config = Config.from_json(document)
except ConfigError as error:
return (None, format_messages([error.message]))
return (config, "")