wt002 commited on
Commit
28a1f20
·
verified ·
1 Parent(s): 8ec51fb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +295 -251
app.py CHANGED
@@ -1,278 +1,322 @@
1
  import os
 
2
  import gradio as gr
 
 
 
 
 
3
  import requests
4
- import inspect
5
  import pandas as pd
6
- from smolagents import tool, Tool, CodeAgent, DuckDuckGoSearchTool, HfApiModel, VisitWebpageTool, SpeechToTextTool, FinalAnswerTool
7
  from dotenv import load_dotenv
8
- import heapq
9
- from collections import Counter
10
- import re
11
- from io import BytesIO
12
- from youtube_transcript_api import YouTubeTranscriptApi
13
- from langchain_community.tools.tavily_search import TavilySearchResults
14
- from langchain_community.document_loaders import WikipediaLoader
15
- from langchain_community.utilities import WikipediaAPIWrapper
16
- from langchain_community.document_loaders import ArxivLoader
17
- from transformers import AutoModelForCausalLM, AutoTokenizer
18
 
19
- # (Keep Constants as is)
20
- # --- Constants ---
21
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
 
22
 
23
- #Load environment variables
24
  load_dotenv()
25
 
 
 
 
 
26
 
 
 
 
27
 
 
28
 
29
- #from smolagents import Tool
30
- #from langchain_community.document_loaders import WikipediaLoader
31
-
32
- class WikiSearchTool(Tool):
33
- name = "wiki_search"
34
- description = "Search Wikipedia for a query and return up to 2 results."
35
- inputs = {
36
- "query": {"type": "string", "description": "The search term for Wikipedia."}
37
- }
38
- output_type = "string"
39
-
40
- def forward(self, query: str) -> str:
41
- search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
42
-
43
- formatted_search_docs = "\n\n---\n\n".join(
44
- [
45
- f'<Document source="{doc.metadata.get("source", "Wikipedia")}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
46
- for doc in search_docs
47
- ]
48
- )
49
- return formatted_search_docs
50
-
51
-
52
-
53
-
54
- class StringReverseTool(Tool):
55
- name = "reverse_message"
56
- description = "When you received a strange text, try to reverse it and perform action described in reversed message."
57
- inputs = {
58
- "message": {
59
- "type": "string",
60
- "description": "A message, which looks like strange and can be reversed to get actions to execute."
61
- }
62
- }
63
- output_type = "string"
64
-
65
- def __init__(self):
66
- return
67
-
68
- def forward(self, message: str):
69
- return message[::-1]
70
-
71
- class KeywordsExtractorTool(Tool):
72
- """Extracts top 5 keywords from a given text based on frequency."""
73
-
74
- name = "keywords_extractor"
75
- description = "This tool returns the 5 most frequent keywords occur in provided block of text."
76
-
77
- inputs = {
78
- "text": {
79
- "type": "string",
80
- "description": "Text to analyze for keywords.",
81
- }
82
- }
83
- output_type = "string"
84
-
85
- def forward(self, text: str) -> str:
86
- try:
87
- all_words = re.findall(r'\b\w+\b', text.lower())
88
- conjunctions = {'a', 'and', 'of', 'is', 'in', 'to', 'the'}
89
- filtered_words = []
90
- for w in all_words:
91
- if w not in conjunctions:
92
- filtered_words.push(w)
93
- word_counts = Counter(filtered_words)
94
- k = 5
95
- return heapq.nlargest(k, word_counts.items(), key=lambda x: x[1])
96
- except Exception as e:
97
- return f"Error during extracting most common words: {e}"
98
-
99
- @tool
100
- def parse_excel_to_json(task_id: str) -> dict:
101
  """
102
- For a given task_id fetch and parse an Excel file and save parsed data in structured JSON file.
103
- Args:
104
- task_id: An task ID to fetch.
105
-
106
- Returns:
107
- {
108
- "task_id": str,
109
- "sheets": {
110
- "SheetName1": [ {col1: val1, col2: val2, ...}, ... ],
111
- ...
112
- },
113
- "status": "Success" | "Error"
114
- }
115
  """
116
- url = f"https://agents-course-unit4-scoring.hf.space/files/{task_id}"
117
-
118
  try:
119
- response = requests.get(url, timeout=100)
 
120
  if response.status_code != 200:
121
- return {"task_id": task_id, "sheets": {}, "status": f"{response.status_code} - Failed"}
122
-
123
- xls_content = pd.ExcelFile(BytesIO(response.content))
124
- json_sheets = {}
125
-
126
- for sheet in xls_content.sheet_names:
127
- df = xls_content.parse(sheet)
128
- df = df.dropna(how="all")
129
- rows = df.head(20).to_dict(orient="records")
130
- json_sheets[sheet] = rows
131
-
132
- return {
133
- "task_id": task_id,
134
- "sheets": json_sheets,
135
- "status": "Success"
136
- }
137
-
138
  except Exception as e:
139
- return {
140
- "task_id": task_id,
141
- "sheets": {},
142
- "status": f"Error in parsing Excel file: {str(e)}"
143
- }
144
-
145
-
146
-
147
- class VideoTranscriptionTool(Tool):
148
- """Fetch transcripts from YouTube videos"""
149
- name = "transcript_video"
150
- description = "Fetch text transcript from YouTube movies with optional timestamps"
151
- inputs = {
152
- "url": {"type": "string", "description": "YouTube video URL or ID"},
153
- "include_timestamps": {"type": "boolean", "description": "If timestamps should be included in output", "nullable": True}
154
- }
155
- output_type = "string"
156
-
157
- def forward(self, url: str, include_timestamps: bool = False) -> str:
158
-
159
- if "youtube.com/watch" in url:
160
- video_id = url.split("v=")[1].split("&")[0]
161
- elif "youtu.be/" in url:
162
- video_id = url.split("youtu.be/")[1].split("?")[0]
163
- elif len(url.strip()) == 11: # Direct ID
164
- video_id = url.strip()
165
- else:
166
- return f"YouTube URL or ID: {url} is invalid!"
167
-
168
- try:
169
- transcription = YouTubeTranscriptApi.get_transcript(video_id)
170
-
171
- if include_timestamps:
172
- formatted_transcription = []
173
- for part in transcription:
174
- timestamp = f"{int(part['start']//60)}:{int(part['start']%60):02d}"
175
- formatted_transcription.append(f"[{timestamp}] {part['text']}")
176
- return "\n".join(formatted_transcription)
177
- else:
178
- return " ".join([part['text'] for part in transcription])
179
-
180
- except Exception as e:
181
- return f"Error in extracting YouTube transcript: {str(e)}"
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  class BasicAgent:
184
  def __init__(self):
185
- # Configuration for Qwen2.5-Coder-32B-Instruct
186
- model_name = "Qwen/Qwen2.5-Coder-32B-Instruct"
187
-
188
- # Load the model and tokenizer directly using Hugging Face Transformers
189
- # This will download the model weights and load them onto your device (GPU if available)
190
- self.model = AutoModelForCausalLM.from_pretrained(
191
- model_name,
192
- torch_dtype="auto", # Uses bfloat16 or float16 if supported, otherwise float32
193
- device_map="auto" # Automatically maps model layers to available devices (e.g., GPU(s), CPU)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  )
195
- self.tokenizer = AutoTokenizer.from_pretrained(model_name)
196
-
197
- # Note: You won't use 'token' for direct Hugging Face model loading unless
198
- # the model is private and requires authentication. For public models like Qwen,
199
- # it's usually not needed for loading.
200
- token = os.environ.get("HF_API_TOKEN") # This line might not be needed now
201
-
202
- search_tool = DuckDuckGoSearchTool()
203
- wiki_search_tool = WikiSearchTool()
204
- str_reverse_tool = StringReverseTool()
205
- keywords_extract_tool = KeywordsExtractorTool()
206
- speech_to_text_tool = SpeechToTextTool()
207
- visit_webpage_tool = VisitWebpageTool()
208
- final_answer_tool = FinalAnswerTool()
209
- video_transcription_tool = VideoTranscriptionTool()
210
-
211
- system_prompt = f"""
212
- You are my general AI assistant. Your task is to answer the question I asked.
213
- First, provide an explanation of your reasoning, step by step, to arrive at the answer.
214
- Then, return your final answer in a single line, formatted as follows: "FINAL ANSWER: [YOUR FINAL ANSWER]".
215
- [YOUR FINAL ANSWER] should be a number, a string, or a comma-separated list of numbers and/or strings, depending on the question.
216
- If the answer is a number, do not use commas or units (e.g., $, %) unless specified.
217
- If the answer is a string, do not use articles or abbreviations (e.g., for cities), and write digits in plain text unless specified.
218
- If the answer is a comma-separated list, apply the above rules for each element based on whether it is a number or a string.
219
- """
220
-
221
- # Here, you might need to adapt how CodeAgent expects the model.
222
- # If CodeAgent is built to work with LangChain's LLM instances,
223
- # you'll need to wrap your Qwen model with a custom LangChain LLM
224
- # or adjust CodeAgent to accept direct Hugging Face model/tokenizer.
225
- # For simplicity, if CodeAgent can take a callable for 'model',
226
- # you could define a simple wrapper.
227
- # Otherwise, you might need to write a custom LangChain LLM class.
228
-
229
- # For demonstration, let's assume CodeAgent can handle a custom callable
230
- # that performs inference using your loaded model and tokenizer.
231
- # This is a simplification and might require adjustment to CodeAgent.
232
- class CustomQwenLLM:
233
- def __init__(self, model, tokenizer):
234
- self.model = model
235
- self.tokenizer = tokenizer
236
-
237
- def __call__(self, prompt: str) -> str:
238
- messages = [
239
- {"role": "system", "content": "You are Qwen, created by Alibaba Cloud. You are a helpful assistant."},
240
- {"role": "user", "content": prompt}
241
- ]
242
- text = self.tokenizer.apply_chat_template(
243
- messages,
244
- tokenize=False,
245
- add_generation_prompt=True
246
- )
247
- model_inputs = self.tokenizer([text], return_tensors="pt").to(self.model.device)
248
-
249
- generated_ids = self.model.generate(
250
- **model_inputs,
251
- max_new_tokens=512,
252
- do_sample=True, # Added for better response quality
253
- temperature=0.7 # Added for better response quality
254
- )
255
- input_length = model_inputs.input_ids.shape[1]
256
- generated_text = self.tokenizer.batch_decode(generated_ids[:, input_length:], skip_special_tokens=True)[0]
257
- return generated_text
258
-
259
- self.llm_for_agent = CustomQwenLLM(self.model, self.tokenizer)
260
-
261
-
262
- self.agent = CodeAgent(
263
- model=self.llm_for_agent, # Pass the custom wrapper
264
- tools=[search_tool, wiki_search_tool, str_reverse_tool, keywords_extract_tool, speech_to_text_tool, visit_webpage_tool, final_answer_tool, parse_excel_to_json, video_transcription_tool],
265
- add_base_tools=True
266
- )
267
- self.agent.prompt_templates["system_prompt"] = self.agent.prompt_templates["system_prompt"] + system_prompt
 
 
 
 
 
 
 
 
 
268
 
269
- def __call__(self, question: str) -> str:
270
- print(f"Agent received question (first 50 chars): {question[:50]}...")
271
- answer = self.agent.run(question)
272
- print(f"Agent returning answer: {answer}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  return answer
274
 
275
-
276
  def run_and_submit_all( profile: gr.OAuthProfile | None):
277
  """
278
  Fetches all questions, runs the BasicAgent on them, submits all answers,
 
1
  import os
2
+ from typing import Annotated, Optional, TypedDict
3
  import gradio as gr
4
+ from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage
5
+ from langchain_openai import ChatOpenAI
6
+ from langgraph.graph.message import add_messages
7
+ from langgraph.graph import StateGraph, START
8
+ from langgraph.prebuilt import tools_condition, ToolNode
9
  import requests
 
10
  import pandas as pd
11
+ from langchain.tools import Tool
12
  from dotenv import load_dotenv
 
 
 
 
 
 
 
 
 
 
13
 
14
+ from arxiv_searcher import ArxivSearcher
15
+ from chess_algebraic_notation_retriever import ChessAlgebraicNotationMoveRetriever
16
+ from excel_file_reader import ExcelFileReader
17
+ from image_question_answer_tool import ImageQuestionAnswerTool
18
+ from python_code_question_answer_tool import PythonCodeQuestionAnswerTool
19
+ from tavily_searcher import TavilySearcher
20
+ from transcriber import Transcriber
21
+ from wikipedia_searcher import WikipediaSearcher
22
+ from youtube_video_question_answer_tool import YoutubeVideoQuestionAnswerTool
23
 
 
24
  load_dotenv()
25
 
26
+ # (Keep Constants as is)
27
+ # --- Constants ---
28
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
29
+ ASSOCIATED_FILE_ENDPOINT = f"{DEFAULT_API_URL}/files/"
30
 
31
+ # --- Basic Agent Definition ---
32
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
33
+ #search_tool = DuckDuckGoSearchRun()
34
 
35
+ #search_tool = DuckDuckGoSearcherTool()
36
 
37
+ def retrieve_task_file(task_id: str) -> Optional[bytes]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  """
39
+ Retrieve the task file for a given task ID.
 
 
 
 
 
 
 
 
 
 
 
 
40
  """
 
 
41
  try:
42
+ response = requests.get(ASSOCIATED_FILE_ENDPOINT + task_id, timeout=15)
43
+ response.raise_for_status()
44
  if response.status_code != 200:
45
+ print(f"Error fetching file: {response.status_code}")
46
+ return None
47
+ #print(f"Fetched file: {response.content}")
48
+ return response.content
49
+ except requests.exceptions.RequestException as e:
50
+ print(f"Error fetching file: {e}")
51
+ return None
 
 
 
 
 
 
 
 
 
 
52
  except Exception as e:
53
+ print(f"An unexpected error occurred fetching file: {e}")
54
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
+ def retrieve_next_chess_move_in_algebraic_notation(task_file_path: str, is_black_turn: bool) -> str:
57
+ """
58
+ Retrieve the next chess move in algebraic notation from an image path.
59
+ """
60
+ if task_file_path is None:
61
+ return "Error: Task file not found."
62
+ # Retrieve the next chess move in algebraic notation
63
+ next_chess_move = ChessAlgebraicNotationMoveRetriever().retrieve(task_file_path, is_black_turn)
64
+ return next_chess_move
65
+
66
+ # Initialize the tool
67
+ retrieve_next_chess_move_in_algebraic_notation_tool = Tool(
68
+ name="retrieve_next_chess_move_in_algebraic_notation",
69
+ func=retrieve_next_chess_move_in_algebraic_notation,
70
+ description="Retrieve the next chess move in algebraic notation from an image path."
71
+ )
72
+
73
+ def transcribe_audio(file_path: str) -> str:
74
+ if file_path is None:
75
+ return "Error: Audio path not found."
76
+ # Transcribe the audio
77
+ return Transcriber().transcribe(file_path)
78
+
79
+ # Initialize the tool
80
+ transcribe_audio_tool = Tool(
81
+ name="transcribe_audio",
82
+ func=transcribe_audio,
83
+ description="Transcribe the audio from an audio path."
84
+ )
85
+
86
+ # Initialize the tool
87
+ answer_python_code_tool = PythonCodeQuestionAnswerTool()
88
+
89
+ # Initialize the tool
90
+ answer_image_question_tool = ImageQuestionAnswerTool()
91
+
92
+ # Initialize the tool
93
+ answer_youtube_video_question_tool = YoutubeVideoQuestionAnswerTool()
94
+
95
+ '''def answer_youtube_video_question(youtube_video_url: str, question: str) -> str:
96
+ """
97
+ Answer the question based on the youtube video.
98
+ """
99
+ if youtube_video_url is None:
100
+ return "Error: Video not found."
101
+ # Download the video
102
+ video_path = YoutubeVideoDownloader().download_video(youtube_video_url)
103
+ # Answer the question
104
+ return VideoQuestionAnswer().answer(video_path, question)
105
+ # Initialize the tool
106
+ answer_youtube_video_question_tool = Tool(
107
+ name="answer_youtube_video_question",
108
+ func=answer_youtube_video_question,
109
+ description="Answer the question based on the youtube video."
110
+ )'''
111
+
112
+ def read_excel_file(file_path: str) -> str:
113
+ if file_path is None:
114
+ return "Error: File not found."
115
+ return ExcelFileReader().read_file(file_path)
116
+
117
+ # Initialize the tool
118
+ read_excel_file_tool = Tool(
119
+ name="read_excel_file",
120
+ func=read_excel_file,
121
+ description="Read the excel file."
122
+ )
123
+
124
+ # Initialize the tool
125
+ wikipedia_search_tool = Tool(
126
+ name="wikipedia_search",
127
+ func=WikipediaSearcher().search,
128
+ description="Search Wikipedia for a given query."
129
+ )
130
+
131
+ # Initialize the tool
132
+ arxiv_search_tool = Tool(
133
+ name="arxiv_search",
134
+ func=ArxivSearcher().search,
135
+ description="Search Arxiv for a given query."
136
+ )
137
+
138
+ tavily_search_tool = Tool(
139
+ name="tavily_search",
140
+ func=TavilySearcher().search,
141
+ description="Search the web for a given query."
142
+ )
143
+
144
+ def format_gaia_answer(answer: str) -> str:
145
+ llm = ChatOpenAI(model="o3-mini", openai_api_key=os.getenv("OPENAI_API_KEY"))
146
+ prompt = f"""
147
+ You are formatting answers for the GAIA benchmark, which requires responses to be concise and unambiguous.
148
+ Given the answer: {answer}
149
+ Return the answer in the correct GAIA format:
150
+ - If the answer is a single word or number, return it without any additional text or formatting.
151
+ - If the answer is a list, return a comma-separated list without any additional text or formatting.
152
+ - If the answer is a string, return it without any additional text or formatting.
153
+ Do not include any prefixes, dots, enumerations, explanations, or quotation marks.
154
+ Do not include any additional text or formatting.
155
+ """
156
+ response = llm.invoke(prompt)
157
+ # Delete double quotes
158
+ return response.content.strip().replace('"', '')
159
+
160
+ class AgentState(TypedDict):
161
+ # The document provided
162
+ messages: Annotated[list[AnyMessage], add_messages]
163
+ file_path: Optional[str]
164
+
165
  class BasicAgent:
166
  def __init__(self):
167
+ tools = [
168
+ tavily_search_tool,
169
+ arxiv_search_tool,
170
+ wikipedia_search_tool,
171
+ transcribe_audio_tool,
172
+ answer_python_code_tool,
173
+ answer_image_question_tool,
174
+ answer_youtube_video_question_tool,
175
+ read_excel_file_tool
176
+ ]
177
+ '''llm = ChatGoogleGenerativeAI(
178
+ model="gemini-2.0-flash",
179
+ temperature=0.2,
180
+ api_key=os.getenv("GEMINI_API_KEY")
181
+ )'''
182
+ llm = ChatOpenAI(model="o3-mini", openai_api_key=os.getenv("OPENAI_API_KEY"))
183
+ self.llm_with_tools = llm.bind_tools(tools)
184
+ builder = StateGraph(AgentState)
185
+
186
+ # Define nodes: these do the work
187
+ builder.add_node("assistant", self.assistant)
188
+ builder.add_node("tools", ToolNode(tools))
189
+
190
+ # Define edges: these determine how the control flow moves
191
+ builder.add_edge(START, "assistant")
192
+ builder.add_conditional_edges(
193
+ "assistant",
194
+ # If the latest message requires a tool, route to tools
195
+ # Otherwise, provide a direct response
196
+ tools_condition,
197
  )
198
+ builder.add_edge("tools", "assistant")
199
+ self.agent = builder.compile()
200
+
201
+ print("BasicAgent initialized.")
202
+
203
+ def assistant(self, state: AgentState):
204
+ # System message
205
+ textual_description_of_tools="""
206
+ tavily_search(query: str) -> str:
207
+ Search the web for a given query.
208
+ Args:
209
+ query: Query to search the web for (string).
210
+ Returns:
211
+ A single string containing the information found on the web.
212
+ arxiv_search(query: str) -> str:
213
+ Search Arxiv, that contains scientific papers, for a given query.
214
+ Args:
215
+ query: Query to search Arxiv for (string).
216
+ Returns:
217
+ A single string containing the answer to the question.
218
+ wikipedia_search(query: str) -> str:
219
+ Search Wikipedia for a given query.
220
+ Args:
221
+ query: Query to search Wikipedia for (string).
222
+ Returns:
223
+ A single string containing the answer to the question.
224
+ transcribe_audio(file_path: str) -> str:
225
+ Transcribe the audio from an audio path.
226
+ Args:
227
+ file_path: File path of the audio file (string).
228
+ Returns:
229
+ A single string containing the transcribed text from the audio.
230
+
231
+ answer_python_code(file_path: str, question: str) -> str:
232
+ Answer the question based on the python code.
233
+ Args:
234
+ file_path: File path of the python file (string).
235
+ question: Question to answer (string).
236
+ Returns:
237
+ A single string containing the answer to the question.
238
+
239
+ answer_image_question(file_path: str, question: str) -> str:
240
+ Answer the question based on the image.
241
+ Args:
242
+ file_path: File path of the image (string).
243
+ question: Question to answer (string).
244
+ Returns:
245
+ A single string containing the answer to the question.
246
+
247
+ download_youtube_video(youtube_video_url: str) -> str:
248
+ Download the Youtube video into a local file based on the URL
249
+ Args:
250
+ youtube_video_url: A youtube video url (string).
251
+ Returns:
252
+ A single string containing the file path of the downloaded youtube video.
253
+ answer_youtube_video_question(file_path: str, question: str) -> str:
254
+ Answer the question based on file path of the downloaded youtube video
255
+ Args:
256
+ file_path: File path of the downloaded youtube video (string).
257
+ question: Question to answer (string).
258
+ Returns:
259
+ A single string containing the answer to the question.
260
+
261
+ read_excel_file(file_path: str) -> str:
262
+ Read the excel file.
263
+ Args:
264
+ file_path: File path of the excel file (string).
265
+ Returns:
266
+ A markdown formatted string containing the contents of the excel file.
267
+ """
268
+ file_path=state["file_path"]
269
+ prompt = f"""
270
+ You are a helpful assistant that can analyse images, videos, excel files and Python scripts and run computations with provided tools:
271
+ {textual_description_of_tools}
272
+ You have access to the file path of the attached file in case it's informed. Currently the file path is: {file_path}
273
+ Be direct and specific. GAIA benchmark requires exact matching answers.
274
+ For example, if asked "What is the capital of France?", respond simply with "Paris".
275
+ Do not include any prefixes, dots, enumerations, explanations, or quotation marks.
276
+ Do not include any additional text or formatting.
277
+ If you are required a number, return a number, not the items.
278
+ """
279
+ sys_msg = SystemMessage(content=prompt)
280
 
281
+ return {
282
+ "messages": [self.llm_with_tools.invoke([sys_msg] + state["messages"], config={"configurable": {"file_path": state["file_path"]}})],
283
+ "file_path": state["file_path"]
284
+ }
285
+ '''return {
286
+ "messages": [self.llm_with_tools.invoke(
287
+ state["messages"],
288
+ config={"configurable": {"file_path": state["file_path"]}} # Aquí pasas el task_id
289
+ )],
290
+ "file_path": state["file_path"]
291
+ }'''
292
+
293
+ def __call__(self, question: str, task_id: str, file_name: str) -> str:
294
+ print(f"######################### Agent received question (first 50 chars): {question[:50]}... with file_name: {file_name}")
295
+
296
+ # Get the file path
297
+ tmp_file_path = None
298
+ if file_name is not None and file_name != "":
299
+ file_content = retrieve_task_file(task_id)
300
+ if file_content is not None:
301
+ print(f"Saving file {file_name} to tmp folder")
302
+ tmp_file_path = f"tmp/{file_name}"
303
+ with open(tmp_file_path, "wb") as f:
304
+ f.write(file_content)
305
+ # Show the file path
306
+ print(f"File path: {tmp_file_path}")
307
+
308
+ messages = self.agent.invoke({"messages": [HumanMessage(question)], "file_path": tmp_file_path})
309
+ # Show the messages
310
+ for m in messages['messages']:
311
+ m.pretty_print()
312
+ answer = messages["messages"][-1].content
313
+ answer = format_gaia_answer(answer)
314
+ print(f"######################### Agent returning answer: {answer}\n")
315
+ # Delete the file
316
+ if tmp_file_path is not None:
317
+ os.remove(tmp_file_path)
318
  return answer
319
 
 
320
  def run_and_submit_all( profile: gr.OAuthProfile | None):
321
  """
322
  Fetches all questions, runs the BasicAgent on them, submits all answers,