| import json |
| import os |
| import sys |
| import tempfile |
| import unittest |
|
|
| HERE = os.path.dirname(os.path.abspath(__file__)) |
| sys.path.insert(0, os.path.join(HERE, "..", "scripts")) |
|
|
| from agent import case_chat_answer, case_chat_grounded_response |
| from case_summary import case_summary_record, load_case_summaries |
|
|
|
|
| class CaseSummaryTest(unittest.TestCase): |
| def test_extracted_summary_has_priority(self): |
| result = case_summary_record( |
| {"held": "Reporter holding"}, |
| {"held": "Synthetic holding", "model": "model-a"}, |
| {"summary": "Extraction summary", "provider": "indian_kanoon", "version": "v1"}, |
| ) |
|
|
| self.assertEqual(result["text"], "Extraction summary") |
| self.assertEqual(result["source"], "extracted_summary") |
| self.assertEqual(result["provider"], "indian_kanoon") |
|
|
| def test_reporter_and_synthetic_fallbacks_are_disclosed(self): |
| synthetic = case_summary_record({}, {"held": "Generated holding", "model": "model-a"}, {}) |
| reporter = case_summary_record({"held": "Official reporter holding"}, {}, {}) |
|
|
| self.assertTrue(synthetic["generated"]) |
| self.assertEqual(synthetic["source"], "synthetic_headnote") |
| self.assertFalse(reporter["generated"]) |
| self.assertEqual(reporter["source"], "reporter_headnote") |
|
|
| def test_opening_text_is_not_mislabeled_as_summary(self): |
| result = case_summary_record({"case_name": "No headnote"}, {}, {}) |
|
|
| self.assertFalse(result["available"]) |
| self.assertEqual(result["source"], "unavailable") |
|
|
| def test_loads_future_summary_sidecar(self): |
| with tempfile.TemporaryDirectory() as folder: |
| path = os.path.join(folder, "judgment_summaries.jsonl") |
| with open(path, "w", encoding="utf-8") as fh: |
| fh.write(json.dumps({"doc_id": "X", "summary": {"holding": "Appeal allowed."}}) + "\n") |
| rows, filename = load_case_summaries(folder) |
|
|
| self.assertEqual(filename, "judgment_summaries.jsonl") |
| self.assertIn("X", rows) |
|
|
|
|
| class CaseChatTest(unittest.TestCase): |
| def test_chat_prompt_contains_summary_but_no_full_text(self): |
| captured = {} |
|
|
| def llm(messages): |
| captured["messages"] = messages |
| return "The appeal was allowed." |
|
|
| answer = case_chat_answer( |
| "The Court allowed the appeal because notice was not served.", |
| "What was the outcome?", |
| [{"role": "assistant", "content": "Earlier answer"}], |
| "A v B", |
| "2024 INSC 1", |
| llm, |
| ) |
|
|
| prompt = "\n".join(m["content"] for m in captured["messages"]) |
| self.assertEqual(answer, "The appeal was allowed.") |
| self.assertIn("CASE SUMMARY", prompt) |
| self.assertIn("notice was not served", prompt) |
| self.assertNotIn("FULL JUDGMENT", prompt) |
|
|
| def test_chat_refuses_missing_summary(self): |
| called = [] |
| answer = case_chat_answer("", "What was held?", [], "A v B", "", lambda m: called.append(m)) |
|
|
| self.assertEqual(answer, "") |
| self.assertEqual(called, []) |
|
|
| def test_grounded_chat_resolves_only_server_held_evidence_ids(self): |
| captured = {} |
| passages = [ |
| {"paragraph_id": "100:para:12", "label": "Paragraph 12", "text": "The appeal was allowed."}, |
| {"paragraph_id": "100:para:18", "label": "Paragraph 18", "text": "The decree was set aside."}, |
| ] |
|
|
| def llm(messages): |
| captured["messages"] = messages |
| return json.dumps({"answer": "The decree was set aside.", "evidence_ids": ["E2", "E2"]}) |
|
|
| result = case_chat_grounded_response( |
| "The Court decided the appeal.", passages, "What was the result?", [], "A v B", "2024 INSC 1", llm |
| ) |
|
|
| prompt = "\n".join(m["content"] for m in captured["messages"]) |
| self.assertTrue(result["supported"]) |
| self.assertEqual([e["paragraph_id"] for e in result["evidence"]], ["100:para:18"]) |
| self.assertIn("[E1] The appeal was allowed.", prompt) |
| self.assertIn("[E2] The decree was set aside.", prompt) |
|
|
| def test_grounded_chat_rejects_unknown_or_missing_evidence(self): |
| passages = [{"paragraph_id": "100:para:12", "text": "The appeal was allowed."}] |
| result = case_chat_grounded_response( |
| "Summary", passages, "Outcome?", [], "A v B", "", lambda _m: '{"answer":"Allowed", "evidence_ids":["E99"]}' |
| ) |
|
|
| self.assertFalse(result["supported"]) |
| self.assertEqual(result["evidence"], []) |
| self.assertIn("do not answer", result["answer"]) |
|
|
| def test_grounded_chat_can_answer_from_passages_when_summary_is_missing(self): |
| passages = [{"paragraph_id": "100:para:12", "text": "The appeal was allowed."}] |
| result = case_chat_grounded_response( |
| "", passages, "What was the outcome?", [], "A v B", "", lambda _m: '{"answer":"The appeal was allowed.", "evidence_ids":["E1"]}' |
| ) |
|
|
| self.assertTrue(result["supported"]) |
| self.assertEqual(result["evidence"][0]["paragraph_id"], "100:para:12") |
|
|
| def test_grounded_chat_does_not_call_model_without_stored_passages(self): |
| called = [] |
| result = case_chat_grounded_response( |
| "Summary", [], "Outcome?", [], "A v B", "", lambda messages: called.append(messages) |
| ) |
|
|
| self.assertFalse(result["supported"]) |
| self.assertEqual(called, []) |
|
|
|
|
| if __name__ == "__main__": |
| unittest.main() |
|
|