Files
M1kep-KepPromptLang/tests/test_parser.py
T
Claude 6fcd3b8a86 Port to current ComfyUI CLIP API and refactor
Rebase the DSL text encoder on comfy.sd1_clip.SDClipModel and
comfy.clip_model.CLIPTextModel_, replacing the old HuggingFace
transformers CLIPTextModel/CLIPTextTransformer/CLIPTextEmbeddings
subclasses (which are no longer how ComfyUI implements CLIP).

Structure:
- Merge lib/action/ into lib/actions/; drop lib/fun_clip_stuff.py
  and the bundled clip_config*.json (comfy ships its own)
- Convert all custom_nodes.KepPromptLang.* absolute imports to
  relative imports so installs via Manager work regardless of
  install directory name

Encoder:
- PromptLangSDClipModel overrides encode_token_weights to walk
  the segment/action tree, assemble [B, seq, hidden] embeds, and
  call self.transformer(None, mask, embeds=..., num_tokens=...)
- posScale / postPos are supported without patching the transformer
  by pre-baking the delta (modified - default) into embeds, so the
  transformer's inline add yields the modified position embedding
- Drop the unused empty-baseline batch that was prepended and then
  sliced off; halves the per-encode forward pass for single prompts

Actions:
- Fix copy-pasted broken __repr__ / depth_repr across sum/diff/avg/
  slerp that referenced fields that didn't exist
- Fix NameError in AverageAction._validate_args (start_arg_token_length)
- Fix class-level mutable state in RandAction
- Unify _parse_scalar / _parse_scalar_weight / _parse_int into a
  single parse_numeric_arg helper in action_utils.py
- Share add_with_broadcast between SumAction and DiffAction
- Convert PostModifiers from TypedDict to dataclass (attribute
  access catches typos that .get() on string keys hides)

Drop the two _exp-pooler / _exp-pooledAvg actions: they recursively
invoked the HF CLIPTextTransformer and would need a rework to fit
the current CLIPTextModel_ interface. They were experimental and
not documented as stable.

BuildGif node:
- Collapse the 10-positional-arg _save_* helpers onto a small
  _SaveContext dataclass
- Stop mutating the input arg semantics (split_every_val was
  reassigned to len(images) when -1)

Tests:
- Add pytest suite covering the parser (grammar, nesting, errors)
  and every action (embedding math, shape, modifier payloads)
- conftest stubs ComfyUI at collection time so tests don't need a
  real ComfyUI install

Drop the broken test_files/ scripts (CI helpers, not a test suite)
and regenerate the README from tools/build_docs.py.
2026-04-12 05:05:58 +00:00

59 lines
2.0 KiB
Python

from KepPromptLang.lib.actions.diff import DiffAction
from KepPromptLang.lib.actions.norm import NormAction
from KepPromptLang.lib.actions.sum import SumAction
from KepPromptLang.lib.parser import PromptParser
from KepPromptLang.lib.parser.prompt_segment import PromptSegment
from KepPromptLang.lib.parser.transformer import PromptTransformer
def parse(text, tokenizer):
tree = PromptParser.parse(text)
return PromptTransformer(tokenizer).transform(tree)
def test_plain_words_become_segments(tokenizer):
result = parse("hello world", tokenizer)
items = result.children
assert len(items) == 2
assert all(isinstance(i, PromptSegment) for i in items)
assert items[0].text == "hello"
assert items[1].text == "world"
def test_sum_action_parses(tokenizer):
action = parse("sum(king|man|woman)", tokenizer)
items = action.children if hasattr(action, "children") else [action]
assert len(items) == 1
assert isinstance(items[0], SumAction)
assert len(items[0].all_args) == 3
def test_nested_actions(tokenizer):
action = parse("sum(diff(king|man)|woman)", tokenizer)
items = action.children if hasattr(action, "children") else [action]
outer = items[0]
assert isinstance(outer, SumAction)
inner = outer.all_args[0][0]
assert isinstance(inner, DiffAction)
def test_norm_single_arg(tokenizer):
action = parse("norm(cat)", tokenizer)
items = action.children if hasattr(action, "children") else [action]
assert isinstance(items[0], NormAction)
def test_quoted_string(tokenizer):
result = parse('"hello world"', tokenizer)
items = result.children if hasattr(result, "children") else [result]
assert isinstance(items[0], PromptSegment)
assert items[0].text == "hello world"
def test_unknown_action_errors(tokenizer):
import pytest
from lark.exceptions import VisitError
with pytest.raises((ValueError, VisitError), match="not found in registry"):
parse("nonexistentAction(cat)", tokenizer)