DATAD2 commited on
Commit
51e4c5e
·
verified ·
1 Parent(s): 369bfec

Upload 9 files

Browse files
Files changed (9) hide show
  1. LICENSE +21 -0
  2. README.md +211 -5
  3. extract_answer.py +41 -0
  4. files_util.py +82 -0
  5. image_processing.py +26 -0
  6. metadata.jsonl +0 -0
  7. requirements.txt +20 -1
  8. supabase_docs.csv +0 -0
  9. system_prompt.txt +17 -0
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Luong Huu Thanh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: Template Final Assignment
3
- emoji: 🕵🏻‍♂️
4
- colorFrom: indigo
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.25.2
8
  app_file: app.py
@@ -12,4 +12,210 @@ hf_oauth: true
12
  hf_oauth_expiration_minutes: 480
13
  ---
14
 
15
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: GAIA Lite
3
+ emoji: 🤖
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: gradio
7
  sdk_version: 5.25.2
8
  app_file: app.py
 
12
  hf_oauth_expiration_minutes: 480
13
  ---
14
 
15
+ # 🤖 **GAIA Lite**
16
+
17
+ ## 🌟 **Introduction**
18
+
19
+ **GAIA Lite** is a small tool-calling agent for learning LangGraph and for the GAIA / Hugging Face Agents Course homework. It keeps a short tool list, a bounded think–act loop, regex answer extraction, and a cached evaluation runner.
20
+
21
+ ## 🚀 **Key Features**
22
+
23
+ - **🔍 Multi-Modal Search**: Web search, Wikipedia, and arXiv paper search
24
+ - **💻 Code Execution**: Support for Python, Bash, SQL, C, and Java
25
+ - **🖼️ Image Processing**: Analysis, transformation, OCR, and generation
26
+ - **📄 Document Processing**: PDF, CSV, Excel, and text file analysis
27
+ - **📁 File Upload Support**: Handle multiple file types with drag-and-drop
28
+ - **🧮 Mathematical Operations**: Complete set of mathematical tools
29
+ - **💬 Conversational Interface**: Natural chat-based interaction
30
+ - **📊 Evaluation System**: Automated benchmark testing and submission
31
+
32
+ ## 🏗️ **Project Structure**
33
+
34
+ ```
35
+ gaia-lite/
36
+ ├── app.py # Gradio Q&A chatbot
37
+ ├── evaluation_app.py # GAIA eval + cached submit
38
+ ├── agent.py # Tools + LangGraph loop
39
+ ├── extract_answer.py # Parse/normalize FINAL ANSWER
40
+ ├── files_util.py # Attachment download + prompt paths
41
+ ├── code_interpreter.py # Python (and other) code execution
42
+ ├── image_processing.py # Optional image helpers (not wired)
43
+ ├── system_prompt.txt # Agent instructions
44
+ ├── answers_cache.json # Local eval cache (gitignored)
45
+ ├── requirements.txt
46
+ └── README.md
47
+ ```
48
+
49
+ ## 🛠️ **Tools (kept small on purpose)**
50
+
51
+ The agent binds six tools. Extra math/image-generation helpers were removed from the loop so the model spends less time picking the wrong tool.
52
+
53
+ - `web_search` — Tavily, up to 3 hits
54
+ - `wiki_search` — Wikipedia, up to 2 pages
55
+ - `execute_python` — calculation, pandas, dates, CSV/Excel via code
56
+ - `download_file_from_url` — save a remote file locally
57
+ - `read_file` — preview text, CSV, Excel, PDF
58
+ - `extract_text_from_image` — Tesseract OCR
59
+
60
+ Vector-store retrieval is **off by default**. Pass `build_graph(use_retriever=True)` only if you explicitly want similar-question context.
61
+
62
+ ## 🎯 **How to Use**
63
+
64
+ ### **Q&A Chatbot Interface (app.py)**
65
+
66
+ 1. **Start the Chatbot:**
67
+ ```bash
68
+ python app.py
69
+ ```
70
+
71
+ 2. **Access the Interface:**
72
+ - Open `http://localhost:7860` in your browser
73
+ - Upload files (images, documents, CSV, etc.) if needed
74
+ - Ask questions in natural language
75
+ - Get comprehensive answers with tool usage
76
+
77
+ 3. **Supported Interactions:**
78
+ - **Text Questions**: "What is the capital of France?"
79
+ - **Math Problems**: "Calculate the square root of 144"
80
+ - **Code Requests**: "Write a Python function to sort a list"
81
+ - **Image Analysis**: Upload an image and ask "What do you see?"
82
+ - **Data Analysis**: Upload a CSV and ask "What are the trends?"
83
+ - **Web Search**: "What are the latest AI developments?"
84
+
85
+ ### **Evaluation Runner (evaluation_app.py)**
86
+
87
+ 1. **Run the Evaluation:**
88
+ ```bash
89
+ python evaluation_app.py
90
+ ```
91
+
92
+ 2. **Benchmark Testing:**
93
+ - Log in with your Hugging Face account
94
+ - Click "Run Evaluation & Submit All Answers"
95
+ - Monitor progress as the agent processes GAIA benchmark questions
96
+ - View results and scores automatically
97
+
98
+ ## 🔧 **Technical Architecture**
99
+
100
+ ### **LangGraph State Machine**
101
+ ```
102
+ START → maybe_example → assistant ⇄ tools
103
+ ↑ ↓
104
+ └───────────┘
105
+ ```
106
+
107
+ 1. **maybe_example**: no-op unless `use_retriever=True`
108
+ 2. **assistant**: Groq Qwen with tools; after 8 tool rounds or a repeated identical call, it is forced to emit `FINAL ANSWER`
109
+ 3. **tools**: `ToolNode` runs the selected Python functions
110
+ 4. **extract_final_answer**: regex parse + light normalization before scoring submit
111
+
112
+ Evaluation writes `answers_cache.json`, skips successful tasks on rerun, and submits from cache in a separate button. Attachments use `GET /files/{task_id}`, with an optional Hugging Face GAIA dataset fallback.
113
+
114
+ ### **Vector Database Integration**
115
+ - **Supabase Vector Store**: Stores GAIA benchmark Q&A pairs
116
+ - **Semantic Search**: Finds similar questions for context
117
+ - **HuggingFace Embeddings**: sentence-transformers/all-mpnet-base-v2
118
+
119
+ ### **Multi-Modal File Support**
120
+ - **Images**: JPG, PNG, GIF, BMP, WebP
121
+ - **Documents**: PDF, DOC, DOCX, TXT, MD
122
+ - **Data**: CSV, Excel, JSON
123
+ - **Code**: Python, Bash, SQL, C, Java
124
+
125
+ ## ⚙️ **Installation & Setup**
126
+
127
+ ### **1. Clone Repository**
128
+ ```bash
129
+ git clone https://github.com/fisherman611/gaia-agent.git gaia-lite
130
+ cd gaia-lite
131
+ ```
132
+
133
+ ### **2. Install Dependencies**
134
+ ```bash
135
+ pip install -r requirements.txt
136
+ ```
137
+
138
+ ### **3. Environment Variables**
139
+ Create a `.env` file with your API keys:
140
+ ```env
141
+ SUPABASE_URL=your_supabase_url
142
+ SUPABASE_SERVICE_ROLE_KEY=your_supabase_key
143
+ GROQ_API_KEY=your_groq_api_key
144
+ TAVILY_API_KEY=your_tavily_api_key
145
+ HUGGINGFACEHUB_API_TOKEN=your_hf_token
146
+ LANGSMITH_API_KEY=your_langsmith_key
147
+
148
+ LANGSMITH_TRACING=true
149
+ LANGSMITH_PROJECT=gaia-lite
150
+ LANGSMITH_ENDPOINT=https://api.smith.langchain.com
151
+ ```
152
+
153
+ ### **4. Database Setup (Supabase)**
154
+ Execute this SQL in your Supabase database:
155
+ ```sql
156
+ -- Enable pgvector extension
157
+ CREATE EXTENSION IF NOT EXISTS vector;
158
+
159
+ -- Create match function for documents2 table
160
+ CREATE OR REPLACE FUNCTION public.match_documents_2(
161
+ query_embedding vector(768)
162
+ )
163
+ RETURNS TABLE(
164
+ id bigint,
165
+ content text,
166
+ metadata jsonb,
167
+ embedding vector(768),
168
+ similarity double precision
169
+ )
170
+ LANGUAGE sql STABLE
171
+ AS $$
172
+ SELECT
173
+ id,
174
+ content,
175
+ metadata,
176
+ embedding,
177
+ 1 - (embedding <=> query_embedding) AS similarity
178
+ FROM public.documents2
179
+ ORDER BY embedding <=> query_embedding
180
+ LIMIT 10;
181
+ $$;
182
+
183
+ -- Grant permissions
184
+ GRANT EXECUTE ON FUNCTION public.match_documents_2(vector) TO anon, authenticated;
185
+ ```
186
+
187
+ ## 🚀 **Running the Application**
188
+
189
+ ### **Chatbot Interface**
190
+ ```bash
191
+ python app.py
192
+ ```
193
+ Access at: `http://localhost:7860`
194
+
195
+ ### **Evaluation Runner**
196
+ ```bash
197
+ python evaluation_app.py
198
+ ```
199
+ Access at: `http://localhost:7860`
200
+
201
+ ### **Live Demo**
202
+ Upstream template Space: [fisherman611/gaia-agent](https://huggingface.co/spaces/fisherman611/gaia-agent)
203
+
204
+ ## 🔗 **Resources**
205
+
206
+ - [GAIA Benchmark](https://huggingface.co/spaces/gaia-benchmark/leaderboard)
207
+ - [Hugging Face Agents Course](https://huggingface.co/agents-course)
208
+ - [LangGraph Documentation](https://langchain-ai.github.io/langgraph/)
209
+ - [Supabase Vector Store](https://supabase.com/docs/guides/ai/vector-columns)
210
+
211
+ ## 🤝 **Contributing**
212
+
213
+ Contributions are welcome! Areas for improvement:
214
+ - **New Tools**: Add specialized tools for specific domains
215
+ - **UI Enhancements**: Improve the chatbot interface
216
+ - **Performance**: Optimize response times and accuracy
217
+ - **Documentation**: Expand examples and use cases
218
+
219
+ ## 📄 **License**
220
+
221
+ This project is licensed under the [MIT License](https://mit-license.org/).
extract_answer.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import Optional
3
+
4
+ _FINAL_ANSWER_RE = re.compile(
5
+ r"FINAL\s*ANSWER\s*:\s*(.+)$",
6
+ re.IGNORECASE | re.DOTALL,
7
+ )
8
+
9
+
10
+ def extract_final_answer(text: Optional[str]) -> str:
11
+ """Pull the GAIA-style final answer out of a free-form model reply."""
12
+ if not text:
13
+ return ""
14
+
15
+ text = str(text).strip()
16
+ match = _FINAL_ANSWER_RE.search(text)
17
+ raw = match.group(1).strip() if match else text
18
+
19
+ if "FINAL ANSWER:" in raw.upper():
20
+ raw = re.split(r"FINAL\s*ANSWER\s*:", raw, flags=re.IGNORECASE)[-1].strip()
21
+
22
+ first_line = raw.splitlines()[0].strip()
23
+ first_line = first_line.strip(" `\"'")
24
+ first_line = re.sub(r"^\*\*?|\*\*?$", "", first_line).strip()
25
+ return normalize_gaia_answer(first_line)
26
+
27
+
28
+ def normalize_gaia_answer(answer: str) -> str:
29
+ """Light cleanup that matches common GAIA scoring pitfalls."""
30
+ answer = answer.strip()
31
+ answer = answer.strip(" `\"'")
32
+ if answer.endswith("."):
33
+ answer = answer[:-1].strip()
34
+
35
+ compact = answer.replace(" ", "")
36
+ if re.fullmatch(r"[+-]?\d{1,3}(,\d{3})+(\.\d+)?", answer):
37
+ return answer.replace(",", "")
38
+ if re.fullmatch(r"[+-]?\d+(\.\d+)?%?", compact) and "," in answer:
39
+ return answer.replace(",", "")
40
+
41
+ return answer
files_util.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ from typing import List, Optional
4
+
5
+ import requests
6
+
7
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
8
+
9
+
10
+ def format_user_message(question: str, file_paths: Optional[List[str]] = None) -> str:
11
+ question = (question or "").strip()
12
+ if not file_paths:
13
+ return question
14
+
15
+ lines = [question, "", "Attached local files (use tools with these paths):"]
16
+ for path in file_paths:
17
+ if not path:
18
+ continue
19
+ name = os.path.basename(path)
20
+ exists = os.path.exists(path)
21
+ size = os.path.getsize(path) if exists else 0
22
+ lines.append(f"- {name}: {path} (exists={exists}, bytes={size})")
23
+ return "\n".join(lines)
24
+
25
+
26
+ def download_scoring_file(
27
+ task_id: str,
28
+ file_name: Optional[str] = None,
29
+ dest_dir: str = ".",
30
+ api_url: str = DEFAULT_API_URL,
31
+ timeout: int = 60,
32
+ ) -> Optional[str]:
33
+ """Download a GAIA attachment from the course scoring API."""
34
+ if not file_name:
35
+ return None
36
+
37
+ os.makedirs(dest_dir, exist_ok=True)
38
+ dest_path = os.path.join(dest_dir, os.path.basename(file_name))
39
+ url = f"{api_url.rstrip('/')}/files/{task_id}"
40
+
41
+ try:
42
+ response = requests.get(url, timeout=timeout)
43
+ if response.status_code != 200:
44
+ print(f"Scoring API file {task_id} -> HTTP {response.status_code}: {response.text[:200]}")
45
+ return download_gaia_dataset_file(file_name, dest_path)
46
+ with open(dest_path, "wb") as handle:
47
+ handle.write(response.content)
48
+ print(f"Saved attachment {file_name} -> {dest_path}")
49
+ return dest_path
50
+ except requests.RequestException as exc:
51
+ print(f"Scoring API file download failed for {task_id}: {exc}")
52
+ return download_gaia_dataset_file(file_name, dest_path)
53
+
54
+
55
+ def download_gaia_dataset_file(file_name: str, dest_path: str) -> Optional[str]:
56
+ """Fallback when /files/{task_id} is 404: try the gated GAIA dataset on the Hub."""
57
+ try:
58
+ from huggingface_hub import hf_hub_download
59
+ except ImportError:
60
+ return None
61
+
62
+ token = os.getenv("HUGGINGFACEHUB_API_TOKEN") or os.getenv("HF_TOKEN")
63
+ candidates = [
64
+ f"2023/validation/{file_name}",
65
+ f"2023/test/{file_name}",
66
+ file_name,
67
+ ]
68
+ for filename in candidates:
69
+ try:
70
+ cached = hf_hub_download(
71
+ repo_id="gaia-benchmark/GAIA",
72
+ filename=filename,
73
+ repo_type="dataset",
74
+ token=token,
75
+ )
76
+ os.makedirs(os.path.dirname(dest_path) or ".", exist_ok=True)
77
+ shutil.copy2(cached, dest_path)
78
+ print(f"Copied GAIA dataset file {filename} -> {dest_path}")
79
+ return dest_path
80
+ except Exception as exc:
81
+ print(f"Hub download missed {filename}: {exc}")
82
+ return None
image_processing.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import io
3
+ import base64
4
+ import uuid
5
+ from PIL import Image
6
+
7
+ # Helper functions for image processing
8
+ def encode_image(image_path: str) -> str:
9
+ """Convert an image file to base64 string."""
10
+ with open(image_path, "rb") as image_file:
11
+ return base64.b64encode(image_file.read()).decode("utf-8")
12
+
13
+
14
+ def decode_image(base64_string: str) -> Image.Image:
15
+ """Convert a base64 string to a PIL Image."""
16
+ image_data = base64.b64decode(base64_string)
17
+ return Image.open(io.BytesIO(image_data))
18
+
19
+
20
+ def save_image(image: Image.Image, directory: str = "image_outputs") -> str:
21
+ """Save a PIL Image to disk and return the path."""
22
+ os.makedirs(directory, exist_ok=True)
23
+ image_id = str(uuid.uuid4())
24
+ image_path = os.path.join(directory, f"{image_id}.png")
25
+ image.save(image_path)
26
+ return image_path
metadata.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt CHANGED
@@ -1,2 +1,21 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ huggingface_hub
13
+ supabase
14
+ arxiv
15
+ pymupdf
16
+ wikipedia
17
+ pgvector
18
+ python-dotenv
19
+ pytesseract
20
+ matplotlib
21
+ sentence_transformers
supabase_docs.csv ADDED
The diff for this file is too large to render. See raw diff
 
system_prompt.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ You are GAIA Lite, a tool-using assistant. Solve questions with tools, then give a scorable short answer.
2
+
3
+ How to work:
4
+ - Use tools when you need facts, files, calculation, or code. Prefer Python for math, tables, dates, and string processing.
5
+ - If a file path is given, read or analyze that local path. Do not invent file contents.
6
+ - If a search fails, change the query once. Do not call the same tool with the same arguments twice.
7
+ - After you have enough evidence, stop using tools.
8
+
9
+ Final message format:
10
+ - You may reason in earlier messages.
11
+ - Your LAST message must be exactly one line:
12
+ FINAL ANSWER: <answer>
13
+ - The answer must be a number, a few words, or a comma-separated list.
14
+ - Numbers: no thousand separators, no units (no $, %, °) unless the question asks for them.
15
+ - Strings: no leading articles, no abbreviations for cities/countries, digits in plain text unless the question wants numerals.
16
+ - Lists: exactly one space after each comma.
17
+ - If unsure, still output the best short guess in this format. Never refuse with a long explanation.