From 92675ef0815c8cd794f60b49d4dd247c250b6b26 Mon Sep 17 00:00:00 2001
From: tad dy
Date: Sat, 31 Aug 2024 15:55:24 +0000
Subject: [PATCH 1/4] adding NON-BREAKING/BACKWARDS-COMPATIBLE support for
LangChain's v1 astream events to the AgentRearrange & Agent classes PLUS
added an example of how to use it
---
.../example.py | 83 +++++++++++
swarms/structs/agent.py | 11 +-
swarms/structs/rearrange.py | 134 ++++++++++++++++++
3 files changed, 227 insertions(+), 1 deletion(-)
create mode 100644 examples/rearrange_swarm_with_langchain_v1_astream_events/example.py
diff --git a/examples/rearrange_swarm_with_langchain_v1_astream_events/example.py b/examples/rearrange_swarm_with_langchain_v1_astream_events/example.py
new file mode 100644
index 00000000..5bd7bd95
--- /dev/null
+++ b/examples/rearrange_swarm_with_langchain_v1_astream_events/example.py
@@ -0,0 +1,83 @@
+import os
+import asyncio
+from dotenv import load_dotenv
+load_dotenv()
+
+from swarms.structs import Agent
+from swarms.models import Anthropic
+from swarms.structs.rearrange import AgentRearrange
+
+llm = Anthropic(anthropic_api_key=os.getenv("ANTHROPIC_API_KEY"), streaming=True)
+
+async def sequential():
+
+ agent1 = Agent(
+ agent_name="Blog generator",
+ system_prompt="Generate a blog post like stephen king",
+ llm=llm,
+ dashboard=False,
+ streaming_on=True
+ )
+
+ agent2 = Agent(
+ agent_name="Summarizer",
+ system_prompt="Summarize the blog post",
+ llm=llm,
+ dashboard=False,
+ streaming_on=True
+ )
+
+ flow = f"{agent1.name} -> {agent2.name}"
+
+ agent_rearrange = AgentRearrange(
+ [agent1, agent2], flow, verbose=False, logging=False
+ )
+
+ result = await agent_rearrange.astream(
+ "Generate a short blog post about Muhammad Ali."
+ )
+
+ # LEAVING THIS CALL BELOW FOR COMPARISON with "completion-style" .run() approach ;)
+ # await agent_rearrange.run(
+ # "Generate a short blog post about Muhammad Ali."
+ # )
+
+async def parallel():
+
+ writer1 = Agent(
+ agent_name="Writer 1",
+ system_prompt="Generate a blog post in the style of J.K. Rowling about Muhammad Ali",
+ llm=llm,
+ dashboard=False,
+ )
+
+ writer2 = Agent(
+ agent_name="Writer 2",
+ system_prompt="Generate a blog post in the style of Stephen King about Muhammad Ali",
+ llm=llm,
+ dashboard=False
+ )
+
+ reviewer = Agent(
+ agent_name="Reviewer",
+ system_prompt="Select the writer that wrote the best story. There can only be one best story.",
+ llm=llm,
+ dashboard=False
+ )
+
+ flow = f"{writer1.name}, {writer2.name} -> {reviewer.name}"
+
+ agent_rearrange = AgentRearrange(
+ [writer1, writer2, reviewer], flow, verbose=False, logging=False
+ )
+
+ result = await agent_rearrange.astream("Generate a 1 sentence story about Michael Jordan.")
+
+ # LEAVING THIS CALL BELOW FOR COMPARISON with "completion-style" .run() approach ;)
+ # result = agent_rearrange.run(
+ # "Generate a short blog post about Michael Jordan."
+ # )
+
+# asyncio.run(sequential())
+asyncio.run(parallel())
+
diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py
index 9173c4fb..dd8fc0c0 100644
--- a/swarms/structs/agent.py
+++ b/swarms/structs/agent.py
@@ -713,7 +713,7 @@ class Agent:
# Print
if self.streaming_on is True:
- response = self.stream_response(response)
+ self.stream_response(response)
else:
print(response)
@@ -882,6 +882,15 @@ class Agent:
)
raise error
+ async def astream_events(
+ self, task: str = None, img: str = None, *args, **kwargs
+ ):
+ try:
+ async for evt in self.llm.astream_events(task, version="v1"):
+ yield evt
+ except Exception as e:
+ print(f"Error streaming events: {e}")
+
def __call__(self, task: str = None, img: str = None, *args, **kwargs):
"""Call the agent
diff --git a/swarms/structs/rearrange.py b/swarms/structs/rearrange.py
index a0ca3563..95a24a2b 100644
--- a/swarms/structs/rearrange.py
+++ b/swarms/structs/rearrange.py
@@ -266,6 +266,140 @@ class AgentRearrange(BaseSwarm):
logger.error(f"An error occurred: {e}")
return e
+ async def astream(
+ self,
+ task: str = None,
+ img: str = None,
+ custom_tasks: Dict[str, str] = None,
+ *args,
+ **kwargs,
+ ):
+ """
+ Runs the swarm with LangChain's astream_events v1 API enabled.
+ NOTICE: Be sure to only call this method if you are using LangChain-based models in your swarm.
+ This is useful for enhancing user experience by providing real-time updates of how each agent
+ in the swarm is processing the current task.
+
+ Args:
+ task: The initial prompt (aka task) passed to the first agent(s) in the swarm.
+
+ Returns:
+ str: The final output generated.
+ """
+ try:
+ if not self.validate_flow():
+ return "Invalid flow configuration."
+
+ tasks = self.flow.split("->")
+ current_task = task
+
+ # If custom_tasks have the agents name and tasks then combine them
+ if custom_tasks is not None:
+ c_agent_name, c_task = next(iter(custom_tasks.items()))
+
+ # Find the position of the custom agent in the tasks list
+ position = tasks.index(c_agent_name)
+
+ # If there is a prebois agent merge its task with the custom tasks
+ if position > 0:
+ tasks[position - 1] += "->" + c_task
+ else:
+ # If there is no prevous agent just insert the custom tasks
+ tasks.insert(position, c_task)
+
+ logger.info('TASK:', task)
+
+ # Set the loop counter
+ loop_count = 0
+ while loop_count < self.max_loops:
+ for task in tasks:
+ agent_names = [
+ name.strip() for name in task.split(",")
+ ]
+ if len(agent_names) > 1:
+ # Parallel processing
+ logger.info(
+ f"Running agents in parallel: {agent_names}"
+ )
+ results = []
+ for agent_name in agent_names:
+ if agent_name == "H":
+ # Human in the loop intervention
+ if (
+ self.human_in_the_loop
+ and self.custom_human_in_the_loop
+ ):
+ current_task = (
+ self.custom_human_in_the_loop(
+ current_task
+ )
+ )
+ else:
+ current_task = input(
+ "Enter your response:"
+ )
+ else:
+ agent = self.agents[agent_name]
+ result = None
+ # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
+ # Below is the link to the `astream_events` spec as outlined in the LangChain v0.1 docs
+ # https://python.langchain.com/v0.1/docs/expression_language/streaming/#event-reference
+ # Below is the link to the `astream_events` spec as outlined in the LangChain v0.2 docs
+ # https://python.langchain.com/v0.2/docs/versions/v0_2/migrating_astream_events/
+ async for evt in agent.astream_events(current_task, version="v1"):
+ # print(evt) # <- useful when building/debugging
+ if evt['event'] == "on_llm_end":
+ result = evt['data']['output']
+ print(agent.name, result)
+ results.append(result)
+
+ current_task = ""
+ for index,res in enumerate(results):
+ current_task += "# OUTPUT of " + agent_names[index] + "" + res + "\n\n"
+ else:
+ # Sequential processing
+ logger.info(
+ f"Running agents sequentially: {agent_names}"
+ )
+
+ agent_name = agent_names[0]
+ if agent_name == "H":
+ # Human-in-the-loop intervention
+ if (
+ self.human_in_the_loop
+ and self.custom_human_in_the_loop
+ ):
+ current_task = (
+ self.custom_human_in_the_loop(
+ current_task
+ )
+ )
+ else:
+ current_task = input(
+ "Enter the next task: "
+ )
+ else:
+ agent = self.agents[agent_name]
+ result = None
+ # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
+ # Below is the link to the `astream_events` spec as outlined in the LangChain v0.1 docs
+ # https://python.langchain.com/v0.1/docs/expression_language/streaming/#event-reference
+ # Below is the link to the `astream_events` spec as outlined in the LangChain v0.2 docs
+ # https://python.langchain.com/v0.2/docs/versions/v0_2/migrating_astream_events/
+ async for evt in agent.astream_events(f"SYSTEM: {agent.system_prompt}\nINPUT:{current_task}", version="v1"):
+ # print(evt) # <- useful when building/debugging
+ if evt['event'] == "on_llm_end":
+ result = evt['data']['output']
+ print(agent.name, 'result', result)
+ current_task = result
+
+ loop_count += 1
+
+ return current_task
+ except Exception as e:
+ logger.error(f"An error occurred: {e}")
+ return e
+
def process_agent_or_swarm(
self, name: str, task: str, img: str, is_last, *args, **kwargs
):
From 8a64e1288504cbe92cddb499230a2e064bf5c30c Mon Sep 17 00:00:00 2001
From: tad dy
Date: Sat, 31 Aug 2024 16:08:23 +0000
Subject: [PATCH 2/4] Adding documentation comments to the newly added
astream_events method on the Agent class
---
swarms/structs/agent.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py
index dd8fc0c0..7e553a8e 100644
--- a/swarms/structs/agent.py
+++ b/swarms/structs/agent.py
@@ -885,6 +885,10 @@ class Agent:
async def astream_events(
self, task: str = None, img: str = None, *args, **kwargs
):
+ """
+ Run the Agent with LangChain's astream_events API.
+ Only works with LangChain-based models.
+ """
try:
async for evt in self.llm.astream_events(task, version="v1"):
yield evt
From 8c312c2df24cfbd0467185384ee393c71977f1ee Mon Sep 17 00:00:00 2001
From: tad dy
Date: Sat, 31 Aug 2024 16:27:59 +0000
Subject: [PATCH 3/4] adding changes suggested by black linter
---
swarms/structs/rearrange.py | 39 ++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/swarms/structs/rearrange.py b/swarms/structs/rearrange.py
index 95a24a2b..22ca45b4 100644
--- a/swarms/structs/rearrange.py
+++ b/swarms/structs/rearrange.py
@@ -3,8 +3,8 @@ from typing import Callable, Dict, List, Optional
from swarms.memory.base_vectordb import BaseVectorDatabase
from swarms.structs.agent import Agent
from swarms.structs.base_swarm import BaseSwarm
-from swarms.utils.loguru_logger import logger
from swarms.structs.omni_agent_types import AgentType
+from swarms.utils.loguru_logger import logger
class AgentRearrange(BaseSwarm):
@@ -307,7 +307,7 @@ class AgentRearrange(BaseSwarm):
# If there is no prevous agent just insert the custom tasks
tasks.insert(position, c_task)
- logger.info('TASK:', task)
+ logger.info("TASK:", task)
# Set the loop counter
loop_count = 0
@@ -341,21 +341,29 @@ class AgentRearrange(BaseSwarm):
else:
agent = self.agents[agent_name]
result = None
- # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
+ # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
# Below is the link to the `astream_events` spec as outlined in the LangChain v0.1 docs
# https://python.langchain.com/v0.1/docs/expression_language/streaming/#event-reference
# Below is the link to the `astream_events` spec as outlined in the LangChain v0.2 docs
# https://python.langchain.com/v0.2/docs/versions/v0_2/migrating_astream_events/
- async for evt in agent.astream_events(current_task, version="v1"):
+ async for evt in agent.astream_events(
+ current_task, version="v1"
+ ):
# print(evt) # <- useful when building/debugging
- if evt['event'] == "on_llm_end":
- result = evt['data']['output']
+ if evt["event"] == "on_llm_end":
+ result = evt["data"]["output"]
print(agent.name, result)
results.append(result)
current_task = ""
- for index,res in enumerate(results):
- current_task += "# OUTPUT of " + agent_names[index] + "" + res + "\n\n"
+ for index, res in enumerate(results):
+ current_task += (
+ "# OUTPUT of "
+ + agent_names[index]
+ + ""
+ + res
+ + "\n\n"
+ )
else:
# Sequential processing
logger.info(
@@ -381,18 +389,21 @@ class AgentRearrange(BaseSwarm):
else:
agent = self.agents[agent_name]
result = None
- # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
+ # As the current `swarms` package is using LangChain v0.1 we need to use the v0.1 version of the `astream_events` API
# Below is the link to the `astream_events` spec as outlined in the LangChain v0.1 docs
# https://python.langchain.com/v0.1/docs/expression_language/streaming/#event-reference
# Below is the link to the `astream_events` spec as outlined in the LangChain v0.2 docs
# https://python.langchain.com/v0.2/docs/versions/v0_2/migrating_astream_events/
- async for evt in agent.astream_events(f"SYSTEM: {agent.system_prompt}\nINPUT:{current_task}", version="v1"):
+ async for evt in agent.astream_events(
+ f"SYSTEM: {agent.system_prompt}\nINPUT:{current_task}",
+ version="v1",
+ ):
# print(evt) # <- useful when building/debugging
- if evt['event'] == "on_llm_end":
- result = evt['data']['output']
- print(agent.name, 'result', result)
+ if evt["event"] == "on_llm_end":
+ result = evt["data"]["output"]
+ print(agent.name, "result", result)
current_task = result
-
+
loop_count += 1
return current_task
From 10b92ec6c30347186c06508aa443299d38fa5cec Mon Sep 17 00:00:00 2001
From: tad dy
Date: Sat, 31 Aug 2024 17:40:05 +0000
Subject: [PATCH 4/4] reorganizing placement of rearrange_swarm example in
examples folder
---
.../rearrange}/example.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename examples/{rearrange_swarm_with_langchain_v1_astream_events => swarms/rearrange}/example.py (97%)
diff --git a/examples/rearrange_swarm_with_langchain_v1_astream_events/example.py b/examples/swarms/rearrange/example.py
similarity index 97%
rename from examples/rearrange_swarm_with_langchain_v1_astream_events/example.py
rename to examples/swarms/rearrange/example.py
index 5bd7bd95..930188db 100644
--- a/examples/rearrange_swarm_with_langchain_v1_astream_events/example.py
+++ b/examples/swarms/rearrange/example.py
@@ -78,6 +78,6 @@ async def parallel():
# "Generate a short blog post about Michael Jordan."
# )
-# asyncio.run(sequential())
-asyncio.run(parallel())
+asyncio.run(sequential())
+# asyncio.run(parallel())