Skip to content

Commit

Permalink
Update Langchain dependency to v0.2 (#58)
Browse files Browse the repository at this point in the history
* Try to update Langchain to v0.2

* WIP crewai expected_output

* Update tests

* Update tests data

* bump version

* More fixes for Langchain v0.2

* update tests data

* add tiktoken embeddings to cache blacklist
  • Loading branch information
whimo authored Jul 9, 2024
1 parent a4c2fdd commit 44f8883
Show file tree
Hide file tree
Showing 248 changed files with 3,340 additions and 101,016 deletions.
4 changes: 2 additions & 2 deletions examples/Blog with Images.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"from motleycrew.storage import MotleyKuzuGraphStore\n",
"from motleycrew import MotleyCrew\n",
"from motleycrew.agents.crewai import CrewAIMotleyAgent\n",
"from motleycrew.agents.langchain.react import ReActMotleyAgent\n",
"from motleycrew.agents.langchain.tool_calling_react import ReActToolCallingAgent\n",
"from motleycrew.agents.llama_index import ReActLlamaIndexMotleyAgent\n",
"from motleycrew.tools.image.dall_e import DallEImageGeneratorTool\n",
"from motleycrew.common import configure_logging\n",
Expand Down Expand Up @@ -114,7 +114,7 @@
"outputs": [],
"source": [
"# You can give agents as tools to other agents\n",
"writer = ReActMotleyAgent(\n",
"writer = ReActToolCallingAgent(\n",
" name=\"AI writer agent\",\n",
" prompt_prefix=\"\"\"Conduct a comprehensive analysis of the latest advancements in AI in 2024.\n",
" Identify key trends, breakthrough technologies, and potential industry impacts.\n",
Expand Down
7 changes: 4 additions & 3 deletions examples/Math via python code with a single agent.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"WORKING_DIR = Path(os.path.realpath(\".\"))\n",
"\n",
"\n",
"try: \n",
"try:\n",
" from motleycrew import MotleyCrew\n",
"except ImportError:\n",
" # if we are running this from source\n",
Expand Down Expand Up @@ -493,15 +493,15 @@
" backstory=\"\"\"You are a high school math teacher with a passion for problem-solving.\n",
"To solve a math problem, you first reason about it, step by step, then generate the code to solve it exactly,\n",
"using sympy, then use the REPL tool to evaluate that code, and then\n",
"use the output to generate a human-readable solution in LaTeX format. \n",
"use the output to generate a human-readable solution in LaTeX format.\n",
"Don't use dashes to indicate bullet points, don't output any backticks, just the LaTeX\"\"\",\n",
" verbose=True,\n",
" delegation=False,\n",
" tools=[repl_tool],\n",
")\n",
"\n",
"problems = [\n",
" \"Problem: If $725x + 727y = 1500$ and $729x+ 731y = 1508$, \"\n",
" \"If $725x + 727y = 1500$ and $729x+ 731y = 1508$, \"\n",
" \"what are the values of $x$, $y$, and $x - y$ ?\",\n",
"]\n",
"\n",
Expand All @@ -511,6 +511,7 @@
" name=\"solve math problem\",\n",
" description=f\"\"\"Create a nice human-readable solution to the following problem:\n",
" {problems[0]}\"\"\",\n",
" additional_params={\"expected_output\": \"human-readable solution in LaTeX format\"},\n",
" agent=solver,\n",
")\n",
"\n",
Expand Down
9 changes: 7 additions & 2 deletions examples/aider_code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
def main():
crew = MotleyCrew()

git_repo_path = r"../../motleycrew-code-generation-example" # cloned repository path
git_repo_path = (
r"../../motleycrew-code-generation-example" # cloned repository path
)
tests_file = os.path.join(git_repo_path, "test_math_functions.py")
target_files = [tests_file]

aider_tool = AiderTool(fnames=target_files, git_dname=git_repo_path, auto_commits=False)
aider_tool = AiderTool(
fnames=target_files, git_dname=git_repo_path, auto_commits=False
)
shell_tool = ShellTool()

developer = CrewAIMotleyAgent(
Expand All @@ -59,6 +63,7 @@ def main():
f"After go to the directory {git_repo_path} and run created unit tests. "
f"If the tests were executed successfully, return the result of execution, "
f"if not, rewrite the tests and rerun them until they are working.",
additional_params={"expected_output": "result of tests execution"},
agent=developer,
)

Expand Down
6 changes: 0 additions & 6 deletions examples/blog_with_images.nblink

This file was deleted.

5 changes: 4 additions & 1 deletion examples/crewai_output_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ def main():

def check_output(output: str):
if "medicine" not in output.lower():
raise InvalidOutput("Add more information about AI applications in medicine.")
raise InvalidOutput(
"Add more information about AI applications in medicine."
)
return {"checked_output": output.lower()}

output_handler = StructuredTool.from_function(
Expand Down Expand Up @@ -58,6 +60,7 @@ def check_output(output: str):
description="""Conduct a comprehensive analysis of the latest advancements in AI in 2024.
Identify key trends, breakthrough technologies, and potential industry impacts.
Your final answer MUST be a full analysis report""",
additional_params={"expected_output": "full analysis report"},
agent=researcher,
)

Expand Down
4 changes: 2 additions & 2 deletions examples/delegation_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from motleycrew.storage import MotleyKuzuGraphStore

from motleycrew.agents.crewai import CrewAIMotleyAgent
from motleycrew.agents.langchain.react import ReActMotleyAgent
from motleycrew.agents.langchain.tool_calling_react import ReActToolCallingAgent
from motleycrew.agents.llama_index import ReActLlamaIndexMotleyAgent
from motleycrew.tools.image.dall_e import DallEImageGeneratorTool
from motleycrew.common import configure_logging
Expand Down Expand Up @@ -56,7 +56,7 @@ def main():
)

# You can give agents as tools to other agents
writer = ReActMotleyAgent(
writer = ReActToolCallingAgent(
name="AI writer agent",
prompt_prefix="You are an experienced writer with a passion for technology.",
description="Experienced writer with a passion for technology.",
Expand Down
45 changes: 33 additions & 12 deletions motleycrew/agents/crewai/agent_with_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
""" Module description """

from typing import Any, Optional, List

from langchain_core.runnables import RunnableConfig
Expand All @@ -8,12 +9,13 @@

try:
from crewai import Agent
from crewai.memory.contextual.contextual_memory import ContextualMemory
except ImportError:
Agent = object
ContextualMemory = object


class CrewAIAgentWithConfig(Agent):

def __init__(self, *args, **kwargs):
"""Subclass for CrewAI Agent that overrides the execute_task method to include a config parameter.
Expand All @@ -37,27 +39,48 @@ def execute_task(
"""Execute a task with the agent.
Args:
task (Any): Task to execute.
context (:obj:`str`, optional): Context to execute the task in.
tools (:obj:`List[Any]`, optional): Tools to use for the task.
config (:obj:`RunnableConfig`, optional): Runnable config.
task: Task to execute.
context: Context to execute the task in.
tools: Tools to use for the task.
Returns:
Any: Output of the agent
Output of the agent
"""
if self.tools_handler:
# type: ignore # Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "ToolCalling")
self.tools_handler.last_used_tool = {}

task_prompt = task.prompt()

if context:
task_prompt = self.i18n.slice("task_with_context").format(
task=task_prompt, context=context
)

tools = self._parse_tools(tools or self.tools)
if self.crew and self.crew.memory:
contextual_memory = ContextualMemory(
self.crew._short_term_memory,
self.crew._long_term_memory,
self.crew._entity_memory,
)
memory = contextual_memory.build_context_for_task(task, context)
if memory.strip() != "":
task_prompt += self.i18n.slice("memory").format(memory=memory)

tools = tools or self.tools
# type: ignore # Argument 1 to "_parse_tools" of "Agent" has incompatible type "list[Any] | None"; expected "list[Any]"
parsed_tools = self._parse_tools(tools or [])
self.create_agent_executor(tools=tools)
self.agent_executor.tools = tools
self.agent_executor.tools = parsed_tools
self.agent_executor.task = task
self.agent_executor.tools_description = render_text_description(tools)
self.agent_executor.tools_names = self.__tools_names(tools)

self.agent_executor.tools_description = render_text_description(parsed_tools)
self.agent_executor.tools_names = self.__tools_names(parsed_tools)

if self.crew and self.crew._train:
task_prompt = self._training_handler(task_prompt=task_prompt)
else:
task_prompt = self._use_trained_data(task_prompt=task_prompt)

result = self.agent_executor.invoke(
{
Expand All @@ -67,10 +90,8 @@ def execute_task(
},
config=config,
)["output"]

if self.max_rpm:
self._rpm_controller.stop_rpm_counter()

return result

@staticmethod
Expand Down
44 changes: 42 additions & 2 deletions motleycrew/agents/crewai/crewai.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from typing import Any, Optional, Sequence

from langchain_core.runnables import RunnableConfig
from langchain_core.tools import StructuredTool
from langchain_core.pydantic_v1 import BaseModel, Field

from motleycrew.agents.crewai import CrewAIAgentWithConfig
from motleycrew.agents.parent import MotleyAgentParent
Expand Down Expand Up @@ -45,7 +47,7 @@ def __init__(
if output_handler:
raise NotImplementedError(
"Output handler is not supported for CrewAI agents "
"because of the specificity of CrewAi's prompts."
"because of the specificity of CrewAI's prompts."
)

ensure_module_is_installed("crewai")
Expand Down Expand Up @@ -80,7 +82,15 @@ def invoke(
langchain_tools = [tool.to_langchain_tool() for tool in self.tools.values()]
config = add_default_callbacks_to_langchain_config(config)

crewai_task = CrewAI__Task(description=prompt)
additional_params = input.get("additional_params") or {}
expected_output = additional_params.get("expected_output")
if not expected_output:
raise ValueError("Expected output is required for CrewAI tasks")

crewai_task = CrewAI__Task(
description=prompt,
expected_output=expected_output,
)

output = self.agent.execute_task(
task=crewai_task,
Expand Down Expand Up @@ -145,3 +155,33 @@ def from_agent(
)
wrapped_agent._agent = agent
return wrapped_agent

def as_tool(self) -> MotleyTool:
if not self.description:
raise ValueError("Agent must have a description to be called as a tool")

class CrewAIAgentInputSchema(BaseModel):
prompt: str = Field(..., description="Prompt to be passed to the agent")
expected_output: str = Field(
..., description="Expected output of the agent"
)

def call_agent(prompt: str, expected_output: str):
return self.invoke(
{
"prompt": prompt,
"additional_params": {"expected_output": expected_output},
}
)

# To be specialized if we expect structured input
return MotleyTool.from_langchain_tool(
StructuredTool(
name=self.name.replace(
" ", "_"
).lower(), # OpenAI doesn't accept spaces in function names
description=self.description,
func=call_agent,
args_schema=CrewAIAgentInputSchema,
)
)
3 changes: 2 additions & 1 deletion motleycrew/agents/crewai/crewai_agent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
""" Module description """

from typing import Optional, Any, Sequence

from motleycrew.tools import MotleyTool
Expand All @@ -23,7 +24,7 @@ def __init__(
output_handler: MotleySupportedTool | None = None,
verbose: bool = False,
):
""" Description
"""Description
Args:
role (str):
Expand Down
32 changes: 25 additions & 7 deletions motleycrew/agents/parent.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
""" Module description """

import inspect
from typing import TYPE_CHECKING, Optional, Sequence, Any, Callable, Dict, Union, Tuple
from typing import (
TYPE_CHECKING,
Optional,
Sequence,
Any,
Callable,
Dict,
Type,
Union,
Tuple,
)

from langchain_core.prompts.chat import ChatPromptTemplate, HumanMessage, SystemMessage
from langchain_core.runnables import Runnable
Expand Down Expand Up @@ -90,7 +100,9 @@ def compose_prompt(
prompt_messages.append(SystemMessage(content=self.prompt_prefix))

else:
raise ValueError("Agent description must be a string or a ChatPromptTemplate")
raise ValueError(
"Agent description must be a string or a ChatPromptTemplate"
)

if prompt:
if isinstance(prompt, ChatPromptTemplate):
Expand Down Expand Up @@ -180,9 +192,13 @@ def materialize(self):

if inspect.signature(self.agent_factory).parameters.get("output_handler"):
logger.info("Agent factory accepts output handler, passing it")
self._agent = self.agent_factory(tools=self.tools, output_handler=output_handler)
self._agent = self.agent_factory(
tools=self.tools, output_handler=output_handler
)
elif output_handler:
logger.info("Agent factory does not accept output handler, passing it as a tool")
logger.info(
"Agent factory does not accept output handler, passing it as a tool"
)
tools_with_output_handler = self.tools.copy()
tools_with_output_handler[output_handler.name] = output_handler
self._agent = self.agent_factory(tools=tools_with_output_handler)
Expand Down Expand Up @@ -226,11 +242,11 @@ def add_tools(self, tools: Sequence[MotleySupportedTool]):
if motley_tool.name not in self.tools:
self.tools[motley_tool.name] = motley_tool

def as_tool(self, input_schema: Optional[BaseModel] = None) -> MotleyTool:
def as_tool(self, input_schema: Optional[Type[BaseModel]] = None) -> MotleyTool:
"""Description
Args:
input_schema (:obj:`BaseModel`, optional):
input_schema (:obj:`Type[BaseModel]`, optional):
Returns:
MotleyTool:
Expand All @@ -250,7 +266,9 @@ def call_agent(*args, **kwargs):
# To be specialized if we expect structured input
return MotleyTool.from_langchain_tool(
Tool(
name=self.name,
name=self.name.replace(
" ", "_"
).lower(), # OpenAI doesn't accept spaces in function names
description=self.description,
func=call_agent,
args_schema=input_schema,
Expand Down
Loading

0 comments on commit 44f8883

Please sign in to comment.