Add dialogue frame selection
This commit is contained in:
parent
3a52cf3517
commit
2b78cd1179
15 changed files with 272 additions and 24 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
106
generate/dialogue.py
Normal 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
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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": ["ω"]}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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"]}
|
||||
|
|
|
|||
|
|
@ -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
72
tests/test_dialogue.py
Normal 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
|
||||
Loading…
Reference in a new issue