| """LLM answer generation via Groq (llama-3.1-8b-instant), strict citation prompt.""" |
| from __future__ import annotations |
|
|
| import os |
|
|
| from dotenv import load_dotenv |
| from groq import Groq |
|
|
| load_dotenv() |
|
|
| MODEL = "llama-3.1-8b-instant" |
| TEMPERATURE = 0.1 |
|
|
| SYSTEM_PROMPT = ( |
| "You are a legal research assistant for Indian court judgments. " |
| "Answer ONLY from the provided context. " |
| "After each claim, cite the source in square brackets using the EXACT case " |
| "name as given, e.g. [Case Name vs Other on date]. " |
| "Use only case names that appear in the context. " |
| "If the answer is not in the context, say exactly: " |
| "\"I cannot find this in the retrieved judgments.\" " |
| "Do not use outside knowledge." |
| ) |
|
|
| _client: Groq | None = None |
|
|
|
|
| def _get_client() -> Groq: |
| global _client |
| if _client is None: |
| key = os.environ.get("GROQ_API_KEY") |
| if not key: |
| raise RuntimeError("GROQ_API_KEY is not set") |
| _client = Groq(api_key=key) |
| return _client |
|
|
|
|
| def _format_context(sources: list[dict]) -> str: |
| blocks = [] |
| for s in sources: |
| blocks.append(f"[{s['case_name']}]\n{s['content']}") |
| return "\n\n---\n\n".join(blocks) |
|
|
|
|
| PLAIN_SYSTEM_PROMPT = ( |
| "You are explaining a legal answer to someone with no legal training. " |
| "Rewrite the given answer in 1-2 short sentences of plain English. " |
| "No legal jargon, no case citations, no section numbers unless unavoidable. " |
| "Be accurate but simple, as if explaining to a friend." |
| ) |
|
|
|
|
| def generate_plain_english(legal_answer: str) -> str: |
| """Second Groq call: simplify the legal answer for non-lawyers.""" |
| refusal = "I cannot find this in the retrieved judgments." |
| if not legal_answer or refusal in legal_answer: |
| return "" |
|
|
| resp = _get_client().chat.completions.create( |
| model=MODEL, |
| temperature=0.2, |
| messages=[ |
| {"role": "system", "content": PLAIN_SYSTEM_PROMPT}, |
| {"role": "user", "content": f"Answer to simplify:\n{legal_answer}"}, |
| ], |
| ) |
| return resp.choices[0].message.content.strip() |
|
|
|
|
| def generate_answer(question: str, sources: list[dict]) -> str: |
| """Build context from retrieved sources, call Groq, return raw answer text.""" |
| if not sources: |
| return "I cannot find this in the retrieved judgments." |
|
|
| context = _format_context(sources) |
| user_msg = ( |
| f"Context:\n{context}\n\n" |
| f"Question: {question}\n\n" |
| "Answer using only the context above, citing case names in [brackets]." |
| ) |
|
|
| resp = _get_client().chat.completions.create( |
| model=MODEL, |
| temperature=TEMPERATURE, |
| messages=[ |
| {"role": "system", "content": SYSTEM_PROMPT}, |
| {"role": "user", "content": user_msg}, |
| ], |
| ) |
| return resp.choices[0].message.content.strip() |
|
|
|
|
| if __name__ == "__main__": |
| from app.retrieval import hybrid_search |
|
|
| q = "What was held regarding Section 302 of the Criminal Procedure Code?" |
| hits = hybrid_search(q) |
| print("ANSWER:\n", generate_answer(q, hits)) |
|
|