Walidb commited on
Commit
cbf4814
·
1 Parent(s): 871486c
Files changed (3) hide show
  1. app.py +11 -2
  2. myagent_langraph.py +116 -23
  3. requirements.txt +3 -0
app.py CHANGED
@@ -30,6 +30,7 @@ class WalidzAgent:
30
  else:
31
  # If "FINAL ANSWER:" is not found, return the original answer
32
  print("Warning: 'FINAL ANSWER:' not found in response")
 
33
  return answer
34
 
35
  # (Keep Constants as is)
@@ -215,7 +216,15 @@ def run_evaluation_only(profile: gr.OAuthProfile | None):
215
  print(f"Skipping item with missing task_id or question: {item}")
216
  continue
217
  try:
218
- submitted_answer = agent(question_text)
 
 
 
 
 
 
 
 
219
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
220
  except Exception as e:
221
  print(f"Error running agent on task {task_id}: {e}")
@@ -226,7 +235,7 @@ def run_evaluation_only(profile: gr.OAuthProfile | None):
226
 
227
  # --- Build Gradio Interface using Blocks ---
228
  with gr.Blocks() as demo:
229
- gr.Markdown("# Walid's Basic Agent Evaluation Runner (v3)")
230
  gr.Markdown(
231
  """
232
  **Instructions:**
 
30
  else:
31
  # If "FINAL ANSWER:" is not found, return the original answer
32
  print("Warning: 'FINAL ANSWER:' not found in response")
33
+ print(answer)
34
  return answer
35
 
36
  # (Keep Constants as is)
 
216
  print(f"Skipping item with missing task_id or question: {item}")
217
  continue
218
  try:
219
+ # Prepare input state
220
+ human_msg = HumanMessage(content=question_text)
221
+ input_state = {
222
+ "messages": [human_msg],
223
+ "question_text": question_text
224
+ }
225
+
226
+ submitted_answer = agent(input_state)
227
+ #submitted_answer = agent(question_text)
228
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
229
  except Exception as e:
230
  print(f"Error running agent on task {task_id}: {e}")
 
235
 
236
  # --- Build Gradio Interface using Blocks ---
237
  with gr.Blocks() as demo:
238
+ gr.Markdown("# Walid's Basic Agent Evaluation Runner (v4)")
239
  gr.Markdown(
240
  """
241
  **Instructions:**
myagent_langraph.py CHANGED
@@ -8,13 +8,11 @@ from langgraph.prebuilt import ToolNode, tools_condition
8
  #from IPython.display import Image, display
9
  from langchain_community.document_loaders import WikipediaLoader
10
  from langchain_community.tools.tavily_search import TavilySearchResults
 
 
11
 
12
  from dotenv import load_dotenv
13
  import os
14
- import sys
15
-
16
- # Do not modify stdout/stdin as it can cause issues
17
- import codecs
18
 
19
  # Load environment variables
20
  load_dotenv()
@@ -30,6 +28,7 @@ class AgentState(TypedDict):
30
  messages: Annotated[list[AnyMessage], add_messages]
31
 
32
  ### TOOLS ###
 
33
  def search_wikipedia(query: str) -> str:
34
  """Searches Wikipedia for a given query and returns maximum 1 results."""
35
  try:
@@ -59,6 +58,7 @@ def search_wikipedia(query: str) -> str:
59
  print(f"Error in search_wikipedia: {e}")
60
  return f"Error searching Wikipedia: {str(e)}"
61
 
 
62
  def multiply(a: int, b: int) -> int:
63
  """Multiply two numbers.
64
  Args:
@@ -67,6 +67,7 @@ def multiply(a: int, b: int) -> int:
67
  """
68
  return a * b
69
 
 
70
  def add(a: int, b: int) -> int:
71
  """Add two numbers.
72
 
@@ -76,6 +77,7 @@ def add(a: int, b: int) -> int:
76
  """
77
  return a + b
78
 
 
79
  def subtract(a: int, b: int) -> int:
80
  """Subtract two numbers.
81
 
@@ -85,6 +87,7 @@ def subtract(a: int, b: int) -> int:
85
  """
86
  return a - b
87
 
 
88
  def divide(a: int, b: int) -> int:
89
  """Divide two numbers.
90
 
@@ -96,6 +99,7 @@ def divide(a: int, b: int) -> int:
96
  raise ValueError("Cannot divide by zero.")
97
  return a / b
98
 
 
99
  def modulus(a: int, b: int) -> int:
100
  """Get the modulus of two numbers.
101
 
@@ -105,19 +109,20 @@ def modulus(a: int, b: int) -> int:
105
  """
106
  return a % b
107
 
108
-
109
  def web_search(query: str) -> str:
110
- """Search Tavily for a query and return maximum 3 results.
111
 
112
  Args:
113
  query: The search query."""
114
- search_docs = TavilySearchResults(max_results=3).invoke(query=query)
 
115
  formatted_search_docs = "\n\n---\n\n".join(
116
  [
117
- f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
118
- for doc in search_docs
119
  ])
120
- return {"web_results": formatted_search_docs}
121
 
122
  # Equip the butler with tools
123
  tools = [
@@ -138,6 +143,7 @@ try:
138
  openai_api_key=OPENAI_API_KEY,
139
  default_headers={"Content-Type": "application/json; charset=utf-8"}
140
  )
 
141
  llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
142
  except Exception as e:
143
  print(f"Error initializing OpenAI model: {e}")
@@ -152,19 +158,21 @@ def assistant(state: AgentState):
152
  # Build the system prompt piece by piece
153
  parts = [
154
  "You are a helpful assistant tasked with answering questions using a set of tools.",
155
- "Report your thoughts, and finish your answer with the following template:",
156
- "FINAL ANSWER: [YOUR FINAL ANSWER].",
157
- "YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.",
158
- "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.",
159
- "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.",
160
- "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.",
 
161
 
162
  # Tool descriptions
163
  "You can use these provided tools:",
164
  "search_wikipedia(query: str) -> str:",
165
  " Searches Wikipedia for a given query and returns maximum 1 results.",
166
- "divide(a: int, b: int) -> float:",
167
- " Divide a and b"
 
168
  ]
169
 
170
  # Add the question
@@ -211,9 +219,94 @@ def build_graph():
211
  return react_graph
212
 
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  if __name__ == "__main__":
215
- react_graph = build_graph()
216
- # Show the butler's thought process
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  # Save the graph visualization to disk
218
  #graph_image = react_graph.get_graph(xray=True).draw_mermaid_png()
219
  #with open("graph_visualization.png", "wb") as f:
@@ -224,10 +317,10 @@ if __name__ == "__main__":
224
  #display(Image(graph_image))
225
 
226
 
227
- question_sample = "How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of english wikipedia."
228
  #question_sample = question_sample.encode('utf-8', errors='replace').decode('utf-8')
229
 
230
- print("Running with sample question:", question_sample)
 
231
 
232
  try:
233
  # Create message with proper encoding
@@ -249,5 +342,5 @@ if __name__ == "__main__":
249
  except Exception as e:
250
  print(f"Error in Agent execution: {str(e)}")
251
  import traceback
252
- traceback.print_exc()
253
 
 
8
  #from IPython.display import Image, display
9
  from langchain_community.document_loaders import WikipediaLoader
10
  from langchain_community.tools.tavily_search import TavilySearchResults
11
+ #from langchain_community.tools.youtube_search import YoutubeSearchResults
12
+ from langchain_core.tools import tool
13
 
14
  from dotenv import load_dotenv
15
  import os
 
 
 
 
16
 
17
  # Load environment variables
18
  load_dotenv()
 
28
  messages: Annotated[list[AnyMessage], add_messages]
29
 
30
  ### TOOLS ###
31
+ @tool
32
  def search_wikipedia(query: str) -> str:
33
  """Searches Wikipedia for a given query and returns maximum 1 results."""
34
  try:
 
58
  print(f"Error in search_wikipedia: {e}")
59
  return f"Error searching Wikipedia: {str(e)}"
60
 
61
+ @tool
62
  def multiply(a: int, b: int) -> int:
63
  """Multiply two numbers.
64
  Args:
 
67
  """
68
  return a * b
69
 
70
+ @tool
71
  def add(a: int, b: int) -> int:
72
  """Add two numbers.
73
 
 
77
  """
78
  return a + b
79
 
80
+ @tool
81
  def subtract(a: int, b: int) -> int:
82
  """Subtract two numbers.
83
 
 
87
  """
88
  return a - b
89
 
90
+ @tool
91
  def divide(a: int, b: int) -> int:
92
  """Divide two numbers.
93
 
 
99
  raise ValueError("Cannot divide by zero.")
100
  return a / b
101
 
102
+ @tool
103
  def modulus(a: int, b: int) -> int:
104
  """Get the modulus of two numbers.
105
 
 
109
  """
110
  return a % b
111
 
112
+ @tool
113
  def web_search(query: str) -> str:
114
+ """Search the web for a query and return maximum 3 results.
115
 
116
  Args:
117
  query: The search query."""
118
+ search_tool = TavilySearchResults(max_results=3)
119
+ search_results = search_tool.invoke(query)
120
  formatted_search_docs = "\n\n---\n\n".join(
121
  [
122
+ f'<Document source="{result["url"]}" title="{result.get("title", "")}"/>\n{result["content"]}\n</Document>'
123
+ for result in search_results
124
  ])
125
+ return formatted_search_docs
126
 
127
  # Equip the butler with tools
128
  tools = [
 
143
  openai_api_key=OPENAI_API_KEY,
144
  default_headers={"Content-Type": "application/json; charset=utf-8"}
145
  )
146
+ print("Binding tools...")
147
  llm_with_tools = llm.bind_tools(tools, parallel_tool_calls=False)
148
  except Exception as e:
149
  print(f"Error initializing OpenAI model: {e}")
 
158
  # Build the system prompt piece by piece
159
  parts = [
160
  "You are a helpful assistant tasked with answering questions using a set of tools.",
161
+ "Report your thoughts step by step.",
162
+ "Once you have determined the answer, provide it in the following XML format:",
163
+ "<FINAL_ANSWER>Your concise final answer here</FINAL_ANSWER>",
164
+ "Your final answer should be as concise as possible - a number OR as few words as possible OR a comma separated list of numbers and/or strings.",
165
+ "If asked for a number, don't use commas or units such as $ or percent sign unless specified otherwise.",
166
+ "If asked for a string, don't use articles or abbreviations (e.g. for cities), and write digits in plain text unless specified otherwise.",
167
+ "The answer inside <FINAL_ANSWER> tags must be complete, clear, and not truncated.",
168
 
169
  # Tool descriptions
170
  "You can use these provided tools:",
171
  "search_wikipedia(query: str) -> str:",
172
  " Searches Wikipedia for a given query and returns maximum 1 results.",
173
+ "web_search(query: str) -> str:",
174
+ " Searches the web for a query and returns maximum 3 results.",
175
+ "multiply, add, subtract, divide, modulus: Basic math operations between two numbers."
176
  ]
177
 
178
  # Add the question
 
219
  return react_graph
220
 
221
 
222
+
223
+ class WalidzAgent:
224
+ """A langgraph agent."""
225
+ def __init__(self):
226
+ print("BasicAgent initialized.")
227
+ self.graph = build_graph()
228
+
229
+ def __call__(self, question: str) -> str:
230
+ if isinstance(question, dict) and "messages" in question and "question_text" in question:
231
+ # We got a properly formatted input
232
+ input_state = question
233
+ else:
234
+ # Create a proper input state from the question string
235
+ human_msg = HumanMessage(content=question)
236
+ input_state = {
237
+ "messages": [human_msg],
238
+ "question_text": question
239
+ }
240
+
241
+ # Stream the execution and capture final step
242
+ final_state = None
243
+ for step in self.graph.stream(input_state):
244
+ print("--- STEP ---")
245
+ print(step)
246
+ final_state = step
247
+
248
+ # Extract the answer from the final state
249
+ if final_state:
250
+ # Extract message content from the appropriate structure
251
+ message_content = None
252
+
253
+ # Check if the result has the nested 'assistant' structure
254
+ if 'assistant' in final_state and 'messages' in final_state['assistant']:
255
+ # Get message from assistant's messages field
256
+ message = final_state['assistant']['messages']
257
+ message_content = message.content
258
+ # Fallback to direct messages list if available
259
+ elif 'messages' in final_state:
260
+ messages_list = final_state['messages']
261
+ if messages_list and len(messages_list) > 0:
262
+ message_content = messages_list[-1].content
263
+
264
+ if not message_content:
265
+ print("Warning: Could not extract message content from response")
266
+ return "Failed to extract message content from agent response"
267
+
268
+ # First try XML tag extraction
269
+ import re
270
+ xml_pattern = re.compile(r'<FINAL_ANSWER>(.*?)</FINAL_ANSWER>', re.DOTALL)
271
+ match = xml_pattern.search(message_content)
272
+
273
+ if match:
274
+ # Found XML tags
275
+ answer = match.group(1).strip()
276
+ else:
277
+ # Fall back to "FINAL ANSWER:" format
278
+ if "FINAL ANSWER:" in message_content:
279
+ # Split by "FINAL ANSWER:" and take the last part
280
+ final_answer_parts = message_content.split("FINAL ANSWER:")
281
+ answer = final_answer_parts[-1].strip()
282
+ else:
283
+ # If no explicit final answer format is found, return the whole message
284
+ print("Warning: No final answer format found in response")
285
+ answer = message_content
286
+
287
+ print(f"Extracted final answer: {answer}")
288
+ return answer
289
+
290
+ # Fallback if no valid answer found
291
+ print("Warning: Could not extract answer from agent response")
292
+ return "No valid answer could be extracted from agent response"
293
+
294
  if __name__ == "__main__":
295
+
296
+ question_text = "How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)? You can use the latest 2022 version of english wikipedia."
297
+ question_text = "What country had the least number of athletes at the 1928 Summer Olympics? If there's a tie for a number of athletes, return the first in alphabetical order. Give the IOC country code as your answer."
298
+
299
+ agent = WalidzAgent()
300
+ # Prepare input state
301
+ human_msg = HumanMessage(content=question_text)
302
+ input_state = {
303
+ "messages": [human_msg],
304
+ "question_text": question_text
305
+ }
306
+
307
+ agent(input_state)
308
+
309
+
310
  # Save the graph visualization to disk
311
  #graph_image = react_graph.get_graph(xray=True).draw_mermaid_png()
312
  #with open("graph_visualization.png", "wb") as f:
 
317
  #display(Image(graph_image))
318
 
319
 
 
320
  #question_sample = question_sample.encode('utf-8', errors='replace').decode('utf-8')
321
 
322
+
323
+ """print("Running with sample question:", question_sample)
324
 
325
  try:
326
  # Create message with proper encoding
 
342
  except Exception as e:
343
  print(f"Error in Agent execution: {str(e)}")
344
  import traceback
345
+ traceback.print_exc()"""
346
 
requirements.txt CHANGED
@@ -10,3 +10,6 @@ langgraph
10
  huggingface_hub
11
  wikipedia
12
  langchain_openai
 
 
 
 
10
  huggingface_hub
11
  wikipedia
12
  langchain_openai
13
+ youtube_search
14
+ youtube-transcript-api
15
+ pytube