Spaces:
Sleeping
Sleeping
| 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) | |