daviddwlee84 commited on
Commit
c5afa64
·
1 Parent(s): 9a8c112

Style the code and make it local runnable

Browse files
.gitignore ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .gradio
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+ cover/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Django stuff:
61
+ *.log
62
+ local_settings.py
63
+ db.sqlite3
64
+ db.sqlite3-journal
65
+
66
+ # Flask stuff:
67
+ instance/
68
+ .webassets-cache
69
+
70
+ # Scrapy stuff:
71
+ .scrapy
72
+
73
+ # Sphinx documentation
74
+ docs/_build/
75
+
76
+ # PyBuilder
77
+ .pybuilder/
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ # For a library or package, you might want to ignore these files since the code is
89
+ # intended to run in multiple environments; otherwise, check them in:
90
+ # .python-version
91
+
92
+ # pipenv
93
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
94
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
95
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
96
+ # install all needed dependencies.
97
+ #Pipfile.lock
98
+
99
+ # UV
100
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
101
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
102
+ # commonly ignored for libraries.
103
+ #uv.lock
104
+
105
+ # poetry
106
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
107
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
108
+ # commonly ignored for libraries.
109
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
110
+ #poetry.lock
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ #pdm.lock
115
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
116
+ # in version control.
117
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
118
+ .pdm.toml
119
+ .pdm-python
120
+ .pdm-build/
121
+
122
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
123
+ __pypackages__/
124
+
125
+ # Celery stuff
126
+ celerybeat-schedule
127
+ celerybeat.pid
128
+
129
+ # SageMath parsed files
130
+ *.sage.py
131
+
132
+ # Environments
133
+ .env
134
+ .venv
135
+ env/
136
+ venv/
137
+ ENV/
138
+ env.bak/
139
+ venv.bak/
140
+
141
+ # Spyder project settings
142
+ .spyderproject
143
+ .spyproject
144
+
145
+ # Rope project settings
146
+ .ropeproject
147
+
148
+ # mkdocs documentation
149
+ /site
150
+
151
+ # mypy
152
+ .mypy_cache/
153
+ .dmypy.json
154
+ dmypy.json
155
+
156
+ # Pyre type checker
157
+ .pyre/
158
+
159
+ # pytype static type analyzer
160
+ .pytype/
161
+
162
+ # Cython debug symbols
163
+ cython_debug/
164
+
165
+ # PyCharm
166
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
167
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
168
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
169
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
170
+ #.idea/
171
+
172
+ # PyPI configuration file
173
+ .pypirc
Gradio_UI.py CHANGED
@@ -19,7 +19,12 @@ import re
19
  import shutil
20
  from typing import Optional
21
 
22
- from smolagents.agent_types import AgentAudio, AgentImage, AgentText, handle_agent_output_types
 
 
 
 
 
23
  from smolagents.agents import ActionStep, MultiStepAgent
24
  from smolagents.memory import MemoryStep
25
  from smolagents.utils import _is_package_available
@@ -33,7 +38,9 @@ def pull_messages_from_step(
33
 
34
  if isinstance(step_log, ActionStep):
35
  # Output the step number
36
- step_number = f"Step {step_log.step_number}" if step_log.step_number is not None else ""
 
 
37
  yield gr.ChatMessage(role="assistant", content=f"**{step_number}**")
38
 
39
  # First yield the thought/reasoning from the LLM
@@ -41,9 +48,15 @@ def pull_messages_from_step(
41
  # Clean up the LLM output
42
  model_output = step_log.model_output.strip()
43
  # Remove any trailing <end_code> and extra backticks, handling multiple possible formats
44
- model_output = re.sub(r"```\s*<end_code>", "```", model_output) # handles ```<end_code>
45
- model_output = re.sub(r"<end_code>\s*```", "```", model_output) # handles <end_code>```
46
- model_output = re.sub(r"```\s*\n\s*<end_code>", "```", model_output) # handles ```\n<end_code>
 
 
 
 
 
 
47
  model_output = model_output.strip()
48
  yield gr.ChatMessage(role="assistant", content=model_output)
49
 
@@ -63,8 +76,12 @@ def pull_messages_from_step(
63
 
64
  if used_code:
65
  # Clean up the content by removing any end code tags
66
- content = re.sub(r"```.*?\n", "", content) # Remove existing code blocks
67
- content = re.sub(r"\s*<end_code>\s*", "", content) # Remove end_code tags
 
 
 
 
68
  content = content.strip()
69
  if not content.startswith("```python"):
70
  content = f"```python\n{content}\n```"
@@ -90,7 +107,11 @@ def pull_messages_from_step(
90
  yield gr.ChatMessage(
91
  role="assistant",
92
  content=f"{log_content}",
93
- metadata={"title": "📝 Execution Logs", "parent_id": parent_id, "status": "done"},
 
 
 
 
94
  )
95
 
96
  # Nesting any errors under the tool call
@@ -98,7 +119,11 @@ def pull_messages_from_step(
98
  yield gr.ChatMessage(
99
  role="assistant",
100
  content=str(step_log.error),
101
- metadata={"title": "💥 Error", "parent_id": parent_id, "status": "done"},
 
 
 
 
102
  )
103
 
104
  # Update parent message metadata to done status without yielding a new message
@@ -106,17 +131,25 @@ def pull_messages_from_step(
106
 
107
  # Handle standalone errors but not from tool calls
108
  elif hasattr(step_log, "error") and step_log.error is not None:
109
- yield gr.ChatMessage(role="assistant", content=str(step_log.error), metadata={"title": "💥 Error"})
 
 
 
 
110
 
111
  # Calculate duration and token information
112
  step_footnote = f"{step_number}"
113
- if hasattr(step_log, "input_token_count") and hasattr(step_log, "output_token_count"):
114
- token_str = (
115
- f" | Input-tokens:{step_log.input_token_count:,} | Output-tokens:{step_log.output_token_count:,}"
116
- )
117
  step_footnote += token_str
118
  if hasattr(step_log, "duration"):
119
- step_duration = f" | Duration: {round(float(step_log.duration), 2)}" if step_log.duration else None
 
 
 
 
120
  step_footnote += step_duration
121
  step_footnote = f"""<span style="color: #bbbbc2; font-size: 12px;">{step_footnote}</span> """
122
  yield gr.ChatMessage(role="assistant", content=f"{step_footnote}")
@@ -139,7 +172,9 @@ def stream_to_gradio(
139
  total_input_tokens = 0
140
  total_output_tokens = 0
141
 
142
- for step_log in agent.run(task, stream=True, reset=reset_agent_memory, additional_args=additional_args):
 
 
143
  # Track tokens if model provides them
144
  if hasattr(agent.model, "last_input_token_count"):
145
  total_input_tokens += agent.model.last_input_token_count
@@ -172,7 +207,9 @@ def stream_to_gradio(
172
  content={"path": final_answer.to_string(), "mime_type": "audio/wav"},
173
  )
174
  else:
175
- yield gr.ChatMessage(role="assistant", content=f"**Final answer:** {str(final_answer)}")
 
 
176
 
177
 
178
  class GradioUI:
@@ -242,10 +279,14 @@ class GradioUI:
242
  sanitized_name = "".join(sanitized_name)
243
 
244
  # Save the uploaded file to the specified folder
245
- file_path = os.path.join(self.file_upload_folder, os.path.basename(sanitized_name))
 
 
246
  shutil.copy(file.name, file_path)
247
 
248
- return gr.Textbox(f"File uploaded: {file_path}", visible=True), file_uploads_log + [file_path]
 
 
249
 
250
  def log_user_message(self, text_input, file_uploads_log):
251
  return (
@@ -277,7 +318,9 @@ class GradioUI:
277
  # If an upload folder is provided, enable the upload feature
278
  if self.file_upload_folder is not None:
279
  upload_file = gr.File(label="Upload a file")
280
- upload_status = gr.Textbox(label="Upload Status", interactive=False, visible=False)
 
 
281
  upload_file.change(
282
  self.upload_file,
283
  [upload_file, file_uploads_log],
@@ -293,4 +336,4 @@ class GradioUI:
293
  demo.launch(debug=True, share=True, **kwargs)
294
 
295
 
296
- __all__ = ["stream_to_gradio", "GradioUI"]
 
19
  import shutil
20
  from typing import Optional
21
 
22
+ from smolagents.agent_types import (
23
+ AgentAudio,
24
+ AgentImage,
25
+ AgentText,
26
+ handle_agent_output_types,
27
+ )
28
  from smolagents.agents import ActionStep, MultiStepAgent
29
  from smolagents.memory import MemoryStep
30
  from smolagents.utils import _is_package_available
 
38
 
39
  if isinstance(step_log, ActionStep):
40
  # Output the step number
41
+ step_number = (
42
+ f"Step {step_log.step_number}" if step_log.step_number is not None else ""
43
+ )
44
  yield gr.ChatMessage(role="assistant", content=f"**{step_number}**")
45
 
46
  # First yield the thought/reasoning from the LLM
 
48
  # Clean up the LLM output
49
  model_output = step_log.model_output.strip()
50
  # Remove any trailing <end_code> and extra backticks, handling multiple possible formats
51
+ model_output = re.sub(
52
+ r"```\s*<end_code>", "```", model_output
53
+ ) # handles ```<end_code>
54
+ model_output = re.sub(
55
+ r"<end_code>\s*```", "```", model_output
56
+ ) # handles <end_code>```
57
+ model_output = re.sub(
58
+ r"```\s*\n\s*<end_code>", "```", model_output
59
+ ) # handles ```\n<end_code>
60
  model_output = model_output.strip()
61
  yield gr.ChatMessage(role="assistant", content=model_output)
62
 
 
76
 
77
  if used_code:
78
  # Clean up the content by removing any end code tags
79
+ content = re.sub(
80
+ r"```.*?\n", "", content
81
+ ) # Remove existing code blocks
82
+ content = re.sub(
83
+ r"\s*<end_code>\s*", "", content
84
+ ) # Remove end_code tags
85
  content = content.strip()
86
  if not content.startswith("```python"):
87
  content = f"```python\n{content}\n```"
 
107
  yield gr.ChatMessage(
108
  role="assistant",
109
  content=f"{log_content}",
110
+ metadata={
111
+ "title": "📝 Execution Logs",
112
+ "parent_id": parent_id,
113
+ "status": "done",
114
+ },
115
  )
116
 
117
  # Nesting any errors under the tool call
 
119
  yield gr.ChatMessage(
120
  role="assistant",
121
  content=str(step_log.error),
122
+ metadata={
123
+ "title": "💥 Error",
124
+ "parent_id": parent_id,
125
+ "status": "done",
126
+ },
127
  )
128
 
129
  # Update parent message metadata to done status without yielding a new message
 
131
 
132
  # Handle standalone errors but not from tool calls
133
  elif hasattr(step_log, "error") and step_log.error is not None:
134
+ yield gr.ChatMessage(
135
+ role="assistant",
136
+ content=str(step_log.error),
137
+ metadata={"title": "💥 Error"},
138
+ )
139
 
140
  # Calculate duration and token information
141
  step_footnote = f"{step_number}"
142
+ if hasattr(step_log, "input_token_count") and hasattr(
143
+ step_log, "output_token_count"
144
+ ):
145
+ token_str = f" | Input-tokens:{step_log.input_token_count:,} | Output-tokens:{step_log.output_token_count:,}"
146
  step_footnote += token_str
147
  if hasattr(step_log, "duration"):
148
+ step_duration = (
149
+ f" | Duration: {round(float(step_log.duration), 2)}"
150
+ if step_log.duration
151
+ else None
152
+ )
153
  step_footnote += step_duration
154
  step_footnote = f"""<span style="color: #bbbbc2; font-size: 12px;">{step_footnote}</span> """
155
  yield gr.ChatMessage(role="assistant", content=f"{step_footnote}")
 
172
  total_input_tokens = 0
173
  total_output_tokens = 0
174
 
175
+ for step_log in agent.run(
176
+ task, stream=True, reset=reset_agent_memory, additional_args=additional_args
177
+ ):
178
  # Track tokens if model provides them
179
  if hasattr(agent.model, "last_input_token_count"):
180
  total_input_tokens += agent.model.last_input_token_count
 
207
  content={"path": final_answer.to_string(), "mime_type": "audio/wav"},
208
  )
209
  else:
210
+ yield gr.ChatMessage(
211
+ role="assistant", content=f"**Final answer:** {str(final_answer)}"
212
+ )
213
 
214
 
215
  class GradioUI:
 
279
  sanitized_name = "".join(sanitized_name)
280
 
281
  # Save the uploaded file to the specified folder
282
+ file_path = os.path.join(
283
+ self.file_upload_folder, os.path.basename(sanitized_name)
284
+ )
285
  shutil.copy(file.name, file_path)
286
 
287
+ return gr.Textbox(
288
+ f"File uploaded: {file_path}", visible=True
289
+ ), file_uploads_log + [file_path]
290
 
291
  def log_user_message(self, text_input, file_uploads_log):
292
  return (
 
318
  # If an upload folder is provided, enable the upload feature
319
  if self.file_upload_folder is not None:
320
  upload_file = gr.File(label="Upload a file")
321
+ upload_status = gr.Textbox(
322
+ label="Upload Status", interactive=False, visible=False
323
+ )
324
  upload_file.change(
325
  self.upload_file,
326
  [upload_file, file_uploads_log],
 
336
  demo.launch(debug=True, share=True, **kwargs)
337
 
338
 
339
+ __all__ = ["stream_to_gradio", "GradioUI"]
README.md CHANGED
@@ -16,3 +16,12 @@ tags:
16
  ---
17
 
18
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
16
  ---
17
 
18
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
19
+
20
+ ---
21
+
22
+ - [First Agent Template - a Hugging Face Space by daviddwlee84](https://huggingface.co/spaces/daviddwlee84/First_Agent)
23
+
24
+ ```bash
25
+ # BUG: somehow failed to inference locally
26
+ python app.py
27
+ ```
app.py CHANGED
@@ -1,23 +1,31 @@
1
- from smolagents import CodeAgent,DuckDuckGoSearchTool, HfApiModel,load_tool,tool
2
  import datetime
3
  import requests
4
  import pytz
5
  import yaml
6
- from tools.final_answer import FinalAnswerTool
 
 
 
 
7
 
8
  from Gradio_UI import GradioUI
9
 
 
10
  # Below is an example of a tool that does nothing. Amaze us with your creativity !
11
  @tool
12
- def my_cutom_tool(arg1:str, arg2:int)-> str: #it's import to specify the return type
13
- #Keep this format for the description / args / args description but feel free to modify the tool
14
- """A tool that does nothing yet
 
 
15
  Args:
16
  arg1: the first argument
17
  arg2: the second argument
18
  """
19
  return "What magic will you build ?"
20
 
 
21
  @tool
22
  def get_current_time_in_timezone(timezone: str) -> str:
23
  """A tool that fetches the current local time in a specified timezone.
@@ -36,30 +44,38 @@ def get_current_time_in_timezone(timezone: str) -> str:
36
 
37
  final_answer = FinalAnswerTool()
38
  model = HfApiModel(
39
- max_tokens=2096,
40
- temperature=0.5,
41
- model_id='https://wxknx1kg971u7k1n.us-east-1.aws.endpoints.huggingface.cloud',# it is possible that this model may be overloaded
42
- custom_role_conversions=None,
43
  )
44
 
45
 
46
  # Import tool from Hub
47
  image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
48
 
49
- with open("prompts.yaml", 'r') as stream:
 
 
 
50
  prompt_templates = yaml.safe_load(stream)
51
-
52
  agent = CodeAgent(
53
  model=model,
54
- tools=[final_answer], ## add your tools here (don't remove final answer)
 
 
 
 
 
55
  max_steps=6,
56
  verbosity_level=1,
57
  grammar=None,
58
  planning_interval=None,
59
  name=None,
60
  description=None,
61
- prompt_templates=prompt_templates
62
  )
63
 
64
 
65
- GradioUI(agent).launch()
 
1
+ from smolagents import CodeAgent, HfApiModel, load_tool, tool
2
  import datetime
3
  import requests
4
  import pytz
5
  import yaml
6
+ from tools import (
7
+ FinalAnswerTool,
8
+ VisitWebpageTool,
9
+ DuckDuckGoSearchTool, # This is also built-in in smolagents
10
+ )
11
 
12
  from Gradio_UI import GradioUI
13
 
14
+
15
  # Below is an example of a tool that does nothing. Amaze us with your creativity !
16
  @tool
17
+ def my_cutom_tool(
18
+ arg1: str, arg2: int
19
+ ) -> str: # it's import to specify the return type
20
+ # Keep this format for the description / args / args description but feel free to modify the tool
21
+ """A tool that does nothing yet
22
  Args:
23
  arg1: the first argument
24
  arg2: the second argument
25
  """
26
  return "What magic will you build ?"
27
 
28
+
29
  @tool
30
  def get_current_time_in_timezone(timezone: str) -> str:
31
  """A tool that fetches the current local time in a specified timezone.
 
44
 
45
  final_answer = FinalAnswerTool()
46
  model = HfApiModel(
47
+ max_tokens=2096,
48
+ temperature=0.5,
49
+ model_id="https://wxknx1kg971u7k1n.us-east-1.aws.endpoints.huggingface.cloud", # it is possible that this model may be overloaded
50
+ custom_role_conversions=None,
51
  )
52
 
53
 
54
  # Import tool from Hub
55
  image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
56
 
57
+ duckduckgo_tool = DuckDuckGoSearchTool()
58
+ visit_webpage_tool = VisitWebpageTool()
59
+
60
+ with open("prompts.yaml", "r") as stream:
61
  prompt_templates = yaml.safe_load(stream)
62
+
63
  agent = CodeAgent(
64
  model=model,
65
+ tools=[
66
+ final_answer,
67
+ image_generation_tool,
68
+ duckduckgo_tool,
69
+ visit_webpage_tool,
70
+ ], # Add your tools here (don't remove final answer)
71
  max_steps=6,
72
  verbosity_level=1,
73
  grammar=None,
74
  planning_interval=None,
75
  name=None,
76
  description=None,
77
+ prompt_templates=prompt_templates,
78
  )
79
 
80
 
81
+ GradioUI(agent).launch()
requirements.txt CHANGED
@@ -3,3 +3,8 @@ smolagents
3
  requests
4
  duckduckgo_search
5
  pandas
 
 
 
 
 
 
3
  requests
4
  duckduckgo_search
5
  pandas
6
+
7
+ # Local development
8
+ gradio
9
+ # ImportError: Using SOCKS proxy, but the 'socksio' package is not installed. Make sure to install httpx using `pip install httpx[socks]`
10
+ httpx[socks]
tools/__init__.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from .visit_webpage import VisitWebpageTool
2
+ from .final_answer import FinalAnswerTool
3
+ from .web_search import DuckDuckGoSearchTool
4
+
5
+ __all__ = ["VisitWebpageTool", "FinalAnswerTool", "DuckDuckGoSearchTool"]
tools/final_answer.py CHANGED
@@ -1,10 +1,13 @@
1
  from typing import Any, Optional
2
  from smolagents.tools import Tool
3
 
 
4
  class FinalAnswerTool(Tool):
5
  name = "final_answer"
6
  description = "Provides a final answer to the given problem."
7
- inputs = {'answer': {'type': 'any', 'description': 'The final answer to the problem'}}
 
 
8
  output_type = "any"
9
 
10
  def forward(self, answer: Any) -> Any:
 
1
  from typing import Any, Optional
2
  from smolagents.tools import Tool
3
 
4
+
5
  class FinalAnswerTool(Tool):
6
  name = "final_answer"
7
  description = "Provides a final answer to the given problem."
8
+ inputs = {
9
+ "answer": {"type": "any", "description": "The final answer to the problem"}
10
+ }
11
  output_type = "any"
12
 
13
  def forward(self, answer: Any) -> Any:
tools/visit_webpage.py CHANGED
@@ -3,11 +3,15 @@ from smolagents.tools import Tool
3
  import requests
4
  import markdownify
5
  import smolagents
 
 
6
 
7
  class VisitWebpageTool(Tool):
8
  name = "visit_webpage"
9
  description = "Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages."
10
- inputs = {'url': {'type': 'string', 'description': 'The url of the webpage to visit.'}}
 
 
11
  output_type = "string"
12
 
13
  def forward(self, url: str) -> str:
 
3
  import requests
4
  import markdownify
5
  import smolagents
6
+ import re
7
+
8
 
9
  class VisitWebpageTool(Tool):
10
  name = "visit_webpage"
11
  description = "Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages."
12
+ inputs = {
13
+ "url": {"type": "string", "description": "The url of the webpage to visit."}
14
+ }
15
  output_type = "string"
16
 
17
  def forward(self, url: str) -> str:
tools/web_search.py CHANGED
@@ -2,10 +2,13 @@ from typing import Any, Optional
2
  from smolagents.tools import Tool
3
  import duckduckgo_search
4
 
 
5
  class DuckDuckGoSearchTool(Tool):
6
  name = "web_search"
7
  description = "Performs a duckduckgo web search based on your query (think a Google search) then returns the top search results."
8
- inputs = {'query': {'type': 'string', 'description': 'The search query to perform.'}}
 
 
9
  output_type = "string"
10
 
11
  def __init__(self, max_results=10, **kwargs):
@@ -23,5 +26,8 @@ class DuckDuckGoSearchTool(Tool):
23
  results = self.ddgs.text(query, max_results=self.max_results)
24
  if len(results) == 0:
25
  raise Exception("No results found! Try a less restrictive/shorter query.")
26
- postprocessed_results = [f"[{result['title']}]({result['href']})\n{result['body']}" for result in results]
 
 
 
27
  return "## Search Results\n\n" + "\n\n".join(postprocessed_results)
 
2
  from smolagents.tools import Tool
3
  import duckduckgo_search
4
 
5
+
6
  class DuckDuckGoSearchTool(Tool):
7
  name = "web_search"
8
  description = "Performs a duckduckgo web search based on your query (think a Google search) then returns the top search results."
9
+ inputs = {
10
+ "query": {"type": "string", "description": "The search query to perform."}
11
+ }
12
  output_type = "string"
13
 
14
  def __init__(self, max_results=10, **kwargs):
 
26
  results = self.ddgs.text(query, max_results=self.max_results)
27
  if len(results) == 0:
28
  raise Exception("No results found! Try a less restrictive/shorter query.")
29
+ postprocessed_results = [
30
+ f"[{result['title']}]({result['href']})\n{result['body']}"
31
+ for result in results
32
+ ]
33
  return "## Search Results\n\n" + "\n\n".join(postprocessed_results)