study-buddy / tests /test_commit_candidate_adapters.py
GitHub Actions
deploy d092bea3608b7a29952f16357fda39b7a29e399b
2e818da
Raw
History Blame Contribute Delete
7.05 kB
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)