ncm_semantic_triggers

NCM Semantic Trigger Matcher — cosine-similarity-based emotion detection.

Replaces the exact-word scan_text_for_triggers with embedding-based semantic matching. Each emotion in reality_marble_recursion_index.yaml gets:

  1. An embedding of its affect description string.

  2. LLM-generated colloquial phrases a person might say when feeling that emotion, each also embedded.

  3. A mean_vec (L2-normalized centroid of all above embeddings) that serves as the emotion’s semantic fingerprint for matching.

At runtime, the incoming text is embedded once and dot-producted against every cached mean_vec. Emotions above MATCH_THRESHOLD are returned as (emotion_name, delta_vector) pairs, capped at TOP_N.

Falls back to an empty list (caller should use scan_text_for_triggers from ncm_delta_parser) when no embeddings are ready yet.

Redis key: ncm:trigger_embed:{sha256_hex_of_emotion_name} Redis value: JSON object — see below

class ncm_semantic_triggers.SemanticTriggerMatcher(redis_client=None, api_key=None, openrouter_client=None)[source]

Bases: object

Cosine-similarity emotion trigger detector.

Parameters:
  • redis_client – An redis.asyncio.Redis instance. May be None.

  • api_key (Optional[str]) – OpenRouter API key. When None the matcher is a no-op and find_triggers always returns an empty list.

  • openrouter_client (OpenRouterClient | None)

__init__(redis_client=None, api_key=None, openrouter_client=None)[source]

Initialize the instance.

Parameters:
  • redis_client – Redis connection client.

  • api_key (Optional[str]) – The api key value.

  • openrouter_client (OpenRouterClient | None) – Shared OpenRouterClient for connection pooling and batch embedding. Falls back to direct HTTP when None.

Return type:

None

async find_triggers(text, threshold=0.55, top_n=6)[source]

Embed text and return top-N emotions above threshold.

Returns list of (emotion_name, delta_vector) tuples, sorted by descending similarity. Returns an empty list if no embeddings are ready (caller should fall back to exact matching).

Return type:

List[Tuple[str, Dict[str, float]]]

Parameters:
async ensure_all_cached()[source]

Build the full emotion embedding index.

Return type:

None

For each emotion in the recursion index:
  1. Load from Redis if already cached.

  2. Otherwise embed the affect string + generate/embed 5 variant trigger phrases, then store in Redis.

Designed to run once at startup as a background task. Safe to call multiple times — already-cached emotions are skipped.