fix(nodes)+docs: expose include_nsfw in Config node, align timeout, sync README (#83)

Audit follow-ups touching shipped code:
- Configuration node now exposes resolver_include_nsfw (the NSFW gate we added
  was unreachable from ComfyUI) and its analyzer_timeout default is aligned to
  300s to match the Scene Analyzer node.
- README: note the large Danbooru-derived KB, clarify the determinism scope
  (compiler core deterministic; LLM analysis is the one non-deterministic step),
  add the V4 (consolidation) roadmap entry, and an AI-assistance disclaimer.

Co-authored-by: kunail0804 <kunail0804@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Crespin Alexandre
2026-07-10 22:22:27 +02:00
committed by GitHub
co-authored by kunail0804 Claude Opus 4.8
parent 1708bdb723
commit a5dd06043f
3 changed files with 25 additions and 3 deletions
+18 -2
View File
@@ -41,7 +41,9 @@ Natural Language
Only the **Scene Analyzer** uses an LLM, and only to understand language — never
to produce tags. Every generated tag is looked up deterministically in a
data-only **Knowledge Base** and stays fully traceable:
data-only **Knowledge Base** (Version 1 ships a large Knowledge Base generated
from a real Danbooru tag vocabulary, so the tags are always valid and never
invented) and stays fully traceable:
```
Natural Language → Scene JSON → Knowledge Base Entry → Resolved Tag → Prompt Output
@@ -51,7 +53,7 @@ Natural Language → Scene JSON → Knowledge Base Entry → Resolved Tag → Pr
## Key principles
- **Determinism first** — same input + Knowledge Base + config → same output, always. No random seeds anywhere.
- **Determinism first** — the compiler core is deterministic: a given Scene JSON + Knowledge Base + config always produces the same output, with no random seeds. (The one non-deterministic step is the LLM understanding the language up front; everything after it is reproducible.)
- **No hallucinations** — if it isn't in the description, it isn't in the output. Unknown concepts are reported, never guessed.
- **Knowledge is data** — every concept→tag mapping lives in the Knowledge Base, never in code.
- **Traceability** — every tag can be explained back to the sentence it came from.
@@ -151,6 +153,9 @@ The complete normative specification is [`MASTER_SPEC.md`](../MASTER_SPEC.md).
fallback search, localization, performance — determinism preserved.
- **Version 3 — Extensible Compiler Platform**: multi-model Resolvers, a plugin
system, multiple Analyzer backends, and a standalone Compiler SDK.
- **Version 4 — Consolidation**: tidy-up and a single unified node that runs the
whole pipeline, since most stage nodes carry little configuration. The granular
nodes stay available for debugging.
See the [Roadmap](../../wiki/Roadmap) for details.
@@ -165,3 +170,14 @@ guide first. Core rule: **never hardcode knowledge, and never break determinism.
## License
See [LICENSE](LICENSE).
---
## A note on AI assistance
This project — its code, tests, and documentation — is built with heavy help from
AI coding assistants, and is developed and maintained by a solo author. AI makes
mistakes: despite testing and review, parts of this project and its docs may be
inaccurate, out of date, or simply wrong. Please treat it as a good-faith best
effort rather than authoritative truth — **if the docs and the code disagree, the
code wins** — and open an issue when you spot something off.
+4 -1
View File
@@ -34,12 +34,13 @@ class ConfigurationNode:
{"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": 60, "min": 1, "max": 3600}),
"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": ","}),
@@ -64,6 +65,7 @@ class ConfigurationNode:
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,
@@ -90,6 +92,7 @@ class ConfigurationNode:
"allow_aliases": resolver_allow_aliases,
"expansion_enabled": resolver_expansion_enabled,
"max_expansion_depth": resolver_max_expansion_depth,
"include_nsfw": resolver_include_nsfw,
},
"validator": {"allow_unknown_fields": validator_allow_unknown_fields},
"prompt_builder": {
+3
View File
@@ -18,6 +18,7 @@ def default_inputs() -> dict:
"resolver_allow_aliases": True,
"resolver_expansion_enabled": True,
"resolver_max_expansion_depth": 8,
"resolver_include_nsfw": False,
"validator_allow_unknown_fields": False,
"prompt_target": "easy_illustrious",
"prompt_separator": ",",
@@ -48,6 +49,7 @@ def test_inputs_flow_into_config() -> None:
inputs = default_inputs()
inputs["analyzer_model"] = "mistral"
inputs["resolver_max_expansion_depth"] = 4
inputs["resolver_include_nsfw"] = True
inputs["validator_allow_unknown_fields"] = True
inputs["prompt_separator"] = " | "
inputs["debug_enabled"] = True
@@ -55,6 +57,7 @@ def test_inputs_flow_into_config() -> None:
config, _ = ConfigurationNode().run(**inputs)
assert config.analyzer.model == "mistral"
assert config.resolver.max_expansion_depth == 4
assert config.resolver.include_nsfw is True
assert config.validator.allow_unknown_fields is True
assert config.prompt_builder.separator == " | "
assert config.debug.enabled is True