From d781ba71dbf46fd2ed31f8b92185ec7da1ea7389 Mon Sep 17 00:00:00 2001 From: Shay Date: Wed, 13 May 2026 13:16:48 -0700 Subject: [PATCH] Avoid identity stalls in generation loop --- generate/stream.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/generate/stream.py b/generate/stream.py index 9f7a9a5b..a093203f 100644 --- a/generate/stream.py +++ b/generate/stream.py @@ -1,7 +1,7 @@ """ Generation loop — token streaming from the versor manifold. -Every token: nearest word to current F via CGA inner product. +Every token: nearest non-current word to current F via CGA inner product. Every step: F <- versor_apply(V, F) where V = word_transition_rotor(A, B). Architectural boundaries enforced here: @@ -12,7 +12,7 @@ Architectural boundaries enforced here: structurally by versor_apply() and the closed algebra. No confidence gates. No IDK fallback. No attractor clamping. -F is always on the manifold. nearest() is always exact. +F is always on the manifold. nearest() is exact. """ from __future__ import annotations @@ -22,6 +22,18 @@ from algebra.rotor import word_transition_rotor from generate.result import GenerationResult +def _nearest_next(vocab, F_voiced, current_node: int) -> tuple[str, int]: + """ + Select the nearest non-current vocabulary point when possible. + + Allowing the current node to win makes V = transition(A, A), which is an + identity-like transition and can stall generation forever on one token. + VocabManifold already exposes exclude_idx for this exact seam. + """ + exclude_idx = current_node if len(vocab) > 1 else -1 + return vocab.nearest(F_voiced, exclude_idx=exclude_idx) + + def generate( state: FieldState, vocab, @@ -34,7 +46,7 @@ def generate( Loop: 1. Apply persona motor to current field - 2. Find nearest vocab node via CGA inner product + 2. Find nearest non-current vocab node via CGA inner product 3. Emit token 4. Build transition rotor: V = word_transition_rotor(A, B) where A = versor at current node, B = versor at nearest node @@ -50,7 +62,7 @@ def generate( for _ in range(max_tokens): F_voiced = persona.apply(current.F) - word, word_idx = vocab.nearest(F_voiced) + word, word_idx = _nearest_next(vocab, F_voiced, current.node) tokens.append(word) if record_trajectory: @@ -89,7 +101,7 @@ async def agenerate( current = state for _ in range(max_tokens): F_voiced = persona.apply(current.F) - word, word_idx = vocab.nearest(F_voiced) + word, word_idx = _nearest_next(vocab, F_voiced, current.node) yield word A = vocab.get_versor_at(current.node)