feat: Add default external tools (#1899)
Co-authored-by: Matt Zhou <mattzhou@Matts-MacBook-Pro.local>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
2047
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user