Spaces:
Build error
Build error
one more try
Browse files- app.py +72 -154
- requirements.txt +7 -15
app.py
CHANGED
@@ -2,169 +2,89 @@ import os
|
|
2 |
import gradio as gr
|
3 |
import requests
|
4 |
import pandas as pd
|
5 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
6 |
-
from transformers import AutoProcessor, AutoModelForVision2Seq
|
7 |
-
from dotenv import load_dotenv
|
8 |
-
from typing import Optional, Dict, Any
|
9 |
-
from PIL import Image
|
10 |
-
import yt_dlp
|
11 |
import torch
|
12 |
-
import
|
13 |
-
from
|
|
|
14 |
|
15 |
# (Keep Constants as is)
|
16 |
# --- Constants ---
|
17 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
18 |
|
19 |
|
20 |
-
# ---
|
21 |
-
# ----- THIS IS
|
22 |
-
class
|
23 |
def __init__(self):
|
24 |
-
print("
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
#
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
)
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
40 |
device_map="auto",
|
41 |
-
|
42 |
-
trust_remote_code=True,
|
43 |
-
)
|
44 |
-
|
45 |
-
# Initialize vision model
|
46 |
-
vision_model_name = "microsoft/kosmos-2-patch14-224"
|
47 |
-
self.vision_processor = AutoProcessor.from_pretrained(vision_model_name)
|
48 |
-
self.vision_model = AutoModelForVision2Seq.from_pretrained(
|
49 |
-
vision_model_name, device_map="auto", trust_remote_code=True
|
50 |
)
|
51 |
-
|
52 |
-
print("Agent initialized with multimodal capabilities.")
|
53 |
-
|
54 |
-
def generate_text(self, prompt: str) -> str:
|
55 |
-
inputs = self.tokenizer(prompt, return_tensors="pt").to(self.model.device)
|
56 |
-
outputs = self.model.generate(
|
57 |
-
**inputs, max_new_tokens=512, temperature=0.1, do_sample=True
|
58 |
-
)
|
59 |
-
return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
|
60 |
-
|
61 |
-
def analyze_image(self, image_url: str) -> str:
|
62 |
-
try:
|
63 |
-
response = requests.get(image_url)
|
64 |
-
image = Image.open(BytesIO(response.content))
|
65 |
-
inputs = self.vision_processor(images=image, return_tensors="pt")
|
66 |
-
outputs = self.vision_model.generate(
|
67 |
-
pixel_values=inputs["pixel_values"], max_length=128, num_beams=5
|
68 |
-
)
|
69 |
-
return self.vision_processor.batch_decode(
|
70 |
-
outputs, skip_special_tokens=True
|
71 |
-
)[0]
|
72 |
-
except Exception as e:
|
73 |
-
print(f"Error analyzing image: {e}")
|
74 |
-
return "Error analyzing image"
|
75 |
-
|
76 |
-
def analyze_video(self, video_url: str) -> str:
|
77 |
-
try:
|
78 |
-
# Extract video info using yt-dlp
|
79 |
-
ydl_opts = {
|
80 |
-
"format": "worst", # Lowest quality to save bandwidth
|
81 |
-
"extract_flat": True,
|
82 |
-
"quiet": True,
|
83 |
-
}
|
84 |
-
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
85 |
-
info = ydl.extract_info(video_url, download=False)
|
86 |
-
|
87 |
-
# Extract relevant information
|
88 |
-
title = info.get("title", "")
|
89 |
-
description = info.get("description", "")
|
90 |
-
duration = info.get("duration", 0)
|
91 |
-
|
92 |
-
# Create prompt for the LLM
|
93 |
-
video_context = f"""
|
94 |
-
Video Title: {title}
|
95 |
-
Duration: {duration} seconds
|
96 |
-
Description: {description}
|
97 |
-
"""
|
98 |
-
|
99 |
-
return self.generate_response(video_context)
|
100 |
-
except Exception as e:
|
101 |
-
print(f"Error analyzing video: {e}")
|
102 |
-
return "Error analyzing video"
|
103 |
-
|
104 |
-
def analyze_data(self, df: pd.DataFrame, question: str) -> str:
|
105 |
-
try:
|
106 |
-
prompt = f"""Analyze this DataFrame:
|
107 |
-
Columns: {', '.join(df.columns)}
|
108 |
-
Sample: {df.head().to_string()}
|
109 |
-
Question: {question}
|
110 |
-
Provide only the numerical answer or specific value."""
|
111 |
-
|
112 |
-
return self.generate_response(prompt)
|
113 |
-
except Exception as e:
|
114 |
-
print(f"Error analyzing data: {e}")
|
115 |
-
return "Error analyzing data"
|
116 |
-
|
117 |
-
def generate_response(self, prompt: str) -> str:
|
118 |
-
try:
|
119 |
-
response = self.llm.complete(prompt)
|
120 |
-
return response.text.strip()
|
121 |
-
except Exception as e:
|
122 |
-
print(f"Error generating response: {e}")
|
123 |
-
return "Error generating response"
|
124 |
|
125 |
def __call__(self, question: str) -> str:
|
126 |
-
print(f"Agent received question: {question[:
|
127 |
|
128 |
-
|
129 |
-
|
130 |
-
if "```python" in question:
|
131 |
-
data_start = question.find("```python")
|
132 |
-
data_end = question.find("```", data_start + 8)
|
133 |
-
data_code = question[data_start + 8 : data_end].strip()
|
134 |
-
local_vars = {}
|
135 |
-
exec(data_code, {"pd": pd}, local_vars)
|
136 |
-
df = local_vars.get("df")
|
137 |
-
actual_question = question[data_end + 3 :].strip()
|
138 |
-
return self.analyze_data(df, actual_question)
|
139 |
|
140 |
-
|
141 |
-
|
142 |
-
video_match = re.search(video_pattern, question)
|
143 |
-
if video_match:
|
144 |
-
return self.analyze_video(video_match.group(0))
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
image_match = re.search(image_pattern, question)
|
149 |
-
if image_match:
|
150 |
-
return self.analyze_image(image_match.group(0))
|
151 |
|
152 |
-
|
153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
|
155 |
-
|
156 |
-
|
157 |
-
return "Error occurred while processing the question"
|
158 |
|
159 |
|
160 |
def run_and_submit_all(profile: gr.OAuthProfile | None):
|
161 |
"""
|
162 |
-
Fetches all questions, runs the
|
163 |
and displays the results.
|
164 |
"""
|
165 |
# --- Determine HF Space Runtime URL and Repo URL ---
|
166 |
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
167 |
-
|
168 |
if profile:
|
169 |
username = f"{profile.username}"
|
170 |
print(f"User logged in: {username}")
|
@@ -176,13 +96,14 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
176 |
questions_url = f"{api_url}/questions"
|
177 |
submit_url = f"{api_url}/submit"
|
178 |
|
179 |
-
# 1. Instantiate Agent (
|
180 |
try:
|
181 |
-
agent =
|
182 |
except Exception as e:
|
183 |
print(f"Error instantiating agent: {e}")
|
184 |
return f"Error initializing agent: {e}", None
|
185 |
-
|
|
|
186 |
agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
|
187 |
print(agent_code)
|
188 |
|
@@ -217,16 +138,21 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
217 |
if not task_id or question_text is None:
|
218 |
print(f"Skipping item with missing task_id or question: {item}")
|
219 |
continue
|
|
|
220 |
try:
|
221 |
submitted_answer = agent(question_text)
|
|
|
|
|
|
|
|
|
222 |
answers_payload.append(
|
223 |
-
{"task_id": task_id, "submitted_answer":
|
224 |
)
|
225 |
results_log.append(
|
226 |
{
|
227 |
"Task ID": task_id,
|
228 |
"Question": question_text,
|
229 |
-
"Submitted Answer":
|
230 |
}
|
231 |
)
|
232 |
except Exception as e:
|
@@ -298,30 +224,24 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
|
|
298 |
|
299 |
# --- Build Gradio Interface using Blocks ---
|
300 |
with gr.Blocks() as demo:
|
301 |
-
gr.Markdown("#
|
302 |
gr.Markdown(
|
303 |
"""
|
304 |
**Instructions:**
|
305 |
-
|
306 |
-
1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
|
307 |
2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
|
308 |
-
3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run
|
309 |
-
|
310 |
---
|
311 |
**Disclaimers:**
|
312 |
-
Once clicking on the "submit button, it can take quite some time
|
313 |
-
|
314 |
"""
|
315 |
)
|
316 |
-
|
317 |
gr.LoginButton()
|
318 |
-
|
319 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
320 |
-
|
321 |
status_output = gr.Textbox(
|
322 |
label="Run Status / Submission Result", lines=5, interactive=False
|
323 |
)
|
324 |
-
# Removed max_rows=10 from DataFrame constructor
|
325 |
results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
|
326 |
|
327 |
run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
|
@@ -331,7 +251,6 @@ if __name__ == "__main__":
|
|
331 |
# Check for SPACE_HOST and SPACE_ID at startup for information
|
332 |
space_host_startup = os.getenv("SPACE_HOST")
|
333 |
space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
|
334 |
-
|
335 |
if space_host_startup:
|
336 |
print(f"✅ SPACE_HOST found: {space_host_startup}")
|
337 |
print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
|
@@ -350,6 +269,5 @@ if __name__ == "__main__":
|
|
350 |
)
|
351 |
|
352 |
print("-" * (60 + len(" App Starting ")) + "\n")
|
353 |
-
|
354 |
-
print("Launching Gradio Interface for Basic Agent Evaluation...")
|
355 |
demo.launch(debug=True, share=False)
|
|
|
2 |
import gradio as gr
|
3 |
import requests
|
4 |
import pandas as pd
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
import torch
|
6 |
+
from llama_index.llms.huggingface import HuggingFaceLLM
|
7 |
+
from llama_index.core import ChatPromptTemplate
|
8 |
+
from llama_index.core.llms import ChatMessage
|
9 |
|
10 |
# (Keep Constants as is)
|
11 |
# --- Constants ---
|
12 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
13 |
|
14 |
|
15 |
+
# --- LlamaIndex Agent Definition ---
|
16 |
+
# ----- THIS IS THE MODIFIED AGENT USING LLAMA-INDEX ------
|
17 |
+
class LlamaIndexAgent:
|
18 |
def __init__(self):
|
19 |
+
print("Initializing LlamaIndexAgent...")
|
20 |
+
|
21 |
+
# Define the system prompt as requested by the user
|
22 |
+
system_prompt = """
|
23 |
+
You are a helpful assistant tasked with answering questions.
|
24 |
+
Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
|
25 |
+
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
|
26 |
+
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.
|
27 |
+
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.
|
28 |
+
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.
|
29 |
+
Your answer should only start with "FINAL ANSWER: ", then follows with the answer.
|
30 |
+
"""
|
31 |
|
32 |
+
# Using a fixed user and assistant message for the prompt template
|
33 |
+
self.chat_prompt_template = ChatPromptTemplate(
|
34 |
+
message_templates=[
|
35 |
+
ChatMessage(
|
36 |
+
role="system",
|
37 |
+
content=system_prompt,
|
38 |
+
),
|
39 |
+
ChatMessage(role="user", content="{question}"),
|
40 |
+
]
|
41 |
)
|
42 |
|
43 |
+
# Load the model. Using a smaller, free model that can run on CPU
|
44 |
+
# Using quantization to reduce memory footprint
|
45 |
+
self.llm = HuggingFaceLLM(
|
46 |
+
model_name="HuggingFaceH4/zephyr-7b-beta",
|
47 |
+
tokenizer_name="HuggingFaceH4/zephyr-7b-beta",
|
48 |
+
query_wrapper_prompt=self.chat_prompt_template,
|
49 |
device_map="auto",
|
50 |
+
model_kwargs={"torch_dtype": torch.float16, "load_in_8bit": True},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
)
|
52 |
+
print("LlamaIndexAgent initialized successfully.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
def __call__(self, question: str) -> str:
|
55 |
+
print(f"Agent received question (first 80 chars): {question[:80]}...")
|
56 |
|
57 |
+
# Format the messages
|
58 |
+
messages = self.chat_prompt_template.format_messages(question=question)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
+
# Get the response from the LLM
|
61 |
+
response = self.llm.chat(messages)
|
|
|
|
|
|
|
62 |
|
63 |
+
# Extract the answer from the response
|
64 |
+
answer = response.message.content.strip()
|
|
|
|
|
|
|
65 |
|
66 |
+
# Ensure the answer starts with "FINAL ANSWER: "
|
67 |
+
if "FINAL ANSWER:" in answer:
|
68 |
+
final_answer = answer.split("FINAL ANSWER:")[-1].strip()
|
69 |
+
else:
|
70 |
+
# If the model fails to follow the template, we return its raw output
|
71 |
+
# as the answer, prefixed with the required string.
|
72 |
+
print(
|
73 |
+
f"Warning: Model did not use the 'FINAL ANSWER:' template. Raw output: {answer}"
|
74 |
+
)
|
75 |
+
final_answer = answer
|
76 |
|
77 |
+
print(f"Agent returning answer: {final_answer}")
|
78 |
+
return f"FINAL ANSWER: {final_answer}"
|
|
|
79 |
|
80 |
|
81 |
def run_and_submit_all(profile: gr.OAuthProfile | None):
|
82 |
"""
|
83 |
+
Fetches all questions, runs the LlamaIndexAgent on them, submits all answers,
|
84 |
and displays the results.
|
85 |
"""
|
86 |
# --- Determine HF Space Runtime URL and Repo URL ---
|
87 |
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
|
|
88 |
if profile:
|
89 |
username = f"{profile.username}"
|
90 |
print(f"User logged in: {username}")
|
|
|
96 |
questions_url = f"{api_url}/questions"
|
97 |
submit_url = f"{api_url}/submit"
|
98 |
|
99 |
+
# 1. Instantiate Agent (modify this part to create your agent)
|
100 |
try:
|
101 |
+
agent = LlamaIndexAgent()
|
102 |
except Exception as e:
|
103 |
print(f"Error instantiating agent: {e}")
|
104 |
return f"Error initializing agent: {e}", None
|
105 |
+
|
106 |
+
# In the case of an app running as a hugging Face space, this link points toward your codebase
|
107 |
agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
|
108 |
print(agent_code)
|
109 |
|
|
|
138 |
if not task_id or question_text is None:
|
139 |
print(f"Skipping item with missing task_id or question: {item}")
|
140 |
continue
|
141 |
+
|
142 |
try:
|
143 |
submitted_answer = agent(question_text)
|
144 |
+
# The agent now returns the full "FINAL ANSWER: ..." string, so we extract the answer part for submission.
|
145 |
+
answer_for_submission = submitted_answer.replace(
|
146 |
+
"FINAL ANSWER:", ""
|
147 |
+
).strip()
|
148 |
answers_payload.append(
|
149 |
+
{"task_id": task_id, "submitted_answer": answer_for_submission}
|
150 |
)
|
151 |
results_log.append(
|
152 |
{
|
153 |
"Task ID": task_id,
|
154 |
"Question": question_text,
|
155 |
+
"Submitted Answer": answer_for_submission,
|
156 |
}
|
157 |
)
|
158 |
except Exception as e:
|
|
|
224 |
|
225 |
# --- Build Gradio Interface using Blocks ---
|
226 |
with gr.Blocks() as demo:
|
227 |
+
gr.Markdown("# LlamaIndex Agent Evaluation Runner for GAIA")
|
228 |
gr.Markdown(
|
229 |
"""
|
230 |
**Instructions:**
|
231 |
+
1. This space is configured with a `LlamaIndexAgent` to answer the GAIA subset questions.
|
|
|
232 |
2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
|
233 |
+
3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run the agent, submit answers, and see the score.
|
|
|
234 |
---
|
235 |
**Disclaimers:**
|
236 |
+
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.
|
237 |
+
Model loading can also take a few minutes on the first run.
|
238 |
"""
|
239 |
)
|
|
|
240 |
gr.LoginButton()
|
|
|
241 |
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
|
|
242 |
status_output = gr.Textbox(
|
243 |
label="Run Status / Submission Result", lines=5, interactive=False
|
244 |
)
|
|
|
245 |
results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
|
246 |
|
247 |
run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
|
|
|
251 |
# Check for SPACE_HOST and SPACE_ID at startup for information
|
252 |
space_host_startup = os.getenv("SPACE_HOST")
|
253 |
space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
|
|
|
254 |
if space_host_startup:
|
255 |
print(f"✅ SPACE_HOST found: {space_host_startup}")
|
256 |
print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
|
|
|
269 |
)
|
270 |
|
271 |
print("-" * (60 + len(" App Starting ")) + "\n")
|
272 |
+
print("Launching Gradio Interface for LlamaIndex Agent Evaluation...")
|
|
|
273 |
demo.launch(debug=True, share=False)
|
requirements.txt
CHANGED
@@ -1,16 +1,8 @@
|
|
1 |
-
gradio
|
2 |
-
|
3 |
-
|
|
|
|
|
4 |
transformers
|
5 |
-
|
6 |
-
|
7 |
-
sentence-transformers==2.3.1
|
8 |
-
nltk==3.8.1
|
9 |
-
accelerate==0.27.2
|
10 |
-
bitsandbytes==0.41.0
|
11 |
-
yt-dlp
|
12 |
-
Pillow==10.2.0
|
13 |
-
pandas==2.1.4
|
14 |
-
gradio[oauth]
|
15 |
-
transformers_stream_generator
|
16 |
-
einops
|
|
|
1 |
+
gradio
|
2 |
+
requests
|
3 |
+
pandas
|
4 |
+
llama-index
|
5 |
+
torch
|
6 |
transformers
|
7 |
+
accelerate
|
8 |
+
bitsandbytes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|