Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add test for pip install on python 3.10, 3.11, and 3.12 and remove crewAI tools #2082

Merged
merged 13 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/test-pip-install.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test Package Installation

on: [push, pull_request, workflow_dispatch]

jobs:
test-install:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"] # Adjust Python versions as needed

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install package with extras
run: pip install '.[external-tools,postgres,dev,server,ollama]' # Replace 'all' with the key that includes all extras

- name: Check package installation
run: pip list # Or any other command to verify successful installation
1 change: 1 addition & 0 deletions .github/workflows/test_anthropic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: Anthropic Claude Opus 3 Capabilities Test

env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }}

on:
push:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test_ollama.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: Endpoint (Ollama)

env:
OLLAMA_BASE_URL: "http://localhost:11434"
COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }}

on:
push:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test_openai.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: OpenAI GPT-4 Capabilities Test

env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }}

on:
push:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test_together.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: Together Llama 3.1 70b Capabilities Test

env:
TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }}
COMPOSIO_API_KEY: ${{ secrets.COMPOSIO_API_KEY }}

on:
push:
Expand Down
78 changes: 0 additions & 78 deletions examples/crewai_tool_usage.py

This file was deleted.

3 changes: 0 additions & 3 deletions letta/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,6 @@ def delete_human(self, id: str):
def load_langchain_tool(self, langchain_tool: "LangChainBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
raise NotImplementedError

def load_crewai_tool(self, crewai_tool: "CrewAIBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
raise NotImplementedError

def load_composio_tool(self, action: "ActionType") -> Tool:
raise NotImplementedError

Expand Down
43 changes: 5 additions & 38 deletions letta/functions/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,46 +61,14 @@ def {func_name}(**kwargs):
return func_name, wrapper_function_str


def generate_crewai_tool_wrapper(tool: "CrewAIBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> tuple[str, str]:
tool_name = tool.__class__.__name__
import_statement = f"from crewai_tools import {tool_name}"
extra_module_imports = generate_import_code(additional_imports_module_attr_map)

# Safety check that user has passed in all required imports:
assert_all_classes_are_imported(tool, additional_imports_module_attr_map)

tool_instantiation = f"tool = {generate_imported_tool_instantiation_call_str(tool)}"
run_call = f"return tool._run(**kwargs)"
func_name = humps.decamelize(tool_name)

# Combine all parts into the wrapper function
wrapper_function_str = f"""
def {func_name}(**kwargs):
if 'self' in kwargs:
del kwargs['self']
import importlib
{import_statement}
{extra_module_imports}
{tool_instantiation}
{run_call}
"""

# Compile safety check
assert_code_gen_compilable(wrapper_function_str)

return func_name, wrapper_function_str


def assert_code_gen_compilable(code_str):
try:
compile(code_str, "<string>", "exec")
except SyntaxError as e:
print(f"Syntax error in code: {e}")


def assert_all_classes_are_imported(
tool: Union["LangChainBaseTool", "CrewAIBaseTool"], additional_imports_module_attr_map: dict[str, str]
) -> None:
def assert_all_classes_are_imported(tool: Union["LangChainBaseTool"], additional_imports_module_attr_map: dict[str, str]) -> None:
# Safety check that user has passed in all required imports:
tool_name = tool.__class__.__name__
current_class_imports = {tool_name}
Expand All @@ -114,7 +82,7 @@ def assert_all_classes_are_imported(
raise RuntimeError(err_msg)


def find_required_class_names_for_import(obj: Union["LangChainBaseTool", "CrewAIBaseTool", BaseModel]) -> list[str]:
def find_required_class_names_for_import(obj: Union["LangChainBaseTool", BaseModel]) -> list[str]:
"""
Finds all the class names for required imports when instantiating the `obj`.
NOTE: This does not return the full import path, only the class name.
Expand Down Expand Up @@ -202,10 +170,10 @@ def generate_imported_tool_instantiation_call_str(obj: Any) -> Optional[str]:
else:
# Otherwise, if it is none of the above, that usually means it is a custom Python class that is NOT a BaseModel
# Thus, we cannot get enough information about it to stringify it
# This may cause issues, but we are making the assumption that any of these custom Python types are handled correctly by the parent library, such as LangChain or CrewAI
# This may cause issues, but we are making the assumption that any of these custom Python types are handled correctly by the parent library, such as LangChain
# An example would be that WikipediaAPIWrapper has an argument that is a wikipedia (pip install wikipedia) object
# We cannot stringify this easily, but WikipediaAPIWrapper handles the setting of this parameter internally
# This assumption seems fair to me, since usually they are external imports, and LangChain and CrewAI should be bundling those as module-level imports within the tool
# This assumption seems fair to me, since usually they are external imports, and LangChain should be bundling those as module-level imports within the tool
# We throw a warning here anyway and provide the class name
print(
f"[WARNING] Skipping parsing unknown class {obj.__class__.__name__} (does not inherit from the Pydantic BaseModel and is not a basic Python type)"
Expand All @@ -219,10 +187,9 @@ def generate_imported_tool_instantiation_call_str(obj: Any) -> Optional[str]:


def is_base_model(obj: Any):
from crewai_tools.tools.base_tool import BaseModel as CrewAiBaseModel
from langchain_core.pydantic_v1 import BaseModel as LangChainBaseModel

return isinstance(obj, BaseModel) or isinstance(obj, LangChainBaseModel) or isinstance(obj, CrewAiBaseModel)
return isinstance(obj, BaseModel) or isinstance(obj, LangChainBaseModel)


def generate_import_code(module_attr_map: Optional[dict]):
Expand Down
47 changes: 2 additions & 45 deletions letta/schemas/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@

from letta.functions.helpers import (
generate_composio_tool_wrapper,
generate_crewai_tool_wrapper,
generate_langchain_tool_wrapper,
)
from letta.functions.schema_generator import (
generate_schema_from_args_schema_v1,
generate_schema_from_args_schema_v2,
)
from letta.functions.schema_generator import generate_schema_from_args_schema_v2
from letta.schemas.letta_base import LettaBase
from letta.schemas.openai.chat_completions import ToolCall

Expand Down Expand Up @@ -132,37 +128,7 @@ def from_langchain(
tags = ["langchain"]
# NOTE: langchain tools may come from different packages
wrapper_func_name, wrapper_function_str = generate_langchain_tool_wrapper(langchain_tool, additional_imports_module_attr_map)
json_schema = generate_schema_from_args_schema_v1(langchain_tool.args_schema, name=wrapper_func_name, description=description)

return cls(
name=wrapper_func_name,
description=description,
source_type=source_type,
tags=tags,
source_code=wrapper_function_str,
json_schema=json_schema,
)

@classmethod
def from_crewai(
cls,
crewai_tool: "CrewAIBaseTool",
additional_imports_module_attr_map: dict[str, str] = None,
) -> "ToolCreate":
"""
Class method to create an instance of Tool from a crewAI BaseTool object.

Args:
crewai_tool (CrewAIBaseTool): An instance of a crewAI BaseTool (BaseTool from crewai)

Returns:
Tool: A Letta Tool initialized with attributes derived from the provided crewAI BaseTool object.
"""
description = crewai_tool.description
source_type = "python"
tags = ["crew-ai"]
wrapper_func_name, wrapper_function_str = generate_crewai_tool_wrapper(crewai_tool, additional_imports_module_attr_map)
json_schema = generate_schema_from_args_schema_v1(crewai_tool.args_schema, name=wrapper_func_name, description=description)
json_schema = generate_schema_from_args_schema_v2(langchain_tool.args_schema, name=wrapper_func_name, description=description)

return cls(
name=wrapper_func_name,
Expand All @@ -185,15 +151,6 @@ def load_default_langchain_tools(cls) -> List["ToolCreate"]:

return [wikipedia_tool]

@classmethod
def load_default_crewai_tools(cls) -> List["ToolCreate"]:
# For now, we only support scrape website tool
from crewai_tools import ScrapeWebsiteTool

web_scrape_tool = ToolCreate.from_crewai(ScrapeWebsiteTool())

return [web_scrape_tool]

@classmethod
def load_default_composio_tools(cls) -> List["ToolCreate"]:
from composio_langchain import Action
Expand Down
2 changes: 1 addition & 1 deletion letta/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,7 @@ def list_all_sources(self, actor: User) -> List[Source]:
def add_default_external_tools(self, actor: User) -> bool:
"""Add default langchain tools. Return true if successful, false otherwise."""
success = True
tool_creates = ToolCreate.load_default_langchain_tools() + ToolCreate.load_default_crewai_tools()
tool_creates = ToolCreate.load_default_langchain_tools()
if tool_settings.composio_api_key:
tool_creates += ToolCreate.load_default_composio_tools()
for tool_create in tool_creates:
Expand Down
Loading
Loading