Skip to content

Commit

Permalink
Merge pull request #2 from ShoggothAI/dalle-tool
Browse files Browse the repository at this point in the history
Dall-E tool + usage example
  • Loading branch information
ZmeiGorynych committed Apr 20, 2024
2 parents f2916d8 + c396619 commit 5c79e8b
Show file tree
Hide file tree
Showing 18 changed files with 799 additions and 635 deletions.
56 changes: 56 additions & 0 deletions examples/image_generation_crewai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from dotenv import load_dotenv

from motleycrew import MotleyCrew, Task
from motleycrew.agent.crewai import CrewAIMotleyAgent
from motleycrew.tool.image_generation import DallEImageGeneratorTool
from motleycrew.common.utils import configure_logging

load_dotenv()
configure_logging(verbose=True)

image_generator_tool = DallEImageGeneratorTool()
# For saving images locally use the line below
# image_generator_tool = DallEImageGeneratorTool(images_directory="images")

writer = CrewAIMotleyAgent(
role="Short stories writer",
goal="Write short stories for children",
backstory="You are an accomplished children's writer, known for your funny and interesting short stories.\n"
"Many parents around the world read your books to their children.",
verbose=True,
delegation=True,
)

illustrator = CrewAIMotleyAgent(
role="Illustrator",
goal="Create beautiful and insightful illustrations",
backstory="You are an expert in creating illustrations for all sorts of concepts and articles. "
"You do it by skillfully prompting a text-to-image model.\n"
"Your final answer MUST be the exact URL or filename of the illustration.",
verbose=True,
delegation=False,
tools=[image_generator_tool],
)

# Create tasks for your agents
crew = MotleyCrew()
write_task = Task(
crew=crew,
name="write a short story about a cat",
description="Creatively write a short story of about 4 paragraphs "
"about a house cat that was smarter than its owners. \n"
"Write it in a cool and simple language, "
"making it intriguing yet suitable for children's comprehension.\n"
"You must include a fun illustration.\n"
"Your final answer MUST be the full story with the illustration URL or filename attached.",
agent=writer,
)


# Get your crew to work!
result = crew.run(
agents=[illustrator, writer],
verbose=2, # You can set it to 1 or 2 to different logging levels
)

print(write_task.outputs)
2 changes: 1 addition & 1 deletion examples/single_openai_tools_react.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from dotenv import load_dotenv
from langchain_community.tools import DuckDuckGoSearchRun

from motleycrew import MotleyCrew, Task, MotleyTool
from motleycrew import MotleyCrew, Task
from motleycrew.agent.langchain.openai_tools_react import ReactOpenAIToolsAgent
from motleycrew.agent.langchain.react import ReactMotleyAgent

Expand Down
20 changes: 12 additions & 8 deletions motleycrew/agent/crewai/crewai.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import logging
from typing import Any, Optional, Sequence, Callable
from typing import Any, Optional, Sequence

from crewai import Agent
from langchain_core.runnables import RunnableConfig

from motleycrew.agent.parent import MotleyAgentAbstractParent
from motleycrew.agent.shared import MotleyAgentParent
from motleycrew.tasks import Task, TaskGraph
from motleycrew.tasks import Task
from motleycrew.tool import MotleyTool
from motleycrew.common import MotleySupportedTool
from motleycrew.common import MotleyAgentFactory
from motleycrew.common.utils import to_str
from motleycrew.common import LLMFramework
from motleycrew.common.llms import init_llm
Expand All @@ -19,7 +19,7 @@ def __init__(
self,
goal: str,
name: str | None = None,
agent_factory: Callable[[dict[str, MotleyTool]], Agent] | None = None,
agent_factory: MotleyAgentFactory | None = None,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False,
Expand All @@ -40,8 +40,10 @@ def invoke(
**kwargs: Any,
) -> Any:
self.materialize()
self.agent: Agent

if isinstance(task, str):
assert self.crew, "can't create a task outside a crew"
# TODO: feed in context/task.message_history correctly
# TODO: attach the current task, if any, as a dependency of the new task
# TODO: this preamble should really be a decorator to be shared across agent wrappers
Expand All @@ -52,7 +54,7 @@ def invoke(
# TODO inject the new subtask as a dep and reschedule the parent
# TODO probably can't do this from here since we won't know if
# there are other tasks to schedule
task_graph=TaskGraph(),
crew=self.crew,
)
elif not isinstance(task, Task):
# TODO: should really have a conversion function here from langchain tools to crewai tools
Expand Down Expand Up @@ -82,7 +84,7 @@ def from_crewai_params(
tools: Sequence[MotleySupportedTool] | None = None,
llm: Optional[Any] = None,
verbose: bool = False,
):
) -> "CrewAIMotleyAgentParent":
if tools is None:
tools = []

Expand Down Expand Up @@ -118,8 +120,10 @@ def from_agent(
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False,
):
tools += agent.tools
) -> "CrewAIMotleyAgentParent":
if tools or agent.tools:
tools = list(tools or []) + list(agent.tools or [])

wrapped_agent = CrewAIMotleyAgentParent(
goal=agent.goal,
name=agent.role,
Expand Down
3 changes: 2 additions & 1 deletion motleycrew/agent/crewai/crewai_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def __new__(
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
llm: Optional[Any] = None,
verbose: bool = False,) -> "CrewAIMotleyAgent":
verbose: bool = False,
):
return cls.from_crewai_params(
role=role,
goal=goal,
Expand Down
44 changes: 25 additions & 19 deletions motleycrew/agent/langchain/langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
from langchain_core.language_models import BaseLanguageModel
from langchain_core.prompts.chat import ChatPromptTemplate

from motleycrew.agent.parent import MotleyAgentAbstractParent
from motleycrew.agent.shared import MotleyAgentParent
from motleycrew.tasks import Task

from motleycrew.tool import MotleyTool
from motleycrew.common import MotleySupportedTool
from motleycrew.common import MotleyAgentFactory
from motleycrew.common import LLMFramework
from motleycrew.common.llms import init_llm

Expand All @@ -19,8 +21,8 @@ def __init__(
self,
goal: str,
name: str | None = None,
agent_factory: Callable[[dict[str, MotleyTool]], AgentExecutor] | None = None,
delegation: bool | Sequence[MotleyAgentParent] = False,
agent_factory: MotleyAgentFactory | None = None,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False,
):
Expand All @@ -40,27 +42,31 @@ def invoke(
**kwargs: Any,
) -> Task:
self.materialize()
self.agent: AgentExecutor

if isinstance(task, str):
assert self.crew, "can't create a task outside a crew"
# TODO: feed in context/task.message_history correctly
# TODO: attach the current task, if any, as a dependency of the new task
new_task = Task(description=task, name=task, agent=self)
return new_task.invoke()
elif isinstance(task, Task):
# TODO: feed in context/task.message_history correctly
# TODO: extract the message history from the agent and attach it to the task
out = self.agent.invoke({"input": task.description}, config, **kwargs)
task.outputs = [out["output"]]
return task
else:
task = Task(
description=task,
name=task,
agent=self,
crew=self.crew
)
elif not isinstance(task, Task):
raise ValueError(f"`task` must be a string or a Task, not {type(task)}")

out = self.agent.invoke({"input": task.description}, config, **kwargs)
task.outputs = [out["output"]]
return task

@staticmethod
def from_function(
function: Callable[..., Any],
goal: str,
llm: BaseLanguageModel | None = None,
delegation: bool | Sequence[MotleyAgentParent] = False,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
prompt: ChatPromptTemplate | Sequence[ChatPromptTemplate] | None = None,
require_tools: bool = False,
Expand All @@ -69,10 +75,8 @@ def from_function(
if llm is None:
llm = init_llm(llm_framework=LLMFramework.LANGCHAIN)

if require_tools and not len(tools):
raise ValueError(
"You must provide at least one tool to the ReactMotleyAgent"
)
if require_tools and not tools:
raise ValueError("You must provide at least one tool to the ReactMotleyAgent")

def agent_factory(tools: dict[str, MotleyTool]):
langchain_tools = [t.to_langchain_tool() for t in tools.values()]
Expand All @@ -97,15 +101,17 @@ def agent_factory(tools: dict[str, MotleyTool]):
def from_agent(
agent: AgentExecutor,
goal: str,
delegation: bool | Sequence[MotleyAgentParent] = False,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False
):
) -> "LangchainMotleyAgentParent":
# TODO: do we really need to unite the tools implicitly like this?
# TODO: confused users might pass tools both ways at the same time
# TODO: and we will silently unite them, which can have side effects (e.g. doubled tools)
# TODO: besides, getting tools can be tricky for other frameworks (e.g. LlamaIndex)
tools += agent.tools
if tools or agent.tools:
tools = list(tools or []) + list(agent.tools or [])

wrapped_agent = LangchainMotleyAgentParent(
goal=goal,
delegation=delegation,
Expand Down
3 changes: 1 addition & 2 deletions motleycrew/agent/langchain/openai_tools_react.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ def create_openai_tools_react_agent(
)
"""
if prompt is None:

think_prompt = default_think_prompt
act_prompt = default_act_prompt
else:
Expand Down Expand Up @@ -209,7 +208,7 @@ def __new__(
cls,
tools: Sequence[MotleySupportedTool],
goal: str = "", # gets ignored at the moment
prompt: str | None = None,
prompt: ChatPromptTemplate | Sequence[ChatPromptTemplate] | None = None,
llm: BaseLanguageModel | None = None,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
verbose: bool = False,
Expand Down
2 changes: 0 additions & 2 deletions motleycrew/agent/langchain/react.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from typing import Sequence


from langchain import hub
from langchain_core.language_models import BaseLanguageModel
from langchain.agents import create_react_agent


from motleycrew.agent.parent import MotleyAgentAbstractParent
from motleycrew.agent.langchain.langchain import LangchainMotleyAgentParent
from motleycrew.common import MotleySupportedTool
Expand Down
14 changes: 8 additions & 6 deletions motleycrew/agent/llama_index/llama_index.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from typing import Any, Optional, Sequence, Callable
from typing import Any, Optional, Sequence

from llama_index.core.agent import AgentRunner
from langchain_core.runnables import RunnableConfig

from motleycrew.agent.parent import MotleyAgentAbstractParent
from motleycrew.agent.shared import MotleyAgentParent
from motleycrew.tasks import Task, TaskGraph
from motleycrew.tool import MotleyTool
from motleycrew.tasks import Task
from motleycrew.common import MotleySupportedTool
from motleycrew.common import MotleyAgentFactory


class LlamaIndexMotleyAgentParent(MotleyAgentParent):
def __init__(
self,
goal: str,
name: str | None = None,
agent_factory: Callable[[dict[str, MotleyTool]], AgentRunner] | None = None,
agent_factory: MotleyAgentFactory | None = None,
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False
Expand All @@ -36,8 +36,10 @@ def invoke(
**kwargs: Any,
) -> Any:
self.materialize()
self.agent: AgentRunner

if isinstance(task, str):
assert self.crew, "can't create a task outside a crew"
# TODO: feed in context/task.message_history correctly
# TODO: attach the current task, if any, as a dependency of the new task
# TODO: this preamble should really be a decorator to be shared across agent wrappers
Expand All @@ -48,7 +50,7 @@ def invoke(
# TODO inject the new subtask as a dep and reschedule the parent
# TODO probably can't do this from here since we won't know if
# there are other tasks to schedule
task_graph=TaskGraph(),
crew=self.crew,
)
elif not isinstance(task, Task):
# TODO: should really have a conversion function here from langchain tools to crewai tools
Expand All @@ -66,7 +68,7 @@ def from_agent(
delegation: bool | Sequence[MotleyAgentAbstractParent] = False,
tools: Sequence[MotleySupportedTool] | None = None,
verbose: bool = False,
):
) -> "LlamaIndexMotleyAgentParent":
wrapped_agent = LlamaIndexMotleyAgentParent(
goal=goal,
delegation=delegation,
Expand Down
6 changes: 4 additions & 2 deletions motleycrew/agent/parent.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from abc import ABC, abstractmethod
from typing import Dict, Optional, Any, Union
from typing import TYPE_CHECKING, Optional, Any, Union

from langchain_core.messages import BaseMessage
from langchain_core.runnables import RunnableConfig

if TYPE_CHECKING:
from motleycrew.tasks import Task


class MotleyAgentAbstractParent(ABC):
@abstractmethod
Expand Down
Loading

0 comments on commit 5c79e8b

Please sign in to comment.