Change all names to 'smolagent'

This commit is contained in:
Aymeric 2024-12-24 10:31:36 +01:00
parent d389f11e37
commit edb0be3adf
34 changed files with 289 additions and 63 deletions

View File

@ -25,10 +25,10 @@ limitations under the License.
</p>
<h3 align="center">
<p>Agents - build great agents!</p>
<p>Smolagents - build great agents!</p>
</h3>
Agents is a library that enables you to run powerful agents in a few lines of code!
Smolagents is a library that enables you to run powerful agents in a few lines of code!
This library offers:
@ -48,7 +48,7 @@ pip install agents
```
Then define your agent, give it the tools it needs and run it!
```py
from agents import CodeAgent, WebSearchTool
from smolagents import CodeAgent, WebSearchTool
agent = CodeAgent(tools=[WebSearchTool()])

View File

@ -78,7 +78,7 @@ Actually, most real-life tasks do not fit in a pre-determined workflow. This is
Agentic systems are a great way to introduce the vast world of real-world tasks to programs!
### Why {Agents}?
### Why Smolagents?
For some low-level agentic use cases, like chains or routers, you can write all the code yourself. You'll be much better that way, since it will let you control and understand your system better.

View File

@ -15,7 +15,7 @@ rendered properly in your Markdown viewer.
-->
# Text-to-SQL
In this tutorial, well see how to implement an agent that leverages SQL using `agents`.
In this tutorial, well see how to implement an agent that leverages SQL using `smolagents`.
> Let's start with the goldnen question: why not keep it simple and use a standard text-to-SQL pipeline?

View File

@ -59,7 +59,7 @@ You will also need a `tools` argument which accepts a list of `Tools` - it can b
Once you have these two arguments, `tools` and `llm_engine`, you can create an agent and run it.
```python
from agents import CodeAgent, HfApiEngine
from smolagents import CodeAgent, HfApiEngine
llm_engine = HfApiEngine(model=model_id)
agent = CodeAgent(tools=[], llm_engine=llm_engine, add_base_tools=True)
@ -72,7 +72,7 @@ agent.run(
You can even leave the argument `llm_engine` undefined, and an [`HfApiEngine`] will be created by default.
```python
from agents import CodeAgent
from smolagents import CodeAgent
agent = CodeAgent(tools=[], add_base_tools=True)
@ -87,7 +87,7 @@ Note that we used an additional `additional_detail` argument: you can additional
You can use this to indicate the path to local or remote files for the model to use:
```py
from agents import CodeAgent, Tool, SpeechToTextTool
from smolagents import CodeAgent, Tool, SpeechToTextTool
agent = CodeAgent(tools=[SpeechToTextTool()], add_base_tools=True)
@ -107,7 +107,7 @@ The Python interpreter also doesn't allow imports by default outside of a safe l
You can authorize additional imports by passing the authorized modules as a list of strings in argument `additional_authorized_imports` upon initialization of your [`CodeAgent`] or [`CodeAgent`]:
```py
from agents import CodeAgent
from smolagents import CodeAgent
agent = CodeAgent(tools=[], additional_authorized_imports=['requests', 'bs4'])
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")
@ -178,7 +178,7 @@ You could improve the system prompt, for example, by adding an explanation of th
For maximum flexibility, you can overwrite the whole system prompt template by passing your custom prompt as an argument to the `system_prompt` parameter.
```python
from agents import JsonAgent, PythonInterpreterTool, JSON_SYSTEM_PROMPT
from smolagents import JsonAgent, PythonInterpreterTool, JSON_SYSTEM_PROMPT
modified_prompt = JSON_SYSTEM_PROMPT
@ -267,7 +267,7 @@ All these will be automatically baked into the agent's system prompt upon initia
Then you can directly initialize your agent:
```py
from agents import CodeAgent
from smolagents import CodeAgent
agent = CodeAgent(tools=[model_download_tool], llm_engine=llm_engine)
agent.run(
"Can you give me the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub?"
@ -290,17 +290,17 @@ And the output:
## Multi-agents
Multi-agent has been introduced in Microsoft's framework [Autogen](https://huggingface.co/papers/2308.08155).
It simply means having several agents working together to solve your task instead of only one.
It empirically yields better performance on most benchmarks. The reason for this better performance is conceptually simple: for many tasks, rather than using a do-it-all system, you would prefer to specialize units on sub-tasks. Here, having agents with separate tool sets and memories allows to achieve efficient specialization.
In this type of framework, you have several agents working together to solve your task instead of only one.
It empirically yields better performance on most benchmarks. The reason for this better performance is conceptually simple: for many tasks, rather than using a do-it-all system, you would prefer to specialize units on sub-tasks. Here, having agents with separate tool sets and memories allows to achieve efficient specialization. For instance, why fill the memory of the code generating agent with all the content of webpages visited by the web search agent? It's better to keep them separate.
You can easily build hierarchical multi-agent systems with `agents`.
You can easily build hierarchical multi-agent systems with `smolagents`.
To do so, encapsulate the agent in a [`ManagedAgent`] object. This object needs arguments `agent`, `name`, and a `description`, which will then be embedded in the manager agent's system prompt to let it know how to call this managed agent, as we also do for tools.
Here's an example of making an agent that managed a specific web search agent using our [`DuckDuckGoSearchTool`]:
```py
from agents import CodeAgent, HfApiEngine, DuckDuckGoSearchTool, ManagedAgent
from smolagents import CodeAgent, HfApiEngine, DuckDuckGoSearchTool, ManagedAgent
llm_engine = HfApiEngine()
@ -328,7 +328,7 @@ manager_agent.run("Who is the CEO of Hugging Face?")
You can use `GradioUI` to interactively submit tasks to your agent and observe its thought and execution process, here is an example:
```py
from agents import (
from smolagents import (
load_tool,
CodeAgent,
HfApiEngine,

View File

@ -13,7 +13,7 @@ specific language governing permissions and limitations under the License.
rendered properly in your Markdown viewer.
-->
# Agents
# Smolagents
This library is the simplest framework out there to build powerful agents! By the way, wtf are "agents"? We provide our definition [in this page](conceptual_guides/intro_agents), whe're you'll also find tips for when to use them or not (spoilers: you'll often be better off without agents).

View File

@ -22,7 +22,7 @@ How to build into this latter category?
In this guide, we're going to see best practices for building agents.
> [!TIP]
> If you're new to `agents`, make sure to first read the [intro to agents](./intro_agents).
> If you're new to building agents, make sure to first read the [intro to agents](./intro_agents) and the [guided tour of smolagents](../guided_tour).
### The best agentic systems are the simplest: simplify the workflow as much as you can
@ -56,7 +56,7 @@ For instance, here's a tool that :
First, here's a poor version:
```python
import datetime
from agents import tool
from smolagents import tool
def get_weather_report_at_coordinates(coordinates, date_time):
# Dummy function, returns a list of [temperature in °C, risk of rain on a scale 0-1, wave height in m]
@ -166,7 +166,7 @@ Better ways to guide your LLM engine are:
We provide a model for a supplementary planning step, that an agent can run regularly in-between normal action steps. In this step, there is no tool call, the LLM is simply asked to update a list of facts it knows and to reflect on what steps it should take next based on those facts.
```py
from agents import load_tool, CodeAgent, HfApiEngine, DuckDuckGoSearchTool
from smolagents import load_tool, CodeAgent, HfApiEngine, DuckDuckGoSearchTool
from dotenv import load_dotenv
load_dotenv()

View File

@ -17,6 +17,9 @@ rendered properly in your Markdown viewer.
[[open-in-colab]]
> [!TIP]
> If you're new to building agents, make sure to first read the [intro to agents](./intro_agents) and the [guided tour of smolagents](../guided_tour).
### Code agents
[Multiple](https://huggingface.co/papers/2402.01030) [research](https://huggingface.co/papers/2411.01747) [papers](https://huggingface.co/papers/2401.00812) have shown that having the LLM write its actions (the tool calls) in code is much better than the current standard format for tool calling, which is across the industry different shades of "writing actions as a JSON of tools names and arguments to use".
@ -65,7 +68,7 @@ To set the code executor to E2B, simply pass the flag `use_e2b_executor=True` wh
Note that you should add all the tool's dependencies in `additional_authorized_imports`, so that the executor installs them.
```py
from agents import CodeAgent, VisitWebpageTool
from smolagents import CodeAgent, VisitWebpageTool
agent = CodeAgent(
tools = [VisitWebpageTool()],
additional_authorized_imports=["requests", "markdownify"],

View File

@ -20,8 +20,7 @@ rendered properly in your Markdown viewer.
Here, we're going to see advanced tool usage.
> [!TIP]
> If you're new to `agents`, make sure to first read the main [agents documentation](./agents).
> If you're new to building agents, make sure to first read the [intro to agents](./intro_agents) and the [guided tour of smolagents](../guided_tour).
### Directly define a tool by subclassing Tool
@ -41,7 +40,7 @@ The types for both `inputs` and `output_type` should be amongst [Pydantic format
Also, all imports should be put within the tool's forward function, else you will get an error.
```python
from agents import Tool
from smolagents import Tool
class HFModelDownloadsTool(Tool):
name = "model_download_counter"
@ -83,7 +82,7 @@ Once your tool is pushed to Hub, you can load it with the [`~Tool.load_tool`] fu
Since running tools means running custom code, you need to make sure you trust the repository, and pass `trust_remote_code=True`.
```python
from agents import load_tool, CodeAgent
from smolagents import load_tool, CodeAgent
model_download_tool = load_tool(
"{your_username}/hf-model-downloads",
@ -115,7 +114,7 @@ And voilà, here's your image! 🏖️
Then you can use this tool just like any other tool. For example, let's improve the prompt `a rabbit wearing a space suit` and generate an image of it.
```python
from agents import CodeAgent, HfApiEngine
from smolagents import CodeAgent, HfApiEngine
llm_engine = HfApiEngine("Qwen/Qwen2.5-Coder-32B-Instruct")
agent = CodeAgent(tools=[image_generation_tool], llm_engine=llm_engine)
@ -182,7 +181,7 @@ You can manage an agent's toolbox by adding or replacing a tool.
Let's add the `model_download_tool` to an existing agent initialized with only the default toolbox.
```python
from agents import HfApiEngine
from smolagents import HfApiEngine
llm_engine = HfApiEngine("Qwen/Qwen2.5-Coder-32B-Instruct")

View File

@ -1,8 +1,8 @@
from agents.default_tools.search import DuckDuckGoSearchTool
from agents.docker_alternative import DockerPythonInterpreter
from smolagents.default_tools.search import DuckDuckGoSearchTool
from smolagents.docker_alternative import DockerPythonInterpreter
from agents.tools import Tool
from smolagents.tools import Tool
class DummyTool(Tool):
name = "echo"

View File

@ -1,5 +1,5 @@
from agents import Tool, CodeAgent
from agents.default_tools.search import VisitWebpageTool
from smolagents import Tool, CodeAgent
from smolagents.default_tools.search import VisitWebpageTool
from dotenv import load_dotenv
load_dotenv()
@ -35,7 +35,7 @@ agent = CodeAgent(
)
if LAUNCH_GRADIO:
from agents import GradioUI
from smolagents import GradioUI
GradioUI(agent).launch()
else:

View File

@ -1,5 +1,5 @@
from agents.agents import ToolCallingAgent
from agents import tool, HfApiEngine, OpenAIEngine, AnthropicEngine
from smolagents.agents import ToolCallingAgent
from smolagents import tool, HfApiEngine, OpenAIEngine, AnthropicEngine
# Choose which LLM engine to use!
llm_engine = OpenAIEngine("gpt-4o")

View File

@ -3,7 +3,7 @@ requires = ["setuptools"]
build-backend = "setuptools.build_meta"
[project]
name = "agents"
name = "smolagents"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"

View File

@ -501,7 +501,7 @@ class MultiStepAgent(BaseAgent):
Example:
```py
from agents import CodeAgent
from smolagents import CodeAgent
agent = CodeAgent(tools=[])
agent.run("What is the result of 2 power 3.7384?")
```
@ -873,7 +873,7 @@ class JsonAgent(MultiStepAgent):
class ToolCallingAgent(MultiStepAgent):
"""
This agent uses JSON-like tool calls, but to the difference of JsonAgents, it makes use of the underlying librarie's tool calling facilities.
This agent uses JSON-like tool calls, but to the difference of JsonAgents, it leverages the underlying librarie's tool calling facilities.
"""
def __init__(

View File

@ -0,0 +1,224 @@
#!/usr/bin/env python
# coding=utf-8
# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import re
from dataclasses import dataclass
from typing import Dict
import torch
from huggingface_hub import hf_hub_download, list_spaces
from transformers.utils import is_offline_mode
from transformers.models.whisper import WhisperProcessor, WhisperForConditionalGeneration
from .local_python_executor import (
BASE_BUILTIN_MODULES,
BASE_PYTHON_TOOLS,
evaluate_python_code,
)
from .tools import TOOL_CONFIG_FILE, Tool, PipelineTool
from .types import AgentAudio
@dataclass
class PreTool:
name: str
inputs: Dict[str, str]
output_type: type
task: str
description: str
repo_id: str
def get_remote_tools(logger, organization="huggingface-tools"):
if is_offline_mode():
logger.info("You are in offline mode, so remote tools are not available.")
return {}
spaces = list_spaces(author=organization)
tools = {}
for space_info in spaces:
repo_id = space_info.id
resolved_config_file = hf_hub_download(
repo_id, TOOL_CONFIG_FILE, repo_type="space"
)
with open(resolved_config_file, encoding="utf-8") as reader:
config = json.load(reader)
task = repo_id.split("/")[-1]
tools[config["name"]] = PreTool(
task=task,
description=config["description"],
repo_id=repo_id,
name=task,
inputs=config["inputs"],
output_type=config["output_type"],
)
return tools
class PythonInterpreterTool(Tool):
name = "python_interpreter"
description = "This is a tool that evaluates python code. It can be used to perform calculations."
inputs = {
"code": {
"type": "string",
"description": "The python code to run in interpreter",
}
}
output_type = "string"
def __init__(self, *args, authorized_imports=None, **kwargs):
if authorized_imports is None:
self.authorized_imports = list(set(BASE_BUILTIN_MODULES))
else:
self.authorized_imports = list(
set(BASE_BUILTIN_MODULES) | set(authorized_imports)
)
self.inputs = {
"code": {
"type": "string",
"description": (
"The code snippet to evaluate. All variables used in this snippet must be defined in this same snippet, "
f"else you will get an error. This code can only import the following python libraries: {authorized_imports}."
),
}
}
self.base_python_tool = BASE_PYTHON_TOOLS
self.python_evaluator = evaluate_python_code
super().__init__(*args, **kwargs)
def forward(self, code: str) -> str:
output = str(
self.python_evaluator(
code,
static_tools=self.base_python_tool,
authorized_imports=self.authorized_imports,
)
)
return output
class FinalAnswerTool(Tool):
name = "final_answer"
description = "Provides a final answer to the given problem."
inputs = {
"answer": {"type": "any", "description": "The final answer to the problem"}
}
output_type = "any"
def forward(self, answer):
return answer
class UserInputTool(Tool):
name = "user_input"
description = "Asks for user's input on a specific question"
inputs = {
"question": {"type": "string", "description": "The question to ask the user"}
}
output_type = "string"
def forward(self, question):
user_input = input(f"{question} => ")
return user_input
import re
from .tools import Tool
class DuckDuckGoSearchTool(Tool):
name = "web_search"
description = """Performs a web search based on your query (think a Google search) then returns the top search results as a list of dict elements.
Each result has keys 'title', 'href' and 'body'."""
inputs = {
"query": {"type": "string", "description": "The search query to perform."}
}
output_type = "any"
def forward(self, query: str) -> str:
try:
from duckduckgo_search import DDGS
except ImportError:
raise ImportError(
"You must install package `duckduckgo_search` to run this tool: for instance run `pip install duckduckgo-search`."
)
results = DDGS().text(query, max_results=7)
return results
class VisitWebpageTool(Tool):
name = "visit_webpage"
description = "Visits a webpage at the given url and returns its content as a markdown string."
inputs = {
"url": {
"type": "string",
"description": "The url of the webpage to visit.",
}
}
output_type = "string"
def forward(self, url: str) -> str:
try:
from markdownify import markdownify
import requests
from requests.exceptions import RequestException
except ImportError:
raise ImportError(
"You must install packages `markdownify` and `requests` to run this tool: for instance run `pip install markdownify requests`."
)
try:
# Send a GET request to the URL
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad status codes
# Convert the HTML content to Markdown
markdown_content = markdownify(response.text).strip()
# Remove multiple line breaks
markdown_content = re.sub(r"\n{3,}", "\n\n", markdown_content)
return markdown_content
except RequestException as e:
return f"Error fetching the webpage: {str(e)}"
except Exception as e:
return f"An unexpected error occurred: {str(e)}"
class SpeechToTextTool(PipelineTool):
default_checkpoint = "openai/whisper-large-v3-turbo"
description = "This is a tool that transcribes an audio into text. It returns the transcribed text."
name = "transcriber"
pre_processor_class = WhisperProcessor
model_class = WhisperForConditionalGeneration
inputs = {"audio": {"type": "audio", "description": "The audio to transcribe"}}
output_type = "string"
def encode(self, audio):
audio = AgentAudio(audio).to_raw()
return self.pre_processor(audio, return_tensors="pt")
def forward(self, inputs):
return self.model.generate(inputs["input_features"])
def decode(self, outputs):
return self.pre_processor.batch_decode(outputs, skip_special_tokens=True)[0]
__all__ = ["PythonInterpreterTool", "FinalAnswerTool", "UserInputTool", "DuckDuckGoSearchTool", "VisitWebpageTool", "SpeechToTextTool"]

View File

@ -3,7 +3,7 @@ from typing import List, Optional
import warnings
import socket
from agents.tools import Tool
from smolagents.tools import Tool
class DockerPythonInterpreter:

View File

@ -53,7 +53,7 @@ class E2BExecutor:
for tool in tools:
validate_tool_attributes(tool.__class__, check_imports=False)
tool_code = instance_to_source(tool, base_cls=Tool)
tool_code = tool_code.replace("from agents.tools import Tool", "")
tool_code = tool_code.replace("from smolagents.tools import Tool", "")
tool_code += f"\n{tool.name} = {tool.__class__.__name__}()\n"
tool_codes.append(tool_code)

View File

@ -23,7 +23,7 @@ import os
from openai import OpenAI
from huggingface_hub import InferenceClient
from agents import Tool
from smolagents import Tool
logger = logging.getLogger(__name__)
@ -258,7 +258,7 @@ class HfApiEngine(HfEngine):
messages: List[Dict[str, str]],
available_tools: List[Tool],
):
"""Generates a tool call for the given message list"""
"""Generates a tool call for the given message list. This method is used only by `ToolCallingAgent`."""
messages = get_clean_message_list(
messages, role_conversions=tool_role_conversions
)
@ -379,7 +379,7 @@ class OpenAIEngine:
messages: List[Dict[str, str]],
available_tools: List[Tool],
):
"""Generates a tool call for the given message list"""
"""Generates a tool call for the given message list. This method is used only by `ToolCallingAgent`."""
messages = get_clean_message_list(
messages, role_conversions=tool_role_conversions
)
@ -473,7 +473,7 @@ class AnthropicEngine:
available_tools: List[Tool],
max_tokens: int = 1500,
):
"""Generates a tool call for the given message list"""
"""Generates a tool call for the given message list. This method is used only by `ToolCallingAgent`."""
messages = get_clean_message_list(
messages, role_conversions=tool_role_conversions
)

View File

@ -83,7 +83,7 @@ def get_repo_type(repo_id, repo_type=None, **hub_kwargs):
def setup_default_tools():
default_tools = {}
main_module = importlib.import_module("agents")
main_module = importlib.import_module("smolagents")
for task_name, tool_class_name in TOOL_MAPPING.items():
tool_class = getattr(main_module, tool_class_name)
@ -239,7 +239,7 @@ class Tool:
forward_source_code = inspect.getsource(self.forward)
tool_code = textwrap.dedent(f"""
from agents import Tool
from smolagents import Tool
class {class_name}(Tool):
name = "{self.name}"
@ -288,7 +288,7 @@ class Tool:
with open(app_file, "w", encoding="utf-8") as f:
f.write(
textwrap.dedent(f"""
from agents import launch_gradio_demo
from smolagents import launch_gradio_demo
from tool import {class_name}
tool = {class_name}()
@ -800,7 +800,7 @@ def load_tool(
"""
if task_or_repo_id in TOOL_MAPPING:
tool_class_name = TOOL_MAPPING[task_or_repo_id]
main_module = importlib.import_module("agents")
main_module = importlib.import_module("smolagents")
tools_module = main_module
tool_class = getattr(tools_module, tool_class_name)
return tool_class(model_repo_id, token=token, **kwargs)

View File

@ -20,8 +20,8 @@ import pytest
from pathlib import Path
from agents.types import AgentText
from agents.agents import (
from smolagents.types import AgentText
from smolagents.agents import (
AgentMaxIterationsError,
ManagedAgent,
CodeAgent,
@ -29,8 +29,8 @@ from agents.agents import (
Toolbox,
ToolCall,
)
from agents.tools import tool
from agents.default_tools import PythonInterpreterTool
from smolagents.tools import tool
from smolagents.default_tools import PythonInterpreterTool
from transformers.testing_utils import get_tests_dir

View File

@ -21,9 +21,9 @@ from PIL import Image
from transformers import is_torch_available
from transformers.testing_utils import get_tests_dir, require_torch
from agents.types import AGENT_TYPE_MAPPING
from smolagents.types import AGENT_TYPE_MAPPING
from agents.default_tools import FinalAnswerTool
from smolagents.default_tools import FinalAnswerTool
from .test_tools_common import ToolTesterMixin

View File

@ -15,7 +15,7 @@
import unittest
from agents import AgentImage, AgentError, CodeAgent, JsonAgent, stream_to_gradio
from smolagents import AgentImage, AgentError, CodeAgent, JsonAgent, stream_to_gradio
class MonitoringTester(unittest.TestCase):

View File

@ -18,10 +18,10 @@ import unittest
import numpy as np
import pytest
from agents import load_tool
from agents.types import AGENT_TYPE_MAPPING
from agents.default_tools import BASE_PYTHON_TOOLS
from agents.local_python_executor import (
from smolagents import load_tool
from smolagents.types import AGENT_TYPE_MAPPING
from smolagents.default_tools import BASE_PYTHON_TOOLS
from smolagents.local_python_executor import (
InterpreterError,
evaluate_python_code,
)

View File

@ -15,7 +15,7 @@
import unittest
from agents import load_tool
from smolagents import load_tool
from .test_tools_common import ToolTesterMixin

View File

@ -20,13 +20,13 @@ import numpy as np
import pytest
from transformers import is_torch_available, is_vision_available
from agents.types import (
from smolagents.types import (
AGENT_TYPE_MAPPING,
AgentAudio,
AgentImage,
AgentText,
)
from agents.tools import Tool, tool, AUTHORIZED_TYPES
from smolagents.tools import Tool, tool, AUTHORIZED_TYPES
from transformers.testing_utils import get_tests_dir

View File

@ -18,7 +18,7 @@ import unittest
import uuid
from pathlib import Path
from agents.types import AgentAudio, AgentImage, AgentText
from smolagents.types import AgentAudio, AgentImage, AgentText
from transformers.testing_utils import (
get_tests_dir,
require_soundfile,