Spaces:
Running
Running
File size: 10,991 Bytes
926755f a74720f 1802dda 926755f 1802dda a74720f 1802dda a74720f 7bf90d4 a74720f 7bf90d4 a74720f 1802dda a74720f 1802dda a74720f 1802dda a74720f 1802dda a74720f 926755f a74720f 1802dda a74720f 1802dda a74720f 1802dda a74720f 1802dda a74720f 1802dda a74720f 1802dda 926755f a74720f fc64d75 a74720f 926755f 1802dda 926755f a74720f 1802dda 926755f 1802dda 926755f 7bf90d4 926755f a74720f 926755f a74720f 1802dda fc64d75 a74720f 1802dda 926755f 1802dda 926755f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | 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)
|