Mehedi2 commited on
Commit
4dcf91a
·
verified ·
1 Parent(s): 3b07081

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -120
app.py CHANGED
@@ -1,12 +1,13 @@
1
  import os
2
- import gradio as gr
 
3
  import requests
4
- import inspect
5
  import pandas as pd
6
- import re
7
  from pathlib import Path
8
  from typing import Optional
9
  from dotenv import load_dotenv
 
 
10
 
11
  from langgraph.prebuilt import create_react_agent
12
  from langchain_core.messages import HumanMessage
@@ -15,13 +16,13 @@ from langchain_openai import ChatOpenAI
15
 
16
  load_dotenv()
17
 
18
- # ------------------ CUSTOM LLM ------------------
19
-
20
  class OpenRouterLLM(ChatOpenAI):
21
  """Custom OpenRouter LLM wrapper for LangGraph"""
22
-
23
  def __init__(self, model: str = "deepseek/deepseek-v3.1-terminus", **kwargs):
24
- api_key = os.getenv("OPENROUTER_API_KEY") or os.getenv("my_key")
 
 
25
  super().__init__(
26
  model=model,
27
  openai_api_key=api_key,
@@ -31,43 +32,54 @@ class OpenRouterLLM(ChatOpenAI):
31
 
32
  # ------------------ TOOLS ------------------
33
 
 
 
34
  @tool
35
  def search_web(query: str) -> str:
36
- """Search the web using DuckDuckGo and return current information."""
 
 
 
 
37
  try:
38
- search_url = f"https://api.duckduckgo.com/?q={query}&format=json&no_html=1&skip_disambig=1"
39
- response = requests.get(search_url, timeout=10)
40
- if response.status_code == 200:
41
- data = response.json()
42
- results = []
43
- if data.get("AbstractText"):
44
- results.append(f"Abstract: {data['AbstractText']}")
45
- if data.get("RelatedTopics"):
46
- for topic in data["RelatedTopics"][:3]:
47
- if isinstance(topic, dict) and topic.get("Text"):
48
- results.append(f"Related: {topic['Text']}")
49
- return "\n".join(results) if results else f"No results for '{query}'."
50
- return f"Search failed with status code {response.status_code}"
51
  except Exception as e:
52
- return f"Search error: {str(e)}"
53
 
54
  @tool
55
  def search_wikipedia(query: str) -> str:
56
- """Search Wikipedia for factual information and return a short summary."""
57
  try:
58
- search_url = "https://en.wikipedia.org/api/rest_v1/page/summary/" + query.replace(" ", "_")
59
- response = requests.get(search_url, timeout=10)
60
- if response.status_code == 200:
61
- data = response.json()
62
- extract = data.get("extract", "")
63
- return f"Wikipedia: {extract[:500]}..." if extract else f"No extract for '{query}'."
64
- return f"Wikipedia search failed for '{query}'"
 
 
 
 
 
 
 
 
65
  except Exception as e:
66
  return f"Wikipedia search error: {str(e)}"
67
 
68
  @tool
69
  def execute_python(code: str) -> str:
70
- """Execute Python code safely and return the output or error message."""
71
  try:
72
  safe_globals = {
73
  '__builtins__': {
@@ -95,7 +107,7 @@ def execute_python(code: str) -> str:
95
 
96
  @tool
97
  def read_excel_file(file_path: str, sheet_name: Optional[str] = None) -> str:
98
- """Read an Excel file and return its contents as a formatted string."""
99
  try:
100
  file_path_obj = Path(file_path)
101
  if not file_path_obj.exists():
@@ -119,7 +131,7 @@ def read_excel_file(file_path: str, sheet_name: Optional[str] = None) -> str:
119
 
120
  @tool
121
  def read_text_file(file_path: str) -> str:
122
- """Read a text file and return its contents as a string."""
123
  try:
124
  file_path_obj = Path(file_path)
125
  if not file_path_obj.exists():
@@ -128,7 +140,7 @@ def read_text_file(file_path: str) -> str:
128
  for encoding in encodings:
129
  try:
130
  with open(file_path_obj, 'r', encoding=encoding) as f:
131
- return f"File content ({encoding} encoding):\n\n{f.read()}"
132
  except UnicodeDecodeError:
133
  continue
134
  return "Error: Could not decode file with any standard encoding"
@@ -136,12 +148,10 @@ def read_text_file(file_path: str) -> str:
136
  return f"Error reading file: {str(e)}"
137
 
138
  # ------------------ GAIA AGENT ------------------
139
-
140
  class GaiaAgent:
141
- """LangGraph-based agent using OpenRouter DeepSeek"""
142
-
143
  def __init__(self):
144
- print("Initializing GaiaAgent...")
145
  self.llm = OpenRouterLLM(
146
  model="deepseek/deepseek-v3.1-terminus",
147
  temperature=0.1,
@@ -149,6 +159,8 @@ class GaiaAgent:
149
  )
150
  self.tools = [search_web, search_wikipedia, execute_python, read_excel_file, read_text_file]
151
  prompt_modifier = self._get_system_prompt()
 
 
152
  sig = inspect.signature(create_react_agent)
153
  accepted = sig.parameters.keys()
154
  kwargs = {}
@@ -158,18 +170,24 @@ class GaiaAgent:
158
  kwargs["state_modifier"] = prompt_modifier
159
  elif "prompt" in accepted:
160
  kwargs["prompt"] = prompt_modifier
 
161
  self.agent = create_react_agent(self.llm, self.tools, **kwargs)
162
  print("GaiaAgent initialized successfully!")
163
 
164
  def _get_system_prompt(self) -> str:
165
- return """You are an advanced AI agent designed to answer complex questions.
166
- Keep answers concise and factual where possible."""
167
 
168
- def __call__(self, question: str) -> str:
169
  try:
170
- messages = [HumanMessage(content=question)]
 
 
 
 
 
171
  result = self.agent.invoke({"messages": messages})
172
- answer = result["messages"][-1].content
 
173
  return self._clean_answer(answer)
174
  except Exception as e:
175
  return f"Agent error: {e}"
@@ -180,94 +198,25 @@ Keep answers concise and factual where possible."""
180
  parts = re.split(r'final answer:', answer, flags=re.IGNORECASE)
181
  if len(parts) > 1:
182
  answer = parts[-1].strip()
183
- prefixes = ["The answer is", "Answer:", "Result:", "Solution:"]
 
184
  for prefix in prefixes:
185
  if answer.lower().startswith(prefix.lower()):
186
  answer = answer[len(prefix):].strip()
187
  if answer.startswith(':'):
188
  answer = answer[1:].strip()
189
  break
 
 
190
  return answer
191
 
192
- # ------------------ RUN AND SUBMIT ------------------
193
-
194
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
195
-
196
- def run_and_submit_all(profile: gr.OAuthProfile | None):
197
- if profile:
198
- username = profile.username
199
- else:
200
- return "Please login to Hugging Face first.", None
201
-
202
- space_id = os.getenv("SPACE_ID") or "your_space_username/your_space_name"
203
- api_url = DEFAULT_API_URL
204
- questions_url = f"{api_url}/questions"
205
- submit_url = f"{api_url}/submit"
206
-
207
- # Instantiate GaiaAgent
208
- try:
209
- agent = GaiaAgent()
210
- except Exception as e:
211
- return f"Error initializing GaiaAgent: {e}", None
212
-
213
- # Fetch questions
214
- try:
215
- response = requests.get(questions_url, timeout=15)
216
- response.raise_for_status()
217
- questions_data = response.json()
218
- except Exception as e:
219
- return f"Error fetching questions: {e}", None
220
-
221
- # Run agent on questions
222
- answers_payload = []
223
- results_log = []
224
- for item in questions_data:
225
- task_id = item.get("task_id")
226
- question_text = item.get("question")
227
- if not task_id or question_text is None:
228
- continue
229
- try:
230
- submitted_answer = agent(question_text)
231
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
232
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
233
- except Exception as e:
234
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
235
-
236
- # Prepare submission
237
- agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
238
- submission_data = {"username": username, "agent_code": agent_code, "answers": answers_payload}
239
-
240
- # Submit answers
241
- try:
242
- response = requests.post(submit_url, json=submission_data, timeout=60)
243
- response.raise_for_status()
244
- result_data = response.json()
245
- final_status = (
246
- f"Submission Successful!\n"
247
- f"User: {result_data.get('username')}\n"
248
- f"Overall Score: {result_data.get('score', 'N/A')}% "
249
- f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
250
- f"Message: {result_data.get('message', 'No message received.')}"
251
- )
252
- results_df = pd.DataFrame(results_log)
253
- return final_status, results_df
254
- except Exception as e:
255
- results_df = pd.DataFrame(results_log)
256
- return f"Submission failed: {e}", results_df
257
-
258
  # ------------------ GRADIO INTERFACE ------------------
 
259
 
260
- with gr.Blocks() as demo:
261
- gr.Markdown("# GAIA Agent Evaluation Runner")
262
- gr.LoginButton()
263
- run_button = gr.Button("Run Evaluation & Submit All Answers")
264
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
265
- results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
266
 
267
- run_button.click(
268
- fn=run_and_submit_all,
269
- outputs=[status_output, results_table]
270
- )
271
 
272
  if __name__ == "__main__":
273
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import os
2
+ import re
3
+ import json
4
  import requests
 
5
  import pandas as pd
 
6
  from pathlib import Path
7
  from typing import Optional
8
  from dotenv import load_dotenv
9
+ import inspect
10
+ import gradio as gr
11
 
12
  from langgraph.prebuilt import create_react_agent
13
  from langchain_core.messages import HumanMessage
 
16
 
17
  load_dotenv()
18
 
19
+ # ------------------ LLM ------------------
 
20
  class OpenRouterLLM(ChatOpenAI):
21
  """Custom OpenRouter LLM wrapper for LangGraph"""
 
22
  def __init__(self, model: str = "deepseek/deepseek-v3.1-terminus", **kwargs):
23
+ api_key = os.getenv("OPENROUTER_API_KEY")
24
+ if not api_key:
25
+ raise ValueError("OPENROUTER_API_KEY not set in environment variables.")
26
  super().__init__(
27
  model=model,
28
  openai_api_key=api_key,
 
32
 
33
  # ------------------ TOOLS ------------------
34
 
35
+ SERPAPI_KEY = os.getenv("SERPAPI_KEY")
36
+
37
  @tool
38
  def search_web(query: str) -> str:
39
+ """Perform a reliable web search using SerpAPI."""
40
+ if not SERPAPI_KEY:
41
+ return "Error: SERPAPI_KEY not set."
42
+ search_url = "https://serpapi.com/search.json"
43
+ params = {"q": query, "api_key": SERPAPI_KEY, "num": 3}
44
  try:
45
+ response = requests.get(search_url, params=params, timeout=10)
46
+ response.raise_for_status()
47
+ data = response.json()
48
+ results = []
49
+ for item in data.get("organic_results", []):
50
+ title = item.get("title", "")
51
+ snippet = item.get("snippet", "")
52
+ link = item.get("link", "")
53
+ results.append(f"{title}\n{snippet}\n{link}")
54
+ return "\n\n".join(results) if results else f"No results for '{query}'."
 
 
 
55
  except Exception as e:
56
+ return f"Web search error: {str(e)}"
57
 
58
  @tool
59
  def search_wikipedia(query: str) -> str:
60
+ """Retrieve full Wikipedia article text."""
61
  try:
62
+ url = f"https://en.wikipedia.org/w/api.php"
63
+ params = {
64
+ "action": "query",
65
+ "format": "json",
66
+ "prop": "extracts",
67
+ "explaintext": True,
68
+ "titles": query
69
+ }
70
+ response = requests.get(url, params=params, timeout=10)
71
+ response.raise_for_status()
72
+ pages = response.json()["query"]["pages"]
73
+ text = next(iter(pages.values())).get("extract", "")
74
+ if not text:
75
+ return f"No Wikipedia content found for '{query}'."
76
+ return text[:2000] # truncate if too long
77
  except Exception as e:
78
  return f"Wikipedia search error: {str(e)}"
79
 
80
  @tool
81
  def execute_python(code: str) -> str:
82
+ """Execute Python code safely and return output."""
83
  try:
84
  safe_globals = {
85
  '__builtins__': {
 
107
 
108
  @tool
109
  def read_excel_file(file_path: str, sheet_name: Optional[str] = None) -> str:
110
+ """Read an Excel file and return contents."""
111
  try:
112
  file_path_obj = Path(file_path)
113
  if not file_path_obj.exists():
 
131
 
132
  @tool
133
  def read_text_file(file_path: str) -> str:
134
+ """Read a text file and return contents."""
135
  try:
136
  file_path_obj = Path(file_path)
137
  if not file_path_obj.exists():
 
140
  for encoding in encodings:
141
  try:
142
  with open(file_path_obj, 'r', encoding=encoding) as f:
143
+ return f.read()
144
  except UnicodeDecodeError:
145
  continue
146
  return "Error: Could not decode file with any standard encoding"
 
148
  return f"Error reading file: {str(e)}"
149
 
150
  # ------------------ GAIA AGENT ------------------
 
151
  class GaiaAgent:
152
+ """LangGraph-based agent with DeepSeek and enhanced tools."""
 
153
  def __init__(self):
154
+ print("Initializing GaiaAgent with LangGraph and OpenRouter DeepSeek...")
155
  self.llm = OpenRouterLLM(
156
  model="deepseek/deepseek-v3.1-terminus",
157
  temperature=0.1,
 
159
  )
160
  self.tools = [search_web, search_wikipedia, execute_python, read_excel_file, read_text_file]
161
  prompt_modifier = self._get_system_prompt()
162
+
163
+ # Detect correct kwarg for your LangGraph version
164
  sig = inspect.signature(create_react_agent)
165
  accepted = sig.parameters.keys()
166
  kwargs = {}
 
170
  kwargs["state_modifier"] = prompt_modifier
171
  elif "prompt" in accepted:
172
  kwargs["prompt"] = prompt_modifier
173
+
174
  self.agent = create_react_agent(self.llm, self.tools, **kwargs)
175
  print("GaiaAgent initialized successfully!")
176
 
177
  def _get_system_prompt(self) -> str:
178
+ return """You are an advanced AI agent designed to answer complex questions using all available tools, including web search, Wikipedia, Python execution, Excel and text file reading."""
 
179
 
180
+ def __call__(self, task_id: str, question: str) -> str:
181
  try:
182
+ print(f"Processing task {task_id}: {question[:100]}...")
183
+ # Combine context from tools for better answers
184
+ wiki_text = search_wikipedia(question)
185
+ web_text = search_web(question)
186
+ combined_input = f"{wiki_text}\n\n{web_text}\n\nQuestion: {question}"
187
+ messages = [HumanMessage(content=combined_input)]
188
  result = self.agent.invoke({"messages": messages})
189
+ final_message = result["messages"][-1]
190
+ answer = final_message.content
191
  return self._clean_answer(answer)
192
  except Exception as e:
193
  return f"Agent error: {e}"
 
198
  parts = re.split(r'final answer:', answer, flags=re.IGNORECASE)
199
  if len(parts) > 1:
200
  answer = parts[-1].strip()
201
+ prefixes = ["The answer is", "Answer:", "Result:", "Solution:",
202
+ "Based on", "Therefore", "In conclusion", "So the answer is"]
203
  for prefix in prefixes:
204
  if answer.lower().startswith(prefix.lower()):
205
  answer = answer[len(prefix):].strip()
206
  if answer.startswith(':'):
207
  answer = answer[1:].strip()
208
  break
209
+ if len(answer.split()) <= 3:
210
+ answer = answer.strip('"\'.')
211
  return answer
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  # ------------------ GRADIO INTERFACE ------------------
214
+ agent = GaiaAgent()
215
 
216
+ def run_agent(prompt: str) -> str:
217
+ return agent("gaia_task", prompt)
 
 
 
 
218
 
219
+ demo = gr.Interface(fn=run_agent, inputs="text", outputs="text", title="GAIA Agent")
 
 
 
220
 
221
  if __name__ == "__main__":
222
  demo.launch(server_name="0.0.0.0", server_port=7860)