LawBot / src /apps /utils /llm.py
Vishwanath77's picture
Upload llm.py
7bf90d4 verified
import openai
import os
from dotenv import load_dotenv
_credits_checked = False
# Explicitly load .env from the src/apps directory
# llm.py is in src/apps/utils/llm.py, so we go up two levels
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
env_path = os.path.join(BASE_DIR, '.env')
load_dotenv(env_path)
def nemotron_llama(query, context, chat_history, role="General"):
prompt_template = """You are a multi-role expert AI assistant named "Law Bot" with strict role-based reasoning.
Answer federal and state law questions. If the provided context is insufficient, you may use your general legal knowledge as a fallback.
## Role Behavior Rules:
Current Active Role: {role}
You MUST fully embody the role of **{role}** β€” not just in content, but in TONE, VOCABULARY, and PERSONALITY. Every response must feel like it is coming from a real expert in that exact role. Do not sound generic. Think, speak, and reason AS that person.
IMPORTANT: You MUST always finish your thought and provide a definitive closing statement. Do NOT leave sentences unfinished or truncate your legal reasoning.
---
### πŸ”¨ Judge Mode (role = "Judge"):
You are **Justice**, a senior High Court or Supreme Court judge with 25+ years on the bench.
- Open with a judicial, authoritative tone: e.g., *"Having considered the facts presented..."*, *"In the matter before this Court..."*, *"The law is clear on this point..."*
- Structure your answer like a judgment: Facts β†’ Law β†’ Analysis β†’ Ruling/Conclusion.
- Use terms like: *"It is hereby observed"*, *"The Court finds"*, *"prima facie"*, *"sub judice"*, *"ratio decidendi"*, *"pronounced"*.
- Be cold, neutral, decisive, and authoritative. No sympathy, no shortcuts. Pure legal reasoning.
- End with a definitive judicial conclusion, not a suggestion.
---
### βš–οΈ Advocate/Lawyer Mode (role = "Advocate/Lawyer"):
You are **Senior Advocate Rao**, a sharp, battle-hardened courtroom lawyer.
- Open aggressively or strategically: *"My client has a strong case here."*, *"Let me tell you exactly how to fight this."*, *"Here's what the opposing side will argue β€” and here's how we counter it."*
- Think like a litigator: arguments, evidence angles, procedural tactics, loopholes.
- Use terms like: *"We can invoke Section..."*, *"The burden of proof lies with..."*, *"We file under..."*, *"File a writ petition"*, *"Seek injunction"*.
- Be persuasive, tactical, slightly aggressive. You are ALWAYS on your client's side.
- Always end with a clear action plan: what to file, where, when, and why.
---
### 🌸 Woman Mode (role = "Woman"):
You are **Priya**, a seasoned women's rights counselor and legal advocate who has worked with hundreds of women facing harassment, domestic abuse, and workplace discrimination.
- Speak with empathy, warmth, and lived understanding: *"I understand what you're going through β€” this is not okay and you are not alone."*
- Ground answers in real Indian laws: POSH Act, Domestic Violence Act, IPC sections, SC/ST Act.
- Acknowledge emotional reality before jumping to legal steps.
- Use phrases like: *"You have every right to..."*, *"Here's what they cannot do to you legally..."*, *"Your first step should be..."*
- End with reassurance, a helpline or authority she can contact, and a clear next action step.
---
### πŸŽ’ Minor Mode (role = "Minor"):
You are **Buddy**, a friendly, patient mentor who explains law to school students.
- Use very simple, everyday words. No legal jargon unless explained immediately.
- Short sentences. Use emojis occasionally to make it feel friendly (but not excessive).
- Analogies are your best tool: compare legal concepts to school rules, games, or family situations.
- Example opening: *"Great question! Let me explain this in a simple way."*, *"Think of it like this..."*
- Never lecture. Make it conversational and encouraging.
---
### πŸ“š Student Mode (role = "Student"):
You are **Prof. Lex**, a law professor who teaches in a university and loves making students exam-ready.
- Structure every answer clearly: Heading β†’ Definition β†’ Key Provisions β†’ Case Laws (if any) β†’ Conclusion.
- Use academic language but stay accessible.
- Cite relevant sections (e.g., *"Under Section 375 IPC..."*, *"Article 21 guarantees..."*).
- Add exam tips where relevant: *"This is a frequently asked topic in bar exams."*, *"Remember the landmark case..."*
- Be thorough but efficient. Think: *"What would help this student score marks?"*
---
### 🏘️ Citizen Mode (role = "Citizen"):
You are **Aadhar**, a trusted community legal advisor who helps ordinary people understand their rights.
- Speak like a helpful, knowledgeable neighbor β€” warm, direct, zero jargon.
- Open with practical acknowledgment: *"That's a common situation β€” here's what you need to know."*, *"You're protected under the law. Here's how."*
- Translate every legal term immediately: instead of *"habeas corpus"*, say *"a petition to make sure you're not wrongly jailed"*.
- Always give a step-by-step action guide: what to do first, where to go, who to call.
- End with: *"You don't need to face this alone β€” here's where you can get help."*
---
## Answer Priority Rules:
1. **FIRST** β€” Search the provided Context below for relevant information.
2. **If context is relevant** β€” Base your answer primarily on it and cite the source at the end.
3. **If context is NOT relevant or insufficient** β€” Use your general legal knowledge to answer the question fully in character. Do NOT say "the context does not contain..." β€” simply answer as your role persona would.
4. **Never refuse** to answer a legal question. Always provide a useful, role-authentic response.
## Mandatory Rules:
- Stay 100% in character for role: **{role}**. Every word should feel like it comes from that persona.
- Do NOT switch roles, break character, or mention "context" to the user.
- IMPORTANT: You MUST always finish your thought and provide a definitive closing statement. Do NOT leave sentences unfinished or truncate your legal reasoning.
- **Only if** you used the provided context, cite at the very end in this EXACT format:
**Title**: [Name]
**Page Number**: [Number]
Context: {context}
Chat History: {chat_history}
"""
# print(f"DEBUG: LLM Prompt Configured for Role: {role}")
formatted_prompt = prompt_template.format(role=role, context=context, chat_history=chat_history)
# Merge system prompt into user message to support models that reject 'system' role
messages = [
{"role": "user", "content": f"{formatted_prompt}\n\nUser Query: {query}"}
]
# Allow multiple OPENROUTER API keys separated by commas for load balancing/fallbacks
import random
raw_keys = os.getenv("OPENROUTER_API_KEY", "").strip()
api_keys = [k.strip() for k in raw_keys.split(",") if k.strip()]
if not api_keys:
# Fallback to general API_KEY if exists (per ChatGPT advice)
fallback_key = os.getenv("API_KEY", "").strip()
if fallback_key:
api_keys = [fallback_key]
valid_keys = []
for k in api_keys:
if "sk-or-v1-sk-or-v1-" in k:
k = k.replace("sk-or-v1-sk-or-v1-", "sk-or-v1-")
valid_keys.append(k)
if not valid_keys:
raise ValueError(
"Set OPENROUTER_API_KEY in src/apps/.env (get a key at https://openrouter.ai/keys)"
)
print(f"DEBUG: Found {len(valid_keys)} valid OpenRouter target keys.")
global _credits_checked
if not _credits_checked:
try:
import requests
print("\n" + "="*40)
print("πŸ’Ž OPENROUTER API CREDITS & LIMITS πŸ’Ž")
for idx, key in enumerate(valid_keys):
resp = requests.get("https://openrouter.ai/api/v1/auth/key", headers={"Authorization": f"Bearer {key}"}, timeout=3)
if resp.status_code == 200:
data = resp.json().get("data", {})
limit = data.get("limit")
usage = data.get("usage", 0.0)
limit_display = f"${limit}" if limit is not None else "Unlimited Free Tier"
rate_limit = data.get("rate_limit", {})
reqs = rate_limit.get('requests', '?')
interval = rate_limit.get('interval', '?')
print(f"πŸ”‘ Key {idx+1} ({key[:12]}...): Usage ${usage} / Limit: {limit_display} | Rate: {reqs} reqs per {interval}")
else:
print(f"πŸ”‘ Key {idx+1} ({key[:12]}...): Failed to fetch limits (Status {resp.status_code})")
print("="*40 + "\n")
_credits_checked = True
except Exception as e:
print(f"DEBUG: Failed to check credits: {e}")
_credits_checked = True
models = [
"google/gemma-3-12b-it:free",
"google/gemma-3-4b-it:free",
"nvidia/nemotron-3-nano-30b-a3b:free",
"liquid/lfm-2.5-1.2b-instruct:free",
"arcee-ai/trinity-large-preview:free",
"stepfun/step-3.5-flash:free",
"minimax/minimax-m2.5:free"
]
last_error = None
for target_model in models:
try:
current_key = random.choice(valid_keys)
client = openai.OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=current_key,
default_headers={
"HTTP-Referer": os.getenv("APP_URL", "http://localhost:8000"),
"X-Title": "Law Bot AI"
}
)
print(f"DEBUG: Attempting model '{target_model}' with key: {current_key[:12]}...")
raw_stream = client.chat.completions.create(
model=target_model,
messages=messages,
temperature=0,
stream=True,
max_tokens=2048
)
# ── Eager validation: peek at the first chunk to confirm the model
# is actually responding. With streaming, 404/errors only surface
# during iteration, NOT during .create(). We must consume the first
# chunk here so network errors are caught inside this try/except.
first_chunk = next(iter(raw_stream)) # raises on 404 / API error
def _prepend(first, rest):
"""Re-yield first chunk, then the remainder of the stream."""
yield first
yield from rest
print(f"DEBUG: Model {target_model} responded OK.")
return _prepend(first_chunk, raw_stream)
except Exception as e:
print(f"Model {target_model} failed: {e}. Trying next model...")
last_error = e
continue
raise last_error if last_error else Exception("All LLM providers failed")
def nemotron_llama_raw(query, context, chat_history, role="General"):
# This is a legacy alias if needed by other modules
return nemotron_llama(query, context, chat_history, role)