Spaces:
Sleeping
Sleeping
File size: 7,048 Bytes
2e818da | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | from app.schemas.commit_adaptation import InteractionQuote, MemoryProposal
from app.services.commit_candidate_adapters import (
StudentInteraction,
candidate_to_project_memory_record,
proposal_to_memory_candidate,
)
from app.services.memory_candidates import MemoryCandidate, make_candidate_id
from app.rag.project_memory import ProjectMemoryRecord
def _interaction(text: str, actor: str = "student") -> StudentInteraction:
return StudentInteraction(
interaction_id="interaction-1",
timestamp=1_700_000_000.0,
actor=actor,
source="chat_turn",
agent_id="tutor",
text=text,
)
def test_adapter_builds_real_strict_memory_candidate():
interactions = {"interaction-1": _interaction("Do not give me the complete code.")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student",
kind="workflow_preference",
statement="Prefers to implement code incrementally rather than receiving complete solutions.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="Do not give me the complete code.")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
assert isinstance(candidate, MemoryCandidate)
assert candidate.candidate_id
assert candidate.interaction_ids == ["interaction-1"]
assert candidate.evidence_ids == []
def test_explicit_but_not_global_wording_downgrades_to_project_scope():
interactions = {"interaction-1": _interaction("Do not give me the complete code.")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student", # LLM proposed global — not corroborated by wording
kind="workflow_preference",
statement="Prefers incremental code guidance.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="Do not give me the complete code.")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
assert candidate.destination == "project"
# attribution reflects whether the STATEMENT was explicit, independent of
# final destination — an explicit-but-project-scoped note is still more
# reliably attributed than a merely-inferred one.
assert candidate.attribution == "explicit_student"
def test_explicit_global_wording_keeps_student_destination():
interactions = {"interaction-1": _interaction("I generally prefer diagrams over equations.")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student",
kind="learning_style",
statement="Generally prefers diagrams over equations.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="I generally prefer diagrams over equations.")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
assert candidate.destination == "student"
assert candidate.attribution == "explicit_student"
def test_quote_from_assistant_actor_is_not_verified():
interactions = {"interaction-1": _interaction("I generally prefer diagrams over equations.", actor="assistant")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student",
kind="learning_style",
statement="Generally prefers diagrams over equations.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="I generally prefer diagrams over equations.")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
# No verified quote -> not explicit -> not global -> falls back to inferred profile proposal
assert candidate.attribution == "idea_observer_profile_proposal"
assert candidate.interaction_ids == []
def test_quote_not_found_in_interaction_text_is_not_verified():
interactions = {"interaction-1": _interaction("I like short answers.")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student",
kind="learning_style",
statement="Fabricated claim.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="I generally prefer diagrams")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
assert candidate.attribution == "idea_observer_profile_proposal"
assert candidate.interaction_ids == []
def test_quote_normalization_tolerates_smart_quotes_case_and_whitespace():
interactions = {"interaction-1": _interaction("I GENERALLY prefer’s diagrams.")}
proposal = MemoryProposal(
proposal_id="p1",
destination="student",
kind="learning_style",
statement="Generally prefers diagrams.",
interaction_ids=["interaction-1"],
evidence_quotes=[InteractionQuote(interaction_id="interaction-1", quote="i generally prefer's diagrams.")],
confidence=0.9,
)
candidate = proposal_to_memory_candidate(proposal, project_id="project-a", interactions_by_id=interactions)
assert candidate.interaction_ids == ["interaction-1"]
def test_candidate_to_project_memory_record_builds_real_record():
candidate = MemoryCandidate(
candidate_id=make_candidate_id("project", "project-a", "project_observation", "Uses PyTorch for all experiments."),
destination="project",
project_id="project-a",
kind="project_observation",
statement="Uses PyTorch for all experiments.",
attribution="idea_observer_interaction",
confidence=0.8,
interaction_ids=["interaction-1"],
evidence_ids=[],
)
record = candidate_to_project_memory_record(candidate)
assert isinstance(record, ProjectMemoryRecord)
assert record.novelty_key == candidate.recurrence_key
assert record.memory_id.startswith("pm_")
assert record.attribution == "idea_observer_interaction"
def test_candidate_to_project_memory_record_rejects_student_destination():
import pytest
candidate = MemoryCandidate(
candidate_id=make_candidate_id("student", "project-a", "preferred_name", "The student prefers to be called Anshuman."),
destination="student",
project_id="project-a",
kind="preferred_name",
statement="The student prefers to be called Anshuman.",
attribution="explicit_student",
confidence=0.99,
interaction_ids=["interaction-1"],
evidence_ids=[],
)
with pytest.raises(ValueError, match="destination='project'"):
candidate_to_project_memory_record(candidate)
|