Jofthomas commited on
Commit
e80aab9
·
verified ·
1 Parent(s): fa7d846

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +416 -81
app.py CHANGED
@@ -1,16 +1,21 @@
1
  import os
2
  import gradio as gr
3
- # Keep using gradio.ChatMessage for type hints if needed, but not for yielding complex structures directly to ChatInterface
4
- # from gradio import ChatMessage # Maybe remove this import if not used elsewhere
5
  import requests
6
- from typing import Dict, List, AsyncGenerator, Union, Tuple
7
- from langchain_core.messages import HumanMessage, AIMessage, ToolMessage # Use LangChain messages internally
 
 
 
 
8
  from langchain_core.tools import tool
9
  from langchain_openai import ChatOpenAI
10
  from langgraph.checkpoint.memory import MemorySaver
11
  from langgraph.prebuilt import create_react_agent
12
 
13
- # --- Tools remain the same ---
 
 
 
14
  @tool
15
  def get_lat_lng(location_description: str) -> dict[str, float]:
16
  """Get the latitude and longitude of a location."""
@@ -30,7 +35,6 @@ def get_weather(lat: float, lng: float) -> dict[str, str]:
30
  """Get the weather at a location."""
31
  print(f"Tool: Getting weather for lat={lat}, lng={lng}")
32
  # Replace with actual API call in a real app
33
- # Dummy logic based on lat
34
  if lat > 45: # Northern locations
35
  return {"temperature": "15°C", "description": "Cloudy"}
36
  elif lat > 30: # Mid locations
@@ -38,81 +42,412 @@ def get_weather(lat: float, lng: float) -> dict[str, str]:
38
  else: # Southern locations
39
  return {"temperature": "30°C", "description": "Very Sunny"}
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- async def Answer_from_agent(message: str, history: List[List[str]]) -> AsyncGenerator[str, None]:
43
- """Processes message through LangChain agent, yielding intermediate steps as strings."""
44
-
45
- # Convert Gradio history to LangChain messages
46
- lc_messages = []
47
- for user_msg, ai_msg in history:
48
- if user_msg:
49
- lc_messages.append(HumanMessage(content=user_msg))
50
- if ai_msg:
51
- # Important: Handle potential previous intermediate strings from AI
52
- # If the ai_msg contains markers like "🛠️ Using", it was an intermediate step.
53
- # For simplicity here, we assume full AI responses were stored previously.
54
- # A more robust solution might involve storing message types in history.
55
- if not ai_msg.startswith("🛠️ Using") and not ai_msg.startswith("Result:"):
56
- lc_messages.append(AIMessage(content=ai_msg))
57
-
58
- lc_messages.append(HumanMessage(content=message))
59
-
60
- llm = ChatOpenAI(temperature=0, model="gpt-4")
61
- memory = MemorySaver() # Be mindful of memory state if agent is re-initialized every time
62
- tools = [get_lat_lng, get_weather]
63
- agent_executor = create_react_agent(llm, tools, checkpointer=memory)
64
-
65
- # Use a unique thread_id per session if needed, or manage state differently
66
- # Using a fixed one like "abc123" means all users share the same memory if server restarts aren't frequent
67
- thread_id = "user_session_" + str(os.urandom(4).hex()) # Example: generate unique ID
68
-
69
- full_response = ""
70
-
71
- async for chunk in agent_executor.astream_events(
72
- {"messages": lc_messages},
73
- config={"configurable": {"thread_id": thread_id}},
74
- version="v1" # Use v1 for events streaming
75
- ):
76
- event = chunk["event"]
77
- data = chunk["data"]
78
-
79
- if event == "on_chat_model_stream":
80
- content = data["chunk"].content
81
- if content:
82
- full_response += content
83
- yield full_response
84
-
85
- elif event == "on_tool_start":
86
- tool_input_str = str(data.get('input', ''))
87
- yield f"🛠️ Using tool: **{data['name']}** with input: `{tool_input_str}`"
88
-
89
- elif event == "on_tool_end":
90
- tool_output_str = str(data.get('output', ''))
91
- yield f"Tool **{data['name']}** finished.\nResult: `{tool_output_str}`"
92
- if full_response:
93
- yield full_response
94
-
95
- if full_response and (not chunk or chunk["event"] != "on_chat_model_stream"):
96
- yield full_response
97
-
98
-
99
- # --- Gradio Interface (mostly unchanged) ---
100
- demo = gr.ChatInterface(
101
- fn=Answer_from_agent,
102
- type="messages",
103
- title="🤖 AGent template",
104
- description="Ask about the weather anywhere! Watch as I gather the information step by step.",
105
- cache_examples=False,
106
- save_history=True,
107
- editable=True,
108
- )
109
 
110
- if __name__ == "__main__":
111
- # Load environment variables
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  try:
113
- openai_api_key = os.getenv("OPENAI_API_KEY")
114
- if openai_api_key:
115
- print("OPENAI_API_KEY found.")
116
- except:
117
- alert("Warning: OPENAI_API_KEY not found in environment variables.")
118
- demo.launch(debug=True, server_name="0.0.0.0")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import gradio as gr
 
 
3
  import requests
4
+ import inspect # To get source code for __repr__
5
+ import asyncio
6
+ from typing import Dict, List, AsyncGenerator, Union, Tuple, Optional
7
+
8
+ # --- LangChain Specific Imports ---
9
+ from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
10
  from langchain_core.tools import tool
11
  from langchain_openai import ChatOpenAI
12
  from langgraph.checkpoint.memory import MemorySaver
13
  from langgraph.prebuilt import create_react_agent
14
 
15
+ # --- Constants ---
16
+ DEFAULT_API_URL = "http://127.0.0.1:8000" # Default URL for your FastAPI app
17
+
18
+ # --- Tools (Keep these defined globally or ensure they are included in __repr__) ---
19
  @tool
20
  def get_lat_lng(location_description: str) -> dict[str, float]:
21
  """Get the latitude and longitude of a location."""
 
35
  """Get the weather at a location."""
36
  print(f"Tool: Getting weather for lat={lat}, lng={lng}")
37
  # Replace with actual API call in a real app
 
38
  if lat > 45: # Northern locations
39
  return {"temperature": "15°C", "description": "Cloudy"}
40
  elif lat > 30: # Mid locations
 
42
  else: # Southern locations
43
  return {"temperature": "30°C", "description": "Very Sunny"}
44
 
45
+ # --- Agent Class Definition ---
46
+ class MyLangChainAgent:
47
+ """
48
+ A sample LangChain agent class designed for interaction and submission.
49
+ NOTE: The current tools (weather/location) are placeholders and WILL NOT
50
+ correctly answer GAIA benchmark questions. This class structure
51
+ demonstrates how to integrate an agent with the submission API.
52
+ Replace LLM, tools, and potentially the agent type for actual GAIA tasks.
53
+ """
54
+ def __init__(self, model_name="gpt-4", temperature=0):
55
+ # Ensure API key is available
56
+ if not os.getenv("OPENAI_API_KEY"):
57
+ raise ValueError("OPENAI_API_KEY environment variable not set.")
58
 
59
+ self.llm = ChatOpenAI(temperature=temperature, model=model_name)
60
+ self.tools = [get_lat_lng, get_weather] # Use the globally defined tools
61
+ self.memory = MemorySaver()
62
+ # Create the agent executor
63
+ self.agent_executor = create_react_agent(self.llm, self.tools, checkpointer=self.memory)
64
+ print("MyLangChainAgent initialized.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ async def __call__(self, question: str, thread_id: str) -> AsyncGenerator[Union[str, Dict[str, str]], str]:
67
+ """
68
+ Runs the agent asynchronously, yielding intermediate steps and returning the final answer.
69
+
70
+ Args:
71
+ question: The input question string.
72
+ thread_id: A unique identifier for the conversation thread.
73
+
74
+ Yields:
75
+ Intermediate steps (tool calls/results) as strings or dicts.
76
+
77
+ Returns:
78
+ The final AI answer as a string.
79
+ """
80
+ print(f"Agent executing for thread_id: {thread_id} on question: {question[:50]}...")
81
+ lc_messages: List[BaseMessage] = [HumanMessage(content=question)]
82
+ final_answer = ""
83
+ full_response_content = "" # Store the complete AI response chunks
84
+
85
+ async for chunk in self.agent_executor.astream_events(
86
+ {"messages": lc_messages},
87
+ config={"configurable": {"thread_id": thread_id}},
88
+ version="v1"
89
+ ):
90
+ event = chunk["event"]
91
+ data = chunk["data"]
92
+ # print(f"DEBUG: Event: {event}, Data Keys: {data.keys()}") # Debugging line
93
+
94
+ if event == "on_chat_model_stream":
95
+ content = data["chunk"].content
96
+ if content:
97
+ # print(f"DEBUG: AI Chunk: {content}") # Debugging line
98
+ full_response_content += content
99
+ # Yield potentially incomplete response for live typing effect if needed
100
+ # yield {"type": "stream", "content": content }
101
+
102
+ elif event == "on_tool_start":
103
+ tool_input_str = str(data.get('input', ''))
104
+ yield f"🛠️ Using tool: **{data['name']}** with input: `{tool_input_str}`"
105
+
106
+ elif event == "on_tool_end":
107
+ tool_output_str = str(data.get('output', ''))
108
+ yield f"✅ Tool **{data['name']}** finished.\nResult: `{tool_output_str}`"
109
+
110
+ # Detect the end of the conversation turn (heuristic)
111
+ # The 'on_chain_end' event for the top-level graph might signal the end.
112
+ # Or check the 'messages' list in the final state if available.
113
+ # For create_react_agent, the final AIMessage is often the last main event.
114
+ # We will capture the last full AI message content after the loop.
115
+
116
+ # After iterating through all chunks, the final answer should be in full_response_content
117
+ final_answer = full_response_content.strip()
118
+ print(f"Agent execution finished. Final Answer: {final_answer[:100]}...")
119
+ # Yield the complete final answer distinctly if needed
120
+ # yield {"type": "final_answer_marker", "content": final_answer} # Example marker
121
+ return final_answer # Return the final answer
122
+
123
+ def __repr__(self) -> str:
124
+ """
125
+ Return the source code required to reconstruct this agent, including
126
+ the class definition, tool functions, and necessary imports.
127
+ """
128
+ imports = [
129
+ "import os",
130
+ "from typing import Dict, List, AsyncGenerator, Union, Tuple, Optional",
131
+ "from langchain_core.messages import HumanMessage, AIMessage, BaseMessage",
132
+ "from langchain_core.tools import tool",
133
+ "from langchain_openai import ChatOpenAI",
134
+ "from langgraph.checkpoint.memory import MemorySaver",
135
+ "from langgraph.prebuilt import create_react_agent",
136
+ "import inspect", # Needed if repr itself uses inspect dynamically
137
+ "import asyncio", # Needed for async call
138
+ "\n"
139
+ ]
140
+ # Get source code of tool functions
141
+ tool_sources = []
142
+ for t in self.tools:
143
+ try:
144
+ tool_sources.append(inspect.getsource(t))
145
+ except (TypeError, OSError) as e:
146
+ print(f"Warning: Could not get source for tool {t.__name__}: {e}")
147
+ tool_sources.append(f"# Could not automatically get source for tool: {t.__name__}\n")
148
+
149
+ # Get source code of the class itself
150
+ class_source = inspect.getsource(MyLangChainAgent)
151
+
152
+ # Combine imports, tools, and class definition
153
+ full_source = "\n".join(imports) + "\n\n" + \
154
+ "\n\n".join(tool_sources) + "\n\n" + \
155
+ class_source
156
+ return full_source
157
+
158
+
159
+ # --- Gradio UI and Logic ---
160
+
161
+ # Initialize the agent (do this once outside the request functions)
162
+ # Handle potential API key error during initialization
163
+ try:
164
+ agent_instance = MyLangChainAgent()
165
+ except ValueError as e:
166
+ print(f"ERROR initializing agent: {e}")
167
+ # Provide a dummy agent or exit if critical
168
+ agent_instance = None # Or raise SystemExit("Agent initialization failed")
169
+
170
+ def format_chat_history(history: List[List[Optional[str]]]) -> List[Tuple[Optional[str], Optional[str]]]:
171
+ """Helper to format Gradio history for display."""
172
+ # Gradio's history format is List[List[user_msg | None, ai_msg | None]]
173
+ # We want List[Tuple[user_msg | None, ai_msg | None]] for Chatbot
174
+ formatted = []
175
+ for turn in history:
176
+ formatted.append(tuple(turn))
177
+ return formatted
178
+
179
+
180
+ async def fetch_and_display_question(api_url: str):
181
+ """Calls the backend to get a random question."""
182
+ if not api_url:
183
+ return "Please enter the API URL.", "", "", gr.update(value=""), gr.update(value="") # Clear chat too
184
+
185
+ question_url = f"{api_url.strip('/')}/random-question"
186
+ print(f"Fetching question from: {question_url}")
187
  try:
188
+ response = requests.get(question_url, timeout=10)
189
+ response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
190
+ data = response.json()
191
+ task_id = data.get("task_id")
192
+ question_text = data.get("question")
193
+ if task_id and question_text:
194
+ print(f"Fetched Task ID: {task_id}")
195
+ # Return updates for Gradio components: Status, Task ID, Question Text, Clear Agent Answer, Clear Chat
196
+ return "Question fetched successfully!", task_id, question_text, "", [] # Clears answer and chat history
197
+ else:
198
+ return "Error: Invalid data format received from API.", "", "", "", []
199
+ except requests.exceptions.RequestException as e:
200
+ print(f"Error fetching question: {e}")
201
+ return f"Error fetching question: {e}", "", "", "", []
202
+ except Exception as e:
203
+ print(f"An unexpected error occurred: {e}")
204
+ return f"An unexpected error occurred: {e}", "", "", "", []
205
+
206
+
207
+ async def run_agent_interaction(
208
+ message: str,
209
+ history: List[List[Optional[str]]],
210
+ current_task_id: str,
211
+ # agent_instance: MyLangChainAgent # Agent passed via state potentially
212
+ ):
213
+ """Handles the chat interaction, runs the agent, yields steps, updates final answer state."""
214
+ if agent_instance is None:
215
+ yield "Agent not initialized. Please check API keys and restart."
216
+ return
217
+
218
+ if not current_task_id:
219
+ yield "Please fetch a question first using the button above."
220
+ return
221
+
222
+ # The 'message' here is the user's latest input in the chat.
223
+ # For this workflow, we assume the main input is the fetched question.
224
+ # We'll use the fetched question (implicitly stored) to run the agent.
225
+ # If you want interactive chat *about* the question, the logic needs adjustment.
226
+
227
+ # For simplicity, let's assume the user's message *is* the question or a prompt related to it.
228
+ # In the GAIA context, usually, the agent just runs on the provided question directly.
229
+ # We'll use the `current_task_id` to generate a unique thread_id for LangGraph memory.
230
+ thread_id = f"gaia_task_{current_task_id}_{os.urandom(4).hex()}"
231
+
232
+ print(f"Running agent for user message: {message[:50]}...")
233
+ history.append([message, None]) # Add user message to history
234
+
235
+ final_agent_answer = None
236
+ full_yielded_response = ""
237
+
238
+ # Use the agent's __call__ method
239
+ async for step in agent_instance(message, thread_id=thread_id):
240
+ if isinstance(step, str):
241
+ # Intermediate step (tool call, result, maybe stream chunk)
242
+ history[-1][1] = step # Update the AI's response in the last turn
243
+ yield format_chat_history(history) # Update chatbot UI
244
+ full_yielded_response = step # Track last yielded message
245
+ # If __call__ yielded dicts for streaming, handle here:
246
+ # elif isinstance(step, dict) and step.get("type") == "stream":
247
+ # history[-1][1] = (history[-1][1] or "") + step["content"]
248
+ # yield format_chat_history(history)
249
+
250
+ # After the loop, the `step` variable holds the return value (final answer)
251
+ final_agent_answer = step
252
+ print(f"Agent final answer received: {final_agent_answer[:100]}...")
253
+
254
+ # Update the history with the definitive final answer
255
+ if final_agent_answer:
256
+ history[-1][1] = final_agent_answer # Replace intermediate steps with final one
257
+ elif full_yielded_response:
258
+ # Fallback if final answer wasn't returned correctly but we yielded something
259
+ history[-1][1] = full_yielded_response
260
+ final_agent_answer = full_yielded_response # Use the last yielded message as answer
261
+ else:
262
+ history[-1][1] = "Agent did not produce a final answer."
263
+ final_agent_answer = "" # Ensure it's a string
264
+
265
+ # Yield the final state of the history and update the hidden state for the final answer
266
+ yield format_chat_history(history), final_agent_answer
267
+
268
+
269
+ def submit_to_leaderboard(
270
+ api_url: str,
271
+ username: str,
272
+ task_id: str,
273
+ agent_answer: str,
274
+ # agent_instance: MyLangChainAgent # Pass agent via state if needed
275
+ ):
276
+ """Submits the agent's answer and code to the FastAPI backend."""
277
+ if agent_instance is None:
278
+ return "Agent not initialized. Cannot submit."
279
+ if not api_url:
280
+ return "Please enter the API URL."
281
+ if not username:
282
+ return "Please enter your Hugging Face username."
283
+ if not task_id:
284
+ return "No task ID available. Please fetch a question first."
285
+ if agent_answer is None or agent_answer.strip() == "": # Check if None or empty
286
+ # Maybe allow submission of empty answer? Depends on requirements.
287
+ print("Warning: Submitting empty answer.")
288
+ # return "Agent has not provided an answer yet."
289
+
290
+
291
+ submit_url = f"{api_url.strip('/')}/submit"
292
+ print(f"Submitting to: {submit_url}")
293
+
294
+ # Get agent code
295
+ try:
296
+ agent_code = agent_instance.__repr__()
297
+ # print(f"Agent Code (first 200 chars):\n{agent_code[:200]}...") # Debug
298
+ except Exception as e:
299
+ print(f"Error getting agent representation: {e}")
300
+ return f"Error generating agent code for submission: {e}"
301
+
302
+ # Prepare submission data according to Pydantic model in FastAPI
303
+ submission_data = {
304
+ "username": username.strip(),
305
+ "agent_code": agent_code,
306
+ "answers": [
307
+ {
308
+ "task_id": task_id,
309
+ "submitted_answer": agent_answer # Use the stored final answer
310
+ }
311
+ # Add more answers here if submitting a batch
312
+ ]
313
+ }
314
+
315
+ try:
316
+ response = requests.post(submit_url, json=submission_data, timeout=30)
317
+ response.raise_for_status()
318
+ result_data = response.json()
319
+ # Format the result nicely for display
320
+ result_message = (
321
+ f"Submission Successful!\n"
322
+ f"User: {result_data.get('username')}\n"
323
+ f"Score: {result_data.get('score')}\n"
324
+ f"Correct: {result_data.get('correct_count')}/{result_data.get('total_attempted')}\n"
325
+ f"Message: {result_data.get('message')}\n"
326
+ f"Timestamp: {result_data.get('timestamp')}"
327
+ )
328
+ print("Submission successful.")
329
+ return result_message
330
+ except requests.exceptions.HTTPError as e:
331
+ # Try to get detail from response body if available
332
+ error_detail = e.response.text
333
+ try:
334
+ error_json = e.response.json()
335
+ error_detail = error_json.get('detail', error_detail)
336
+ except requests.exceptions.JSONDecodeError:
337
+ pass # Keep the raw text if not JSON
338
+ print(f"HTTP Error during submission: {e.response.status_code} - {error_detail}")
339
+ return f"Submission Failed (HTTP {e.response.status_code}): {error_detail}"
340
+ except requests.exceptions.RequestException as e:
341
+ print(f"Network error during submission: {e}")
342
+ return f"Submission Failed: Network error - {e}"
343
+ except Exception as e:
344
+ print(f"An unexpected error occurred during submission: {e}")
345
+ return f"Submission Failed: An unexpected error occurred - {e}"
346
+
347
+
348
+ # --- Build Gradio Interface using Blocks ---
349
+ with gr.Blocks() as demo:
350
+ gr.Markdown("# Agent Evaluation Interface")
351
+ gr.Markdown(
352
+ "Fetch a random question from the evaluation API, interact with the agent "
353
+ "(Note: the default agent answers weather questions, not GAIA), "
354
+ "and submit the agent's final answer to the leaderboard."
355
+ )
356
+
357
+ # --- State Variables ---
358
+ # Store current task info, agent's final answer, and the agent instance
359
+ current_task_id = gr.State("")
360
+ current_question_text = gr.State("")
361
+ current_agent_answer = gr.State("") # Stores the final answer string from the agent
362
+ # agent_state = gr.State(agent_instance) # Pass agent instance via state
363
+
364
+ with gr.Row():
365
+ api_url_input = gr.Textbox(label="FastAPI API URL", value=DEFAULT_API_URL)
366
+ hf_username_input = gr.Textbox(label="Hugging Face Username")
367
+
368
+ with gr.Row():
369
+ fetch_button = gr.Button("Get Random Question")
370
+ submission_status_display = gr.Textbox(label="Status", interactive=False) # For fetch status
371
+
372
+ with gr.Row():
373
+ question_display = gr.Textbox(label="Current Question", lines=3, interactive=False)
374
+
375
+ gr.Markdown("---")
376
+ gr.Markdown("## Agent Interaction")
377
+
378
+ chatbot = gr.Chatbot(label="Agent Conversation", height=400)
379
+ msg_input = gr.Textbox(label="Send a message to the Agent (or just observe)") # Input for chat
380
+
381
+ # Hidden Textbox to display the final extracted answer (optional, for clarity)
382
+ final_answer_display = gr.Textbox(label="Agent's Final Answer (Extracted)", interactive=False)
383
+
384
+ gr.Markdown("---")
385
+ gr.Markdown("## Submission")
386
+ with gr.Row():
387
+ submit_button = gr.Button("Submit Current Answer to Leaderboard")
388
+
389
+ submission_result_display = gr.Markdown(label="Submission Result", value="*Submit an answer to see the result here.*") # Use Markdown for better formatting
390
+
391
+
392
+ # --- Component Interactions ---
393
+
394
+ # Fetch Button Action
395
+ fetch_button.click(
396
+ fn=fetch_and_display_question,
397
+ inputs=[api_url_input],
398
+ outputs=[
399
+ submission_status_display, # Shows fetch status
400
+ current_task_id, # Updates hidden state
401
+ question_display, # Updates question text box
402
+ final_answer_display, # Clears old final answer
403
+ chatbot # Clears chat history
404
+ ]
405
+ )
406
+
407
+ # Chat Submission Action (when user sends message in chat)
408
+ msg_input.submit(
409
+ fn=run_agent_interaction,
410
+ inputs=[
411
+ msg_input, # User message from chat input
412
+ chatbot, # Current chat history
413
+ current_task_id, # Current task ID from state
414
+ # agent_state # Pass agent instance state
415
+ ],
416
+ outputs=[
417
+ chatbot, # Updated chat history
418
+ current_agent_answer # Update the hidden state holding the final answer
419
+ ]
420
+ ).then(
421
+ # After agent runs, update the visible "Final Answer" box from the state
422
+ lambda answer_state: answer_state,
423
+ inputs=[current_agent_answer],
424
+ outputs=[final_answer_display]
425
+ )
426
+
427
+ # Clear message input after submission
428
+ msg_input.submit(lambda: "", None, msg_input, queue=False)
429
+
430
+
431
+ # Submit Button Action
432
+ submit_button.click(
433
+ fn=submit_to_leaderboard,
434
+ inputs=[
435
+ api_url_input,
436
+ hf_username_input,
437
+ current_task_id,
438
+ current_agent_answer, # Use the stored final answer state
439
+ # agent_state # Pass agent instance state
440
+ ],
441
+ outputs=[submission_result_display] # Display result message
442
+ )
443
+
444
+
445
+ if __name__ == "__main__":
446
+ if agent_instance is None:
447
+ print("\nFATAL: Agent could not be initialized. Gradio app will not run correctly.")
448
+ print("Please ensure OPENAI_API_KEY is set and valid.\n")
449
+ # Optionally exit here if agent is critical
450
+ # exit(1)
451
+ else:
452
+ print("Launching Gradio Interface...")
453
+ demo.launch(debug=True, server_name="0.0.0.0") # Share=False by default for security