Adding Festinger with wordnet
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
Scenario A — full integration walk-through (in-memory only, no DB required).
|
||||
|
||||
Demonstrates the degenerate parent_id=dim_id pattern and the misclassification
|
||||
collision pipeline from initial seed through recollection rendering.
|
||||
|
||||
Story:
|
||||
1. World model receives coarse early knowledge: "michigan is in usa"
|
||||
→ stored as michigan ISPART usa in dim:usa (parent_id = dim_id)
|
||||
2. Agent sends prompt: "michigan is a state of USA"
|
||||
→ cue scanner extracts michigan ISA state in dim:usa
|
||||
3. Collision detected: existing ISPART vs incoming ISA in dim:usa → misclassification
|
||||
4. Recollection rendered with [usa?] marker while conflict is pending
|
||||
5. After resolution (simulated): fact moves to correct dimension
|
||||
→ michigan ISA state in dim:type
|
||||
→ michigan ISPART usa in dim:geography
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from festinger import cache
|
||||
from festinger.cache import SoasRow, UrdEdge
|
||||
from festinger.cue_scanner import scan_cues
|
||||
from festinger.recollection import (
|
||||
build_recollection_block, inject_recollection, render_hit, query_edges
|
||||
)
|
||||
from festinger.urd_writer import InsertRequest, insert_urd_edge, CollisionInfo
|
||||
from tests.helpers import reset_cache, add_soas, add_urd
|
||||
|
||||
|
||||
def make_mock_pool():
|
||||
conn = AsyncMock()
|
||||
conn.execute = AsyncMock(return_value="INSERT 0 1")
|
||||
conn.fetchrow = AsyncMock(return_value=None)
|
||||
conn.__aenter__ = AsyncMock(return_value=conn)
|
||||
conn.__aexit__ = AsyncMock(return_value=False)
|
||||
pool = MagicMock()
|
||||
pool.acquire = MagicMock(return_value=conn)
|
||||
return pool, conn
|
||||
|
||||
|
||||
class TestScenarioAIntegration:
|
||||
|
||||
def setup_method(self):
|
||||
reset_cache()
|
||||
# SOAS vocabulary
|
||||
self.michigan = add_soas(101, "michigan", saliency=1.5, novelty=1.0)
|
||||
self.usa = add_soas(102, "usa", saliency=0.8, novelty=0.8)
|
||||
self.state = add_soas(103, "state", saliency=0.6, novelty=0.5)
|
||||
self.type_dim = add_soas(1, "type", saliency=0.0)
|
||||
self.geo_dim = add_soas(5, "geography", saliency=0.0)
|
||||
|
||||
# Degenerate seed edge: michigan ISPART usa, dim=usa (parent_id = dim_id = 102)
|
||||
self.seed_edge = add_urd(
|
||||
concept_id=101,
|
||||
parent_id=102, # usa
|
||||
dim_id=102, # usa — same as parent_id
|
||||
is_isa=False,
|
||||
confidence=0.85,
|
||||
source="test",
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 1: Verify the degenerate seed state
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_seed_edge_has_parent_id_equals_dim_id(self):
|
||||
edge = cache.urd_by_concept_dim.get((101, 102))
|
||||
assert edge is not None, "seed edge must be in cache"
|
||||
assert edge.parent_id == edge.dim_id, (
|
||||
f"degenerate edge requires parent_id == dim_id, "
|
||||
f"got parent_id={edge.parent_id}, dim_id={edge.dim_id}"
|
||||
)
|
||||
assert edge.is_isa is False
|
||||
|
||||
def test_seed_recollection_renders_correctly(self):
|
||||
edges = query_edges(101, confidence_floor=0.5, recency_days=90)
|
||||
assert len(edges) == 1
|
||||
line = render_hit("michigan", edges, concept_id=101)
|
||||
# dim token = "usa", parent token = "usa"
|
||||
assert "[usa] usa" in line
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 2: Cue scanner extracts the incoming ISA triple
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_cue_scanner_extracts_michigan_isa_state(self):
|
||||
prompt = "michigan is a state of USA"
|
||||
triples = scan_cues(prompt)
|
||||
match = next(
|
||||
(t for t in triples if t.subject == "michigan" and t.parent == "state"),
|
||||
None,
|
||||
)
|
||||
assert match is not None, "cue scanner must extract michigan ISA state"
|
||||
assert match.is_isa is True
|
||||
assert match.dimension == "usa" # from "of USA" modifier
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 3: Incoming ISA in dim:usa collides with existing ISPART in dim:usa
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_collision_classified_as_misclassification(self):
|
||||
pool, _ = make_mock_pool()
|
||||
captured: list[CollisionInfo] = []
|
||||
|
||||
async def fake_queue(pool, col, priority):
|
||||
captured.append(col)
|
||||
cache.pending_conflicts.add(col.concept_id)
|
||||
|
||||
with patch("festinger.urd_writer._queue_collision", side_effect=fake_queue):
|
||||
req = InsertRequest(
|
||||
concept_id=101, # michigan
|
||||
parent_id=103, # state
|
||||
dim_id=102, # usa — same dim as existing ISPART
|
||||
is_isa=True,
|
||||
confidence=0.85,
|
||||
source="gutask",
|
||||
)
|
||||
collision = await insert_urd_edge(pool, req)
|
||||
|
||||
assert collision is not None
|
||||
assert collision.collision_type == "misclassification", (
|
||||
f"expected misclassification, got {collision.collision_type!r}. "
|
||||
f"existing is_isa={self.seed_edge.is_isa}, incoming is_isa=True"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 4: Recollection renders the [usa?] pending-conflict marker
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_recollection_shows_pending_marker_after_collision(self):
|
||||
pool, _ = make_mock_pool()
|
||||
|
||||
async def fake_queue(pool, col, priority):
|
||||
cache.pending_conflicts.add(col.concept_id)
|
||||
|
||||
with patch("festinger.urd_writer._queue_collision", side_effect=fake_queue):
|
||||
req = InsertRequest(
|
||||
concept_id=101, parent_id=103, dim_id=102,
|
||||
is_isa=True, confidence=0.85, source="gutask",
|
||||
)
|
||||
await insert_urd_edge(pool, req)
|
||||
|
||||
assert 101 in cache.pending_conflicts
|
||||
block = build_recollection_block([101], confidence_floor=0.5, recency_days=90)
|
||||
assert block is not None
|
||||
assert "[usa?]" in block, (
|
||||
f"recollection must show [usa?] pending marker. Got:\n{block}"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 5: Simulate resolution — move facts to correct dimensions
|
||||
# After nightly job:
|
||||
# michigan ISA state in dim:type (the ISA fact)
|
||||
# michigan ISPART usa in dim:geography (the ISPART fact, moved off degenerate dim)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def test_recollection_clean_after_simulated_resolution(self):
|
||||
# Remove the degenerate edge
|
||||
del cache.urd_by_concept_dim[(101, 102)]
|
||||
cache.urd_by_concept[101] = []
|
||||
|
||||
# Insert two correctly-dimensioned edges
|
||||
geo_edge = add_urd(
|
||||
concept_id=101, parent_id=102, dim_id=5, # geography dim
|
||||
is_isa=False, confidence=0.9, source="festinger",
|
||||
)
|
||||
type_edge = add_urd(
|
||||
concept_id=101, parent_id=103, dim_id=1, # type dim
|
||||
is_isa=True, confidence=0.9, source="festinger",
|
||||
)
|
||||
cache.pending_conflicts.discard(101)
|
||||
|
||||
edges = query_edges(101, confidence_floor=0.5, recency_days=90)
|
||||
assert len(edges) == 2
|
||||
|
||||
line = render_hit("michigan", edges, concept_id=101)
|
||||
assert "[geography] usa" in line
|
||||
assert "[type] state" in line
|
||||
assert "?" not in line, "no pending marker after resolution"
|
||||
|
||||
def test_full_recollection_block_after_resolution(self):
|
||||
# Simulate post-resolution state
|
||||
del cache.urd_by_concept_dim[(101, 102)]
|
||||
cache.urd_by_concept[101] = []
|
||||
add_urd(concept_id=101, parent_id=102, dim_id=5, is_isa=False, confidence=0.9, source="festinger")
|
||||
add_urd(concept_id=101, parent_id=103, dim_id=1, is_isa=True, confidence=0.9, source="festinger")
|
||||
cache.pending_conflicts.discard(101)
|
||||
|
||||
block = build_recollection_block([101], confidence_floor=0.5, recency_days=90)
|
||||
assert block is not None
|
||||
assert "<recollection>" in block
|
||||
assert "michigan:" in block
|
||||
assert "[geography] usa" in block
|
||||
assert "[type] state" in block
|
||||
# No pending markers
|
||||
assert "?" not in block
|
||||
Reference in New Issue
Block a user