Add compiler/builder/prompt_builder.py: build_prompts(category_map, config, logger=None) -> CompilerResult (data = tuple[PromptOutput]), per MASTER_SPEC §19. - Emits one output per canonical category (in order) plus reserved `negative` and `scene`; V1 always emits all 21 outputs for stable node sockets. - Tags joined with config.prompt_builder.separator (default comma, no surrounding whitespace, no trailing separator). Empty categories yield an empty string, never null/placeholder (§19.4). - Formatting only: no tag generation, removal, reordering, dedup, or escaping (§19.1, §19.5). Output names are the lowercase canonical category ids and `negative`/`scene`, mapping to the §19.2/§19.3 display names. Tests: output set (19 + 2 reserved), comma join, order preservation, empty strings, reserved-empty, custom separator, no-trailing-separator, no escaping, determinism. Co-authored-by: kunail0804 <kunail0804@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
51 lines
1.9 KiB
Python
51 lines
1.9 KiB
Python
"""The Prompt Builder (MASTER_SPEC §19).
|
|
|
|
Formats the Category Map into one plain UTF-8 Prompt Output per category, plus
|
|
the reserved ``negative`` and ``scene`` outputs. This stage is formatting only:
|
|
it never generates, removes, reorders, deduplicates, escapes, or optimizes tags
|
|
(§19.1, §19.5).
|
|
|
|
Version 1 always emits all 21 outputs (19 categories + 2 reserved) so the ComfyUI
|
|
node exposes a stable set of sockets. Empty categories yield an empty string —
|
|
never null or placeholder text (§19.4). Output names are the lowercase canonical
|
|
category identifiers (and ``negative`` / ``scene``), corresponding one-to-one to
|
|
the §19.2/§19.3 display names.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from compiler.common.categories import CANONICAL_CATEGORIES
|
|
from compiler.common.config import Config
|
|
from compiler.common.log import StructuredLogger
|
|
from compiler.common.result import CompilerResult
|
|
from schemas.models import CategoryMap, PromptOutput
|
|
|
|
# Reserved outputs, always emitted empty in V1 (nothing populates them).
|
|
RESERVED_OUTPUTS: tuple[str, ...] = ("negative", "scene")
|
|
|
|
|
|
def build_prompts(
|
|
category_map: CategoryMap,
|
|
config: Config,
|
|
logger: StructuredLogger | None = None,
|
|
) -> CompilerResult:
|
|
"""Format a Category Map into the final Prompt Outputs.
|
|
|
|
Returns:
|
|
A CompilerResult whose ``data`` is the tuple of :class:`PromptOutput`:
|
|
one per canonical category (in order) followed by the reserved outputs.
|
|
"""
|
|
separator = config.prompt_builder.separator
|
|
|
|
outputs: list[PromptOutput] = []
|
|
for category in CANONICAL_CATEGORIES:
|
|
value = separator.join(tag.tag for tag in category_map.tags_for(category))
|
|
outputs.append(PromptOutput(name=category, value=value))
|
|
|
|
for reserved in RESERVED_OUTPUTS:
|
|
outputs.append(PromptOutput(name=reserved, value=""))
|
|
|
|
if logger is not None:
|
|
logger.basic("prompts_built", outputs=len(outputs))
|
|
return CompilerResult(data=tuple(outputs))
|