Update docs and add more examples

This commit is contained in:
Brandon Hancock
2024-09-24 15:41:11 -04:00
parent bfaba72da2
commit 128872a482
11 changed files with 298 additions and 14 deletions

View File

@@ -543,9 +543,14 @@ In this example, the `PoemFlow` class defines a flow that generates a sentence c
## Next Steps
- Recommend checking out our flow examples in the CrewAI Examples repository to see more use cases.
- Currently, there are 4 flow examples:
- email auto responder flow
- lead score flow
- Write a book flow
- Meeting assistant flow
If you're interested in exploring additional examples of flows, we have a variety of recommendations in our examples repository. Here are four specific flow examples, each showcasing unique use cases to help you match your current problem type to a specific example:
1. **Email Auto Responder Flow**: This example demonstrates an infinite loop where a background job continually runs to automate email responses. It's a great use case for tasks that need to be performed repeatedly without manual intervention. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/email_auto_responder_flow)
2. **Lead Score Flow**: This flow showcases adding human-in-the-loop feedback and handling different conditional branches using the router. It's an excellent example of how to incorporate dynamic decision-making and human oversight into your workflows. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/lead-score-flow)
3. **Write a Book Flow**: This example excels at chaining multiple crews together, where the output of one crew is used by another. Specifically, one crew outlines an entire book, and another crew generates chapters based on the outline. Eventually, everything is connected to produce a complete book. This flow is perfect for complex, multi-step processes that require coordination between different tasks. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/write_a_book_with_flows)
4. **Meeting Assistant Flow**: This flow demonstrates how to broadcast one event to trigger multiple follow-up actions. For instance, after a meeting is completed, the flow can update a Trello board, send a Slack message, and save the results. It's a great example of handling multiple outcomes from a single event, making it ideal for comprehensive task management and notification systems. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/meeting_assistant_flow)
By exploring these examples, you can gain insights into how to leverage CrewAI Flows for various use cases, from automating repetitive tasks to managing complex, multi-step processes with dynamic decision-making and human feedback.

View File

@@ -53,6 +53,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
Crews
</a>
</li>
<li>
<a href="./core-concepts/Flows">
Flows
</a>
</li>
<li>
<a href="./core-concepts/Pipeline">
Pipeline
@@ -193,6 +198,26 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
Landing Page Generator
</a>
</li>
<li>
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/email_auto_responder_flow">
Email Auto Responder Flow
</a>
</li>
<li>
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/lead-score-flow">
Lead Score Flow
</a>
</li>
<li>
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/write_a_book_with_flows">
Write a Book Flow
</a>
</li>
<li>
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/meeting_assistant_flow">
Meeting Assistant Flow
</a>
</li>
</ul>
</div>
</div>

39
poetry.lock generated
View File

@@ -1539,12 +1539,12 @@ files = [
google-auth = ">=2.14.1,<3.0.dev0"
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
grpcio = [
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
{version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
]
grpcio-status = [
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
]
proto-plus = ">=1.22.3,<2.0.0dev"
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
@@ -1806,6 +1806,22 @@ cachetools = "*"
numpy = "*"
requests = "*"
[[package]]
name = "graphviz"
version = "0.20.3"
description = "Simple Python interface for Graphviz"
optional = false
python-versions = ">=3.8"
files = [
{file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"},
{file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"},
]
[package.extras]
dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"]
docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"]
test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"]
[[package]]
name = "greenlet"
version = "3.1.0"
@@ -4046,8 +4062,8 @@ files = [
[package.dependencies]
numpy = [
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
]
python-dateutil = ">=2.8.2"
@@ -4546,6 +4562,19 @@ files = [
{file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"},
{file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"},
{file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"},
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"},
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"},
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"},
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"},
{file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"},
{file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"},
{file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"},
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"},
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"},
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"},
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"},
{file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"},
{file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"},
]
[package.dependencies]
@@ -4561,6 +4590,7 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs
optional = false
python-versions = ">=3.8"
files = [
{file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
]
@@ -4571,6 +4601,7 @@ description = "A collection of ASN.1-based protocols modules"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
{file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
]
@@ -6992,4 +7023,4 @@ tools = ["crewai-tools"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<=3.13"
content-hash = "0b03c4204e1f31a5bf692ba4585b88d5f6ad4028693fdbc4d9b1546450c9af5a"
content-hash = "80b7909ba10f19ffeeac6d6ce3c802158c75ff4328da0379549c28c054bd16eb"

View File

@@ -32,6 +32,7 @@ json-repair = "^0.25.2"
auth0-python = "^4.7.1"
poetry = "^1.8.3"
litellm = "^1.44.22"
graphviz = "^0.20.3"
[tool.poetry.extras]
tools = ["crewai-tools"]

View File

@@ -2,6 +2,7 @@ import asyncio
import inspect
from typing import Any, Callable, Dict, Generic, List, Set, Type, TypeVar, Union
import graphviz
from pydantic import BaseModel
T = TypeVar("T", bound=Union[BaseModel, Dict[str, Any]])
@@ -250,3 +251,103 @@ class Flow(Generic[T], metaclass=FlowMeta):
import traceback
traceback.print_exc()
def visualize(self, filename="flow_graph"):
dot = graphviz.Digraph(comment="Flow Graph", engine="dot")
dot.attr(
rankdir="TB", size="20,20", splines="curved"
) # Changed to curved splines
# Color scheme (using company colors)
colors = {
"bg": "#FFFFFF",
"start": "#FF5A50",
"method": "#333333",
"router_outline": "#FF5A50",
"edge": "#333333",
"text": "#FFFFFF",
}
dot.attr(bgcolor=colors["bg"])
# Add nodes for each relevant method
for method_name, method in self._methods.items():
if (
hasattr(method, "__is_start_method__")
or method_name in self._listeners
or method_name in self._routers.values()
):
shape = "rectangle"
style = "filled,rounded"
fillcolor = colors["method"]
if hasattr(method, "__is_start_method__"):
fillcolor = colors["start"]
dot.node(
method_name,
method_name,
shape=shape,
style=style,
fillcolor=fillcolor,
fontcolor=colors["text"],
penwidth="2",
)
# Add edges and enhanced router representation
for method_name, method in self._methods.items():
if method_name in self._listeners:
condition_type, trigger_methods = self._listeners[method_name]
for trigger in trigger_methods:
if condition_type == "AND":
dot.edge(
trigger,
method_name,
color=colors["edge"],
style="dashed",
penwidth="2",
)
else: # OR condition
dot.edge(
trigger, method_name, color=colors["edge"], penwidth="2"
)
if method_name in self._routers.values():
for trigger, router in self._routers.items():
if router == method_name:
# Create a subgraph for the router and its outputs
subgraph_name = f"cluster_{method_name}"
subgraph = graphviz.Digraph(name=subgraph_name)
subgraph.attr(
label="",
style="filled,rounded",
color=colors["router_outline"],
fillcolor=colors["method"],
penwidth="3",
) # Changed to solid line and increased penwidth
# Router label (method name) and outputs
label = f"{method_name}\\n\\nPossible outcomes:\\n• Success\\n• Failure"
subgraph.node(
method_name,
label,
shape="plaintext",
fontcolor=colors["text"],
)
# Add the subgraph to the main graph
dot.subgraph(subgraph)
# Connect trigger to router (to the border of the subgraph)
dot.edge(
trigger,
method_name,
color=colors["edge"],
style="solid",
penwidth="2",
lhead=subgraph_name,
)
# Render and save the graph
dot.render(filename, format="png", cleanup=True, view=True)
print(f"Graph saved as {filename}.png")

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,119 @@
import asyncio
from crewai.flow.flow import Flow, listen, start
class BinaryBranchingFlow(Flow):
@start()
def start_method(self):
print("Starting the binary branching flow")
self.state["counter"] = 1
return "Start result"
# Level 1
@listen(start_method)
def first_branch_left(self):
print("First branch (left)")
self.state["counter"] += 1
return "First branch left result"
@listen(start_method)
def first_branch_right(self):
print("First branch (right)")
self.state["counter"] += 1
return "First branch right result"
# Level 2 - Left Branch
@listen(first_branch_left)
def second_branch_left_left(self):
print("Second branch from first left (left)")
self.state["counter"] += 1
return "Second branch left left result"
@listen(first_branch_left)
def second_branch_left_right(self):
print("Second branch from first left (right)")
self.state["counter"] += 1
return "Second branch left right result"
# Level 2 - Right Branch
@listen(first_branch_right)
def second_branch_right_left(self):
print("Second branch from first right (left)")
self.state["counter"] += 1
return "Second branch right left result"
@listen(first_branch_right)
def second_branch_right_right(self):
print("Second branch from first right (right)")
self.state["counter"] += 1
return "Second branch right right result"
# Level 3 - Left Left Branch
@listen(second_branch_left_left)
def third_branch_left_left_left(self):
print("Third branch from second left left (left)")
self.state["counter"] += 1
return "Third branch left left left result"
@listen(second_branch_left_left)
def third_branch_left_left_right(self):
print("Third branch from second left left (right)")
self.state["counter"] += 1
return "Third branch left left right result"
# Level 3 - Left Right Branch
@listen(second_branch_left_right)
def third_branch_left_right_left(self):
print("Third branch from second left right (left)")
self.state["counter"] += 1
return "Third branch left right left result"
@listen(second_branch_left_right)
def third_branch_left_right_right(self):
print("Third branch from second left right (right)")
self.state["counter"] += 1
return "Third branch left right right result"
# Level 3 - Right Left Branch
@listen(second_branch_right_left)
def third_branch_right_left_left(self):
print("Third branch from second right left (left)")
self.state["counter"] += 1
return "Third branch right left left result"
@listen(second_branch_right_left)
def third_branch_right_left_right(self):
print("Third branch from second right left (right)")
self.state["counter"] += 1
return "Third branch right left right result"
# Level 3 - Right Right Branch
@listen(second_branch_right_right)
def third_branch_right_right_left(self):
print("Third branch from second right right (left)")
self.state["counter"] += 1
return "Third branch right right left result"
@listen(second_branch_right_right)
def third_branch_right_right_right(self):
print("Third branch from second right right (right)")
self.state["counter"] += 1
return "Third branch right right right result"
# Final method for visualization
@listen(third_branch_left_left_left) # This is the deepest branch in the tree
def final_method(self):
print("Final method reached!")
print(f"Final counter value: {self.state['counter']}")
return "Final result"
async def main():
flow = BinaryBranchingFlow()
# Uncomment this if you want to run the flow with kickoff
# await flow.kickoff()
flow.visualize()
asyncio.run(main())

View File

@@ -21,7 +21,7 @@ class AndExampleFlow(Flow):
async def main():
flow = AndExampleFlow()
await flow.kickoff()
flow.visualize()
asyncio.run(main())

View File

@@ -20,7 +20,8 @@ class OrExampleFlow(Flow):
async def main():
flow = OrExampleFlow()
await flow.kickoff()
flow.visualize()
# await flow.kickoff()
asyncio.run(main())

View File

@@ -35,7 +35,7 @@ class RouterFlow(Flow[ExampleState]):
async def main():
flow = RouterFlow()
await flow.kickoff()
flow.visualize()
asyncio.run(main())

View File

@@ -27,7 +27,8 @@ class FlexibleExampleFlow(Flow):
async def main():
flow = FlexibleExampleFlow()
await flow.kickoff()
# await flow.kickoff()
flow.visualize()
asyncio.run(main())