Commit
·
b4f9800
1
Parent(s):
86368de
Refactoring and started filling in the README file
Browse files- README.md +90 -0
- core/messages.py +3 -26
- nodes/nodes.py +1 -1
README.md
CHANGED
|
@@ -12,4 +12,94 @@ 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
|
|
|
|
| 12 |
hf_oauth_expiration_minutes: 480
|
| 13 |
---
|
| 14 |
|
| 15 |
+
# General AI Assistant
|
| 16 |
+
|
| 17 |
+
## Background
|
| 18 |
+
Created as a final project for the HuggingFace Agents course ( https://huggingface.co/learn/agents-course).
|
| 19 |
+
Aims to answer Level 1 questions from the **GAIA** validation set. It was tested on 20 such questions with a success rate of 65%.
|
| 20 |
+
### GAIA
|
| 21 |
+
|
| 22 |
+
GAIA is a benchmark for AI assistants evaluation on real-world tasks that require a combination of capabilities—such
|
| 23 |
+
as reasoning, multimodal understanding, web browsing, and proficient tool use (see https://huggingface.co/learn/agents-course/unit4/what-is-gaia).
|
| 24 |
+
|
| 25 |
+
GAIA was introduced in the paper [”GAIA: A Benchmark for General AI Assistants”](https://arxiv.org/abs/2311.12983).
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
The questions challenge AI systems in several ways:
|
| 29 |
+
|
| 30 |
+
- Involve multimodal reasoning (e.g., analyzing images, audio, documents)
|
| 31 |
+
- Demand multi-hop retrieval of interdependent facts
|
| 32 |
+
- Involve running python code
|
| 33 |
+
- Require a structured response format
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
## Implementation Highlights
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
**The agent** is implemented using the LangGraph framework.
|
| 41 |
+
|
| 42 |
+
**Nodes**:
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
**Tools**
|
| 46 |
+
|
| 47 |
+
🔎 **Web Search**: uses `tavily` search and extract tools.
|
| 48 |
+
|
| 49 |
+
- **Chunking**: The content returned by the exact might be too large to be further analyzed at once by a model (depending on the chosen model context window size or on the rate limitation),
|
| 50 |
+
so if its size exceeds a pre-configured threshold, it will be chunked and only the most relevant chunks will be analyzed.
|
| 51 |
+
- **Text Splitting**: First by markdown (used Langchain's `MarkdownHeaderTextSplitter`) and then further by size with a sliding window (used LangChain's `RecursiveCharacterTextSplitter`).
|
| 52 |
+
- **Embeddings**: langchain_community.embeddings.OpenAIEmbeddings
|
| 53 |
+
- **Vector DB**: FAISS vector db.
|
| 54 |
+
- **Retrieval**: FAISS similarity search
|
| 55 |
+
|
| 56 |
+
Updated the original extract tool response message content only with the relevant chunks content.
|
| 57 |
+
|
| 58 |
+
🔉 **Audio**: uses `gpt-4o-audio-preview` to analyze the input
|
| 59 |
+
|
| 60 |
+
🧮 **Math problems**: this is a subagent that uses `gpt-5` equipped with the following tools:
|
| 61 |
+
|
| 62 |
+
- **Pyhton code executor**: executes a snipped of python code provided as input
|
| 63 |
+
- **Think tool**: used for strategic reflection on the progress of the solving process
|
| 64 |
+
|
| 65 |
+
**The Math Agent States:**
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
🧩 **Python code**
|
| 69 |
+
This tool can run either a snippet of python code or a python file. The python file is executed in a sub-process.
|
| 70 |
+
|
| 71 |
+
📊 **Spreadsheets**
|
| 72 |
+
In order to analyze `excel` files this tool uses the pandas dataframe agent
|
| 73 |
+
`langchain_experimental.agents import create_pandas_dataframe_agent`
|
| 74 |
+
It uses `gpt-4.1` model.
|
| 75 |
+
|
| 76 |
+
♟️ **Chess**
|
| 77 |
+
Given a chess board and the active color, this tool is able to suggest the best move to be performed by the active color.
|
| 78 |
+
|
| 79 |
+
- **Picture analysis**: the tool must detect
|
| 80 |
+
|
| 81 |
+
🎥 **Videos**
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
## Challenges
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
## Future improvements
|
| 90 |
+
#### 1. Evaluation
|
| 91 |
+
#### 2. Chunking
|
| 92 |
+
#### 3. Audio Analysis
|
| 93 |
+
#### 3. Video Analysis
|
| 94 |
+
#### 4. Chessboard Images analysis
|
| 95 |
+
|
| 96 |
+
## References:
|
| 97 |
+
https://github.com/langchain-ai/open_deep_research
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
|
| 105 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
core/messages.py
CHANGED
|
@@ -40,28 +40,11 @@ class AttachmentHandler:
|
|
| 40 |
def __init__(self, supported_types: list):
|
| 41 |
self.supported_types = supported_types
|
| 42 |
|
| 43 |
-
def get_attachment_representation(self, attachment: Attachment) -> dict:
|
| 44 |
-
if attachment.type not in self.supported_types:
|
| 45 |
-
raise Exception(f"Invalid attachment type{attachment.type}")
|
| 46 |
-
|
| 47 |
-
if attachment.type == "image":
|
| 48 |
-
return {"type": "image_url",
|
| 49 |
-
"image_url": {"url": f"data:{attachment.mime_type};base64," + attachment.get_encoded_content_b64()}}
|
| 50 |
-
|
| 51 |
-
if attachment.type == "audio":
|
| 52 |
-
return {"type": "text",
|
| 53 |
-
"text": attachment.get_encoded_content_b64()}
|
| 54 |
-
if attachment.type == "text":
|
| 55 |
-
return {"type": attachment.type, "data": attachment.content, "mime_type": attachment.mime_type}
|
| 56 |
-
|
| 57 |
-
# The remaining types are image, file, audio
|
| 58 |
-
return {"type": attachment.type, "source": "base64", "data": attachment.get_encoded_content_b64(),
|
| 59 |
-
"mime_type": attachment.mime_type}
|
| 60 |
-
|
| 61 |
def get_representation(self, type: str, content: bytes, format: str, mime_type) -> dict:
|
| 62 |
-
base64_content = base64.b64encode(content).decode("utf-8")
|
| 63 |
if type not in self.supported_types:
|
| 64 |
raise Exception(f"Invalid attachment type{type}")
|
|
|
|
|
|
|
| 65 |
if type == "audio":
|
| 66 |
return {"type": "input_audio",
|
| 67 |
"input_audio": {"data": base64_content, "format": format}}
|
|
@@ -72,18 +55,12 @@ class AttachmentHandler:
|
|
| 72 |
raise Exception(f"Cannot extract a representation for type {type}")
|
| 73 |
|
| 74 |
def fetch_file_from_reference(self, file_reference: str) -> bytes:
|
| 75 |
-
"""Fetches file bytes from a reference
|
| 76 |
-
|
| 77 |
# It's a local file path
|
| 78 |
file = Path(file_reference)
|
| 79 |
if file_reference.startswith("/") or file_reference.startswith("./") or file.exists():
|
| 80 |
return file.read_bytes()
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
# Example 3: It's an ID in your database (pseudocode)
|
| 84 |
else:
|
| 85 |
-
# file_bytes = database.lookup_file_bytes(file_reference)
|
| 86 |
-
# return file_bytes
|
| 87 |
raise ValueError(
|
| 88 |
f"Could not resolve file reference: {file_reference}. Implement 'fetch_file_from_reference' for your "
|
| 89 |
f"storage system.")
|
|
|
|
| 40 |
def __init__(self, supported_types: list):
|
| 41 |
self.supported_types = supported_types
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
def get_representation(self, type: str, content: bytes, format: str, mime_type) -> dict:
|
|
|
|
| 44 |
if type not in self.supported_types:
|
| 45 |
raise Exception(f"Invalid attachment type{type}")
|
| 46 |
+
base64_content = base64.b64encode(content).decode("utf-8")
|
| 47 |
+
|
| 48 |
if type == "audio":
|
| 49 |
return {"type": "input_audio",
|
| 50 |
"input_audio": {"data": base64_content, "format": format}}
|
|
|
|
| 55 |
raise Exception(f"Cannot extract a representation for type {type}")
|
| 56 |
|
| 57 |
def fetch_file_from_reference(self, file_reference: str) -> bytes:
|
| 58 |
+
"""Fetches file bytes from a reference """
|
|
|
|
| 59 |
# It's a local file path
|
| 60 |
file = Path(file_reference)
|
| 61 |
if file_reference.startswith("/") or file_reference.startswith("./") or file.exists():
|
| 62 |
return file.read_bytes()
|
|
|
|
|
|
|
|
|
|
| 63 |
else:
|
|
|
|
|
|
|
| 64 |
raise ValueError(
|
| 65 |
f"Could not resolve file reference: {file_reference}. Implement 'fetch_file_from_reference' for your "
|
| 66 |
f"storage system.")
|
nodes/nodes.py
CHANGED
|
@@ -66,7 +66,7 @@ def assistant(state: State):
|
|
| 66 |
response = model.invoke([sys_msg] + state["messages"])
|
| 67 |
except Exception as e:
|
| 68 |
if "429" in str(e):
|
| 69 |
-
time.sleep(
|
| 70 |
print("Retrying after receiving 429 error")
|
| 71 |
response = model.invoke([sys_msg] + state["messages"])
|
| 72 |
return {"messages": [response]}
|
|
|
|
| 66 |
response = model.invoke([sys_msg] + state["messages"])
|
| 67 |
except Exception as e:
|
| 68 |
if "429" in str(e):
|
| 69 |
+
time.sleep(20)
|
| 70 |
print("Retrying after receiving 429 error")
|
| 71 |
response = model.invoke([sys_msg] + state["messages"])
|
| 72 |
return {"messages": [response]}
|