faizanwasif commited on
Commit ·
909c15c
1
Parent(s): 9800a40
fixed error api key
Browse files- __pycache__/agents.cpython-312.pyc +0 -0
- agents.py +42 -42
- app.py +144 -84
__pycache__/agents.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/agents.cpython-312.pyc and b/__pycache__/agents.cpython-312.pyc differ
|
|
|
agents.py
CHANGED
|
@@ -1,62 +1,55 @@
|
|
| 1 |
# agents.py
|
| 2 |
from langgraph.prebuilt import create_react_agent
|
| 3 |
import os
|
| 4 |
-
import
|
| 5 |
-
from typing import Dict
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
return str(uuid.uuid4())
|
| 13 |
|
| 14 |
-
def set_api_key(api_key: str
|
| 15 |
-
"""
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
return
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
return session_id
|
| 26 |
-
|
| 27 |
-
def get_api_key(session_id: str) -> str:
|
| 28 |
-
"""Retrieve API key for session"""
|
| 29 |
-
return _session_keys.get(session_id, None)
|
| 30 |
-
|
| 31 |
-
def clear_session(session_id: str):
|
| 32 |
-
"""Clear session data"""
|
| 33 |
-
if session_id in _session_keys:
|
| 34 |
-
del _session_keys[session_id]
|
| 35 |
|
| 36 |
-
def create_agent_response(prompt: str, user_input: str,
|
| 37 |
"""Generic function to create and invoke any agent"""
|
| 38 |
global _api_key
|
| 39 |
|
| 40 |
if not _api_key:
|
| 41 |
-
|
|
|
|
|
|
|
| 42 |
|
| 43 |
try:
|
|
|
|
| 44 |
agent = create_react_agent(
|
| 45 |
model=model,
|
| 46 |
tools=[],
|
| 47 |
prompt=prompt
|
| 48 |
)
|
| 49 |
|
|
|
|
| 50 |
result = agent.invoke({
|
| 51 |
"messages": [{"role": "user", "content": user_input}]
|
| 52 |
})
|
| 53 |
|
| 54 |
# Handle different possible return types from LangGraph
|
| 55 |
if hasattr(result, 'content'):
|
| 56 |
-
|
| 57 |
return str(result.content)
|
| 58 |
elif isinstance(result, dict):
|
| 59 |
-
|
| 60 |
if 'content' in result:
|
| 61 |
return str(result['content'])
|
| 62 |
elif 'messages' in result and len(result['messages']) > 0:
|
|
@@ -66,19 +59,23 @@ def create_agent_response(prompt: str, user_input: str, session_id: str, model:
|
|
| 66 |
elif isinstance(last_msg, dict) and 'content' in last_msg:
|
| 67 |
return str(last_msg['content'])
|
| 68 |
elif isinstance(result, str):
|
| 69 |
-
|
| 70 |
return result
|
| 71 |
|
| 72 |
# Fallback: convert whatever we got to string
|
|
|
|
| 73 |
return str(result)
|
| 74 |
|
| 75 |
except Exception as e:
|
| 76 |
-
|
|
|
|
|
|
|
| 77 |
|
| 78 |
# ==================== CORE AGENT FUNCTIONS ====================
|
| 79 |
|
| 80 |
-
def judge_agent(student_arg: str, opponent_arg: str = "", documents: str = ""
|
| 81 |
"""Judge agent for evaluating legal arguments"""
|
|
|
|
| 82 |
prompt = """You are an experienced federal judge presiding over a courtroom.
|
| 83 |
|
| 84 |
Your role is to:
|
|
@@ -93,10 +90,11 @@ ALWAYS respond in this exact format: Judge: [your response]
|
|
| 93 |
Keep responses 2-4 sentences, focusing on legal analysis."""
|
| 94 |
|
| 95 |
user_input = f"Student Argument: {student_arg}\nOpponent Argument: {opponent_arg}\nCase Documents: {documents}"
|
| 96 |
-
return create_agent_response(prompt, user_input
|
| 97 |
|
| 98 |
-
def opponent_agent(student_arg: str, case_context: str = ""
|
| 99 |
"""Opponent agent for counter-arguments"""
|
|
|
|
| 100 |
prompt = """You are a skilled attorney arguing the opposing side in this case.
|
| 101 |
|
| 102 |
Your role is to:
|
|
@@ -111,10 +109,11 @@ ALWAYS respond in this exact format: Opponent: [your response]
|
|
| 111 |
Keep responses 2-4 sentences, focused on strong legal counter-arguments."""
|
| 112 |
|
| 113 |
user_input = f"Student's Argument: {student_arg}\nCase Context: {case_context}"
|
| 114 |
-
return create_agent_response(prompt, user_input
|
| 115 |
|
| 116 |
-
def narrator_agent(context: str, student_arg: str = "", opponent_arg: str = ""
|
| 117 |
"""Enhanced narrator agent that handles courtroom atmosphere AND side character arguments"""
|
|
|
|
| 118 |
prompt = """You are a courtroom narrator who describes the scene and occasionally voices side characters.
|
| 119 |
|
| 120 |
Your roles include:
|
|
@@ -136,10 +135,9 @@ Otherwise format as: "Narrator: [your narration]"
|
|
| 136 |
Keep responses 2-4 sentences, atmospheric and engaging."""
|
| 137 |
|
| 138 |
user_input = f"Context: {context}\nStudent Argument: {student_arg}\nOpponent Argument: {opponent_arg}"
|
| 139 |
-
return create_agent_response(prompt, user_input
|
| 140 |
|
| 141 |
# ==================== AGENT REGISTRY ====================
|
| 142 |
-
# Simplified registry with only the three core agents
|
| 143 |
AVAILABLE_AGENTS = {
|
| 144 |
'judge': judge_agent,
|
| 145 |
'opponent': opponent_agent,
|
|
@@ -151,4 +149,6 @@ def get_agent_response(agent_type: str, *args, **kwargs) -> str:
|
|
| 151 |
if agent_type in AVAILABLE_AGENTS:
|
| 152 |
return AVAILABLE_AGENTS[agent_type](*args, **kwargs)
|
| 153 |
else:
|
| 154 |
-
|
|
|
|
|
|
|
|
|
| 1 |
# agents.py
|
| 2 |
from langgraph.prebuilt import create_react_agent
|
| 3 |
import os
|
| 4 |
+
import logging
|
|
|
|
| 5 |
|
| 6 |
+
# Set up logging
|
| 7 |
+
logging.basicConfig(level=logging.INFO)
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
|
| 10 |
+
# Simple global API key storage
|
| 11 |
+
_api_key = None
|
|
|
|
| 12 |
|
| 13 |
+
def set_api_key(api_key: str) -> bool:
|
| 14 |
+
"""Set the API key for Claude"""
|
| 15 |
+
global _api_key
|
| 16 |
+
if api_key and api_key.strip():
|
| 17 |
+
_api_key = api_key.strip()
|
| 18 |
+
os.environ["ANTHROPIC_API_KEY"] = _api_key
|
| 19 |
+
logger.info("API key set successfully")
|
| 20 |
+
return True
|
| 21 |
+
else:
|
| 22 |
+
logger.error("Invalid API key provided")
|
| 23 |
+
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
|
| 25 |
+
def create_agent_response(prompt: str, user_input: str, model: str = "anthropic:claude-3-haiku-20240307") -> str:
|
| 26 |
"""Generic function to create and invoke any agent"""
|
| 27 |
global _api_key
|
| 28 |
|
| 29 |
if not _api_key:
|
| 30 |
+
error_msg = "API key not set. Please set your Claude API key first."
|
| 31 |
+
logger.error(error_msg)
|
| 32 |
+
return f"Error: {error_msg}"
|
| 33 |
|
| 34 |
try:
|
| 35 |
+
logger.info(f"Creating agent with model: {model}")
|
| 36 |
agent = create_react_agent(
|
| 37 |
model=model,
|
| 38 |
tools=[],
|
| 39 |
prompt=prompt
|
| 40 |
)
|
| 41 |
|
| 42 |
+
logger.info("Invoking agent with user input")
|
| 43 |
result = agent.invoke({
|
| 44 |
"messages": [{"role": "user", "content": user_input}]
|
| 45 |
})
|
| 46 |
|
| 47 |
# Handle different possible return types from LangGraph
|
| 48 |
if hasattr(result, 'content'):
|
| 49 |
+
logger.info("Received AIMessage response")
|
| 50 |
return str(result.content)
|
| 51 |
elif isinstance(result, dict):
|
| 52 |
+
logger.info("Received dict response")
|
| 53 |
if 'content' in result:
|
| 54 |
return str(result['content'])
|
| 55 |
elif 'messages' in result and len(result['messages']) > 0:
|
|
|
|
| 59 |
elif isinstance(last_msg, dict) and 'content' in last_msg:
|
| 60 |
return str(last_msg['content'])
|
| 61 |
elif isinstance(result, str):
|
| 62 |
+
logger.info("Received string response")
|
| 63 |
return result
|
| 64 |
|
| 65 |
# Fallback: convert whatever we got to string
|
| 66 |
+
logger.warning("Using fallback response conversion")
|
| 67 |
return str(result)
|
| 68 |
|
| 69 |
except Exception as e:
|
| 70 |
+
error_msg = f"Unable to process request - {str(e)}"
|
| 71 |
+
logger.error(f"Agent error: {error_msg}", exc_info=True)
|
| 72 |
+
return f"Error: {error_msg}"
|
| 73 |
|
| 74 |
# ==================== CORE AGENT FUNCTIONS ====================
|
| 75 |
|
| 76 |
+
def judge_agent(student_arg: str, opponent_arg: str = "", documents: str = "") -> str:
|
| 77 |
"""Judge agent for evaluating legal arguments"""
|
| 78 |
+
logger.info("Judge agent called")
|
| 79 |
prompt = """You are an experienced federal judge presiding over a courtroom.
|
| 80 |
|
| 81 |
Your role is to:
|
|
|
|
| 90 |
Keep responses 2-4 sentences, focusing on legal analysis."""
|
| 91 |
|
| 92 |
user_input = f"Student Argument: {student_arg}\nOpponent Argument: {opponent_arg}\nCase Documents: {documents}"
|
| 93 |
+
return create_agent_response(prompt, user_input)
|
| 94 |
|
| 95 |
+
def opponent_agent(student_arg: str, case_context: str = "") -> str:
|
| 96 |
"""Opponent agent for counter-arguments"""
|
| 97 |
+
logger.info("Opponent agent called")
|
| 98 |
prompt = """You are a skilled attorney arguing the opposing side in this case.
|
| 99 |
|
| 100 |
Your role is to:
|
|
|
|
| 109 |
Keep responses 2-4 sentences, focused on strong legal counter-arguments."""
|
| 110 |
|
| 111 |
user_input = f"Student's Argument: {student_arg}\nCase Context: {case_context}"
|
| 112 |
+
return create_agent_response(prompt, user_input)
|
| 113 |
|
| 114 |
+
def narrator_agent(context: str, student_arg: str = "", opponent_arg: str = "") -> str:
|
| 115 |
"""Enhanced narrator agent that handles courtroom atmosphere AND side character arguments"""
|
| 116 |
+
logger.info("Narrator agent called")
|
| 117 |
prompt = """You are a courtroom narrator who describes the scene and occasionally voices side characters.
|
| 118 |
|
| 119 |
Your roles include:
|
|
|
|
| 135 |
Keep responses 2-4 sentences, atmospheric and engaging."""
|
| 136 |
|
| 137 |
user_input = f"Context: {context}\nStudent Argument: {student_arg}\nOpponent Argument: {opponent_arg}"
|
| 138 |
+
return create_agent_response(prompt, user_input)
|
| 139 |
|
| 140 |
# ==================== AGENT REGISTRY ====================
|
|
|
|
| 141 |
AVAILABLE_AGENTS = {
|
| 142 |
'judge': judge_agent,
|
| 143 |
'opponent': opponent_agent,
|
|
|
|
| 149 |
if agent_type in AVAILABLE_AGENTS:
|
| 150 |
return AVAILABLE_AGENTS[agent_type](*args, **kwargs)
|
| 151 |
else:
|
| 152 |
+
error_msg = f"Unknown agent type '{agent_type}'. Available agents: {list(AVAILABLE_AGENTS.keys())}"
|
| 153 |
+
logger.error(error_msg)
|
| 154 |
+
return f"Error: {error_msg}"
|
app.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import time
|
|
|
|
| 3 |
from typing import Dict, List, Tuple, Optional
|
| 4 |
-
from agents import judge_agent, opponent_agent, narrator_agent, set_api_key
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
import os
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
# Load environment variables
|
| 9 |
load_dotenv()
|
| 10 |
|
|
@@ -16,32 +21,41 @@ class LawTrainingSystem:
|
|
| 16 |
self.student_side = None
|
| 17 |
self.conversation_history = []
|
| 18 |
self.processing_lock = False
|
| 19 |
-
self.
|
| 20 |
|
| 21 |
def set_api_key(self, api_key: str):
|
| 22 |
"""Set the API key for the agents"""
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
else:
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
-
def clear_session(self):
|
| 33 |
-
"""Clear session data when done"""
|
| 34 |
-
if self.session_id:
|
| 35 |
-
clear_session(self.session_id)
|
| 36 |
-
self.session_id = None
|
| 37 |
-
|
| 38 |
def initialize_session(self, case_title: str, case_description: str, student_side: str):
|
| 39 |
"""Initialize a new training session"""
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
def is_processing(self) -> bool:
|
| 47 |
"""Check if system is currently processing a response"""
|
|
@@ -50,6 +64,7 @@ class LawTrainingSystem:
|
|
| 50 |
def set_processing(self, status: bool):
|
| 51 |
"""Set processing lock status"""
|
| 52 |
self.processing_lock = status
|
|
|
|
| 53 |
|
| 54 |
# Global system instance
|
| 55 |
system = LawTrainingSystem()
|
|
@@ -58,55 +73,72 @@ system = LawTrainingSystem()
|
|
| 58 |
def generate_judge_response(student_message: str, opponent_message: str = "", documents: str = "") -> str:
|
| 59 |
"""Generate Judge AI response"""
|
| 60 |
try:
|
| 61 |
-
|
|
|
|
| 62 |
except Exception as e:
|
| 63 |
-
|
|
|
|
| 64 |
|
| 65 |
def generate_opponent_response(student_message: str) -> str:
|
| 66 |
"""Generate Opponent AI response"""
|
| 67 |
try:
|
|
|
|
| 68 |
case_context = f"Case: {system.current_case['title']} - {system.current_case['description']}" if system.current_case else ""
|
| 69 |
-
return opponent_agent(student_message, case_context
|
| 70 |
except Exception as e:
|
| 71 |
-
|
|
|
|
| 72 |
|
| 73 |
def generate_narrator_response(context: str, student_message: str = "", opponent_message: str = "") -> str:
|
| 74 |
"""Generate Narrator AI response - handles side character arguments too"""
|
| 75 |
try:
|
| 76 |
-
|
|
|
|
| 77 |
except Exception as e:
|
| 78 |
-
|
|
|
|
| 79 |
|
| 80 |
# ==================== UI RESPONSE HANDLERS ====================
|
| 81 |
def handle_student_response(student_message: str, judge_history: List, opponent_history: List, narrator_history: List) -> Tuple:
|
| 82 |
"""Process student message and generate AI responses"""
|
| 83 |
|
| 84 |
-
# Initialize histories if they are None
|
| 85 |
-
if judge_history is None:
|
| 86 |
-
judge_history = []
|
| 87 |
-
if opponent_history is None:
|
| 88 |
-
opponent_history = []
|
| 89 |
-
if narrator_history is None:
|
| 90 |
-
narrator_history = []
|
| 91 |
-
|
| 92 |
-
# Prevent multiple simultaneous processing
|
| 93 |
-
if system.is_processing():
|
| 94 |
-
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=False)
|
| 95 |
-
|
| 96 |
-
if not system.session_active:
|
| 97 |
-
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 98 |
-
|
| 99 |
-
# Set processing lock
|
| 100 |
-
system.set_processing(True)
|
| 101 |
-
|
| 102 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
# Generate opponent response first
|
|
|
|
| 104 |
opponent_response = generate_opponent_response(student_message)
|
| 105 |
|
| 106 |
# Generate judge response (evaluating both student and opponent)
|
|
|
|
| 107 |
judge_response = generate_judge_response(student_message, opponent_response)
|
| 108 |
|
| 109 |
# Generate narrator response (describing the courtroom scene)
|
|
|
|
| 110 |
narrator_response = generate_narrator_response("Arguments presented", student_message, opponent_response)
|
| 111 |
|
| 112 |
# Update chat histories
|
|
@@ -114,49 +146,72 @@ def handle_student_response(student_message: str, judge_history: List, opponent_
|
|
| 114 |
opponent_history.append({"role": "assistant", "content": opponent_response})
|
| 115 |
narrator_history.append({"role": "assistant", "content": narrator_response})
|
| 116 |
|
|
|
|
|
|
|
| 117 |
# Clear student input and re-enable
|
| 118 |
return "", judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 119 |
|
|
|
|
|
|
|
|
|
|
| 120 |
finally:
|
| 121 |
# Always release processing lock
|
| 122 |
system.set_processing(False)
|
| 123 |
|
| 124 |
def start_case_session(case_title: str, case_description: str, student_side: str) -> Tuple:
|
| 125 |
"""Initialize a new case session"""
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(interactive=False)
|
| 131 |
-
|
| 132 |
-
# Initialize system
|
| 133 |
-
init_message = system.initialize_session(case_title, case_description, student_side)
|
| 134 |
-
|
| 135 |
-
# Generate initial context from Narrator
|
| 136 |
-
initial_context = generate_narrator_response(f"Case session begins: {case_title}")
|
| 137 |
-
|
| 138 |
-
# Create initial chat histories
|
| 139 |
-
judge_history = [{"role": "assistant", "content": "Judge: I am ready to evaluate your arguments based on legal precedent and constitutional law."}]
|
| 140 |
-
opponent_side = 'prosecution' if student_side == 'defense' else 'defense'
|
| 141 |
-
opponent_history = [{"role": "assistant", "content": f"Opponent: I will argue for the {opponent_side} side. Present your case."}]
|
| 142 |
-
narrator_history = [{"role": "assistant", "content": initial_context}]
|
| 143 |
-
|
| 144 |
-
return (
|
| 145 |
-
judge_history,
|
| 146 |
-
opponent_history,
|
| 147 |
-
narrator_history,
|
| 148 |
-
gr.update(visible=True),
|
| 149 |
-
gr.update(interactive=True)
|
| 150 |
-
)
|
| 151 |
|
| 152 |
def upload_case_documents(files) -> str:
|
| 153 |
"""Handle case document uploads"""
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
# ==================== GRADIO INTERFACE ====================
|
| 162 |
def create_law_training_interface():
|
|
@@ -322,13 +377,18 @@ def create_law_training_interface():
|
|
| 322 |
|
| 323 |
# ==================== MAIN EXECUTION ====================
|
| 324 |
if __name__ == "__main__":
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import time
|
| 3 |
+
import logging
|
| 4 |
from typing import Dict, List, Tuple, Optional
|
| 5 |
+
from agents import judge_agent, opponent_agent, narrator_agent, set_api_key
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
import os
|
| 8 |
|
| 9 |
+
# Set up logging
|
| 10 |
+
logging.basicConfig(level=logging.INFO)
|
| 11 |
+
logger = logging.getLogger(__name__)
|
| 12 |
+
|
| 13 |
# Load environment variables
|
| 14 |
load_dotenv()
|
| 15 |
|
|
|
|
| 21 |
self.student_side = None
|
| 22 |
self.conversation_history = []
|
| 23 |
self.processing_lock = False
|
| 24 |
+
self.api_key_set = False
|
| 25 |
|
| 26 |
def set_api_key(self, api_key: str):
|
| 27 |
"""Set the API key for the agents"""
|
| 28 |
+
try:
|
| 29 |
+
if api_key and api_key.strip():
|
| 30 |
+
success = set_api_key(api_key)
|
| 31 |
+
if success:
|
| 32 |
+
self.api_key_set = True
|
| 33 |
+
logger.info("API key set successfully in system")
|
| 34 |
+
return "✅ API Key set successfully!"
|
| 35 |
+
else:
|
| 36 |
+
self.api_key_set = False
|
| 37 |
+
logger.error("Failed to set API key")
|
| 38 |
+
return "❌ Invalid API key format"
|
| 39 |
else:
|
| 40 |
+
self.api_key_set = False
|
| 41 |
+
logger.error("Empty API key provided")
|
| 42 |
+
return "❌ Please enter a valid API key"
|
| 43 |
+
except Exception as e:
|
| 44 |
+
logger.error(f"Error setting API key: {e}", exc_info=True)
|
| 45 |
+
return f"❌ Error setting API key: {str(e)}"
|
| 46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
def initialize_session(self, case_title: str, case_description: str, student_side: str):
|
| 48 |
"""Initialize a new training session"""
|
| 49 |
+
try:
|
| 50 |
+
self.session_active = True
|
| 51 |
+
self.current_case = {"title": case_title, "description": case_description}
|
| 52 |
+
self.student_side = student_side
|
| 53 |
+
self.conversation_history = []
|
| 54 |
+
logger.info(f"Session initialized: {case_title} - Student side: {student_side}")
|
| 55 |
+
return f"Session initialized: {case_title} - You are the {student_side}"
|
| 56 |
+
except Exception as e:
|
| 57 |
+
logger.error(f"Error initializing session: {e}", exc_info=True)
|
| 58 |
+
raise
|
| 59 |
|
| 60 |
def is_processing(self) -> bool:
|
| 61 |
"""Check if system is currently processing a response"""
|
|
|
|
| 64 |
def set_processing(self, status: bool):
|
| 65 |
"""Set processing lock status"""
|
| 66 |
self.processing_lock = status
|
| 67 |
+
logger.info(f"Processing lock set to: {status}")
|
| 68 |
|
| 69 |
# Global system instance
|
| 70 |
system = LawTrainingSystem()
|
|
|
|
| 73 |
def generate_judge_response(student_message: str, opponent_message: str = "", documents: str = "") -> str:
|
| 74 |
"""Generate Judge AI response"""
|
| 75 |
try:
|
| 76 |
+
logger.info("Generating judge response")
|
| 77 |
+
return judge_agent(student_message, opponent_message, documents)
|
| 78 |
except Exception as e:
|
| 79 |
+
logger.error(f"Error in judge response: {e}", exc_info=True)
|
| 80 |
+
return f"Judge: I'm having trouble processing your argument. Error: {str(e)}"
|
| 81 |
|
| 82 |
def generate_opponent_response(student_message: str) -> str:
|
| 83 |
"""Generate Opponent AI response"""
|
| 84 |
try:
|
| 85 |
+
logger.info("Generating opponent response")
|
| 86 |
case_context = f"Case: {system.current_case['title']} - {system.current_case['description']}" if system.current_case else ""
|
| 87 |
+
return opponent_agent(student_message, case_context)
|
| 88 |
except Exception as e:
|
| 89 |
+
logger.error(f"Error in opponent response: {e}", exc_info=True)
|
| 90 |
+
return f"Opponent: I'm experiencing technical difficulties. Error: {str(e)}"
|
| 91 |
|
| 92 |
def generate_narrator_response(context: str, student_message: str = "", opponent_message: str = "") -> str:
|
| 93 |
"""Generate Narrator AI response - handles side character arguments too"""
|
| 94 |
try:
|
| 95 |
+
logger.info("Generating narrator response")
|
| 96 |
+
return narrator_agent(context, student_message, opponent_message)
|
| 97 |
except Exception as e:
|
| 98 |
+
logger.error(f"Error in narrator response: {e}", exc_info=True)
|
| 99 |
+
return f"Narrator: The courtroom atmosphere is tense as technical difficulties arise. Error: {str(e)}"
|
| 100 |
|
| 101 |
# ==================== UI RESPONSE HANDLERS ====================
|
| 102 |
def handle_student_response(student_message: str, judge_history: List, opponent_history: List, narrator_history: List) -> Tuple:
|
| 103 |
"""Process student message and generate AI responses"""
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
try:
|
| 106 |
+
logger.info(f"Processing student response: {student_message[:50]}...")
|
| 107 |
+
|
| 108 |
+
# Initialize histories if they are None
|
| 109 |
+
if judge_history is None:
|
| 110 |
+
judge_history = []
|
| 111 |
+
if opponent_history is None:
|
| 112 |
+
opponent_history = []
|
| 113 |
+
if narrator_history is None:
|
| 114 |
+
narrator_history = []
|
| 115 |
+
|
| 116 |
+
# Prevent multiple simultaneous processing
|
| 117 |
+
if system.is_processing():
|
| 118 |
+
logger.warning("System is already processing, skipping request")
|
| 119 |
+
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=False)
|
| 120 |
+
|
| 121 |
+
if not system.session_active:
|
| 122 |
+
logger.warning("No active session")
|
| 123 |
+
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 124 |
+
|
| 125 |
+
if not system.api_key_set:
|
| 126 |
+
logger.warning("API key not set")
|
| 127 |
+
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 128 |
+
|
| 129 |
+
# Set processing lock
|
| 130 |
+
system.set_processing(True)
|
| 131 |
+
|
| 132 |
# Generate opponent response first
|
| 133 |
+
logger.info("Generating opponent response")
|
| 134 |
opponent_response = generate_opponent_response(student_message)
|
| 135 |
|
| 136 |
# Generate judge response (evaluating both student and opponent)
|
| 137 |
+
logger.info("Generating judge response")
|
| 138 |
judge_response = generate_judge_response(student_message, opponent_response)
|
| 139 |
|
| 140 |
# Generate narrator response (describing the courtroom scene)
|
| 141 |
+
logger.info("Generating narrator response")
|
| 142 |
narrator_response = generate_narrator_response("Arguments presented", student_message, opponent_response)
|
| 143 |
|
| 144 |
# Update chat histories
|
|
|
|
| 146 |
opponent_history.append({"role": "assistant", "content": opponent_response})
|
| 147 |
narrator_history.append({"role": "assistant", "content": narrator_response})
|
| 148 |
|
| 149 |
+
logger.info("All responses generated successfully")
|
| 150 |
+
|
| 151 |
# Clear student input and re-enable
|
| 152 |
return "", judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 153 |
|
| 154 |
+
except Exception as e:
|
| 155 |
+
logger.error(f"Error in handle_student_response: {e}", exc_info=True)
|
| 156 |
+
return student_message, judge_history, opponent_history, narrator_history, gr.update(interactive=True)
|
| 157 |
finally:
|
| 158 |
# Always release processing lock
|
| 159 |
system.set_processing(False)
|
| 160 |
|
| 161 |
def start_case_session(case_title: str, case_description: str, student_side: str) -> Tuple:
|
| 162 |
"""Initialize a new case session"""
|
| 163 |
+
try:
|
| 164 |
+
logger.info(f"Starting case session: {case_title}")
|
| 165 |
+
|
| 166 |
+
if not system.api_key_set:
|
| 167 |
+
logger.warning("Cannot start session - API key not set")
|
| 168 |
+
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(interactive=False)
|
| 169 |
+
|
| 170 |
+
if not case_title or not case_description:
|
| 171 |
+
logger.warning("Cannot start session - missing case details")
|
| 172 |
+
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(interactive=False)
|
| 173 |
+
|
| 174 |
+
# Initialize system
|
| 175 |
+
init_message = system.initialize_session(case_title, case_description, student_side)
|
| 176 |
+
|
| 177 |
+
# Generate initial context from Narrator
|
| 178 |
+
logger.info("Generating initial narrator context")
|
| 179 |
+
initial_context = generate_narrator_response(f"Case session begins: {case_title}")
|
| 180 |
+
|
| 181 |
+
# Create initial chat histories
|
| 182 |
+
judge_history = [{"role": "assistant", "content": "Judge: I am ready to evaluate your arguments based on legal precedent and constitutional law."}]
|
| 183 |
+
opponent_side = 'prosecution' if student_side == 'defense' else 'defense'
|
| 184 |
+
opponent_history = [{"role": "assistant", "content": f"Opponent: I will argue for the {opponent_side} side. Present your case."}]
|
| 185 |
+
narrator_history = [{"role": "assistant", "content": initial_context}]
|
| 186 |
+
|
| 187 |
+
logger.info("Case session started successfully")
|
| 188 |
+
|
| 189 |
+
return (
|
| 190 |
+
judge_history,
|
| 191 |
+
opponent_history,
|
| 192 |
+
narrator_history,
|
| 193 |
+
gr.update(visible=True),
|
| 194 |
+
gr.update(interactive=True)
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
except Exception as e:
|
| 198 |
+
logger.error(f"Error starting case session: {e}", exc_info=True)
|
| 199 |
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(interactive=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
def upload_case_documents(files) -> str:
|
| 202 |
"""Handle case document uploads"""
|
| 203 |
+
try:
|
| 204 |
+
if not files:
|
| 205 |
+
return "No documents uploaded"
|
| 206 |
+
|
| 207 |
+
# TODO: Process uploaded documents with document parsing
|
| 208 |
+
file_names = [f.name for f in files]
|
| 209 |
+
logger.info(f"Uploaded {len(files)} documents: {file_names}")
|
| 210 |
+
return f"Uploaded {len(files)} documents: {', '.join(file_names)}"
|
| 211 |
+
|
| 212 |
+
except Exception as e:
|
| 213 |
+
logger.error(f"Error uploading documents: {e}", exc_info=True)
|
| 214 |
+
return f"Error uploading documents: {str(e)}"
|
| 215 |
|
| 216 |
# ==================== GRADIO INTERFACE ====================
|
| 217 |
def create_law_training_interface():
|
|
|
|
| 377 |
|
| 378 |
# ==================== MAIN EXECUTION ====================
|
| 379 |
if __name__ == "__main__":
|
| 380 |
+
try:
|
| 381 |
+
logger.info("Starting Law Training System")
|
| 382 |
+
# Create and launch the interface
|
| 383 |
+
app = create_law_training_interface()
|
| 384 |
+
|
| 385 |
+
# Launch configuration
|
| 386 |
+
app.launch(
|
| 387 |
+
server_name="0.0.0.0",
|
| 388 |
+
server_port=7860,
|
| 389 |
+
share=False,
|
| 390 |
+
debug=True
|
| 391 |
+
)
|
| 392 |
+
except Exception as e:
|
| 393 |
+
logger.error(f"Failed to start application: {e}", exc_info=True)
|
| 394 |
+
raise
|