Add dialogue frame selection

This commit is contained in:
Shay 2026-05-13 20:19:21 -07:00
parent 3a52cf3517
commit 2b78cd1179
15 changed files with 272 additions and 24 deletions

View file

@ -21,4 +21,4 @@ def propagate_step(state: FieldState, V) -> FieldState:
Returns a new FieldState one step forward on the manifold.
"""
new_F = versor_apply(V, state.F)
return FieldState(F=new_F, node=state.node, step=state.step + 1)
return FieldState(F=new_F, node=state.node, step=state.step + 1, holonomy=state.holonomy)

View file

@ -5,14 +5,28 @@ from .proposition import (
PropositionFrame,
propose,
)
from .dialogue import (
DialogueRole,
DialogueTurn,
blade_alignment,
classify_dialogue_blade,
propose_dialogue,
trajectory_blade,
)
from .stream import generate, agenerate
__all__ = [
"DialogueRole",
"DialogueTurn",
"FrameRegistry",
"FrameSlot",
"Proposition",
"PropositionFrame",
"agenerate",
"blade_alignment",
"classify_dialogue_blade",
"generate",
"propose",
"propose_dialogue",
"trajectory_blade",
]

106
generate/dialogue.py Normal file
View file

@ -0,0 +1,106 @@
"""
Dialogue move selection from proposition relation blades.
The proposition layer constructs X_prompt ^ X_field. Dialogue reads that
grade-2 relation against the prior relation blade and chooses the next move
kind: assertion, elaboration, question/contrast, or refutation.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Literal
import numpy as np
from algebra.cga import cga_inner, outer_product
from field.state import FieldState
from generate.proposition import FrameRegistry, Proposition, propose
DialogueRole = Literal["assert", "elaborate", "question", "refute"]
_PARALLEL_THRESHOLD = 0.35
_ANTI_PARALLEL_THRESHOLD = -0.35
_ORTHOGONAL_THRESHOLD = 0.20
@dataclass(frozen=True, slots=True)
class DialogueTurn:
proposition: Proposition
outer_product_blade: np.ndarray = field(repr=False)
def __post_init__(self) -> None:
blade = np.asarray(self.outer_product_blade, dtype=np.float32).copy()
object.__setattr__(self, "outer_product_blade", blade)
def blade_alignment(blade: np.ndarray, reference: np.ndarray) -> float:
"""
Return signed orientation of two relation blades.
Positive values mean same plane orientation, near-zero values mean
orthogonal relation, and negative values mean the same relation reversed.
The scalar is derived from CORE's algebraic inner product, not an external
approximate metric.
"""
blade = np.asarray(blade, dtype=np.float32)
reference = np.asarray(reference, dtype=np.float32)
blade_norm = abs(cga_inner(blade, blade)) ** 0.5
reference_norm = abs(cga_inner(reference, reference)) ** 0.5
if blade_norm < 1e-8 or reference_norm < 1e-8:
return 0.0
return float(-cga_inner(blade, reference) / (blade_norm * reference_norm))
def classify_dialogue_blade(
blade: np.ndarray,
reference_blade: np.ndarray | None = None,
) -> DialogueRole:
"""
Classify a relation blade as the next dialogue move.
With no prior relation the engine has no contrastive plane yet, so the
first move is an assertion. After that:
same orientation -> elaboration
near-orthogonal -> question/contrast
reversed orientation -> refutation
"""
if reference_blade is None:
return "assert"
alignment = blade_alignment(blade, reference_blade)
if alignment >= _PARALLEL_THRESHOLD:
return "elaborate"
if alignment <= _ANTI_PARALLEL_THRESHOLD:
return "refute"
if abs(alignment) <= _ORTHOGONAL_THRESHOLD:
return "question"
return "question"
def trajectory_blade(blades: tuple[np.ndarray, ...] | list[np.ndarray]) -> np.ndarray | None:
"""Fold a dialogue path into the running outer product of its blades."""
if not blades:
return None
running = np.asarray(blades[0], dtype=np.float32).copy()
for blade in blades[1:]:
running = outer_product(running, blade)
return running
def propose_dialogue(
field_state: FieldState,
vault,
vocab,
frame_registry: FrameRegistry,
reference_blade: np.ndarray | None = None,
) -> Proposition:
"""
Generate a proposition through a dialogue-role constrained frame choice.
"""
base = propose(field_state, None, vocab, frame_registry)
role = classify_dialogue_blade(base.relation, reference_blade)
frame = frame_registry.select_dialogue(base.relation, role)
role_registry = FrameRegistry((frame,))
proposition = propose(field_state, vault, vocab, role_registry)
return proposition

View file

@ -41,6 +41,7 @@ class PropositionFrame:
frame_id: str
language: str
predicate_type: str
dialogue_role: str
slots: tuple[FrameSlot, ...]
constraints: tuple[str, ...] = field(default_factory=tuple)
relation: np.ndarray = field(default_factory=lambda: np.zeros(32, dtype=np.float32))
@ -113,6 +114,13 @@ class FrameRegistry:
relation = np.asarray(relation, dtype=np.float32)
return max(self._frames, key=lambda frame: cga_inner(relation, frame.relation))
def select_dialogue(self, relation: np.ndarray, dialogue_role: str) -> PropositionFrame:
relation = np.asarray(relation, dtype=np.float32)
candidates = tuple(frame for frame in self._frames if frame.dialogue_role == dialogue_role)
if not candidates:
candidates = self._frames
return max(candidates, key=lambda frame: cga_inner(relation, frame.relation))
def __iter__(self):
return iter(self._frames)
@ -193,6 +201,7 @@ def _parse_frame(payload: dict, vocab) -> PropositionFrame:
frame_id=payload["frame_id"],
language=payload["language"],
predicate_type=payload["predicate_type"],
dialogue_role=payload.get("dialogue_role", "assert"),
slots=slots,
constraints=tuple(payload.get("constraints", ())),
relation=relation,

View file

@ -6,3 +6,6 @@
{"entry_id": "grc-006", "surface": "πνεῦμα", "lemma": "πνεῦμα", "language": "grc", "pos": "NOUN", "morphology_id": "grc-morph-006", "semantic_domains": ["logos.pneuma.breath", "logos.core", "breath", "existence.animating", "logos.presence"], "morphology_tags": ["noun", "neuter", "nominative", "singular", "third_declension"]}
{"entry_id": "grc-007", "surface": "κτίζω", "lemma": "κτίζω", "language": "grc", "pos": "VERB", "morphology_id": "grc-morph-007", "semantic_domains": ["logos.ktizo.formation", "logos.core", "formation", "logos.genesis", "divine.agency"], "morphology_tags": ["verb", "present", "active", "indicative", "first_person", "singular"]}
{"entry_id": "grc-008", "surface": "λαμβάνω", "lemma": "λαμβάνω", "language": "grc", "pos": "VERB", "morphology_id": "grc-morph-008", "semantic_domains": ["logos.reception", "relation.shared", "epistemic.grasp", "divine.agency"], "morphology_tags": ["verb", "present", "active", "indicative", "first_person", "singular"]}
{"entry_id": "grc-009", "surface": "ἀποκρίνομαι", "lemma": "ἀποκρίνομαι", "language": "grc", "pos": "VERB", "morphology_id": "grc-morph-009", "semantic_domains": ["response", "communication.articulation", "relation.dialogic", "knowledge.ground"], "morphology_tags": ["verb", "present", "middle", "indicative", "first_person", "singular", "deponent"]}
{"entry_id": "grc-010", "surface": "λέγω", "lemma": "λέγω", "language": "grc", "pos": "VERB", "morphology_id": "grc-morph-010", "semantic_domains": ["speech", "logos.utterance", "communication.articulation", "relation.dialogic"], "morphology_tags": ["verb", "present", "active", "indicative", "first_person", "singular"]}
{"entry_id": "grc-011", "surface": "ἐρωτάω", "lemma": "ἐρωτάω", "language": "grc", "pos": "VERB", "morphology_id": "grc-morph-011", "semantic_domains": ["question", "communication.articulation", "relation.dialogic", "epistemic.seeking"], "morphology_tags": ["verb", "present", "active", "indicative", "first_person", "singular"]}

View file

@ -6,7 +6,7 @@
"normalization_policy": "unitize_versor",
"source_manifest": "grc_logos_micro_v1.lexicon.jsonl",
"determinism_class": "D0",
"checksum": "580ce1b32872fb39768ea0398f6d5d77bab2b9d04a1e808cdc1ecc09fbcb959b",
"checksum": "9a66f98f2adcbecb907b9afb2d6bf0de5b8c200b3b67df3cc548bf29d9aec935",
"version": "1.0.0",
"gate_engaged": true,
"oov_policy": "fail_closed"

View file

@ -6,3 +6,6 @@
{"morphology_id": "grc-morph-006", "surface": "\u03c0\u03bd\u03b5\u1fe6\u03bc\u03b1", "lemma": "\u03c0\u03bd\u03b5\u1fe6\u03bc\u03b1", "language": "grc", "root": "\u03c0\u03bd\u03b5\u03c5", "prefix_chain": [], "stem": "\u03c0\u03bd\u03b5\u03c5\u03bc\u03b1\u03c4", "inflection": {"pos": "noun", "case": "nominative", "gender": "neuter", "number": "singular", "declension": "third"}, "suffix_chain": []}
{"morphology_id": "grc-morph-007", "surface": "\u03ba\u03c4\u03af\u03b6\u03c9", "lemma": "\u03ba\u03c4\u03af\u03b6\u03c9", "language": "grc", "root": "\u03ba\u03c4\u03b9\u03b4", "prefix_chain": [], "stem": "\u03ba\u03c4\u03b9\u03b6", "inflection": {"pos": "verb", "tense": "present", "voice": "active", "mood": "indicative", "person": "first", "number": "singular"}, "suffix_chain": ["\u03c9"]}
{"morphology_id": "grc-morph-008", "surface": "λαμβάνω", "lemma": "λαμβάνω", "language": "grc", "root": "λαβ", "prefix_chain": [], "stem": "λαμβαν", "inflection": {"pos": "verb", "tense": "present", "voice": "active", "mood": "indicative", "person": "first", "number": "singular"}, "suffix_chain": ["ω"]}
{"morphology_id": "grc-morph-009", "surface": "ἀποκρίνομαι", "lemma": "ἀποκρίνομαι", "language": "grc", "root": "κριν", "prefix_chain": ["ἀπο"], "stem": "κριν", "inflection": {"pos": "verb", "tense": "present", "voice": "middle", "mood": "indicative", "person": "first", "number": "singular", "deponency": "deponent"}, "suffix_chain": ["ομαι"]}
{"morphology_id": "grc-morph-010", "surface": "λέγω", "lemma": "λέγω", "language": "grc", "root": "λεγ", "prefix_chain": [], "stem": "λεγ", "inflection": {"pos": "verb", "tense": "present", "voice": "active", "mood": "indicative", "person": "first", "number": "singular"}, "suffix_chain": ["ω"]}
{"morphology_id": "grc-morph-011", "surface": "ἐρωτάω", "lemma": "ἐρωτάω", "language": "grc", "root": "ἐρωτα", "prefix_chain": [], "stem": "ἐρωτα", "inflection": {"pos": "verb", "tense": "present", "voice": "active", "mood": "indicative", "person": "first", "number": "singular"}, "suffix_chain": ["ω"]}

View file

@ -3,7 +3,7 @@
"title": "CORE Frame Record",
"description": "A syntactic-semantic frame template defining participant structure for a predicate type.",
"type": "object",
"required": ["frame_id", "language", "predicate_type", "slots"],
"required": ["frame_id", "language", "predicate_type", "dialogue_role", "slots"],
"properties": {
"frame_id": {
"type": "string",
@ -17,6 +17,11 @@
"type": "string",
"description": "The predicate class this frame serves. Examples: copular, transitive, ditransitive, existential, causative."
},
"dialogue_role": {
"type": "string",
"enum": ["assert", "elaborate", "question", "refute"],
"description": "Dialogue move kind selected from relation-blade geometry."
},
"slots": {
"type": "array",
"items": {
@ -34,6 +39,9 @@
"constraints": {
"type": "array",
"items": { "type": "string" }
},
"notes": {
"type": "string"
}
},
"additionalProperties": false

View file

@ -1,4 +1,4 @@
{"frame_id":"el:copular-basic","language":"el","predicate_type":"copular","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate_nominative","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement","predicate-nominative-case"]}
{"frame_id":"el:colwell-construction","language":"el","predicate_type":"copular-qualitative","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"anarthrous_predicate","required":true,"semantic_role":"qualitative-attribute","agreement_target":null}],"constraints":["predicate-precedes-copula","predicate-is-anarthrous","subject-has-article"],"notes":"Colwell's Rule: a definite predicate nominative that precedes the copula is anarthrous. John 1:1c is the canonical instance."}
{"frame_id":"el:existential-imperfect","language":"el","predicate_type":"existential-continuous","slots":[{"name":"existent","required":true,"semantic_role":"continuous-being","agreement_target":"verb"},{"name":"locus","required":false,"semantic_role":"sphere","agreement_target":null}],"constraints":["verb-must-be-imperfect"],"notes":"The imperfect of eimi signals durative pre-existent being. Distinct from aorist which would signal point-in-time origin."}
{"frame_id":"el:relational-pros","language":"el","predicate_type":"relational-directional","slots":[{"name":"figure","required":true,"semantic_role":"oriented-entity","agreement_target":null},{"name":"ground","required":true,"semantic_role":"orientation-target","agreement_target":null}],"constraints":["pros-governs-accusative"]}
{"frame_id":"el:copular-basic","language":"el","predicate_type":"copular","dialogue_role":"assert","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate_nominative","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement","predicate-nominative-case"]}
{"frame_id":"el:colwell-construction","language":"el","predicate_type":"copular-qualitative","dialogue_role":"elaborate","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"anarthrous_predicate","required":true,"semantic_role":"qualitative-attribute","agreement_target":null}],"constraints":["predicate-precedes-copula","predicate-is-anarthrous","subject-has-article"],"notes":"Colwell's Rule: a definite predicate nominative that precedes the copula is anarthrous. John 1:1c is the canonical instance."}
{"frame_id":"el:existential-imperfect","language":"el","predicate_type":"existential-continuous","dialogue_role":"question","slots":[{"name":"existent","required":true,"semantic_role":"continuous-being","agreement_target":"verb"},{"name":"locus","required":false,"semantic_role":"sphere","agreement_target":null}],"constraints":["verb-must-be-imperfect"],"notes":"The imperfect of eimi signals durative pre-existent being. Distinct from aorist which would signal point-in-time origin."}
{"frame_id":"el:relational-pros","language":"el","predicate_type":"relational-directional","dialogue_role":"refute","slots":[{"name":"figure","required":true,"semantic_role":"oriented-entity","agreement_target":null},{"name":"ground","required":true,"semantic_role":"orientation-target","agreement_target":null}],"constraints":["pros-governs-accusative"]}

View file

@ -1,3 +1,4 @@
{"frame_id":"en:copular-basic","language":"en","predicate_type":"copular","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement"]}
{"frame_id":"en:existential-basic","language":"en","predicate_type":"existential","slots":[{"name":"pivot","required":true,"semantic_role":"existent","agreement_target":"verb"},{"name":"location","required":false,"semantic_role":"locus","agreement_target":null}],"constraints":[]}
{"frame_id":"en:accompaniment-prep","language":"en","predicate_type":"prepositional","slots":[{"name":"figure","required":true,"semantic_role":"accompanier","agreement_target":null},{"name":"ground","required":true,"semantic_role":"accompanied","agreement_target":null}],"constraints":[]}
{"frame_id":"en:copular-basic","language":"en","predicate_type":"copular","dialogue_role":"assert","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement"]}
{"frame_id":"en:existential-basic","language":"en","predicate_type":"existential","dialogue_role":"elaborate","slots":[{"name":"pivot","required":true,"semantic_role":"existent","agreement_target":"verb"},{"name":"location","required":false,"semantic_role":"locus","agreement_target":null}],"constraints":[]}
{"frame_id":"en:accompaniment-prep","language":"en","predicate_type":"prepositional","dialogue_role":"question","slots":[{"name":"figure","required":true,"semantic_role":"accompanier","agreement_target":null},{"name":"ground","required":true,"semantic_role":"accompanied","agreement_target":null}],"constraints":[]}
{"frame_id":"en:negated-copular","language":"en","predicate_type":"copular-negated","dialogue_role":"refute","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate","required":true,"semantic_role":"rejected-attribute","agreement_target":null}],"constraints":["negation-precedes-predicate"]}

View file

@ -1,4 +1,4 @@
{"frame_id":"el:copular-basic","language":"el","predicate_type":"copular","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate_nominative","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement","predicate-nominative-case"]}
{"frame_id":"el:colwell-construction","language":"el","predicate_type":"copular-qualitative","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"anarthrous_predicate","required":true,"semantic_role":"qualitative-attribute","agreement_target":null}],"constraints":["predicate-precedes-copula","predicate-is-anarthrous","subject-has-article"],"notes":"Colwell's Rule: a definite predicate nominative that precedes the copula is anarthrous. John 1:1c is the canonical instance."}
{"frame_id":"el:existential-imperfect","language":"el","predicate_type":"existential-continuous","slots":[{"name":"existent","required":true,"semantic_role":"continuous-being","agreement_target":"verb"},{"name":"locus","required":false,"semantic_role":"sphere","agreement_target":null}],"constraints":["verb-must-be-imperfect"],"notes":"The imperfect of eimi signals durative pre-existent being. Distinct from aorist which would signal point-in-time origin."}
{"frame_id":"el:relational-pros","language":"el","predicate_type":"relational-directional","slots":[{"name":"figure","required":true,"semantic_role":"oriented-entity","agreement_target":null},{"name":"ground","required":true,"semantic_role":"orientation-target","agreement_target":null}],"constraints":["pros-governs-accusative"]}
{"frame_id":"el:copular-basic","language":"el","predicate_type":"copular","dialogue_role":"assert","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"predicate_nominative","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["subject-verb-agreement","predicate-nominative-case"]}
{"frame_id":"el:colwell-construction","language":"el","predicate_type":"copular-qualitative","dialogue_role":"elaborate","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":"verb"},{"name":"anarthrous_predicate","required":true,"semantic_role":"qualitative-attribute","agreement_target":null}],"constraints":["predicate-precedes-copula","predicate-is-anarthrous","subject-has-article"],"notes":"Colwell's Rule: a definite predicate nominative that precedes the copula is anarthrous. John 1:1c is the canonical instance."}
{"frame_id":"el:existential-imperfect","language":"el","predicate_type":"existential-continuous","dialogue_role":"question","slots":[{"name":"existent","required":true,"semantic_role":"continuous-being","agreement_target":"verb"},{"name":"locus","required":false,"semantic_role":"sphere","agreement_target":null}],"constraints":["verb-must-be-imperfect"],"notes":"The imperfect of eimi signals durative pre-existent being. Distinct from aorist which would signal point-in-time origin."}
{"frame_id":"el:relational-pros","language":"el","predicate_type":"relational-directional","dialogue_role":"refute","slots":[{"name":"figure","required":true,"semantic_role":"oriented-entity","agreement_target":null},{"name":"ground","required":true,"semantic_role":"orientation-target","agreement_target":null}],"constraints":["pros-governs-accusative"]}

View file

@ -7,3 +7,6 @@
{"lemma_id":"grc:eimi","language":"grc","script_form":"εἰμί","root":"εσ","stem":"εἰμ","transliteration":"eimi","gloss_seed":"be","pos":"verb","morph_class":"athematic","semantic_family":"existence","field_hooks":["existence.state","existence.state.identity","existence.state.continuous"],"readback_priority":1}
{"lemma_id":"grc:theos","language":"grc","script_form":"θεός","root":"θε","stem":"θεο","transliteration":"theos","gloss_seed":"God","pos":"noun","morph_class":"second-declension-masculine","semantic_family":"divine","field_hooks":["divine.identity","divine.presence"],"readback_priority":1}
{"lemma_id":"grc:pros","language":"grc","script_form":"πρός","root":null,"stem":"πρός","transliteration":"pros","gloss_seed":"with","pos":"preposition","morph_class":"regular","semantic_family":"relation","field_hooks":["relation.directional","relation.accompaniment","relation.presence-toward"],"readback_priority":1}
{"lemma_id":"grc:apokrinomai","language":"grc","script_form":"ἀποκρίνομαι","root":"κριν","stem":"κριν","transliteration":"apokrinomai","gloss_seed":"answer","pos":"verb","morph_class":"present-middle-deponent-indicative","semantic_family":"response","field_hooks":["response","communication.articulation","relation.dialogic","knowledge.ground"],"readback_priority":1}
{"lemma_id":"grc:lego","language":"grc","script_form":"λέγω","root":"λεγ","stem":"λεγ","transliteration":"lego","gloss_seed":"say","pos":"verb","morph_class":"present-active-indicative","semantic_family":"speech","field_hooks":["speech","logos.utterance","communication.articulation","relation.dialogic"],"readback_priority":1}
{"lemma_id":"grc:erotao","language":"grc","script_form":"ἐρωτάω","root":"ἐρωτα","stem":"ἐρωτα","transliteration":"erotao","gloss_seed":"ask","pos":"verb","morph_class":"present-active-indicative","semantic_family":"question","field_hooks":["question","communication.articulation","relation.dialogic","epistemic.seeking"],"readback_priority":1}

View file

@ -1,4 +1,4 @@
{"frame_id":"he:verbal-basic","language":"he","predicate_type":"verbal","slots":[{"name":"subject","required":true,"semantic_role":"agent","agreement_target":"verb"},{"name":"object","required":false,"semantic_role":"patient","agreement_target":null}],"constraints":["verb-subject-agreement","direct-object-requires-et"]}
{"frame_id":"he:copular-implicit","language":"he","predicate_type":"copular","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":null},{"name":"predicate","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["copula-may-be-absent"]}
{"frame_id":"he:construct-chain","language":"he","predicate_type":"genitive-relational","slots":[{"name":"head","required":true,"semantic_role":"possessed","agreement_target":null},{"name":"genitive","required":true,"semantic_role":"possessor-or-qualifier","agreement_target":null}],"constraints":["head-must-be-construct-state","no-article-on-construct-head"]}
{"frame_id":"he:creation-act","language":"he","predicate_type":"creation","slots":[{"name":"creator","required":true,"semantic_role":"divine-agent","agreement_target":"verb"},{"name":"created","required":true,"semantic_role":"patient","agreement_target":null}],"constraints":["bara-qal-subject-must-be-divine"]}
{"frame_id":"he:verbal-basic","language":"he","predicate_type":"verbal","dialogue_role":"assert","slots":[{"name":"subject","required":true,"semantic_role":"agent","agreement_target":"verb"},{"name":"object","required":false,"semantic_role":"patient","agreement_target":null}],"constraints":["verb-subject-agreement","direct-object-requires-et"]}
{"frame_id":"he:copular-implicit","language":"he","predicate_type":"copular","dialogue_role":"elaborate","slots":[{"name":"subject","required":true,"semantic_role":"carrier","agreement_target":null},{"name":"predicate","required":true,"semantic_role":"attribute","agreement_target":null}],"constraints":["copula-may-be-absent"]}
{"frame_id":"he:construct-chain","language":"he","predicate_type":"genitive-relational","dialogue_role":"question","slots":[{"name":"head","required":true,"semantic_role":"possessed","agreement_target":null},{"name":"genitive","required":true,"semantic_role":"possessor-or-qualifier","agreement_target":null}],"constraints":["head-must-be-construct-state","no-article-on-construct-head"]}
{"frame_id":"he:creation-act","language":"he","predicate_type":"creation","dialogue_role":"refute","slots":[{"name":"creator","required":true,"semantic_role":"divine-agent","agreement_target":"verb"},{"name":"created","required":true,"semantic_role":"patient","agreement_target":null}],"constraints":["bara-qal-subject-must-be-divine"]}

View file

@ -12,13 +12,18 @@ field that entered the turn.
from __future__ import annotations
from field.state import FieldState
from vault.store import VaultStore
from persona.motor import PersonaMotor
from ingest.gate import inject
from generate.stream import generate
from generate.result import GenerationResult
import numpy as np
from algebra.backend import versor_apply
from algebra.cga import outer_product
from field.state import FieldState
from generate.dialogue import DialogueTurn
from generate.proposition import Proposition
from generate.result import GenerationResult
from generate.stream import generate
from ingest.gate import inject
from persona.motor import PersonaMotor
from vault.store import VaultStore
class SessionContext:
@ -28,6 +33,8 @@ class SessionContext:
self.vault = vault or VaultStore()
self.state: FieldState | None = None
self.turn: int = 0
self.dialogue_history: list[DialogueTurn] = []
self.running_dialogue_blade: np.ndarray | None = None
def ingest(self, tokens: list) -> FieldState:
"""Inject a prompt into the running field. Stores the user field in vault."""
@ -50,6 +57,28 @@ class SessionContext:
self.vault.store(self.state.F, {"turn": self.turn, "role": "user"})
return self.state
def record_dialogue(self, proposition: Proposition) -> DialogueTurn:
"""
Store a proposition as geometric dialogue state.
The transcript surface is deliberately not used as session memory here;
the retained object is the proposition paired with its relation blade.
"""
blade = proposition.relation
turn = DialogueTurn(proposition=proposition, outer_product_blade=blade)
self.dialogue_history.append(turn)
if self.running_dialogue_blade is None:
self.running_dialogue_blade = blade.copy()
else:
self.running_dialogue_blade = outer_product(self.running_dialogue_blade, blade)
return turn
@property
def last_dialogue_blade(self) -> np.ndarray | None:
if not self.dialogue_history:
return None
return self.dialogue_history[-1].outer_product_blade.copy()
def respond(self, max_tokens: int = 128) -> GenerationResult:
"""
Generate a response from current state and preserve the evolved field.

72
tests/test_dialogue.py Normal file
View file

@ -0,0 +1,72 @@
from __future__ import annotations
import numpy as np
from generate.dialogue import (
blade_alignment,
classify_dialogue_blade,
propose_dialogue,
)
from generate.proposition import FrameRegistry
from language_packs.compiler import load_mounted_packs
from session.context import SessionContext
def _dialogue_proposition(tokens: list[str], registry: FrameRegistry, vocab, reference=None):
session = SessionContext(vocab=vocab)
session.ingest(tokens)
generated = session.respond(max_tokens=3)
return propose_dialogue(generated.final_state, None, vocab, registry, reference)
def test_relation_blade_classifies_parallel_as_elaboration():
vocab = load_mounted_packs(
("en_minimal_v1", "he_logos_micro_v1", "grc_logos_micro_v1")
)
registry = FrameRegistry.from_pack("grc", vocab)
first = _dialogue_proposition(["light", "φῶς"], registry, vocab)
second = _dialogue_proposition(["φῶς", "אוֹר"], registry, vocab, first.relation)
assert classify_dialogue_blade(second.relation, first.relation) == "elaborate"
assert second.frame_id == "el:colwell-construction"
assert second.surface != first.surface
def test_two_turn_light_exchange_tracks_parallel_dialogue_trajectory():
vocab = load_mounted_packs(
("en_minimal_v1", "he_logos_micro_v1", "grc_logos_micro_v1")
)
registry = FrameRegistry.from_pack("grc", vocab)
session = SessionContext(vocab=vocab)
session.ingest(["light", "φῶς"])
first_generated = session.respond(max_tokens=3)
first = propose_dialogue(first_generated.final_state, None, vocab, registry)
first_turn = session.record_dialogue(first)
session.ingest(["light", "truth"])
second_generated = session.respond(max_tokens=3)
second = propose_dialogue(
second_generated.final_state,
None,
vocab,
registry,
session.last_dialogue_blade,
)
second_turn = session.record_dialogue(second)
random_turn = _dialogue_proposition(["truth", "λόγος"], registry, vocab)
second_alignment = blade_alignment(
second_turn.outer_product_blade,
first_turn.outer_product_blade,
)
random_alignment = blade_alignment(random_turn.relation, first_turn.outer_product_blade)
assert second.frame_id == "el:colwell-construction"
assert second.surface != first.surface
assert second_alignment > random_alignment
assert second_alignment > 0.35
assert len(session.dialogue_history) == 2
assert session.running_dialogue_blade is not None
assert np.linalg.norm(session.running_dialogue_blade) > 0.0