feat: Add default external tools (#1899)

Co-authored-by: Matt Zhou <mattzhou@Matts-MacBook-Pro.local>
This commit is contained in:
Matthew Zhou
2024-10-17 10:26:37 -07:00
committed by GitHub
parent ff36e1b258
commit c9701f490c
10 changed files with 1162 additions and 960 deletions

View File

@@ -2,6 +2,8 @@ import json
import uuid
from letta import create_client
from letta.schemas.embedding_config import EmbeddingConfig
from letta.schemas.llm_config import LLMConfig
from letta.schemas.memory import ChatMemory
from letta.schemas.tool import Tool
@@ -38,6 +40,8 @@ def main():
# Create a `LocalClient` (you can also use a `RESTClient`, see the letta_rest_client.py example)
client = create_client()
client.set_default_llm_config(LLMConfig.default_config("gpt-4o-mini"))
client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai"))
# create tool
client.add_tool(wikipedia_query_tool)

View File

@@ -49,7 +49,6 @@ def server(
ade: Annotated[bool, typer.Option(help="Allows remote access")] = False,
):
"""Launch a Letta server process"""
if type == ServerChoice.rest_api:
pass

View File

@@ -139,7 +139,7 @@ CORE_MEMORY_PERSONA_CHAR_LIMIT = 2000
CORE_MEMORY_HUMAN_CHAR_LIMIT = 2000
# Function return limits
FUNCTION_RETURN_CHAR_LIMIT = 3000 # ~300 words
FUNCTION_RETURN_CHAR_LIMIT = 6000 # ~300 words
MAX_PAUSE_HEARTBEATS = 360 # in min

View File

@@ -5,10 +5,10 @@ from pydantic import BaseModel
def generate_composio_tool_wrapper(action: "ActionType") -> tuple[str, str]:
# Instantiate the object
tool_instantiation_str = f"composio_toolset.get_tools(actions=[Action.{action.name}])[0]"
tool_instantiation_str = f"composio_toolset.get_tools(actions=[Action.{str(action)}])[0]"
# Generate func name
func_name = f"run_{action.name}"
func_name = f"run_{action.name.lower()}"
wrapper_function_str = f"""
def {func_name}(**kwargs):
@@ -19,7 +19,7 @@ def {func_name}(**kwargs):
composio_toolset = ComposioToolSet()
tool = {tool_instantiation_str}
tool.func(**kwargs)
return tool.func(**kwargs)['data']
"""
# Compile safety check

View File

@@ -188,7 +188,7 @@ class ChatMLInnerMonologueWrapper(LLMChatCompletionWrapper):
try:
# indent the function replies
function_return_dict = json_loads(message["content"])
function_return_str = json_dumps(function_return_dict, indent=self.json_indent)
function_return_str = json_dumps(function_return_dict, indent=0)
except:
function_return_str = message["content"]

View File

@@ -183,7 +183,7 @@ class ConfigurableJSONWrapper(LLMChatCompletionWrapper):
try:
# indent the function replies
function_return_dict = json_loads(message["content"])
function_return_str = json_dumps(function_return_dict, indent=self.json_indent)
function_return_str = json_dumps(function_return_dict, indent=0)
except:
function_return_str = message["content"]

View File

@@ -112,11 +112,11 @@ class Tool(BaseTool):
Class method to create an instance of Tool from a Langchain tool (must be from langchain_community.tools).
Args:
langchain_tool (LangChainBaseTool): An instance of a crewAI BaseTool (BaseTool from crewai)
langchain_tool (LangChainBaseTool): An instance of a LangChain BaseTool (BaseTool from LangChain)
additional_imports_module_attr_map (dict[str, str]): A mapping of module names to attribute name. This is used internally to import all the required classes for the langchain tool. For example, you would pass in `{"langchain_community.utilities": "WikipediaAPIWrapper"}` for `from langchain_community.tools import WikipediaQueryRun`. NOTE: You do NOT need to specify the tool import here, that is done automatically for you.
Returns:
Tool: A Letta Tool initialized with attributes derived from the provided crewAI BaseTool object.
Tool: A Letta Tool initialized with attributes derived from the provided LangChain BaseTool object.
"""
description = langchain_tool.description
source_type = "python"
@@ -174,6 +174,38 @@ class Tool(BaseTool):
json_schema=json_schema,
)
@classmethod
def load_default_langchain_tools(cls) -> List["Tool"]:
# For now, we only support wikipedia tool
from langchain_community.tools import WikipediaQueryRun
from langchain_community.utilities import WikipediaAPIWrapper
wikipedia_tool = Tool.from_langchain(
WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()), {"langchain_community.utilities": "WikipediaAPIWrapper"}
)
return [wikipedia_tool]
@classmethod
def load_default_crewai_tools(cls) -> List["Tool"]:
# For now, we only support scrape website tool
from crewai_tools import ScrapeWebsiteTool
web_scrape_tool = Tool.from_crewai(ScrapeWebsiteTool())
return [web_scrape_tool]
@classmethod
def load_default_composio_tools(cls) -> List["Tool"]:
from composio_langchain import Action
calculator = Tool.get_composio_tool(action=Action.MATHEMATICAL_CALCULATOR)
serp_news = Tool.get_composio_tool(action=Action.SERPAPI_NEWS_SEARCH)
serp_google_search = Tool.get_composio_tool(action=Action.SERPAPI_SEARCH)
serp_google_maps = Tool.get_composio_tool(action=Action.SERPAPI_GOOGLE_MAPS_SEARCH)
return [calculator, serp_news, serp_google_search, serp_google_maps]
class ToolCreate(BaseTool):
id: Optional[str] = Field(None, description="The unique identifier of the tool. If this is not provided, it will be autogenerated.")

View File

@@ -249,6 +249,9 @@ class SyncServer(Server):
# add global default tools (for admin)
self.add_default_tools(module_name="base")
if settings.load_default_external_tools:
self.add_default_external_tools()
# collect providers (always has Letta as a default)
self._enabled_providers: List[Provider] = [LettaProvider()]
if model_settings.openai_api_key:
@@ -1969,11 +1972,13 @@ class SyncServer(Server):
# Handle other general exceptions
raise e
functions_to_schema = []
try:
# Load the function set
functions_to_schema = load_function_set(module)
except ValueError as e:
err = f"Error loading function set '{module_name}': {e}"
warnings.warn(err)
# create tool in db
for name, schema in functions_to_schema.items():
@@ -1997,6 +2002,20 @@ class SyncServer(Server):
update=True,
)
def add_default_external_tools(self, user_id: Optional[str] = None) -> bool:
"""Add default langchain tools. Return true if successful, false otherwise."""
success = True
tools = Tool.load_default_langchain_tools() + Tool.load_default_crewai_tools() + Tool.load_default_composio_tools()
for tool in tools:
try:
self.ms.create_tool(tool)
except Exception as e:
warnings.warn(f"An error occurred while creating tool {tool}: {e}")
warnings.warn(traceback.format_exc())
success = False
return success
def add_default_blocks(self, user_id: str):
from letta.utils import list_human_files, list_persona_files

View File

@@ -65,6 +65,9 @@ class Settings(BaseSettings):
pg_port: Optional[int] = None
pg_uri: Optional[str] = None # option to specifiy full uri
# tools configuration
load_default_external_tools: Optional[bool] = None
@property
def letta_pg_uri(self) -> str:
if self.pg_uri:

2047
poetry.lock generated

File diff suppressed because it is too large Load Diff