pkheria's picture
fixed
537c898
Raw
History Blame Contribute Delete
5.98 kB
import os
import json
import uuid
from fastapi import APIRouter, HTTPException
from app.models import ChatRequest
from app.config import ask_groq
from fastapi import status
router = APIRouter(tags=["Chat Assistant"])
# Folder to store session files
SESSIONS_DIR = "sessions"
os.makedirs(SESSIONS_DIR, exist_ok=True)
# In-memory cache for quick access
conversation_history = {}
def load_doc_context(uid: str) -> str:
"""Load all clauses for a document UID into a single context string"""
doc_dir = os.path.join("store", uid)
if not os.path.exists(doc_dir):
raise HTTPException(404, "document not found")
context = ""
files = sorted([f for f in os.listdir(doc_dir) if f.endswith(".txt")])
for idx, f in enumerate(files, start=1):
with open(os.path.join(doc_dir, f), "r", encoding="utf-8") as clause_file:
context += f"Clause {idx}: {clause_file.read().strip()}\n"
return context
# ---------------- Session Persistence ---------------- #
def save_session(session_id: str):
"""Save session history to a JSON file"""
filepath = os.path.join(SESSIONS_DIR, f"{session_id}.json")
with open(filepath, "w", encoding="utf-8") as f:
json.dump(conversation_history[session_id], f, indent=2)
def load_session(session_id: str):
"""Load session history from file if exists"""
filepath = os.path.join(SESSIONS_DIR, f"{session_id}.json")
if os.path.exists(filepath):
with open(filepath, "r", encoding="utf-8") as f:
conversation_history[session_id] = json.load(f)
# ---------------- Chat Endpoint ---------------- #
@router.post("/chat")
async def chat(body: ChatRequest):
context = load_doc_context(body.uid)
# Use provided session_id or create new one
session_id = body.session_id or str(uuid.uuid4())
# Load from file if exists
if session_id not in conversation_history:
load_session(session_id)
if session_id not in conversation_history:
conversation_history[session_id] = []
history = conversation_history[session_id]
# Add user turn
history.append({"role": "user", "content": body.question})
# Keep last 6 turns in context
conversation_str = ""
for turn in history[-6:]:
conversation_str += f"{turn['role'].capitalize()}: {turn['content']}\n"
# Enhanced dual-mode prompt
prompt = f"""
You are a specialized AI assistant trained to analyze and explain LEGAL CONTRACT CLAUSES.
You have access to the clauses of a contract and the ongoing conversation history.
Your role is to act like a precise, reliable, and user-friendly legal explainer.
## Core Behavior Guidelines:
1. **Grounding**:
- Always base answers ONLY on the provided contract clauses and conversation history.
- Never invent or assume terms not found in the clauses.
- If the question is unrelated to the clauses, reply: "I am not sure, please check with a lawyer."
2. **Clause Citation**:
- Always cite the exact clause number(s) you are referring to.
- Example: "Clause 5 states …" or "Both Clause 2 and Clause 8 cover this issue."
3. **Dual Answering Modes**:
- **Specific Question Mode** (user asks about a clause, phrase, or term):
- Restate the relevant clause(s).
- Explain their meaning in plain legal English.
- Optionally, use a simple analogy or example.
- **General Question Mode** (broad Qs like "Is there any issue?", "What should I watch out for?"):
- Summarize the **top 2–3 most important clauses** that may create issues or obligations.
- Keep it **short and focused**, not a clause-by-clause dump.
- Highlight the risk in plain terms, then reference clause numbers.
4. **Formatting**:
- Use bullet points or numbered lists if multiple clauses are relevant.
- Highlight key legal terms in quotes.
5. **Follow-up Context**:
- If the user asks about a term from a previous clause or your prior answer,
explain it using the same clause context.
- Re-use the last cited clause(s) unless the question explicitly shifts context.
6. **Tone**:
- Neutral, professional, and concise.
- Think like a junior lawyer explaining things simply to a client.
---
## Contract Clauses:
{context}
## Conversation so far:
{conversation_str}
## User’s Current Question:
{body.question}
Now, provide the best possible structured answer following the rules above.
"""
try:
answer = ask_groq(prompt)
# Save bot turn
history.append({"role": "assistant", "content": answer})
# Persist to file
save_session(session_id)
return {"answer": answer, "session_id": session_id}
except Exception as e:
raise HTTPException(500, f"Chatbot error: {e}")
# ---------------- Session Management Endpoints ---------------- #
@router.get("/sessions")
async def list_sessions():
"""List all saved session IDs"""
files = [f.replace(".json", "") for f in os.listdir(SESSIONS_DIR) if f.endswith(".json")]
return {"sessions": files}
@router.get("/sessions/{session_id}")
async def get_session(session_id: str):
"""Retrieve full conversation history for a session"""
filepath = os.path.join(SESSIONS_DIR, f"{session_id}.json")
if not os.path.exists(filepath):
raise HTTPException(404, "Session not found")
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
return {"session_id": session_id, "history": data}
@router.delete("/sessions/{session_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_session(session_id: str):
"""Delete a session and its saved history"""
filepath = os.path.join(SESSIONS_DIR, f"{session_id}.json")
# Remove from memory
if session_id in conversation_history:
del conversation_history[session_id]
# Remove from disk
if os.path.exists(filepath):
os.remove(filepath)
return # 204 has no body
raise HTTPException(404, "Session not found")