Nihal2000 commited on
Commit
ae622ab
Β·
1 Parent(s): 23b8df2

first commit

Browse files
Files changed (4) hide show
  1. README.md +9 -7
  2. app.py +196 -0
  3. basic_agent.py +272 -0
  4. requirements.txt +16 -0
README.md CHANGED
@@ -1,13 +1,15 @@
1
  ---
2
- title: My Agent
3
- emoji: πŸ”₯
4
- colorFrom: pink
5
- colorTo: yellow
6
  sdk: gradio
7
- sdk_version: 5.31.0
8
  app_file: app.py
9
  pinned: false
10
- license: mit
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
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
9
  pinned: false
10
+ hf_oauth: true
11
+ # optional, default duration is 8 hours/480 minutes. Max duration is 30 days/43200 minutes.
12
+ hf_oauth_expiration_minutes: 480
13
  ---
14
 
15
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import inspect
5
+ import pandas as pd
6
+
7
+ # (Keep Constants as is)
8
+ # --- Constants ---
9
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
+
11
+ # --- Basic Agent Definition ---
12
+ # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
+ class BasicAgent:
14
+ def __init__(self):
15
+ print("BasicAgent initialized.")
16
+ def __call__(self, question: str) -> str:
17
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
18
+ fixed_answer = "This is a default answer."
19
+ print(f"Agent returning fixed answer: {fixed_answer}")
20
+ return fixed_answer
21
+
22
+ def run_and_submit_all( profile: gr.OAuthProfile | None):
23
+ """
24
+ Fetches all questions, runs the BasicAgent on them, submits all answers,
25
+ and displays the results.
26
+ """
27
+ # --- Determine HF Space Runtime URL and Repo URL ---
28
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
+
30
+ if profile:
31
+ username= f"{profile.username}"
32
+ print(f"User logged in: {username}")
33
+ else:
34
+ print("User not logged in.")
35
+ return "Please Login to Hugging Face with the button.", None
36
+
37
+ api_url = DEFAULT_API_URL
38
+ questions_url = f"{api_url}/questions"
39
+ submit_url = f"{api_url}/submit"
40
+
41
+ # 1. Instantiate Agent ( modify this part to create your agent)
42
+ try:
43
+ agent = BasicAgent()
44
+ except Exception as e:
45
+ print(f"Error instantiating agent: {e}")
46
+ return f"Error initializing agent: {e}", None
47
+ # In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
48
+ agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
49
+ print(agent_code)
50
+
51
+ # 2. Fetch Questions
52
+ print(f"Fetching questions from: {questions_url}")
53
+ try:
54
+ response = requests.get(questions_url, timeout=15)
55
+ response.raise_for_status()
56
+ questions_data = response.json()
57
+ if not questions_data:
58
+ print("Fetched questions list is empty.")
59
+ return "Fetched questions list is empty or invalid format.", None
60
+ print(f"Fetched {len(questions_data)} questions.")
61
+ except requests.exceptions.RequestException as e:
62
+ print(f"Error fetching questions: {e}")
63
+ return f"Error fetching questions: {e}", None
64
+ except requests.exceptions.JSONDecodeError as e:
65
+ print(f"Error decoding JSON response from questions endpoint: {e}")
66
+ print(f"Response text: {response.text[:500]}")
67
+ return f"Error decoding server response for questions: {e}", None
68
+ except Exception as e:
69
+ print(f"An unexpected error occurred fetching questions: {e}")
70
+ return f"An unexpected error occurred fetching questions: {e}", None
71
+
72
+ # 3. Run your Agent
73
+ results_log = []
74
+ answers_payload = []
75
+ print(f"Running agent on {len(questions_data)} questions...")
76
+ for item in questions_data:
77
+ task_id = item.get("task_id")
78
+ question_text = item.get("question")
79
+ if not task_id or question_text is None:
80
+ print(f"Skipping item with missing task_id or question: {item}")
81
+ continue
82
+ try:
83
+ submitted_answer = agent(question_text)
84
+ answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
86
+ except Exception as e:
87
+ print(f"Error running agent on task {task_id}: {e}")
88
+ results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
89
+
90
+ if not answers_payload:
91
+ print("Agent did not produce any answers to submit.")
92
+ return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
+
94
+ # 4. Prepare Submission
95
+ submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
96
+ status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
+ print(status_update)
98
+
99
+ # 5. Submit
100
+ print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
101
+ try:
102
+ response = requests.post(submit_url, json=submission_data, timeout=60)
103
+ response.raise_for_status()
104
+ result_data = response.json()
105
+ final_status = (
106
+ f"Submission Successful!\n"
107
+ f"User: {result_data.get('username')}\n"
108
+ f"Overall Score: {result_data.get('score', 'N/A')}% "
109
+ f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
110
+ f"Message: {result_data.get('message', 'No message received.')}"
111
+ )
112
+ print("Submission successful.")
113
+ results_df = pd.DataFrame(results_log)
114
+ return final_status, results_df
115
+ except requests.exceptions.HTTPError as e:
116
+ error_detail = f"Server responded with status {e.response.status_code}."
117
+ try:
118
+ error_json = e.response.json()
119
+ error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
120
+ except requests.exceptions.JSONDecodeError:
121
+ error_detail += f" Response: {e.response.text[:500]}"
122
+ status_message = f"Submission Failed: {error_detail}"
123
+ print(status_message)
124
+ results_df = pd.DataFrame(results_log)
125
+ return status_message, results_df
126
+ except requests.exceptions.Timeout:
127
+ status_message = "Submission Failed: The request timed out."
128
+ print(status_message)
129
+ results_df = pd.DataFrame(results_log)
130
+ return status_message, results_df
131
+ except requests.exceptions.RequestException as e:
132
+ status_message = f"Submission Failed: Network error - {e}"
133
+ print(status_message)
134
+ results_df = pd.DataFrame(results_log)
135
+ return status_message, results_df
136
+ except Exception as e:
137
+ status_message = f"An unexpected error occurred during submission: {e}"
138
+ print(status_message)
139
+ results_df = pd.DataFrame(results_log)
140
+ return status_message, results_df
141
+
142
+
143
+ # --- Build Gradio Interface using Blocks ---
144
+ with gr.Blocks() as demo:
145
+ gr.Markdown("# Basic Agent Evaluation Runner")
146
+ gr.Markdown(
147
+ """
148
+ **Instructions:**
149
+
150
+ 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
151
+ 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
152
+ 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
153
+
154
+ ---
155
+ **Disclaimers:**
156
+ 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).
157
+ This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
158
+ """
159
+ )
160
+
161
+ gr.LoginButton()
162
+
163
+ run_button = gr.Button("Run Evaluation & Submit All Answers")
164
+
165
+ status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
166
+ # Removed max_rows=10 from DataFrame constructor
167
+ results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
+
169
+ run_button.click(
170
+ fn=run_and_submit_all,
171
+ outputs=[status_output, results_table]
172
+ )
173
+
174
+ if __name__ == "__main__":
175
+ print("\n" + "-"*30 + " App Starting " + "-"*30)
176
+ # Check for SPACE_HOST and SPACE_ID at startup for information
177
+ space_host_startup = os.getenv("SPACE_HOST")
178
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
+
180
+ if space_host_startup:
181
+ print(f"βœ… SPACE_HOST found: {space_host_startup}")
182
+ print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
183
+ else:
184
+ print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
+
186
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
187
+ print(f"βœ… SPACE_ID found: {space_id_startup}")
188
+ print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
+ print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
190
+ else:
191
+ print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
192
+
193
+ print("-"*(60 + len(" App Starting ")) + "\n")
194
+
195
+ print("Launching Gradio Interface for Basic Agent Evaluation...")
196
+ demo.launch(debug=True, share=False)
basic_agent.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A LangGraph-powered agent with at least 14 tools for question answering.
3
+ """
4
+ import wikipedia
5
+ import sympy
6
+ import requests
7
+ from datetime import datetime
8
+ from dateutil import parser as date_parser
9
+ from duckduckgo_search import DDGS
10
+ import pytz
11
+ import openai
12
+ import wolframalpha
13
+ import pandas as pd
14
+ from bs4 import BeautifulSoup
15
+ import httpx
16
+ from langgraph.graph import StateGraph, END
17
+
18
+ # 1. Wikipedia Search Tool
19
+ def wikipedia_search(query):
20
+ try:
21
+ return wikipedia.summary(query, sentences=2)
22
+ except Exception as e:
23
+ return f"Wikipedia error: {e}"
24
+
25
+ # 2. Math Evaluation Tool
26
+ def math_eval(expr):
27
+ try:
28
+ return str(sympy.sympify(expr).evalf())
29
+ except Exception as e:
30
+ return f"Math error: {e}"
31
+
32
+ # 3. DuckDuckGo Web Search Tool
33
+ def ddg_search(query):
34
+ try:
35
+ results = list(DDGS().text(query, max_results=1))
36
+ return results[0]['body'] if results else 'No results.'
37
+ except Exception as e:
38
+ return f"DDG error: {e}"
39
+
40
+ # 4. Current Date Tool
41
+ def get_current_date():
42
+ return datetime.now().strftime('%Y-%m-%d')
43
+
44
+ # 5. Current Time Tool
45
+ def get_current_time():
46
+ return datetime.now().strftime('%H:%M:%S')
47
+
48
+ # 6. Timezone Conversion Tool
49
+ def convert_timezone(dt_str, from_tz, to_tz):
50
+ try:
51
+ dt = date_parser.parse(dt_str)
52
+ from_zone = pytz.timezone(from_tz)
53
+ to_zone = pytz.timezone(to_tz)
54
+ dt = from_zone.localize(dt)
55
+ return dt.astimezone(to_zone).strftime('%Y-%m-%d %H:%M:%S')
56
+ except Exception as e:
57
+ return f"Timezone error: {e}"
58
+
59
+ # 7. OpenAI GPT-3.5 Completion Tool (requires OPENAI_API_KEY env var)
60
+ def openai_completion(prompt):
61
+ try:
62
+ openai.api_key = os.getenv('OPENAI_API_KEY')
63
+ resp = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}])
64
+ return resp.choices[0].message.content.strip()
65
+ except Exception as e:
66
+ return f"OpenAI error: {e}"
67
+
68
+ # 8. WolframAlpha Short Answer Tool (requires WOLFRAMALPHA_APPID env var)
69
+ def wolfram_query(query):
70
+ try:
71
+ appid = os.getenv('WOLFRAMALPHA_APPID')
72
+ client = wolframalpha.Client(appid)
73
+ res = client.query(query)
74
+ return next(res.results).text
75
+ except Exception as e:
76
+ return f"WolframAlpha error: {e}"
77
+
78
+ # 9. Weather API Tool (using python-weather-api)
79
+ def get_weather(city):
80
+ try:
81
+ import python_weather
82
+ import asyncio
83
+ async def getweather():
84
+ async with python_weather.Client(unit=python_weather.METRIC) as client:
85
+ weather = await client.get(city)
86
+ return f"{weather.current.temperature}Β°C, {weather.current.sky_text}"
87
+ return asyncio.run(getweather())
88
+ except Exception as e:
89
+ return f"Weather error: {e}"
90
+
91
+ # 10. Pandas DataFrame Tool
92
+ def describe_dataframe(csv_url):
93
+ try:
94
+ df = pd.read_csv(csv_url)
95
+ return str(df.describe())
96
+ except Exception as e:
97
+ return f"Pandas error: {e}"
98
+
99
+ # 11. BeautifulSoup HTML Title Extractor
100
+ def extract_title(url):
101
+ try:
102
+ resp = requests.get(url, timeout=10)
103
+ soup = BeautifulSoup(resp.text, 'lxml')
104
+ return soup.title.string.strip() if soup.title else 'No title found.'
105
+ except Exception as e:
106
+ return f"Soup error: {e}"
107
+
108
+ # 12. HTTPX GET Tool
109
+ def httpx_get(url):
110
+ try:
111
+ resp = httpx.get(url, timeout=10)
112
+ return resp.text[:500]
113
+ except Exception as e:
114
+ return f"HTTPX error: {e}"
115
+
116
+ # 13. Currency Conversion Tool (using exchangerate.host)
117
+ def convert_currency(amount, from_cur, to_cur):
118
+ try:
119
+ url = f"https://api.exchangerate.host/convert?from={from_cur}&to={to_cur}&amount={amount}"
120
+ resp = requests.get(url)
121
+ data = resp.json()
122
+ return f"{amount} {from_cur} = {data['result']} {to_cur}"
123
+ except Exception as e:
124
+ return f"Currency error: {e}"
125
+
126
+ # 14. IP Geolocation Tool
127
+ def ip_geolocate(ip):
128
+ try:
129
+ resp = requests.get(f"https://ipinfo.io/{ip}/json")
130
+ data = resp.json()
131
+ return f"{data.get('city', '?')}, {data.get('region', '?')}, {data.get('country', '?')}"
132
+ except Exception as e:
133
+ return f"IP error: {e}"
134
+
135
+ # --- LangGraph Agent Setup ---
136
+ from langgraph.agent import Tool, Agent
137
+
138
+ tools = [
139
+ Tool("wikipedia_search", wikipedia_search, description="Search Wikipedia for a summary."),
140
+ Tool("math_eval", math_eval, description="Evaluate a math expression."),
141
+ Tool("ddg_search", ddg_search, description="DuckDuckGo web search."),
142
+ Tool("get_current_date", get_current_date, description="Get the current date."),
143
+ Tool("get_current_time", get_current_time, description="Get the current time."),
144
+ Tool("convert_timezone", convert_timezone, description="Convert time between timezones."),
145
+ Tool("openai_completion", openai_completion, description="Get a completion from OpenAI GPT-3.5."),
146
+ Tool("wolfram_query", wolfram_query, description="Query WolframAlpha for a short answer."),
147
+ Tool("get_weather", get_weather, description="Get current weather for a city."),
148
+ Tool("describe_dataframe", describe_dataframe, description="Describe a CSV file using pandas."),
149
+ Tool("extract_title", extract_title, description="Extract the title from a webpage."),
150
+ Tool("httpx_get", httpx_get, description="Fetch a webpage using HTTPX."),
151
+ Tool("convert_currency", convert_currency, description="Convert currency using exchangerate.host."),
152
+ Tool("ip_geolocate", ip_geolocate, description="Get geolocation info for an IP address."),
153
+ ]
154
+
155
+ class LangGraphAgent:
156
+ def __init__(self):
157
+ self.agent = Agent(tools=tools)
158
+ def __call__(self, question: str) -> str:
159
+ """Use LangGraph agent to answer the question."""
160
+ try:
161
+ return self.agent.run(question)
162
+ except Exception as e:
163
+ return f"LangGraphAgent error: {e}"
164
+
165
+ # Define a simple state for demonstration (can be extended for more complex workflows)
166
+ class AgentState(dict):
167
+ pass
168
+
169
+ def build_langgraph():
170
+ # Create a graph
171
+ graph = StateGraph(AgentState)
172
+
173
+ # Add each tool as a node
174
+ for tool in tools:
175
+ def make_tool_node(tool_func):
176
+ def node(state: AgentState):
177
+ question = state.get('question', '')
178
+ try:
179
+ result = tool_func(question)
180
+ except Exception as e:
181
+ result = f"Tool error: {e}"
182
+ state['result'] = result
183
+ return state
184
+ return node
185
+ graph.add_node(tool.name, make_tool_node(tool.func))
186
+
187
+ # For demonstration, connect all nodes to END (in practice, you may want to chain or branch)
188
+ for tool in tools:
189
+ graph.add_edge(tool.name, END)
190
+
191
+ # Set entry point (first tool)
192
+ graph.set_entry_point(tools[0].name)
193
+
194
+ return graph
195
+
196
+ # --- GAIA-style Multi-step Agent ---
197
+ import re
198
+
199
+ class GAIAAgent:
200
+ """
201
+ A simple GAIA-style agent that can plan and chain multiple tools for multi-step reasoning.
202
+ This is a minimal demonstration and can be extended for more advanced planning.
203
+ """
204
+ def __init__(self):
205
+ self.tool_map = {t.name: t.func for t in tools}
206
+
207
+ def plan(self, question: str):
208
+ """
209
+ Very simple planner: looks for keywords to select tools and chain them.
210
+ Returns a list of (tool_name, tool_input) tuples.
211
+ """
212
+ steps = []
213
+ q = question.lower()
214
+ # Example: If question asks for weather in a city and then convert time
215
+ if 'weather' in q and 'time' in q and 'convert' in q:
216
+ # e.g., "What is the weather in Paris and convert the time to Tokyo timezone?"
217
+ city = re.findall(r'weather in ([a-zA-Z ]+)', q)
218
+ city = city[0].strip() if city else 'London'
219
+ steps.append(('get_weather', city))
220
+ # Assume user wants to convert current time from city to Tokyo
221
+ steps.append(('convert_timezone', [datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'Europe/Paris', 'Asia/Tokyo']))
222
+ elif 'weather' in q:
223
+ city = re.findall(r'weather in ([a-zA-Z ]+)', q)
224
+ city = city[0].strip() if city else 'London'
225
+ steps.append(('get_weather', city))
226
+ elif 'currency' in q or 'convert' in q and 'usd' in q and 'eur' in q:
227
+ amount = re.findall(r'(\d+)', q)
228
+ amount = amount[0] if amount else '1'
229
+ steps.append(('convert_currency', [amount, 'USD', 'EUR']))
230
+ elif 'wikipedia' in q or 'who is' in q or 'what is' in q:
231
+ topic = re.findall(r'(?:wikipedia|who is|what is) ([a-zA-Z0-9 ]+)', q)
232
+ topic = topic[0].strip() if topic else question
233
+ steps.append(('wikipedia_search', topic))
234
+ elif 'math' in q or any(op in q for op in ['+', '-', '*', '/']):
235
+ expr = re.findall(r'([\d\s\+\-\*/\.]+)', q)
236
+ expr = expr[0] if expr else question
237
+ steps.append(('math_eval', expr))
238
+ else:
239
+ # Default: try DuckDuckGo search
240
+ steps.append(('ddg_search', question))
241
+ return steps
242
+
243
+ def __call__(self, question: str) -> str:
244
+ """
245
+ Execute the planned steps, chaining outputs if needed.
246
+ """
247
+ steps = self.plan(question)
248
+ last_output = None
249
+ for tool_name, tool_input in steps:
250
+ func = self.tool_map.get(tool_name)
251
+ if not func:
252
+ last_output = f"Tool {tool_name} not found."
253
+ break
254
+ # If previous output is needed as input, use it
255
+ if isinstance(tool_input, list):
256
+ # Replace any placeholder with last_output
257
+ tool_input = [last_output if x == '__PREV__' else x for x in tool_input]
258
+ try:
259
+ last_output = func(*tool_input)
260
+ except Exception as e:
261
+ last_output = f"Error in {tool_name}: {e}"
262
+ else:
263
+ try:
264
+ last_output = func(tool_input)
265
+ except Exception as e:
266
+ last_output = f"Error in {tool_name}: {e}"
267
+ return last_output
268
+
269
+ # Example usage:
270
+ # graph = build_langgraph()
271
+ # result = graph.run({'question': 'What is the capital of France?'})
272
+ # print(result['result'])
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio[oauth]
2
+ requests
3
+ langgraph
4
+ langchain
5
+ wikipedia
6
+ python-dateutil
7
+ duckduckgo-search
8
+ openai
9
+ wolframalpha
10
+ pytz
11
+ python-weather-api
12
+ sympy
13
+ pandas
14
+ beautifulsoup4
15
+ lxml
16
+ httpx