Files
87d9525176 feat(resolver): opt-in semantic nearest-neighbour fallback (#34) (#140)
Closes #114, #115, #116, #117.

- Deterministic, offline, dependency-free char-ngram embedding backend + config
  section (semantic.enabled default false, min_similarity, backend).
- Committed embedding index (data/kb_embedding_index.json) mapping vectors to real
  KB entry ids only; built by scripts/build_embedding_index.py, loaded once/cached.
- Resolver falls back to the nearest entry on a deterministic miss when enabled,
  accepting >= min_similarity and emitting SC0020; NSFW-gated neighbours are
  skipped. Disabled => byte-identical to before.
- Guarantee tests: deterministic lookup always wins (exact hit never queries the
  index), fallback emits only KB tags (no invention), identical input => identical
  output. SC0020 registered.

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

126 lines
3.8 KiB
Python

"""Tests for the semantic nearest-neighbour fallback (issue #116, epic #34)."""
from __future__ import annotations
from compiler.common.config import Config
from compiler.common.embedding_index import EmbeddingIndex, build_index_rows
from compiler.common.knowledge_base import KnowledgeBase, KnowledgeBaseEntry
from compiler.resolver.illustrious_resolver import resolve_scene
from schemas.models import Scene
def entry(id_, tags, category="clothing", aliases=(), rating="general"):
return KnowledgeBaseEntry(
id=id_, tags=tuple(tags), category=category, aliases=tuple(aliases), rating=rating
)
def kb() -> KnowledgeBase:
return KnowledgeBase(
[
entry("thighhighs", ["thighhighs"], aliases=["stockings"]),
entry("dress", ["dress"]),
entry("pussy", ["pussy"], category="body", rating="explicit"),
]
)
def index(knowledge: KnowledgeBase) -> EmbeddingIndex:
return EmbeddingIndex(build_index_rows(knowledge))
def scene_with(concept: str) -> Scene:
return Scene.from_json(
{
"characters": [
{
"id": 0,
"identity": [concept],
"appearance": [],
"clothing": [],
"accessories": [],
"pose": [],
"expression": [],
"actions": [],
}
],
"interactions": [],
"objects": [],
"environment": [],
"camera": [],
"lighting": [],
"metadata": {"schema_version": "1.0"},
}
)
def semantic_config(min_similarity: float = 0.5, include_nsfw: bool = False) -> Config:
return Config.from_json(
{
"semantic": {"enabled": True, "min_similarity": min_similarity},
"resolver": {"include_nsfw": include_nsfw},
}
)
def tags(result):
return [t.tag for t in result.data]
def codes(messages):
return [m.code for m in messages]
def test_near_concept_resolves_via_fallback_and_emits_sc0020() -> None:
knowledge = kb()
result = resolve_scene(
scene_with("thighighs"), knowledge, semantic_config(), embedding_index=index(knowledge)
)
assert tags(result) == ["thighhighs"]
assert "SC0020" in codes(result.warnings)
def test_far_concept_still_drops() -> None:
knowledge = kb()
result = resolve_scene(
scene_with("quantum flux inverter"),
knowledge,
semantic_config(min_similarity=0.6),
embedding_index=index(knowledge),
)
assert tags(result) == []
assert "SC0001" in codes(result.warnings)
assert "SC0020" not in codes(result.warnings)
def test_disabled_feature_is_byte_identical() -> None:
knowledge = kb()
disabled = resolve_scene(scene_with("thighighs"), knowledge, Config())
with_index_but_disabled = resolve_scene(
scene_with("thighighs"), knowledge, Config(), embedding_index=index(knowledge)
)
assert tags(disabled) == tags(with_index_but_disabled) == []
assert "SC0020" not in codes(with_index_but_disabled.warnings)
def test_deterministic_lookup_always_wins() -> None:
knowledge = kb()
result = resolve_scene(
scene_with("dress"), knowledge, semantic_config(), embedding_index=index(knowledge)
)
assert tags(result) == ["dress"]
assert "SC0020" not in codes(result.warnings) # exact hit, fallback not used
def test_gated_nsfw_neighbour_is_not_used() -> None:
knowledge = kb()
# "pusy" is closest to the explicit "pussy" entry, but NSFW is gated off.
result = resolve_scene(
scene_with("pusy"),
knowledge,
semantic_config(include_nsfw=False),
embedding_index=index(knowledge),
)
assert "SC0020" not in codes(result.warnings)
assert "SC0001" in codes(result.warnings)