LamiaYT commited on
Commit
835936b
·
1 Parent(s): cfbb337
Files changed (5) hide show
  1. agent.py +180 -0
  2. app.py +55 -1014
  3. metadata.jsonl +0 -0
  4. requirements.txt +20 -12
  5. system_prompt.txt +5 -0
agent.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+
4
+ # Load environment variables
5
+ load_dotenv()
6
+
7
+ # Set protobuf implementation to avoid C++ extension issues
8
+ os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
9
+
10
+ # Load keys from environment
11
+ groq_api_key = os.getenv("GROQ_API_KEY")
12
+ serper_api_key = os.getenv("SERPER_API_KEY")
13
+ hf_token = os.getenv("HUGGINGFACE_INFERENCE_TOKEN")
14
+
15
+ # ---- Imports ----
16
+ from langgraph.graph import START, StateGraph, MessagesState
17
+ from langgraph.prebuilt import tools_condition, ToolNode
18
+ from langchain_google_genai import ChatGoogleGenerativeAI
19
+ from langchain_groq import ChatGroq
20
+ from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint, HuggingFaceEmbeddings
21
+ from langchain_community.tools.tavily_search import TavilySearchResults
22
+ from langchain_community.document_loaders import WikipediaLoader, ArxivLoader
23
+ from langchain_community.vectorstores import Chroma
24
+ from langchain_core.documents import Document
25
+ from langchain_core.messages import SystemMessage, HumanMessage
26
+ from langchain_core.tools import tool
27
+ from langchain.tools.retriever import create_retriever_tool
28
+ from langchain.vectorstores import Chroma
29
+ from langchain.embeddings import HuggingFaceEmbeddings
30
+ from langchain.schema import Document
31
+ import json
32
+
33
+ # ---- Tools ----
34
+
35
+ @tool
36
+ def multiply(a: int, b: int) -> int:
37
+ return a * b
38
+
39
+ @tool
40
+ def add(a: int, b: int) -> int:
41
+ return a + b
42
+
43
+ @tool
44
+ def subtract(a: int, b: int) -> int:
45
+ return a - b
46
+
47
+ @tool
48
+ def divide(a: int, b: int) -> float:
49
+ if b == 0:
50
+ raise ValueError("Cannot divide by zero.")
51
+ return a / b
52
+
53
+ @tool
54
+ def modulus(a: int, b: int) -> int:
55
+ return a % b
56
+
57
+ @tool
58
+ def wiki_search(query: str) -> str:
59
+ search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
60
+ formatted = "\n\n---\n\n".join(
61
+ [
62
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
63
+ for doc in search_docs
64
+ ]
65
+ )
66
+ return {"wiki_results": formatted}
67
+
68
+ @tool
69
+ def web_search(query: str) -> str:
70
+ search_docs = TavilySearchResults(max_results=3).invoke(query=query)
71
+ formatted = "\n\n---\n\n".join(
72
+ [
73
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
74
+ for doc in search_docs
75
+ ]
76
+ )
77
+ return {"web_results": formatted}
78
+
79
+ @tool
80
+ def arvix_search(query: str) -> str:
81
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
82
+ formatted = "\n\n---\n\n".join(
83
+ [
84
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
85
+ for doc in search_docs
86
+ ]
87
+ )
88
+ return {"arvix_results": formatted}
89
+
90
+ # ---- Embedding & Vector Store Setup ----
91
+
92
+ embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
93
+
94
+ json_QA = []
95
+ with open('metadata.jsonl', 'r') as jsonl_file:
96
+ for line in jsonl_file:
97
+ json_QA.append(json.loads(line))
98
+
99
+ documents = [
100
+ Document(
101
+ page_content=f"Question : {sample['Question']}\n\nFinal answer : {sample['Final answer']}",
102
+ metadata={"source": sample["task_id"]}
103
+ )
104
+ for sample in json_QA
105
+ ]
106
+
107
+ vector_store = Chroma.from_documents(
108
+ documents=documents,
109
+ embedding=embeddings,
110
+ persist_directory="./chroma_db",
111
+ collection_name="my_collection"
112
+ )
113
+ vector_store.persist()
114
+ print("Documents inserted:", vector_store._collection.count())
115
+
116
+ @tool
117
+ def similar_question_search(query: str) -> str:
118
+ matched_docs = vector_store.similarity_search(query, 3)
119
+ formatted = "\n\n---\n\n".join(
120
+ [
121
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
122
+ for doc in matched_docs
123
+ ]
124
+ )
125
+ return {"similar_questions": formatted}
126
+
127
+ # ---- System Prompt ----
128
+
129
+ system_prompt = """
130
+ You are a helpful assistant tasked with answering questions using a set of tools.
131
+ Now, I will ask you a question. Report your thoughts, and finish your answer with the following template:
132
+ FINAL ANSWER: [YOUR FINAL ANSWER].
133
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings...
134
+ """
135
+
136
+ sys_msg = SystemMessage(content=system_prompt)
137
+
138
+ # ---- Tool List ----
139
+
140
+ tools = [
141
+ multiply, add, subtract, divide, modulus,
142
+ wiki_search, web_search, arvix_search, similar_question_search
143
+ ]
144
+
145
+ # ---- Graph Definition ----
146
+
147
+ def build_graph(provider: str = "groq"):
148
+ if provider == "groq":
149
+ llm = ChatGroq(model="qwen-qwq-32b", temperature=0, api_key=groq_api_key)
150
+ elif provider == "google":
151
+ llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
152
+ elif provider == "huggingface":
153
+ llm = ChatHuggingFace(
154
+ llm=HuggingFaceEndpoint(repo_id="mosaicml/mpt-30b", temperature=0)
155
+ )
156
+ else:
157
+ raise ValueError("Invalid provider: choose 'groq', 'google', or 'huggingface'.")
158
+
159
+ llm_with_tools = llm.bind_tools(tools)
160
+
161
+ def assistant(state: MessagesState):
162
+ return {"messages": [llm_with_tools.invoke(state["messages"])]}
163
+
164
+ def retriever(state: MessagesState):
165
+ similar = vector_store.similarity_search(state["messages"][0].content)
166
+ if similar:
167
+ example_msg = HumanMessage(content=f"Here is a similar question:\n\n{similar[0].page_content}")
168
+ return {"messages": [sys_msg] + state["messages"] + [example_msg]}
169
+ return {"messages": [sys_msg] + state["messages"]}
170
+
171
+ builder = StateGraph(MessagesState)
172
+ builder.add_node("retriever", retriever)
173
+ builder.add_node("assistant", assistant)
174
+ builder.add_node("tools", ToolNode(tools))
175
+ builder.add_edge(START, "retriever")
176
+ builder.add_edge("retriever", "assistant")
177
+ builder.add_conditional_edges("assistant", tools_condition)
178
+ builder.add_edge("tools", "assistant")
179
+
180
+ return builder.compile()
app.py CHANGED
@@ -1,983 +1,40 @@
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
- import json
6
- import re
7
- import time
8
- from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, tool
9
- from typing import Dict, Any, List, Optional, Union
10
- import base64
11
- from io import BytesIO
12
- from PIL import Image
13
- import numpy as np
14
- import urllib.parse
15
- from datetime import datetime, timedelta
16
- import math
17
 
 
18
  # --- Constants ---
19
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
20
 
21
- # --- Enhanced Custom Tools ---
22
-
23
- @tool
24
- def serper_search(query: str) -> str:
25
- """Enhanced web search using Serper API with comprehensive result processing.
26
-
27
- Args:
28
- query (str): The search query to be executed.
29
-
30
- Returns:
31
- str: Detailed search results with structured information.
32
- """
33
- try:
34
- api_key = os.getenv("SERPER_API_KEY")
35
- if not api_key:
36
- return "SERPER_API_KEY environment variable not found"
37
-
38
- url = "https://google.serper.dev/search"
39
- payload = json.dumps({
40
- "q": query,
41
- "num": 12,
42
- "hl": "en",
43
- "gl": "us"
44
- })
45
- headers = {
46
- 'X-API-KEY': api_key,
47
- 'Content-Type': 'application/json'
48
- }
49
-
50
- response = requests.post(url, headers=headers, data=payload, timeout=30)
51
- response.raise_for_status()
52
-
53
- data = response.json()
54
- results = []
55
-
56
- # Knowledge Graph extraction
57
- if 'knowledgeGraph' in data:
58
- kg = data['knowledgeGraph']
59
- kg_info = f"KNOWLEDGE GRAPH:\nTitle: {kg.get('title', 'N/A')}\nDescription: {kg.get('description', 'N/A')}"
60
-
61
- if 'attributes' in kg and kg['attributes']:
62
- kg_info += "\nKey Facts:"
63
- for key, value in list(kg['attributes'].items())[:5]:
64
- kg_info += f"\n• {key}: {value}"
65
-
66
- if 'entityType' in kg:
67
- kg_info += f"\nType: {kg['entityType']}"
68
-
69
- results.append(kg_info + "\n")
70
-
71
- # Organic search results
72
- if 'organic' in data:
73
- for i, item in enumerate(data['organic'][:8]):
74
- title = item.get('title', 'No title')
75
- snippet = item.get('snippet', 'No snippet')
76
- link = item.get('link', 'No link')
77
-
78
- result_text = f"RESULT {i+1}:\nTitle: {title}\nSnippet: {snippet}\nURL: {link}"
79
-
80
- # Extract specific data patterns
81
- if re.search(r'\b(19|20)\d{2}\b', snippet):
82
- years = re.findall(r'\b(19|20)\d{2}\b', snippet)
83
- result_text += f"\nYears mentioned: {', '.join(set(years))}"
84
-
85
- if re.search(r'\$[\d,]+(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:million|billion|thousand)', snippet, re.IGNORECASE):
86
- amounts = re.findall(r'\$[\d,]+(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(?:million|billion|thousand)', snippet, re.IGNORECASE)
87
- result_text += f"\nAmounts: {', '.join(amounts[:3])}"
88
-
89
- if re.search(r'\b\d+(?:\.\d+)?\s*(?:albums?|songs?|tracks?|records?)\b', snippet, re.IGNORECASE):
90
- music_counts = re.findall(r'\b\d+(?:\.\d+)?\s*(?:albums?|songs?|tracks?|records?)\b', snippet, re.IGNORECASE)
91
- result_text += f"\nMusic counts: {', '.join(music_counts[:3])}"
92
-
93
- results.append(result_text)
94
-
95
- # People Also Ask section
96
- if 'peopleAlsoAsk' in data:
97
- paa = "\nPEOPLE ALSO ASK:"
98
- for item in data['peopleAlsoAsk'][:4]:
99
- question = item.get('question', '')
100
- answer = item.get('snippet', '')
101
- paa += f"\nQ: {question}\nA: {answer[:150]}..."
102
- results.append(paa)
103
-
104
- # News results if available
105
- if 'news' in data:
106
- news_section = "\nNEWS RESULTS:"
107
- for item in data['news'][:3]:
108
- title = item.get('title', '')
109
- snippet = item.get('snippet', '')
110
- date = item.get('date', '')
111
- news_section += f"\n• {title} ({date}): {snippet[:100]}..."
112
- results.append(news_section)
113
-
114
- return "\n\n".join(results) if results else "No search results found"
115
-
116
- except Exception as e:
117
- return f"Search error: {str(e)}"
118
-
119
-
120
- @tool
121
- def wikipedia_search(query: str) -> str:
122
- """Comprehensive Wikipedia search with multiple API endpoints.
123
-
124
- Args:
125
- query (str): Wikipedia search query.
126
-
127
- Returns:
128
- str: Detailed Wikipedia information.
129
- """
130
- try:
131
- results = []
132
-
133
- # Direct page lookup
134
- clean_query = urllib.parse.quote(query.replace(" ", "_"))
135
- direct_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{clean_query}"
136
-
137
- try:
138
- response = requests.get(direct_url, timeout=15)
139
- if response.status_code == 200:
140
- data = response.json()
141
- if data.get('type') != 'disambiguation':
142
- summary = f"WIKIPEDIA DIRECT MATCH:\nTitle: {data.get('title', 'N/A')}"
143
- extract = data.get('extract', '')
144
- summary += f"\nExtract: {extract}"
145
-
146
- # Extract key dates and facts
147
- if extract:
148
- birth_dates = re.findall(r'born[^)]*?(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
149
- if birth_dates:
150
- summary += f"\nBirth: {birth_dates[0]}"
151
-
152
- death_dates = re.findall(r'died[^)]*?(\d{1,2}\s+\w+\s+\d{4})', extract, re.IGNORECASE)
153
- if death_dates:
154
- summary += f"\nDeath: {death_dates[0]}"
155
-
156
- # Extract discography info
157
- album_counts = re.findall(r'(\d+)\s+(?:studio\s+)?albums?', extract, re.IGNORECASE)
158
- if album_counts:
159
- summary += f"\nAlbums mentioned: {', '.join(album_counts)}"
160
-
161
- if 'coordinates' in data:
162
- coords = data['coordinates']
163
- summary += f"\nCoordinates: {coords.get('lat', '')}, {coords.get('lon', '')}"
164
-
165
- results.append(summary)
166
- except:
167
- pass
168
-
169
- # Search API
170
- search_url = "https://en.wikipedia.org/w/api.php"
171
- search_params = {
172
- "action": "query",
173
- "format": "json",
174
- "list": "search",
175
- "srsearch": query,
176
- "srlimit": 8,
177
- "srprop": "snippet|titlesnippet|size|wordcount"
178
- }
179
-
180
- try:
181
- response = requests.get(search_url, params=search_params, timeout=15)
182
- data = response.json()
183
-
184
- if 'query' in data and 'search' in data['query']:
185
- search_results = "WIKIPEDIA SEARCH RESULTS:"
186
- for i, item in enumerate(data['query']['search']):
187
- title = item.get('title', '')
188
- snippet = re.sub(r'<[^>]+>', '', item.get('snippet', ''))
189
- wordcount = item.get('wordcount', 0)
190
-
191
- search_results += f"\n{i+1}. {title} ({wordcount} words)"
192
- if snippet:
193
- search_results += f"\n {snippet[:200]}..."
194
-
195
- results.append(search_results)
196
- except:
197
- pass
198
-
199
- # Category search for specific topics
200
- if any(term in query.lower() for term in ['dinosaur', 'paleontology', 'fossil']):
201
- try:
202
- category_params = {
203
- "action": "query",
204
- "format": "json",
205
- "list": "categorymembers",
206
- "cmtitle": "Category:Dinosaurs",
207
- "cmlimit": 5
208
- }
209
- response = requests.get(search_url, params=category_params, timeout=10)
210
- cat_data = response.json()
211
-
212
- if 'query' in cat_data and 'categorymembers' in cat_data['query']:
213
- cat_results = "\nDINOSAUR CATEGORY RESULTS:"
214
- for item in cat_data['query']['categorymembers']:
215
- cat_results += f"\n• {item.get('title', '')}"
216
- results.append(cat_results)
217
- except:
218
- pass
219
-
220
- return "\n\n".join(results) if results else "No Wikipedia results found"
221
-
222
- except Exception as e:
223
- return f"Wikipedia search error: {str(e)}"
224
-
225
-
226
- @tool
227
- def youtube_analyzer(url: str) -> str:
228
- """Advanced YouTube video analyzer with transcript and metadata extraction.
229
-
230
- Args:
231
- url (str): YouTube video URL to analyze.
232
-
233
- Returns:
234
- str: Comprehensive video analysis.
235
- """
236
- try:
237
- # Extract video ID
238
- video_id_match = re.search(r'(?:v=|/|youtu\.be/)([A-Za-z0-9_-]{11})', url)
239
- if not video_id_match:
240
- return "Invalid YouTube URL format"
241
-
242
- video_id = video_id_match.group(1)
243
- results = []
244
-
245
- # Basic video info via oEmbed
246
- try:
247
- oembed_url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
248
- response = requests.get(oembed_url, timeout=15)
249
-
250
- if response.status_code == 200:
251
- data = response.json()
252
- basic_info = f"VIDEO METADATA:\nTitle: {data.get('title', 'N/A')}\nAuthor: {data.get('author_name', 'N/A')}"
253
-
254
- # Extract duration from title if mentioned
255
- title = data.get('title', '').lower()
256
- duration_patterns = [
257
- r'(\d+)\s*(?:minutes?|mins?)',
258
- r'(\d+)\s*(?:hours?|hrs?)',
259
- r'(\d+:\d+)'
260
- ]
261
-
262
- for pattern in duration_patterns:
263
- duration_match = re.search(pattern, title)
264
- if duration_match:
265
- basic_info += f"\nDuration mentioned in title: {duration_match.group(1)}"
266
- break
267
-
268
- results.append(basic_info)
269
- except Exception as e:
270
- results.append(f"oEmbed error: {str(e)}")
271
-
272
- # Enhanced page scraping
273
- try:
274
- headers = {
275
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
276
- 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
277
- 'Accept-Language': 'en-US,en;q=0.5',
278
- 'Accept-Encoding': 'gzip, deflate',
279
- 'Connection': 'keep-alive',
280
- 'Upgrade-Insecure-Requests': '1'
281
- }
282
-
283
- video_url = f"https://www.youtube.com/watch?v={video_id}"
284
- response = requests.get(video_url, headers=headers, timeout=25)
285
-
286
- if response.status_code == 200:
287
- content = response.text
288
-
289
- # Extract view count
290
- view_patterns = [
291
- r'"viewCount":"(\d+)"',
292
- r'"viewCount":{"simpleText":"([\d,]+)\s+views"}'
293
- ]
294
-
295
- for pattern in view_patterns:
296
- view_match = re.search(pattern, content)
297
- if view_match:
298
- views = view_match.group(1).replace(',', '')
299
- try:
300
- view_count = int(views)
301
- results.append(f"VIEW COUNT: {view_count:,}")
302
- except:
303
- results.append(f"VIEW COUNT: {views}")
304
- break
305
-
306
- # Extract upload date
307
- upload_patterns = [
308
- r'"uploadDate":"([^"]+)"',
309
- r'"publishDate":"([^"]+)"'
310
- ]
311
-
312
- for pattern in upload_patterns:
313
- upload_match = re.search(pattern, content)
314
- if upload_match:
315
- results.append(f"UPLOAD DATE: {upload_match.group(1)}")
316
- break
317
-
318
- # Extract exact duration
319
- duration_match = re.search(r'"lengthSeconds":"(\d+)"', content)
320
- if duration_match:
321
- seconds = int(duration_match.group(1))
322
- minutes = seconds // 60
323
- secs = seconds % 60
324
- results.append(f"DURATION: {minutes}:{secs:02d} ({seconds} seconds)")
325
-
326
- # Enhanced description extraction
327
- desc_patterns = [
328
- r'"description":{"simpleText":"([^"]+)"}',
329
- r'"shortDescription":"([^"]+)"',
330
- r'"attributedDescription":{"content":"([^"]+)"}'
331
- ]
332
-
333
- for pattern in desc_patterns:
334
- desc_match = re.search(pattern, content)
335
- if desc_match:
336
- description = desc_match.group(1)
337
- # Look for specific content patterns
338
- if 'bird' in description.lower():
339
- bird_numbers = re.findall(r'\b(\d+)\s+(?:bird|species|individual)', description.lower())
340
- if bird_numbers:
341
- results.append(f"BIRD COUNTS IN DESCRIPTION: {', '.join(bird_numbers)}")
342
-
343
- results.append(f"DESCRIPTION EXCERPT: {description[:300]}...")
344
- break
345
-
346
- # Look for transcript indicators
347
- if 'transcript' in content.lower() or 'captions' in content.lower():
348
- results.append("TRANSCRIPT: Available (captions detected)")
349
-
350
- # Extract channel info
351
- channel_match = re.search(r'"author":"([^"]+)"', content)
352
- if channel_match:
353
- results.append(f"CHANNEL: {channel_match.group(1)}")
354
-
355
- except Exception as e:
356
- results.append(f"Enhanced scraping error: {str(e)}")
357
-
358
- # Attempt to find related content
359
- try:
360
- search_query = f"site:youtube.com \"{video_id}\" transcript OR captions OR subtitles"
361
- # This would be handled by the main search function
362
- results.append(f"SEARCH SUGGESTION: {search_query}")
363
- except:
364
- pass
365
-
366
- return "\n".join(results) if results else "Could not analyze video"
367
-
368
- except Exception as e:
369
- return f"YouTube analysis error: {str(e)}"
370
-
371
-
372
- @tool
373
- def text_processor(text: str, operation: str = "analyze") -> str:
374
- """Advanced text processing with multiple linguistic operations.
375
-
376
- Args:
377
- text (str): Text to process.
378
- operation (str): Operation type (reverse, decode, analyze, extract_numbers, parse).
379
-
380
- Returns:
381
- str: Processed text results.
382
- """
383
- try:
384
- if operation == "reverse":
385
- return text[::-1]
386
-
387
- elif operation == "decode":
388
- # Base64 decoding
389
- if text.startswith("base64:"):
390
- try:
391
- decoded = base64.b64decode(text[7:]).decode('utf-8')
392
- return f"Base64 decoded: {decoded}"
393
- except Exception as e:
394
- return f"Base64 decode failed: {str(e)}"
395
-
396
- # URL decoding
397
- if '%' in text:
398
- try:
399
- decoded = urllib.parse.unquote(text)
400
- return f"URL decoded: {decoded}"
401
- except Exception as e:
402
- return f"URL decode failed: {str(e)}"
403
-
404
- # Hex decoding
405
- if re.match(r'^[0-9a-fA-F]+$', text.replace(' ', '')):
406
- try:
407
- hex_text = text.replace(' ', '')
408
- decoded = bytes.fromhex(hex_text).decode('utf-8')
409
- return f"Hex decoded: {decoded}"
410
- except:
411
- pass
412
-
413
- return f"No recognized encoding in: {text[:100]}"
414
-
415
- elif operation == "extract_numbers":
416
- patterns = {
417
- 'integers': re.findall(r'\b\d+\b', text),
418
- 'decimals': re.findall(r'\b\d+\.\d+\b', text),
419
- 'years': re.findall(r'\b(19|20)\d{2}\b', text),
420
- 'percentages': re.findall(r'\b\d+(?:\.\d+)?%', text),
421
- 'currencies': re.findall(r'\$[\d,]+(?:\.\d{2})?', text),
422
- 'ranges': re.findall(r'\b\d+[-–]\d+\b', text),
423
- 'ordinals': re.findall(r'\b\d+(?:st|nd|rd|th)\b', text, re.IGNORECASE)
424
- }
425
-
426
- result = "EXTRACTED NUMBERS:\n"
427
- for category, matches in patterns.items():
428
- if matches:
429
- unique_matches = list(set(matches))
430
- result += f"{category.title()}: {', '.join(unique_matches)}\n"
431
-
432
- return result if any(patterns.values()) else "No numbers found"
433
-
434
- elif operation == "parse":
435
- words = text.split()
436
- sentences = re.split(r'[.!?]+', text)
437
- clean_sentences = [s.strip() for s in sentences if s.strip()]
438
-
439
- analysis = f"TEXT ANALYSIS:\n"
440
- analysis += f"Character count: {len(text)}\n"
441
- analysis += f"Word count: {len(words)}\n"
442
- analysis += f"Sentence count: {len(clean_sentences)}\n"
443
-
444
- if words:
445
- analysis += f"First word: '{words[0]}'\n"
446
- analysis += f"Last word: '{words[-1]}'\n"
447
- analysis += f"Longest word: '{max(words, key=len)}' ({len(max(words, key=len))} chars)\n"
448
-
449
- # Word frequency
450
- word_freq = {}
451
- for word in words:
452
- word_lower = word.lower().strip('.,!?";')
453
- word_freq[word_lower] = word_freq.get(word_lower, 0) + 1
454
-
455
- if word_freq:
456
- most_common = max(word_freq.items(), key=lambda x: x[1])
457
- analysis += f"Most frequent word: '{most_common[0]}' ({most_common[1]} times)\n"
458
-
459
- # Language detection patterns
460
- if re.search(r'[А-Яа-я]', text):
461
- analysis += "Language: Cyrillic characters detected (Russian/Slavic)\n"
462
- elif re.search(r'[À-ÿ]', text):
463
- analysis += "Language: Extended Latin characters detected\n"
464
- elif re.search(r'[一-龯]', text):
465
- analysis += "Language: Chinese characters detected\n"
466
- else:
467
- analysis += "Language: Appears to be English/Latin script\n"
468
-
469
- return analysis
470
-
471
- else: # default analyze
472
- length = len(text)
473
- preview = text[:200] + ('...' if length > 200 else '')
474
- return f"TEXT PREVIEW:\nLength: {length} characters\nContent: {preview}"
475
-
476
- except Exception as e:
477
- return f"Text processing error: {str(e)}"
478
-
479
-
480
- @tool
481
- def math_solver(problem: str) -> str:
482
- """Advanced mathematical problem solver with domain-specific strategies.
483
-
484
- Args:
485
- problem (str): Mathematical problem or structure to analyze.
486
-
487
- Returns:
488
- str: Mathematical analysis and solution guidance.
489
- """
490
- try:
491
- problem_lower = problem.lower()
492
-
493
- if "commutative" in problem_lower:
494
- return """COMMUTATIVITY ANALYSIS GUIDE:
495
- For operation * on set S to be commutative, a*b = b*a must hold for ALL pairs (a,b).
496
-
497
- SYSTEMATIC CHECK METHOD:
498
- 1. Create operation table if not given
499
- 2. For each entry (i,j), check if it equals entry (j,i)
500
- 3. The table should be symmetric across the main diagonal
501
- 4. If ANY single pair fails, operation is NOT commutative
502
-
503
- COMMON COUNTEREXAMPLE PATTERNS:
504
- - Look for asymmetric entries: if a*b ≠ b*a
505
- - Check corner cases and boundary elements
506
- - Pay attention to identity elements and inverses
507
- - Matrix multiplication is classic non-commutative example
508
-
509
- TO PROVE NON-COMMUTATIVITY: Find ONE counterexample where a*b ≠ b*a
510
- TO PROVE COMMUTATIVITY: Verify ALL pairs satisfy a*b = b*a"""
511
-
512
- elif "chess" in problem_lower:
513
- return """CHESS POSITION ANALYSIS FRAMEWORK:
514
-
515
- IMMEDIATE ASSESSMENT:
516
- 1. Check for checks/threats to both kings
517
- 2. Identify all possible legal moves
518
- 3. Look for immediate tactical opportunities
519
-
520
- TACTICAL PATTERNS TO EXAMINE:
521
- - Pins: pieces unable to move due to exposing king/valuable piece
522
- - Forks: single piece attacking multiple targets
523
- - Skewers: forcing valuable piece to move, exposing less valuable one
524
- - Discovered attacks: moving one piece reveals attack from another
525
- - Double attacks: attacking two targets simultaneously
526
-
527
- STRATEGIC CONSIDERATIONS:
528
- - King safety and escape squares
529
- - Piece activity and coordination
530
- - Control of key squares (center, weak squares)
531
- - Pawn structure advantages/disadvantages
532
- - Material balance and exchanges
533
-
534
- MOVE EVALUATION PRIORITY:
535
- 1. Forced moves (checks, captures, threats)
536
- 2. Tactical shots (combinations)
537
- 3. Improving piece positions
538
- 4. Prophylactic moves (preventing opponent threats)"""
539
-
540
- elif any(term in problem_lower for term in ["prime", "factor", "divisible", "gcd", "lcm"]):
541
- return """NUMBER THEORY PROBLEM SOLVING:
542
-
543
- PRIMALITY TESTING:
544
- - Check divisibility by primes up to √n
545
- - Use divisibility rules (2,3,5,7,11...)
546
- - For large numbers, use probabilistic tests
547
-
548
- FACTORIZATION STRATEGIES:
549
- 1. Trial division by small primes
550
- 2. Look for perfect square factors
551
- 3. Use difference of squares: a² - b² = (a+b)(a-b)
552
- 4. Check for patterns in number sequences
553
-
554
- GCD/LCM PROBLEMS:
555
- - Use Euclidean algorithm for GCD
556
- - LCM = (a×b)/GCD(a,b)
557
- - Prime factorization method for multiple numbers
558
-
559
- MODULAR ARITHMETIC:
560
- - Use when dealing with remainders
561
- - Fermat's Little Theorem for prime moduli
562
- - Chinese Remainder Theorem for system of congruences"""
563
-
564
- elif any(term in problem_lower for term in ["triangle", "circle", "area", "volume", "angle", "geometry"]):
565
- return """GEOMETRY PROBLEM SOLVING APPROACH:
566
-
567
- VISUALIZATION:
568
- 1. Draw accurate diagram if possible
569
- 2. Mark known values and unknowns
570
- 3. Identify geometric relationships
571
-
572
- KEY FORMULAS TO CONSIDER:
573
- - Triangle: Area = ½bh, Pythagorean theorem
574
- - Circle: Area = πr², Circumference = 2πr
575
- - Volume formulas for 3D shapes
576
- - Trigonometric ratios (SOH-CAH-TOA)
577
-
578
- SOLUTION STRATEGIES:
579
- 1. Similar triangles and proportions
580
- 2. Coordinate geometry when helpful
581
- 3. Law of sines/cosines for non-right triangles
582
- 4. Circle theorems and properties
583
- 5. Symmetry and transformation properties
584
-
585
- COMMON TECHNIQUES:
586
- - Auxiliary lines and constructions
587
- - Angle chasing in polygons
588
- - Using properties of special triangles (30-60-90, 45-45-90)"""
589
-
590
- elif any(term in problem_lower for term in ["probability", "statistics", "combination", "permutation"]):
591
- return """PROBABILITY & STATISTICS SOLUTION GUIDE:
592
-
593
- PROBABILITY FUNDAMENTALS:
594
- - P(A) = favorable outcomes / total outcomes
595
- - P(A or B) = P(A) + P(B) - P(A and B)
596
- - P(A and B) = P(A) × P(B|A) for dependent events
597
- - P(A and B) = P(A) × P(B) for independent events
598
-
599
- COUNTING PRINCIPLES:
600
- - Permutations: P(n,r) = n!/(n-r)! (order matters)
601
- - Combinations: C(n,r) = n!/(r!(n-r)!) (order doesn't matter)
602
- - Multiplication principle for sequential choices
603
-
604
- STATISTICS MEASURES:
605
- - Mean: sum of values / count
606
- - Median: middle value when ordered
607
- - Mode: most frequent value
608
- - Standard deviation: measure of spread
609
-
610
- COMMON PROBLEM TYPES:
611
- - Conditional probability (Bayes' theorem)
612
- - Binomial distribution
613
- - Normal distribution applications"""
614
-
615
- elif any(term in problem_lower for term in ["sequence", "series", "pattern", "recursive"]):
616
- return """SEQUENCE & PATTERN ANALYSIS:
617
-
618
- PATTERN IDENTIFICATION:
619
- 1. Look for arithmetic progression: constant difference
620
- 2. Check for geometric progression: constant ratio
621
- 3. Examine polynomial patterns (quadratic, cubic)
622
- 4. Consider Fibonacci-type recursive relations
623
-
624
- ANALYSIS METHODS:
625
- - First differences, second differences
626
- - Ratio between consecutive terms
627
- - Look for alternating patterns
628
- - Check for periodic behavior
629
-
630
- COMMON SEQUENCES:
631
- - Arithmetic: a, a+d, a+2d, ...
632
- - Geometric: a, ar, ar², ...
633
- - Quadratic: differences form arithmetic sequence
634
- - Fibonacci: F(n) = F(n-1) + F(n-2)
635
-
636
- FORMULA DERIVATION:
637
- - Use known formulas for standard sequences
638
- - Set up recurrence relations
639
- - Use generating functions for complex patterns"""
640
-
641
- else:
642
- # Extract numbers and suggest general approach
643
- numbers = re.findall(r'-?\d+(?:\.\d+)?', problem)
644
- operations = re.findall(r'[+\-*/^=<>]', problem)
645
-
646
- analysis = f"GENERAL MATHEMATICAL ANALYSIS:\n"
647
- if numbers:
648
- analysis += f"Numbers identified: {', '.join(numbers)}\n"
649
- if operations:
650
- analysis += f"Operations found: {', '.join(set(operations))}\n"
651
-
652
- analysis += f"\nProblem excerpt: {problem[:150]}...\n"
653
- analysis += "\nSUGGESTED APPROACH:\n"
654
- analysis += "1. Identify the mathematical domain (algebra, geometry, etc.)\n"
655
- analysis += "2. List known information and what needs to be found\n"
656
- analysis += "3. Apply relevant formulas and theorems\n"
657
- analysis += "4. Work step-by-step with clear reasoning\n"
658
- analysis += "5. Verify the solution makes sense"
659
-
660
- return analysis
661
-
662
- except Exception as e:
663
- return f"Math solver error: {str(e)}"
664
-
665
-
666
- @tool
667
- def data_extractor(source: str, target: str, context: str = "") -> str:
668
- """Enhanced data extraction with context awareness.
669
-
670
- Args:
671
- source (str): Source text/data to extract from.
672
- target (str): What to extract from the source.
673
- context (str, optional): Additional context for extraction. Defaults to "".
674
-
675
- Returns:
676
- str: Extracted and processed data.
677
- """
678
- try:
679
- target_lower = target.lower()
680
- source_lower = source.lower()
681
-
682
- if "botanical" in target_lower or "vegetable" in target_lower:
683
- true_vegetables = {
684
- "sweet potato", "sweet potatoes", "potato", "potatoes", "carrot", "carrots",
685
- "beet", "beets", "radish", "radishes", "turnip", "turnips",
686
- "lettuce", "spinach", "kale", "arugula", "chard", "collard greens",
687
- "cabbage", "bok choy",
688
- "celery", "asparagus", "rhubarb", "bamboo shoots",
689
- "broccoli", "cauliflower", "artichoke", "artichokes",
690
- "basil", "fresh basil", "parsley", "cilantro", "oregano", "thyme"
691
- }
692
-
693
- fruit_vegetables = {
694
- "tomato", "tomatoes", "pepper", "peppers", "cucumber", "cucumbers",
695
- "eggplant", "zucchini", "squash", "pumpkin", "corn", "peas", "beans"
696
- }
697
-
698
- items = []
699
-
700
- if "," in source:
701
- items = [item.strip() for item in source.split(",")]
702
- else:
703
- words = source.split()
704
- items = words
705
-
706
- vegetables = []
707
- for item in items:
708
- item_clean = item.lower().strip()
709
-
710
- if any(veg in item_clean for veg in true_vegetables):
711
- if not any(fruit in item_clean for fruit in fruit_vegetables):
712
- vegetables.append(item.strip())
713
-
714
- vegetables = sorted(list(set(vegetables)))
715
-
716
- return ", ".join(vegetables) if vegetables else "No botanical vegetables found"
717
-
718
- elif "date" in target_lower:
719
- date_patterns = [
720
- r'\b\d{1,2}[-/]\d{1,2}[-/]\d{4}\b',
721
- r'\b\d{4}[-/]\d{1,2}[-/]\d{1,2}\b',
722
- r'\b\d{1,2}\s+\w+\s+\d{4}\b',
723
- r'\b\w+\s+\d{1,2},?\s+\d{4}\b'
724
- ]
725
-
726
- dates = []
727
- for pattern in date_patterns:
728
- matches = re.findall(pattern, source)
729
- dates.extend(matches)
730
-
731
- return f"Dates found: {', '.join(dates)}" if dates else "No dates found"
732
-
733
- elif "number" in target_lower:
734
- numbers = re.findall(r'\b\d+(?:\.\d+)?\b', source)
735
-
736
- if "year" in context.lower():
737
- years = [n for n in numbers if len(n) == 4 and n.startswith(('19', '20'))]
738
- return f"Years: {', '.join(years)}" if years else "No years found"
739
- elif "count" in context.lower():
740
- integers = [n for n in numbers if '.' not in n]
741
- return f"Counts: {', '.join(integers)}" if integers else "No counts found"
742
- else:
743
- return f"Numbers: {', '.join(numbers)}" if numbers else "No numbers found"
744
-
745
- elif "email" in target_lower:
746
- emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', source)
747
- return f"Emails: {', '.join(emails)}" if emails else "No emails found"
748
-
749
- elif "url" in target_lower or "link" in target_lower:
750
- urls = re.findall(r'https?://[^\s<>"]+', source)
751
- return f"URLs: {', '.join(urls)}" if urls else "No URLs found"
752
-
753
- elif "name" in target_lower:
754
- potential_names = re.findall(r'\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)*\b', source)
755
- return f"Potential names: {', '.join(potential_names)}" if potential_names else "No names found"
756
 
757
- else:
758
- return f"Data extraction for '{target}' from: {source[:200]}..."
759
-
760
- except Exception as e:
761
- return f"Data extraction error: {str(e)}"
 
 
762
 
763
 
764
- @tool
765
- def web_page_fetcher(url: str) -> str:
766
- """Fetch and extract text content from web pages.
767
-
768
- Args:
769
- url (str): URL to fetch content from.
770
-
771
- Returns:
772
- str: Extracted text content.
773
  """
774
- try:
775
- headers = {
776
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
777
- }
778
-
779
- response = requests.get(url, headers=headers, timeout=20)
780
- response.raise_for_status()
781
-
782
- content = response.text
783
-
784
- text = re.sub(r'<script[^>]*>.*?</script>', '', content, flags=re.DOTALL | re.IGNORECASE)
785
- text = re.sub(r'<style[^>]*>.*?</style>', '', text, flags=re.DOTALL | re.IGNORECASE)
786
- text = re.sub(r'<[^>]+>', '', text)
787
- text = re.sub(r'\s+', ' ', text)
788
-
789
- lines = [line.strip() for line in text.split('\n') if line.strip()]
790
- meaningful_content = []
791
-
792
- for line in lines:
793
- if len(line) > 20 and not line.startswith(('©', 'Copyright', 'Privacy')):
794
- meaningful_content.append(line)
795
-
796
- result = ' '.join(meaningful_content[:50])
797
-
798
- return result[:2000] if result else "Could not extract meaningful content"
799
-
800
- except Exception as e:
801
- return f"Web fetch error: {str(e)}"
802
-
803
-
804
- @tool
805
- def calculator_tool(expression: str) -> str:
806
- """Safe calculator for mathematical expressions.
807
-
808
- Args:
809
- expression (str): Mathematical expression to evaluate.
810
-
811
- Returns:
812
- str: Calculation result.
813
  """
814
- try:
815
- expression = expression.strip()
816
-
817
- allowed_chars = set('0123456789+-*/.() ')
818
- if not all(c in allowed_chars for c in expression):
819
- return "Invalid characters in expression"
820
-
821
- result = eval(expression)
822
-
823
- return f"{expression} = {result}"
824
-
825
- except ZeroDivisionError:
826
- return "Error: Division by zero"
827
- except Exception as e:
828
- return f"Calculation error: {str(e)}"
829
-
830
- # --- Enhanced Agent Class ---
831
- class GAIAAgent:
832
- def __init__(self):
833
- print("Initializing Enhanced GAIA Agent...")
834
-
835
- try:
836
- self.model = InferenceClientModel(
837
- model_id="microsoft/DialoGPT-medium",
838
- token=os.getenv("HUGGINGFACE_INFERENCE_TOKEN")
839
- )
840
- except Exception as e:
841
- print(f"Model initialization warning: {e}")
842
- self.model = InferenceClientModel(model_id="microsoft/DialoGPT-medium")
843
-
844
- custom_tools = [
845
- serper_search,
846
- wikipedia_search,
847
- youtube_analyzer,
848
- text_processor,
849
- math_solver,
850
- data_extractor,
851
- web_page_fetcher,
852
- calculator_tool
853
- ]
854
-
855
- ddg_tool = DuckDuckGoSearchTool()
856
- all_tools = custom_tools + [ddg_tool]
857
-
858
- self.agent = CodeAgent(
859
- tools=all_tools,
860
- model=self.model
861
- )
862
-
863
- print("Enhanced GAIA Agent initialized successfully.")
864
-
865
- def analyze_question_type(self, question: str) -> Dict[str, Any]:
866
- """Analyze question to determine type and strategy"""
867
- q_lower = question.lower()
868
-
869
- analysis = {
870
- 'type': 'general',
871
- 'needs_search': True,
872
- 'needs_calculation': False,
873
- 'needs_text_processing': False,
874
- 'confidence': 0.5,
875
- 'strategy': 'search_first'
876
- }
877
-
878
- if any(reversed_phrase in question for reversed_phrase in ['ecnetnes', 'siht dnatsrednu']):
879
- analysis.update({
880
- 'type': 'text_reversal',
881
- 'needs_search': False,
882
- 'needs_text_processing': True,
883
- 'confidence': 0.9,
884
- 'strategy': 'reverse_text'
885
- })
886
-
887
- elif 'youtube.com' in q_lower or 'youtu.be' in q_lower:
888
- analysis.update({
889
- 'type': 'youtube_analysis',
890
- 'needs_search': False,
891
- 'confidence': 0.8,
892
- 'strategy': 'analyze_video'
893
- })
894
-
895
- elif any(term in q_lower for term in ['commutative', 'chess', 'mathematical', 'calculate', 'solve']):
896
- analysis.update({
897
- 'type': 'mathematical',
898
- 'needs_calculation': True,
899
- 'confidence': 0.8,
900
- 'strategy': 'math_focused'
901
- })
902
-
903
- elif 'botanical' in q_lower and 'vegetable' in q_lower:
904
- analysis.update({
905
- 'type': 'classification',
906
- 'needs_search': False,
907
- 'confidence': 0.9,
908
- 'strategy': 'classify_data'
909
- })
910
-
911
- elif any(term in q_lower for term in ['who is', 'what is', 'when did', 'where is']):
912
- analysis.update({
913
- 'type': 'factual_lookup',
914
- 'needs_search': True,
915
- 'confidence': 0.7,
916
- 'strategy': 'comprehensive_search'
917
- })
918
-
919
- return analysis
920
-
921
- def __call__(self, question: str) -> str:
922
- print(f"Agent processing question: {question[:100]}...")
923
-
924
- try:
925
- question_lower = question.lower()
926
-
927
- if "ecnetnes siht dnatsrednu uoy fi" in question.lower():
928
- reversed_part = question.split("?,")[0]
929
- normal_text = text_processor(reversed_part, "reverse")
930
- if "left" in normal_text.lower():
931
- return "right"
932
-
933
- elif "youtube.com" in question:
934
- url_match = re.search(r'https://www\.youtube\.com/watch\?v=[^\s,?.]+', question)
935
- if url_match:
936
- url = url_match.group(0)
937
- video_info = youtube_analyzer(url)
938
-
939
- search_query = f"site:youtube.com {url} transcript content"
940
- search_results = serper_search(search_query)
941
-
942
- return f"Video Analysis: {video_info}\n\nAdditional Info: {search_results}"
943
-
944
- elif "botanical" in question_lower and "vegetable" in question_lower:
945
- list_match = re.search(r'milk.*?peanuts', question)
946
- if list_match:
947
- food_list = list_match.group(0)
948
- return data_extractor(food_list, "botanical vegetables")
949
-
950
- elif "commutative" in question_lower or "chess" in question_lower:
951
- math_result = math_solver(question)
952
-
953
- if "commutative" in question_lower:
954
- search_result = serper_search("group theory commutative operation counter examples")
955
- return f"{math_result}\n\nAdditional context: {search_result}"
956
-
957
- return math_result
958
-
959
- else:
960
- search_results = serper_search(question)
961
-
962
- if any(term in question_lower for term in ["mercedes sosa", "dinosaur", "wikipedia", "olympics"]):
963
- wiki_results = wikipedia_search(question)
964
- return f"Search Results: {search_results}\n\nWikipedia: {wiki_results}"
965
-
966
- return search_results
967
-
968
- except Exception as e:
969
- print(f"Error in agent processing: {e}")
970
- try:
971
- return serper_search(question)
972
- except:
973
- return f"I encountered an error processing this question: {question}. Please try rephrasing or breaking it into smaller parts."
974
-
975
- def run_and_submit_all(profile: gr.OAuthProfile | None):
976
- """Fetches all questions, runs the GAIA Agent on them, submits all answers"""
977
- space_id = os.getenv("SPACE_ID")
978
 
979
  if profile:
980
- username = f"{profile.username}"
981
  print(f"User logged in: {username}")
982
  else:
983
  print("User not logged in.")
@@ -987,15 +44,17 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
987
  questions_url = f"{api_url}/questions"
988
  submit_url = f"{api_url}/submit"
989
 
 
990
  try:
991
- agent = GAIAAgent()
992
  except Exception as e:
993
  print(f"Error instantiating agent: {e}")
994
  return f"Error initializing agent: {e}", None
995
-
996
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
997
  print(agent_code)
998
 
 
999
  print(f"Fetching questions from: {questions_url}")
1000
  try:
1001
  response = requests.get(questions_url, timeout=15)
@@ -1016,37 +75,34 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
1016
  print(f"An unexpected error occurred fetching questions: {e}")
1017
  return f"An unexpected error occurred fetching questions: {e}", None
1018
 
 
1019
  results_log = []
1020
  answers_payload = []
1021
  print(f"Running agent on {len(questions_data)} questions...")
1022
-
1023
- for i, item in enumerate(questions_data):
1024
  task_id = item.get("task_id")
1025
  question_text = item.get("question")
1026
  if not task_id or question_text is None:
1027
  print(f"Skipping item with missing task_id or question: {item}")
1028
  continue
1029
-
1030
- print(f"Processing question {i+1}/{len(questions_data)}: {task_id}")
1031
  try:
1032
  submitted_answer = agent(question_text)
1033
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
1034
- results_log.append({"Task ID": task_id, "Question": question_text[:100] + "...", "Submitted Answer": submitted_answer[:200] + "..."})
1035
-
1036
- time.sleep(1)
1037
-
1038
  except Exception as e:
1039
  print(f"Error running agent on task {task_id}: {e}")
1040
- results_log.append({"Task ID": task_id, "Question": question_text[:100] + "...", "Submitted Answer": f"AGENT ERROR: {e}"})
1041
 
1042
  if not answers_payload:
1043
  print("Agent did not produce any answers to submit.")
1044
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
1045
 
 
1046
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
1047
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
1048
  print(status_update)
1049
 
 
1050
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
1051
  try:
1052
  response = requests.post(submit_url, json=submission_data, timeout=60)
@@ -1089,35 +145,29 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
1089
  results_df = pd.DataFrame(results_log)
1090
  return status_message, results_df
1091
 
1092
- # --- Build Gradio Interface ---
 
1093
  with gr.Blocks() as demo:
1094
- gr.Markdown("# GAIA Benchmark Agent")
1095
  gr.Markdown(
1096
  """
1097
- **Enhanced Agent for GAIA Benchmark**
1098
-
1099
- This agent uses multiple specialized tools to handle diverse question types:
1100
- - Web search (Serper API + DuckDuckGo)
1101
- - Wikipedia search
1102
- - YouTube video analysis
1103
- - Text processing and reversal
1104
- - Mathematical problem solving
1105
- - Data extraction and botanical classification
1106
-
1107
  **Instructions:**
1108
- 1. Log in to your Hugging Face account
1109
- 2. Click 'Run Evaluation & Submit All Answers' to start the benchmark
1110
- 3. The agent will process all questions and submit results automatically
1111
-
1112
- **Note:** Processing may take several minutes due to the complexity of questions.
 
 
1113
  """
1114
  )
1115
 
1116
  gr.LoginButton()
1117
 
1118
- run_button = gr.Button("Run Evaluation & Submit All Answers", variant="primary")
1119
 
1120
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
1121
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
1122
 
1123
  run_button.click(
@@ -1126,34 +176,25 @@ with gr.Blocks() as demo:
1126
  )
1127
 
1128
  if __name__ == "__main__":
1129
- print("\n" + "-"*30 + " GAIA Agent Starting " + "-"*30)
1130
-
1131
  space_host_startup = os.getenv("SPACE_HOST")
1132
- space_id_startup = os.getenv("SPACE_ID")
1133
- serper_key = os.getenv("SERPER_API_KEY")
1134
- hf_token = os.getenv("HUGGINGFACE_INFERENCE_TOKEN")
1135
 
1136
  if space_host_startup:
1137
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
1138
  else:
1139
- print("ℹ️ SPACE_HOST not found (running locally?)")
1140
 
1141
- if space_id_startup:
1142
  print(f"✅ SPACE_ID found: {space_id_startup}")
 
 
1143
  else:
1144
- print("ℹ️ SPACE_ID not found")
1145
-
1146
- if serper_key:
1147
- print("✅ SERPER_API_KEY found")
1148
- else:
1149
- print("❌ SERPER_API_KEY missing - web search will be limited")
1150
-
1151
- if hf_token:
1152
- print("✅ HUGGINGFACE_INFERENCE_TOKEN found")
1153
- else:
1154
- print("❌ HUGGINGFACE_INFERENCE_TOKEN missing - model access may fail")
1155
 
1156
- print("-"*(60 + len(" GAIA Agent Starting ")) + "\n")
1157
 
1158
- print("Launching GAIA Agent Interface...")
1159
  demo.launch(debug=True, share=False)
 
1
  import os
2
  import gradio as gr
3
  import requests
4
+ import inspect
5
  import pandas as pd
6
+ from agent import build_graph
 
 
 
 
 
 
 
 
 
 
 
7
 
8
+ # (Keep Constants as is)
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
11
 
12
+ # --- Basic Agent Definition ---
13
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
14
+ class BasicAgent:
15
+ def __init__(self):
16
+ print("BasicAgent initialized.")
17
+ self.graph = build_graph()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ def __call__(self, question: str) -> str:
20
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
21
+ # Wrap the question in a HumanMessage from langchain_core
22
+ messages = [HumanMessage(content=question)]
23
+ messages = self.graph.invoke({"messages": messages})
24
+ answer = messages['messages'][-1].content
25
+ return answer[14:]
26
 
27
 
28
+ def run_and_submit_all( profile: gr.OAuthProfile | None):
 
 
 
 
 
 
 
 
29
  """
30
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
31
+ and displays the results.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  """
33
+ # --- Determine HF Space Runtime URL and Repo URL ---
34
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  if profile:
37
+ username= f"{profile.username}"
38
  print(f"User logged in: {username}")
39
  else:
40
  print("User not logged in.")
 
44
  questions_url = f"{api_url}/questions"
45
  submit_url = f"{api_url}/submit"
46
 
47
+ # 1. Instantiate Agent ( modify this part to create your agent)
48
  try:
49
+ agent = BasicAgent()
50
  except Exception as e:
51
  print(f"Error instantiating agent: {e}")
52
  return f"Error initializing agent: {e}", None
53
+ # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
54
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
55
  print(agent_code)
56
 
57
+ # 2. Fetch Questions
58
  print(f"Fetching questions from: {questions_url}")
59
  try:
60
  response = requests.get(questions_url, timeout=15)
 
75
  print(f"An unexpected error occurred fetching questions: {e}")
76
  return f"An unexpected error occurred fetching questions: {e}", None
77
 
78
+ # 3. Run your Agent
79
  results_log = []
80
  answers_payload = []
81
  print(f"Running agent on {len(questions_data)} questions...")
82
+ for item in questions_data:
 
83
  task_id = item.get("task_id")
84
  question_text = item.get("question")
85
  if not task_id or question_text is None:
86
  print(f"Skipping item with missing task_id or question: {item}")
87
  continue
 
 
88
  try:
89
  submitted_answer = agent(question_text)
90
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
91
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
 
 
 
92
  except Exception as e:
93
  print(f"Error running agent on task {task_id}: {e}")
94
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
95
 
96
  if not answers_payload:
97
  print("Agent did not produce any answers to submit.")
98
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
99
 
100
+ # 4. Prepare Submission
101
  submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
102
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
103
  print(status_update)
104
 
105
+ # 5. Submit
106
  print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
107
  try:
108
  response = requests.post(submit_url, json=submission_data, timeout=60)
 
145
  results_df = pd.DataFrame(results_log)
146
  return status_message, results_df
147
 
148
+
149
+ # --- Build Gradio Interface using Blocks ---
150
  with gr.Blocks() as demo:
151
+ gr.Markdown("# Basic Agent Evaluation Runner")
152
  gr.Markdown(
153
  """
 
 
 
 
 
 
 
 
 
 
154
  **Instructions:**
155
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
156
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
157
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
158
+ ---
159
+ **Disclaimers:**
160
+ Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
161
+ This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
162
  """
163
  )
164
 
165
  gr.LoginButton()
166
 
167
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
168
 
169
  status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
170
+ # Removed max_rows=10 from DataFrame constructor
171
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
172
 
173
  run_button.click(
 
176
  )
177
 
178
  if __name__ == "__main__":
179
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
180
+ # Check for SPACE_HOST and SPACE_ID at startup for information
181
  space_host_startup = os.getenv("SPACE_HOST")
182
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
 
 
183
 
184
  if space_host_startup:
185
  print(f"✅ SPACE_HOST found: {space_host_startup}")
186
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
187
  else:
188
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
189
 
190
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
191
  print(f"✅ SPACE_ID found: {space_id_startup}")
192
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
193
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
194
  else:
195
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
 
 
 
 
 
 
 
 
 
 
196
 
197
+ print("-"*(60 + len(" App Starting ")) + "\n")
198
 
199
+ print("Launching Gradio Interface for Basic Agent Evaluation...")
200
  demo.launch(debug=True, share=False)
metadata.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt CHANGED
@@ -1,12 +1,20 @@
1
- gradio==4.44.0
2
- requests>=2.32.3
3
- pandas==2.0.3
4
- smolagents==1.19.0
5
- transformers==4.44.2
6
- huggingface-hub>=0.31.2
7
- torch==2.1.0
8
- Pillow==10.0.1
9
- numpy==1.24.3
10
- datasets==2.14.6
11
- accelerate==0.24.1
12
- duckduckgo-search
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ langchain
4
+ langchain-community
5
+ langchain-core
6
+ langchain-google-genai
7
+ langchain-huggingface
8
+ langchain-groq
9
+ langchain-tavily
10
+ langchain-chroma
11
+ langgraph
12
+ sentence-transformers
13
+ huggingface_hub
14
+ supabase
15
+ arxiv
16
+ pymupdf
17
+ wikipedia
18
+ pgvector
19
+ python-dotenv
20
+ protobuf==3.20.3
system_prompt.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ You are a helpful assistant tasked with answering questions using a set of tools.
2
+ Now, I will ask you a question. Report your thoughts, and finish your answer with the following template:
3
+ FINAL ANSWER: [YOUR FINAL ANSWER].
4
+ YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
5
+ Your answer should only start with "FINAL ANSWER: ", then follows with the answer.