[feat][interactivegroupchat][random dynamic speaker functions

pull/935/head^2
Kye Gomez 2 days ago
parent 5343590656
commit 76b9f5162e

@ -16,6 +16,8 @@ GEMINI_API_KEY=""
## Hugging Face ## Hugging Face
HUGGINGFACE_TOKEN="" HUGGINGFACE_TOKEN=""
GROQ_API_KEY=""
## Perplexity AI ## Perplexity AI
PPLX_API_KEY="" PPLX_API_KEY=""

@ -260,7 +260,6 @@ nav:
- Examples: - Examples:
- Overview: "examples/index.md" - Overview: "examples/index.md"
- CookBook Index: "examples/cookbook_index.md" - CookBook Index: "examples/cookbook_index.md"
# - PreBuilt Templates: "examples/templates_index.md"
- Basic Examples: - Basic Examples:
- Individual Agents: - Individual Agents:
- Basic Agent: "swarms/examples/basic_agent.md" - Basic Agent: "swarms/examples/basic_agent.md"

@ -14,6 +14,10 @@ The InteractiveGroupChat is a sophisticated multi-agent system that enables inte
- **Dynamic Speaker Management**: Change speaker functions and priorities during runtime - **Dynamic Speaker Management**: Change speaker functions and priorities during runtime
- **Random Dynamic Speaker**: Advanced speaker function that follows @mentions in agent responses
- **Parallel and Sequential Strategies**: Support for both parallel and sequential agent execution
- **Callable Function Support**: Supports both Agent instances and callable functions as chat participants - **Callable Function Support**: Supports both Agent instances and callable functions as chat participants
- **Comprehensive Error Handling**: Custom error classes for different scenarios - **Comprehensive Error Handling**: Custom error classes for different scenarios
@ -22,6 +26,8 @@ The InteractiveGroupChat is a sophisticated multi-agent system that enables inte
- **Flexible Output Formatting**: Configurable output format for conversation history - **Flexible Output Formatting**: Configurable output format for conversation history
- **Interactive Terminal Mode**: Full REPL interface for real-time chat with agents
## Installation ## Installation
```bash ```bash
@ -46,7 +52,7 @@ Initializes a new InteractiveGroupChat instance with the specified configuration
| `max_loops` | int | Maximum conversation turns | 1 | | `max_loops` | int | Maximum conversation turns | 1 |
| `output_type` | str | Type of output format | "string" | | `output_type` | str | Type of output format | "string" |
| `interactive` | bool | Whether to enable interactive mode | False | | `interactive` | bool | Whether to enable interactive mode | False |
| `speaker_function` | Callable | Function to determine speaking order | round_robin_speaker | | `speaker_function` | Union[str, Callable] | Function to determine speaking order | round_robin_speaker |
| `speaker_state` | dict | Initial state for speaker function | {"current_index": 0} | | `speaker_state` | dict | Initial state for speaker function | {"current_index": 0} |
**Example:** **Example:**
@ -90,6 +96,8 @@ Processes a task and gets responses from mentioned agents. This is the main meth
**Arguments:** **Arguments:**
- `task` (str): The input task containing @mentions to agents - `task` (str): The input task containing @mentions to agents
- `img` (Optional[str]): Optional image for the task
- `imgs` (Optional[List[str]]): Optional list of images for the task
**Returns:** **Returns:**
@ -104,6 +112,10 @@ print(response)
# Multiple agent collaboration # Multiple agent collaboration
response = chat.run("@FinancialAdvisor and @TaxExpert, how can I minimize taxes on my investments?") response = chat.run("@FinancialAdvisor and @TaxExpert, how can I minimize taxes on my investments?")
print(response) print(response)
# With image input
response = chat.run("@FinancialAdvisor analyze this chart", img="chart.png")
print(response)
``` ```
### Start Interactive Session (`start_interactive_session`) ### Start Interactive Session (`start_interactive_session`)
@ -114,6 +126,13 @@ Starts an interactive terminal session for real-time chat with agents. This crea
**Arguments:** **Arguments:**
None None
**Features:**
- Real-time chat with agents using @mentions
- View available agents and their descriptions
- Change speaker functions during the session
- Built-in help system
- Graceful exit with 'exit' or 'quit' commands
**Example:** **Example:**
```python ```python
@ -127,6 +146,119 @@ chat = InteractiveGroupChat(
chat.start_interactive_session() chat.start_interactive_session()
``` ```
**Interactive Session Commands:**
- `@agent_name message` - Mention specific agents
- `help` or `?` - Show help information
- `speaker` - Change speaker function
- `exit` or `quit` - End the session
### Set Speaker Function (`set_speaker_function`)
**Description:**
Dynamically changes the speaker function and optional state during runtime.
**Arguments:**
- `speaker_function` (Union[str, Callable]): Function that determines speaking order
- String options: "round-robin-speaker", "random-speaker", "priority-speaker", "random-dynamic-speaker"
- Callable: Custom function that takes (agents: List[str], **kwargs) -> str
- `speaker_state` (dict, optional): State for the speaker function
**Example:**
```python
from swarms.structs.interactive_groupchat import random_speaker, priority_speaker
# Change to random speaker function
chat.set_speaker_function(random_speaker)
# Change to priority speaker with custom priorities
chat.set_speaker_function(priority_speaker, {"financial_advisor": 3, "tax_expert": 2})
# Change to random dynamic speaker
chat.set_speaker_function("random-dynamic-speaker")
```
### Get Available Speaker Functions (`get_available_speaker_functions`)
**Description:**
Returns a list of all available built-in speaker function names.
**Arguments:**
None
**Returns:**
- List[str]: List of available speaker function names
**Example:**
```python
available_functions = chat.get_available_speaker_functions()
print(available_functions)
# Output: ['round-robin-speaker', 'random-speaker', 'priority-speaker', 'random-dynamic-speaker']
```
### Get Current Speaker Function (`get_current_speaker_function`)
**Description:**
Returns the name of the currently active speaker function.
**Arguments:**
None
**Returns:**
- str: Name of the current speaker function, or "custom" if it's a custom function
**Example:**
```python
current_function = chat.get_current_speaker_function()
print(current_function) # Output: "round-robin-speaker"
```
### Set Priorities (`set_priorities`)
**Description:**
Sets agent priorities for priority-based speaking order.
**Arguments:**
- `priorities` (dict): Dictionary mapping agent names to priority weights
**Example:**
```python
# Set agent priorities (higher numbers = higher priority)
chat.set_priorities({
"financial_advisor": 5,
"tax_expert": 3,
"investment_analyst": 1
})
```
### Set Dynamic Strategy (`set_dynamic_strategy`)
**Description:**
Sets the strategy for the random-dynamic-speaker function.
**Arguments:**
- `strategy` (str): Either "sequential" or "parallel"
- "sequential": Process one agent at a time based on @mentions
- "parallel": Process all mentioned agents simultaneously
**Example:**
```python
# Set to sequential strategy (one agent at a time)
chat.set_dynamic_strategy("sequential")
# Set to parallel strategy (all mentioned agents respond simultaneously)
chat.set_dynamic_strategy("parallel")
```
### Extract Mentions (`_extract_mentions`) ### Extract Mentions (`_extract_mentions`)
**Description:** **Description:**
@ -207,49 +339,6 @@ chat = InteractiveGroupChat(agents=[financial_advisor, tax_expert])
# Each agent now knows about the other participants and how to collaborate effectively # Each agent now knows about the other participants and how to collaborate effectively
``` ```
### Set Speaker Function (`set_speaker_function`)
**Description:**
Dynamically changes the speaker function and optional state during runtime.
**Arguments:**
- `speaker_function` (Callable): Function that determines speaking order
- `speaker_state` (dict, optional): State for the speaker function
**Example:**
```python
from swarms.structs.interactive_groupchat import random_speaker, priority_speaker
# Change to random speaker function
chat.set_speaker_function(random_speaker)
# Change to priority speaker with custom priorities
chat.set_speaker_function(priority_speaker, {"financial_advisor": 3, "tax_expert": 2})
```
### Set Priorities (`set_priorities`)
**Description:**
Sets agent priorities for priority-based speaking order.
**Arguments:**
- `priorities` (dict): Dictionary mapping agent names to priority weights
**Example:**
```python
# Set agent priorities (higher numbers = higher priority)
chat.set_priorities({
"financial_advisor": 5,
"tax_expert": 3,
"investment_analyst": 1
})
```
### Get Speaking Order (`_get_speaking_order`) ### Get Speaking Order (`_get_speaking_order`)
**Description:** **Description:**
@ -345,6 +434,41 @@ chat = InteractiveGroupChat(
- Good for hierarchical teams or expert-led discussions - Good for hierarchical teams or expert-led discussions
#### Random Dynamic Speaker (`random_dynamic_speaker`)
Advanced speaker function that follows @mentions in agent responses, enabling dynamic conversation flow.
```python
from swarms.structs.interactive_groupchat import InteractiveGroupChat, random_dynamic_speaker
chat = InteractiveGroupChat(
agents=agents,
speaker_function=random_dynamic_speaker,
speaker_state={"strategy": "parallel"}, # or "sequential"
interactive=False,
)
```
**Behavior:**
- **First Call**: Randomly selects an agent to start the conversation
- **Subsequent Calls**: Extracts @mentions from the previous agent's response and selects the next speaker(s)
- **Two Strategies**:
- **Sequential**: Processes one agent at a time based on @mentions
- **Parallel**: Processes all mentioned agents simultaneously
**Example Dynamic Flow:**
```python
# Agent A responds: "I think @AgentB should analyze this data and @AgentC should review the methodology"
# With sequential strategy: Agent B speaks next
# With parallel strategy: Both Agent B and Agent C speak simultaneously
```
**Use Cases:**
- Complex problem-solving where agents need to delegate to specific experts
- Dynamic workflows where the conversation flow depends on agent responses
- Collaborative decision-making processes
### Custom Speaker Functions ### Custom Speaker Functions
You can create your own speaker functions to implement custom logic: You can create your own speaker functions to implement custom logic:
@ -394,6 +518,10 @@ chat.set_speaker_function(random_speaker)
# Change to priority with custom priorities # Change to priority with custom priorities
chat.set_priorities({"financial_advisor": 5, "tax_expert": 3, "analyst": 1}) chat.set_priorities({"financial_advisor": 5, "tax_expert": 3, "analyst": 1})
chat.set_speaker_function(priority_speaker) chat.set_speaker_function(priority_speaker)
# Change to dynamic speaker with parallel strategy
chat.set_speaker_function("random-dynamic-speaker")
chat.set_dynamic_strategy("parallel")
``` ```
## Enhanced Collaborative Behavior ## Enhanced Collaborative Behavior
@ -518,6 +646,8 @@ except InvalidSpeakerFunctionError as e:
| Agent Naming | Use clear, unique names for agents to avoid confusion | `financial_advisor`, `tax_expert` | | Agent Naming | Use clear, unique names for agents to avoid confusion | `financial_advisor`, `tax_expert` |
| Task Format | Always use @mentions to direct tasks to specific agents | `@financial_advisor What's your investment advice?` | | Task Format | Always use @mentions to direct tasks to specific agents | `@financial_advisor What's your investment advice?` |
| Speaker Functions | Choose appropriate speaker functions for your use case | Round robin for fairness, priority for expert-led discussions | | Speaker Functions | Choose appropriate speaker functions for your use case | Round robin for fairness, priority for expert-led discussions |
| Dynamic Speaker | Use random-dynamic-speaker for complex workflows with delegation | When agents need to call on specific experts |
| Strategy Selection | Choose sequential for focused discussions, parallel for brainstorming | Sequential for analysis, parallel for idea generation |
| Collaborative Design | Design agents with complementary expertise for better collaboration | Analyst + Researcher + Strategist | | Collaborative Design | Design agents with complementary expertise for better collaboration | Analyst + Researcher + Strategist |
| Error Handling | Implement proper error handling for various scenarios | `try/except` blocks for `AgentNotFoundError` | | Error Handling | Implement proper error handling for various scenarios | `try/except` blocks for `AgentNotFoundError` |
| Context Management | Be aware that agents can see the full conversation history | Monitor conversation length and relevance | | Context Management | Be aware that agents can see the full conversation history | Monitor conversation length and relevance |
@ -614,6 +744,53 @@ response = chat.run(task)
print(response) print(response)
``` ```
### Dynamic Speaker Function with Delegation
```python
from swarms.structs.interactive_groupchat import InteractiveGroupChat, random_dynamic_speaker
# Create specialized medical agents
cardiologist = Agent(
agent_name="cardiologist",
system_prompt="You are a cardiologist specializing in heart conditions.",
llm="gpt-4",
)
oncologist = Agent(
agent_name="oncologist",
system_prompt="You are an oncologist specializing in cancer treatment.",
llm="gpt-4",
)
endocrinologist = Agent(
agent_name="endocrinologist",
system_prompt="You are an endocrinologist specializing in hormone disorders.",
llm="gpt-4",
)
# Create dynamic group chat
chat = InteractiveGroupChat(
name="Medical Panel Discussion",
description="A collaborative panel of medical specialists",
agents=[cardiologist, oncologist, endocrinologist],
speaker_function=random_dynamic_speaker,
speaker_state={"strategy": "sequential"},
interactive=False,
)
# Complex medical case with dynamic delegation
case = """CASE PRESENTATION:
A 65-year-old male with Type 2 diabetes, hypertension, and recent diagnosis of
stage 3 colon cancer presents with chest pain and shortness of breath.
ECG shows ST-segment elevation. Recent blood work shows elevated blood glucose (280 mg/dL)
and signs of infection (WBC 15,000, CRP elevated).
@cardiologist @oncologist @endocrinologist please provide your assessment and treatment recommendations."""
response = chat.run(case)
print(response)
```
### Dynamic Speaker Function Changes ### Dynamic Speaker Function Changes
```python ```python
@ -621,7 +798,8 @@ from swarms.structs.interactive_groupchat import (
InteractiveGroupChat, InteractiveGroupChat,
round_robin_speaker, round_robin_speaker,
random_speaker, random_speaker,
priority_speaker priority_speaker,
random_dynamic_speaker
) )
# Create brainstorming agents # Create brainstorming agents
@ -647,10 +825,16 @@ chat.set_speaker_function(priority_speaker)
task2 = "Now let's analyze the feasibility of these ideas. @creative @analytical @practical" task2 = "Now let's analyze the feasibility of these ideas. @creative @analytical @practical"
response2 = chat.run(task2) response2 = chat.run(task2)
# Phase 3: Implementation (round robin for equal input) # Phase 3: Dynamic delegation (agents mention each other)
chat.set_speaker_function(round_robin_speaker) chat.set_speaker_function(random_dynamic_speaker)
task3 = "Finally, let's plan implementation. @creative @analytical @practical" chat.set_dynamic_strategy("sequential")
task3 = "Let's plan implementation with dynamic delegation. @creative @analytical @practical"
response3 = chat.run(task3) response3 = chat.run(task3)
# Phase 4: Final synthesis (round robin for equal input)
chat.set_speaker_function(round_robin_speaker)
task4 = "Finally, let's synthesize our findings. @creative @analytical @practical"
response4 = chat.run(task4)
``` ```
### Custom Speaker Function ### Custom Speaker Function
@ -726,38 +910,47 @@ chat.start_interactive_session()
3. **Better Delegation**: Agents naturally delegate to appropriate experts 3. **Better Delegation**: Agents naturally delegate to appropriate experts
4. **Enhanced Problem Solving**: Complex problems are addressed systematically 4. **Enhanced Problem Solving**: Complex problems are addressed systematically
5. **More Natural Interactions**: Agents respond like real team members 5. **More Natural Interactions**: Agents respond like real team members
6. **Dynamic Workflows**: Conversation flow adapts based on agent responses
7. **Flexible Execution**: Support for both sequential and parallel processing
### Use Cases ### Use Cases
| Use Case Category | Specific Use Case | Agent Team Composition | | Use Case Category | Specific Use Case | Agent Team Composition | Recommended Speaker Function |
|------------------|-------------------|----------------------| |------------------|-------------------|----------------------|------------------------------|
| **Business Analysis and Strategy** | Data Analysis | Analyst + Researcher + Strategist | | **Business Analysis and Strategy** | Data Analysis | Analyst + Researcher + Strategist | Round Robin |
| | Market Research | Multiple experts analyzing different aspects | | | Market Research | Multiple experts analyzing different aspects | Random Dynamic |
| | Strategic Planning | Expert-led discussions with collaborative input | | | Strategic Planning | Expert-led discussions with collaborative input | Priority |
| **Product Development** | Requirements Gathering | Product Manager + Developer + Designer | | **Product Development** | Requirements Gathering | Product Manager + Developer + Designer | Round Robin |
| | Technical Architecture | Senior + Junior developers with different expertise | | | Technical Architecture | Senior + Junior developers with different expertise | Priority |
| | User Experience | UX Designer + Product Manager + Developer | | | User Experience | UX Designer + Product Manager + Developer | Random Dynamic |
| **Research and Development** | Scientific Research | Multiple researchers with different specializations | | **Research and Development** | Scientific Research | Multiple researchers with different specializations | Random Dynamic |
| | Literature Review | Different experts reviewing various aspects | | | Literature Review | Different experts reviewing various aspects | Round Robin |
| | Experimental Design | Statistician + Domain Expert + Methodologist | | | Experimental Design | Statistician + Domain Expert + Methodologist | Priority |
| **Creative Projects** | Content Creation | Writer + Editor + Designer | | **Creative Projects** | Content Creation | Writer + Editor + Designer | Random |
| | Marketing Campaigns | Creative + Analyst + Strategist | | | Marketing Campaigns | Creative + Analyst + Strategist | Random Dynamic |
| | Design Projects | Designer + Developer + Product Manager | | | Design Projects | Designer + Developer + Product Manager | Round Robin |
| **Problem Solving** | Troubleshooting | Technical + Business + User perspective experts | | **Problem Solving** | Troubleshooting | Technical + Business + User perspective experts | Priority |
| | Crisis Management | Emergency + Communication + Technical teams | | | Crisis Management | Emergency + Communication + Technical teams | Priority |
| | Decision Making | Executive + Analyst + Specialist | | | Decision Making | Executive + Analyst + Specialist | Priority |
| **Medical Consultation** | Complex Cases | Multiple specialists (Cardiologist + Oncologist + Endocrinologist) | Random Dynamic |
| | Treatment Planning | Senior + Junior doctors with different expertise | Priority |
| | Research Review | Multiple researchers reviewing different aspects | Round Robin |
### Speaker Function Selection Guide ### Speaker Function Selection Guide
| Use Case | Recommended Speaker Function | Reasoning | | Use Case | Recommended Speaker Function | Strategy | Reasoning |
|----------|------------------------------|-----------| |----------|------------------------------|----------|-----------|
| Team Meetings | Round Robin | Ensures equal participation | | Team Meetings | Round Robin | N/A | Ensures equal participation |
| Brainstorming | Random | Prevents bias and encourages creativity | | Brainstorming | Random | N/A | Prevents bias and encourages creativity |
| Expert Consultation | Priority | Senior experts speak first | | Expert Consultation | Priority | N/A | Senior experts speak first |
| Problem Solving | Priority | Most relevant experts prioritize | | Problem Solving | Priority | N/A | Most relevant experts prioritize |
| Creative Sessions | Random | Encourages diverse perspectives | | Creative Sessions | Random | N/A | Encourages diverse perspectives |
| Decision Making | Priority | Decision makers speak first | | Decision Making | Priority | N/A | Decision makers speak first |
| Research Review | Round Robin | Equal contribution from all reviewers | | Research Review | Round Robin | N/A | Equal contribution from all reviewers |
| Complex Workflows | Random Dynamic | Sequential | Follows natural conversation flow |
| Parallel Analysis | Random Dynamic | Parallel | Multiple agents work simultaneously |
| Medical Panels | Random Dynamic | Sequential | Specialists delegate to relevant experts |
| Technical Architecture | Random Dynamic | Sequential | Senior architects guide the discussion |
## Contributing ## Contributing

@ -0,0 +1,163 @@
"""
Medical Panel Discussion Example
This example demonstrates a panel of medical specialists discussing treatment solutions
for various diseases using InteractiveGroupChat with different speaker functions:
- Round Robin: Doctors speak in a fixed order
- Random: Doctors speak in random order
- Priority: Senior doctors speak first
- Custom: Disease-specific speaker function
The panel includes specialists from different medical fields who can collaborate
on complex medical cases and treatment plans.
"""
from swarms import Agent
from swarms.structs.interactive_groupchat import (
InteractiveGroupChat,
)
def create_medical_panel():
"""Create a panel of medical specialists for discussion."""
# Cardiologist - Heart and cardiovascular system specialist
cardiologist = Agent(
agent_name="cardiologist",
system_prompt="""You are Dr. Sarah Chen, a board-certified cardiologist with 15 years of experience.
You specialize in cardiovascular diseases, heart failure, arrhythmias, and interventional cardiology.
You have expertise in:
- Coronary artery disease and heart attacks
- Heart failure and cardiomyopathy
- Arrhythmias and electrophysiology
- Hypertension and lipid disorders
- Cardiac imaging and diagnostic procedures
When discussing cases, provide evidence-based treatment recommendations,
consider patient risk factors, and collaborate with other specialists for comprehensive care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Oncologist - Cancer specialist
oncologist = Agent(
agent_name="oncologist",
system_prompt="""You are Dr. Michael Rodriguez, a medical oncologist with 12 years of experience.
You specialize in the diagnosis and treatment of various types of cancer.
You have expertise in:
- Solid tumors (lung, breast, colon, prostate, etc.)
- Hematologic malignancies (leukemia, lymphoma, multiple myeloma)
- Targeted therapy and immunotherapy
- Clinical trials and novel treatments
- Palliative care and symptom management
When discussing cases, consider the cancer type, stage, molecular profile,
patient performance status, and available treatment options including clinical trials.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Neurologist - Nervous system specialist
neurologist = Agent(
agent_name="neurologist",
system_prompt="""You are Dr. Emily Watson, a neurologist with 10 years of experience.
You specialize in disorders of the nervous system, brain, and spinal cord.
You have expertise in:
- Stroke and cerebrovascular disease
- Neurodegenerative disorders (Alzheimer's, Parkinson's, ALS)
- Multiple sclerosis and demyelinating diseases
- Epilepsy and seizure disorders
- Headache and migraine disorders
- Neuromuscular diseases
When discussing cases, consider neurological symptoms, imaging findings,
and the impact of neurological conditions on overall patient care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Endocrinologist - Hormone and metabolism specialist
endocrinologist = Agent(
agent_name="endocrinologist",
system_prompt="""You are Dr. James Thompson, an endocrinologist with 8 years of experience.
You specialize in disorders of the endocrine system and metabolism.
You have expertise in:
- Diabetes mellitus (Type 1, Type 2, gestational)
- Thyroid disorders (hyperthyroidism, hypothyroidism, thyroid cancer)
- Adrenal disorders and Cushing's syndrome
- Pituitary disorders and growth hormone issues
- Osteoporosis and calcium metabolism
- Reproductive endocrinology
When discussing cases, consider metabolic factors, hormone levels,
and how endocrine disorders may affect other organ systems.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Infectious Disease Specialist
infectious_disease = Agent(
agent_name="infectious_disease",
system_prompt="""You are Dr. Lisa Park, an infectious disease specialist with 11 years of experience.
You specialize in the diagnosis and treatment of infectious diseases.
You have expertise in:
- Bacterial, viral, fungal, and parasitic infections
- Antibiotic resistance and antimicrobial stewardship
- HIV/AIDS and opportunistic infections
- Travel medicine and tropical diseases
- Hospital-acquired infections
- Emerging infectious diseases
When discussing cases, consider the infectious agent, antimicrobial susceptibility,
host factors, and infection control measures.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
return [
cardiologist,
oncologist,
neurologist,
endocrinologist,
infectious_disease,
]
def example_round_robin_panel():
"""Example with round robin speaking order."""
print("=== ROUND ROBIN MEDICAL PANEL ===\n")
agents = create_medical_panel()
group_chat = InteractiveGroupChat(
name="Medical Panel Discussion",
description="A collaborative panel of medical specialists discussing complex cases",
agents=agents,
speaker_function="round-robin-speaker",
interactive=False,
)
print(group_chat.speaker_function)
# Case 1: Complex patient with multiple conditions
case1 = """CASE PRESENTATION:
A 65-year-old male with Type 2 diabetes, hypertension, and recent diagnosis of
stage 3 colon cancer presents with chest pain and shortness of breath.
ECG shows ST-segment elevation. Recent blood work shows elevated blood glucose (280 mg/dL)
and signs of infection (WBC 15,000, CRP elevated).
@cardiologist @oncologist @endocrinologist @infectious_disease please provide your
assessment and treatment recommendations for this complex case."""
response = group_chat.run(case1)
print(f"Response:\n{response}\n")
print("=" * 80 + "\n")
if __name__ == "__main__":
example_round_robin_panel()

@ -0,0 +1,162 @@
"""
Medical Panel Discussion Example
This example demonstrates a panel of medical specialists discussing treatment solutions
for various diseases using InteractiveGroupChat with different speaker functions:
- Round Robin: Doctors speak in a fixed order
- Random: Doctors speak in random order
- Priority: Senior doctors speak first
- Custom: Disease-specific speaker function
The panel includes specialists from different medical fields who can collaborate
on complex medical cases and treatment plans.
"""
from swarms import Agent
from swarms.structs.interactive_groupchat import (
InteractiveGroupChat,
round_robin_speaker,
)
def create_medical_panel():
"""Create a panel of medical specialists for discussion."""
# Cardiologist - Heart and cardiovascular system specialist
cardiologist = Agent(
agent_name="cardiologist",
system_prompt="""You are Dr. Sarah Chen, a board-certified cardiologist with 15 years of experience.
You specialize in cardiovascular diseases, heart failure, arrhythmias, and interventional cardiology.
You have expertise in:
- Coronary artery disease and heart attacks
- Heart failure and cardiomyopathy
- Arrhythmias and electrophysiology
- Hypertension and lipid disorders
- Cardiac imaging and diagnostic procedures
When discussing cases, provide evidence-based treatment recommendations,
consider patient risk factors, and collaborate with other specialists for comprehensive care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Oncologist - Cancer specialist
oncologist = Agent(
agent_name="oncologist",
system_prompt="""You are Dr. Michael Rodriguez, a medical oncologist with 12 years of experience.
You specialize in the diagnosis and treatment of various types of cancer.
You have expertise in:
- Solid tumors (lung, breast, colon, prostate, etc.)
- Hematologic malignancies (leukemia, lymphoma, multiple myeloma)
- Targeted therapy and immunotherapy
- Clinical trials and novel treatments
- Palliative care and symptom management
When discussing cases, consider the cancer type, stage, molecular profile,
patient performance status, and available treatment options including clinical trials.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Neurologist - Nervous system specialist
neurologist = Agent(
agent_name="neurologist",
system_prompt="""You are Dr. Emily Watson, a neurologist with 10 years of experience.
You specialize in disorders of the nervous system, brain, and spinal cord.
You have expertise in:
- Stroke and cerebrovascular disease
- Neurodegenerative disorders (Alzheimer's, Parkinson's, ALS)
- Multiple sclerosis and demyelinating diseases
- Epilepsy and seizure disorders
- Headache and migraine disorders
- Neuromuscular diseases
When discussing cases, consider neurological symptoms, imaging findings,
and the impact of neurological conditions on overall patient care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Endocrinologist - Hormone and metabolism specialist
endocrinologist = Agent(
agent_name="endocrinologist",
system_prompt="""You are Dr. James Thompson, an endocrinologist with 8 years of experience.
You specialize in disorders of the endocrine system and metabolism.
You have expertise in:
- Diabetes mellitus (Type 1, Type 2, gestational)
- Thyroid disorders (hyperthyroidism, hypothyroidism, thyroid cancer)
- Adrenal disorders and Cushing's syndrome
- Pituitary disorders and growth hormone issues
- Osteoporosis and calcium metabolism
- Reproductive endocrinology
When discussing cases, consider metabolic factors, hormone levels,
and how endocrine disorders may affect other organ systems.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Infectious Disease Specialist
infectious_disease = Agent(
agent_name="infectious_disease",
system_prompt="""You are Dr. Lisa Park, an infectious disease specialist with 11 years of experience.
You specialize in the diagnosis and treatment of infectious diseases.
You have expertise in:
- Bacterial, viral, fungal, and parasitic infections
- Antibiotic resistance and antimicrobial stewardship
- HIV/AIDS and opportunistic infections
- Travel medicine and tropical diseases
- Hospital-acquired infections
- Emerging infectious diseases
When discussing cases, consider the infectious agent, antimicrobial susceptibility,
host factors, and infection control measures.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
return [
cardiologist,
oncologist,
neurologist,
endocrinologist,
infectious_disease,
]
def example_round_robin_panel():
"""Example with round robin speaking order."""
print("=== ROUND ROBIN MEDICAL PANEL ===\n")
agents = create_medical_panel()
group_chat = InteractiveGroupChat(
name="Medical Panel Discussion",
description="A collaborative panel of medical specialists discussing complex cases",
agents=agents,
speaker_function=round_robin_speaker,
interactive=False,
)
# Case 1: Complex patient with multiple conditions
case1 = """CASE PRESENTATION:
A 65-year-old male with Type 2 diabetes, hypertension, and recent diagnosis of
stage 3 colon cancer presents with chest pain and shortness of breath.
ECG shows ST-segment elevation. Recent blood work shows elevated blood glucose (280 mg/dL)
and signs of infection (WBC 15,000, CRP elevated).
@cardiologist @oncologist @endocrinologist @infectious_disease please provide your
assessment and treatment recommendations for this complex case."""
response = group_chat.run(case1)
print(f"Response:\n{response}\n")
print("=" * 80 + "\n")
if __name__ == "__main__":
example_round_robin_panel()

@ -24,7 +24,7 @@ def create_example_agents():
analyst = Agent( analyst = Agent(
agent_name="analyst", agent_name="analyst",
system_prompt="You are a data analyst. You excel at analyzing data, creating charts, and providing insights.", system_prompt="You are a data analyst. You excel at analyzing data, creating charts, and providing insights.",
model_name="gpt-4.1", model_name="claude-3-5-sonnet-20240620",
streaming_on=True, streaming_on=True,
print_on=True, print_on=True,
) )
@ -32,7 +32,7 @@ def create_example_agents():
researcher = Agent( researcher = Agent(
agent_name="researcher", agent_name="researcher",
system_prompt="You are a research specialist. You are great at gathering information, fact-checking, and providing detailed research.", system_prompt="You are a research specialist. You are great at gathering information, fact-checking, and providing detailed research.",
model_name="gpt-4.1", model_name="claude-3-5-sonnet-20240620",
streaming_on=True, streaming_on=True,
print_on=True, print_on=True,
) )
@ -40,7 +40,7 @@ def create_example_agents():
writer = Agent( writer = Agent(
agent_name="writer", agent_name="writer",
system_prompt="You are a content writer. You excel at writing clear, engaging content and summarizing information.", system_prompt="You are a content writer. You excel at writing clear, engaging content and summarizing information.",
model_name="gpt-4.1", model_name="claude-3-5-sonnet-20240620",
streaming_on=True, streaming_on=True,
print_on=True, print_on=True,
) )
@ -61,7 +61,7 @@ def example_random():
) )
# Test the random behavior # Test the random behavior
task = "Let's create a marketing strategy. @analyst @researcher @writer please contribute." task = "Let's create a marketing strategy for a personal healthcare ai consumer assistant app. @analyst @researcher @writer please contribute."
response = group_chat.run(task) response = group_chat.run(task)
print(f"Response:\n{response}\n") print(f"Response:\n{response}\n")

@ -3,10 +3,12 @@ from swarms import Agent
# Enable real-time streaming # Enable real-time streaming
agent = Agent( agent = Agent(
agent_name="StoryAgent", agent_name="StoryAgent",
model_name="gpt-4o-mini", # model_name="groq/llama-3.1-8b-instant",
model_name="claude-3-5-sonnet-20240620",
# system_prompt="",
streaming_on=True, # 🔥 This enables real streaming! streaming_on=True, # 🔥 This enables real streaming!
max_loops=1, max_loops=1,
print_on=False, print_on=True,
output_type="all", output_type="all",
) )

@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "swarms" name = "swarms"
version = "7.8.9" version = "7.9.0"
description = "Swarms - TGSC" description = "Swarms - TGSC"
license = "MIT" license = "MIT"
authors = ["Kye Gomez <kye@apac.ai>"] authors = ["Kye Gomez <kye@apac.ai>"]

@ -0,0 +1,162 @@
"""
Medical Panel Discussion Example
This example demonstrates a panel of medical specialists discussing treatment solutions
for various diseases using InteractiveGroupChat with different speaker functions:
- Round Robin: Doctors speak in a fixed order
- Random: Doctors speak in random order
- Priority: Senior doctors speak first
- Custom: Disease-specific speaker function
The panel includes specialists from different medical fields who can collaborate
on complex medical cases and treatment plans.
"""
from swarms import Agent
from swarms.structs.interactive_groupchat import (
InteractiveGroupChat,
)
def create_medical_panel():
"""Create a panel of medical specialists for discussion."""
# Cardiologist - Heart and cardiovascular system specialist
cardiologist = Agent(
agent_name="cardiologist",
system_prompt="""You are Dr. Sarah Chen, a board-certified cardiologist with 15 years of experience.
You specialize in cardiovascular diseases, heart failure, arrhythmias, and interventional cardiology.
You have expertise in:
- Coronary artery disease and heart attacks
- Heart failure and cardiomyopathy
- Arrhythmias and electrophysiology
- Hypertension and lipid disorders
- Cardiac imaging and diagnostic procedures
When discussing cases, provide evidence-based treatment recommendations,
consider patient risk factors, and collaborate with other specialists for comprehensive care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Oncologist - Cancer specialist
oncologist = Agent(
agent_name="oncologist",
system_prompt="""You are Dr. Michael Rodriguez, a medical oncologist with 12 years of experience.
You specialize in the diagnosis and treatment of various types of cancer.
You have expertise in:
- Solid tumors (lung, breast, colon, prostate, etc.)
- Hematologic malignancies (leukemia, lymphoma, multiple myeloma)
- Targeted therapy and immunotherapy
- Clinical trials and novel treatments
- Palliative care and symptom management
When discussing cases, consider the cancer type, stage, molecular profile,
patient performance status, and available treatment options including clinical trials.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Neurologist - Nervous system specialist
neurologist = Agent(
agent_name="neurologist",
system_prompt="""You are Dr. Emily Watson, a neurologist with 10 years of experience.
You specialize in disorders of the nervous system, brain, and spinal cord.
You have expertise in:
- Stroke and cerebrovascular disease
- Neurodegenerative disorders (Alzheimer's, Parkinson's, ALS)
- Multiple sclerosis and demyelinating diseases
- Epilepsy and seizure disorders
- Headache and migraine disorders
- Neuromuscular diseases
When discussing cases, consider neurological symptoms, imaging findings,
and the impact of neurological conditions on overall patient care.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Endocrinologist - Hormone and metabolism specialist
endocrinologist = Agent(
agent_name="endocrinologist",
system_prompt="""You are Dr. James Thompson, an endocrinologist with 8 years of experience.
You specialize in disorders of the endocrine system and metabolism.
You have expertise in:
- Diabetes mellitus (Type 1, Type 2, gestational)
- Thyroid disorders (hyperthyroidism, hypothyroidism, thyroid cancer)
- Adrenal disorders and Cushing's syndrome
- Pituitary disorders and growth hormone issues
- Osteoporosis and calcium metabolism
- Reproductive endocrinology
When discussing cases, consider metabolic factors, hormone levels,
and how endocrine disorders may affect other organ systems.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
# Infectious Disease Specialist
infectious_disease = Agent(
agent_name="infectious_disease",
system_prompt="""You are Dr. Lisa Park, an infectious disease specialist with 11 years of experience.
You specialize in the diagnosis and treatment of infectious diseases.
You have expertise in:
- Bacterial, viral, fungal, and parasitic infections
- Antibiotic resistance and antimicrobial stewardship
- HIV/AIDS and opportunistic infections
- Travel medicine and tropical diseases
- Hospital-acquired infections
- Emerging infectious diseases
When discussing cases, consider the infectious agent, antimicrobial susceptibility,
host factors, and infection control measures.""",
model_name="claude-3-5-sonnet-20240620",
streaming_on=True,
print_on=True,
)
return [
cardiologist,
oncologist,
neurologist,
endocrinologist,
infectious_disease,
]
def example_round_robin_panel():
"""Example with round robin speaking order."""
print("=== ROUND ROBIN MEDICAL PANEL ===\n")
agents = create_medical_panel()
group_chat = InteractiveGroupChat(
name="Medical Panel Discussion",
description="A collaborative panel of medical specialists discussing complex cases",
agents=agents,
speaker_function="random-dynamic-speaker",
interactive=False,
)
print(group_chat.speaker_function)
print(group_chat.get_current_speaker_function())
# Case 1: Complex patient with multiple conditions
case1 = """CASE PRESENTATION:
A 65-year-old male with Type 2 diabetes, hypertension, and recent diagnosis of
stage 3 colon cancer presents with chest pain and shortness of breath.
ECG shows ST-segment elevation. Recent blood work shows elevated blood glucose (280 mg/dL)
and signs of infection (WBC 15,000, CRP elevated).
@cardiologist @oncologist @endocrinologist @infectious_disease please provide your
assessment and treatment recommendations for this complex case."""
response = group_chat.run(case1)
if __name__ == "__main__":
example_round_robin_panel()

@ -83,7 +83,14 @@ from swarms.structs.swarming_architectures import (
staircase_swarm, staircase_swarm,
star_swarm, star_swarm,
) )
from swarms.structs.interactive_groupchat import InteractiveGroupChat from swarms.structs.interactive_groupchat import (
InteractiveGroupChat,
speaker_function,
round_robin_speaker,
random_speaker,
priority_speaker,
random_dynamic_speaker,
)
__all__ = [ __all__ = [
"Agent", "Agent",
@ -156,4 +163,9 @@ __all__ = [
"find_agent_by_name", "find_agent_by_name",
"run_agent", "run_agent",
"InteractiveGroupChat", "InteractiveGroupChat",
"speaker_function",
"round_robin_speaker",
"random_speaker",
"priority_speaker",
"random_dynamic_speaker",
] ]

@ -1524,10 +1524,13 @@ class Agent:
f"The model '{self.model_name}' does not support function calling. Please use a model that supports function calling." f"The model '{self.model_name}' does not support function calling. Please use a model that supports function calling."
) )
if self.max_tokens > get_max_tokens(self.model_name): try:
raise AgentInitializationError( if self.max_tokens > get_max_tokens(self.model_name):
f"Max tokens is set to {self.max_tokens}, but the model '{self.model_name}' only supports {get_max_tokens(self.model_name)} tokens. Please set max tokens to {get_max_tokens(self.model_name)} or less." raise AgentInitializationError(
) f"Max tokens is set to {self.max_tokens}, but the model '{self.model_name}' only supports {get_max_tokens(self.model_name)} tokens. Please set max tokens to {get_max_tokens(self.model_name)} or less."
)
except Exception:
pass
if self.model_name not in model_list: if self.model_name not in model_list:
logger.warning( logger.warning(

@ -121,6 +121,71 @@ def priority_speaker(
return available_agents[-1] # Fallback return available_agents[-1] # Fallback
def random_dynamic_speaker(
agents: List[str],
response: str = "",
strategy: str = "parallel",
**kwargs,
) -> Union[str, List[str]]:
"""
Random dynamic speaker function that selects agents based on @mentions in responses.
This function works in two phases:
1. If no response is provided (first call), randomly selects an agent
2. If a response is provided, extracts @mentions and returns agent(s) based on strategy
Args:
agents: List of available agent names
response: The response from the previous agent (may contain @mentions)
strategy: How to handle multiple mentions - "sequential" or "parallel"
**kwargs: Additional arguments (ignored)
Returns:
For sequential strategy: str (single agent name)
For parallel strategy: List[str] (list of agent names)
"""
if not agents:
raise ValueError(
"No agents provided for random dynamic selection"
)
# If no response provided, randomly select first agent
if not response:
return random.choice(agents)
# Extract @mentions from the response
mentions = re.findall(r"@(\w+)", response)
# Filter mentions to only include valid agents
valid_mentions = [
mention for mention in mentions if mention in agents
]
if not valid_mentions:
# If no valid mentions, randomly select from all agents
return random.choice(agents)
# Handle multiple mentions based on strategy
if strategy == "sequential":
# Return the first mentioned agent for sequential execution
return valid_mentions[0]
elif strategy == "parallel":
# Return all mentioned agents for parallel execution
return valid_mentions
else:
raise ValueError(
f"Invalid strategy: {strategy}. Must be 'sequential' or 'parallel'"
)
speaker_functions = {
"round-robin-speaker": round_robin_speaker,
"random-speaker": random_speaker,
"priority-speaker": priority_speaker,
"random-dynamic-speaker": random_dynamic_speaker,
}
class InteractiveGroupChat: class InteractiveGroupChat:
""" """
An interactive group chat system that enables conversations with multiple agents using @mentions. An interactive group chat system that enables conversations with multiple agents using @mentions.
@ -145,11 +210,38 @@ class InteractiveGroupChat:
max_loops (int, optional): Maximum conversation turns. Defaults to 1. max_loops (int, optional): Maximum conversation turns. Defaults to 1.
output_type (str, optional): Type of output format. Defaults to "string". output_type (str, optional): Type of output format. Defaults to "string".
interactive (bool, optional): Whether to enable interactive terminal mode. Defaults to False. interactive (bool, optional): Whether to enable interactive terminal mode. Defaults to False.
speaker_function (Callable, optional): Function to determine speaking order. Defaults to round_robin_speaker. speaker_function (Union[str, Callable], optional): Function to determine speaking order. Can be:
- A string name: "round-robin-speaker", "random-speaker", "priority-speaker", "random-dynamic-speaker"
- A custom callable function
- None (defaults to round_robin_speaker)
speaker_state (dict, optional): Initial state for speaker function. Defaults to empty dict. speaker_state (dict, optional): Initial state for speaker function. Defaults to empty dict.
Raises: Raises:
ValueError: If invalid initialization parameters are provided ValueError: If invalid initialization parameters are provided
InvalidSpeakerFunctionError: If the speaker function is invalid
Examples:
# Initialize with string-based speaker function
group_chat = InteractiveGroupChat(
agents=[agent1, agent2, agent3],
speaker_function="random-speaker"
)
# Initialize with priority speaker function
group_chat = InteractiveGroupChat(
agents=[agent1, agent2, agent3],
speaker_function="priority-speaker",
speaker_state={"priorities": {"agent1": 3, "agent2": 2, "agent3": 1}}
)
# Initialize with dynamic speaker function (agents mention each other)
group_chat = InteractiveGroupChat(
agents=[agent1, agent2, agent3],
speaker_function="random-dynamic-speaker"
)
# Change speaker function during runtime
group_chat.set_speaker_function("round-robin-speaker")
""" """
def __init__( def __init__(
@ -161,7 +253,7 @@ class InteractiveGroupChat:
max_loops: int = 1, max_loops: int = 1,
output_type: str = "string", output_type: str = "string",
interactive: bool = False, interactive: bool = False,
speaker_function: Optional[Callable] = None, speaker_function: Optional[Union[str, Callable]] = None,
speaker_state: Optional[dict] = None, speaker_state: Optional[dict] = None,
): ):
self.id = id self.id = id
@ -173,9 +265,27 @@ class InteractiveGroupChat:
self.interactive = interactive self.interactive = interactive
# Speaker function configuration # Speaker function configuration
self.speaker_function = ( if speaker_function is None:
speaker_function or round_robin_speaker self.speaker_function = round_robin_speaker
) elif isinstance(speaker_function, str):
if speaker_function not in speaker_functions:
available_functions = ", ".join(
speaker_functions.keys()
)
raise InvalidSpeakerFunctionError(
f"Invalid speaker function: '{speaker_function}'. "
f"Available functions: {available_functions}"
)
self.speaker_function = speaker_functions[
speaker_function
]
elif callable(speaker_function):
self.speaker_function = speaker_function
else:
raise InvalidSpeakerFunctionError(
"Speaker function must be either a string, callable, or None"
)
self.speaker_state = speaker_state or {"current_index": 0} self.speaker_state = speaker_state or {"current_index": 0}
# Validate speaker function # Validate speaker function
@ -197,6 +307,230 @@ class InteractiveGroupChat:
self._setup_conversation_context() self._setup_conversation_context()
self._update_agent_prompts() self._update_agent_prompts()
def set_speaker_function(
self,
speaker_function: Union[str, Callable],
speaker_state: Optional[dict] = None,
) -> None:
"""
Set the speaker function using either a string name or a custom callable.
Args:
speaker_function: Either a string name of a predefined function or a custom callable
String options:
- "round-robin-speaker": Cycles through agents in order
- "random-speaker": Selects agents randomly
- "priority-speaker": Selects based on priority weights
- "random-dynamic-speaker": Randomly selects first agent, then follows @mentions in responses
Callable: Custom function that takes (agents: List[str], **kwargs) -> str
speaker_state: Optional state for the speaker function
Raises:
InvalidSpeakerFunctionError: If the speaker function is invalid
"""
if isinstance(speaker_function, str):
# Handle string-based speaker function
if speaker_function not in speaker_functions:
available_functions = ", ".join(
speaker_functions.keys()
)
raise InvalidSpeakerFunctionError(
f"Invalid speaker function: '{speaker_function}'. "
f"Available functions: {available_functions}"
)
self.speaker_function = speaker_functions[
speaker_function
]
logger.info(
f"Speaker function set to: {speaker_function}"
)
elif callable(speaker_function):
# Handle callable speaker function
self.speaker_function = speaker_function
logger.info(
f"Custom speaker function set to: {speaker_function.__name__}"
)
else:
raise InvalidSpeakerFunctionError(
"Speaker function must be either a string or a callable"
)
# Update speaker state if provided
if speaker_state:
self.speaker_state.update(speaker_state)
# Validate the speaker function
self._validate_speaker_function()
def set_priorities(self, priorities: dict) -> None:
"""
Set agent priorities for priority-based speaking order.
Args:
priorities: Dictionary mapping agent names to priority weights
"""
self.speaker_state["priorities"] = priorities
logger.info(f"Agent priorities set: {priorities}")
def get_available_speaker_functions(self) -> List[str]:
"""
Get a list of available speaker function names.
Returns:
List[str]: List of available speaker function names
"""
return list(speaker_functions.keys())
def get_current_speaker_function(self) -> str:
"""
Get the name of the current speaker function.
Returns:
str: Name of the current speaker function, or "custom" if it's a custom function
"""
for name, func in speaker_functions.items():
if self.speaker_function == func:
return name
return "custom"
def start_interactive_session(self):
"""
Start an interactive terminal session for chatting with agents.
This method creates a REPL (Read-Eval-Print Loop) that allows users to:
- Chat with agents using @mentions
- See available agents and their descriptions
- Exit the session using 'exit' or 'quit'
- Get help using 'help' or '?'
"""
if not self.interactive:
raise InteractiveGroupChatError(
"Interactive mode is not enabled. Initialize with interactive=True"
)
print(f"\nWelcome to {self.name}!")
print(f"Description: {self.description}")
print(
f"Current speaker function: {self.get_current_speaker_function()}"
)
print("\nAvailable agents:")
for name, agent in self.agent_map.items():
if isinstance(agent, Agent):
print(
f"- @{name}: {agent.system_prompt.splitlines()[0]}"
)
else:
print(f"- @{name}: Custom callable function")
print("\nCommands:")
print("- Type 'help' or '?' for help")
print("- Type 'exit' or 'quit' to end the session")
print("- Type 'speaker' to change speaker function")
print("- Use @agent_name to mention agents")
print("\nStart chatting:")
while True:
try:
# Get user input
user_input = input("\nYou: ").strip()
# Handle special commands
if user_input.lower() in ["exit", "quit"]:
print("Goodbye!")
break
if user_input.lower() in ["help", "?"]:
print("\nHelp:")
print("1. Mention agents using @agent_name")
print(
"2. You can mention multiple agents in one task"
)
print("3. Available agents:")
for name in self.agent_map:
print(f" - @{name}")
print(
"4. Type 'speaker' to change speaker function"
)
print(
"5. Type 'exit' or 'quit' to end the session"
)
continue
if user_input.lower() == "speaker":
print(
f"\nCurrent speaker function: {self.get_current_speaker_function()}"
)
print("Available speaker functions:")
for i, func_name in enumerate(
self.get_available_speaker_functions(), 1
):
print(f" {i}. {func_name}")
try:
choice = input(
"\nEnter the number or name of the speaker function: "
).strip()
# Try to parse as number first
try:
func_index = int(choice) - 1
if (
0
<= func_index
< len(
self.get_available_speaker_functions()
)
):
selected_func = self.get_available_speaker_functions()[
func_index
]
else:
print(
"Invalid number. Please try again."
)
continue
except ValueError:
# Try to parse as name
selected_func = choice
self.set_speaker_function(selected_func)
print(
f"Speaker function changed to: {self.get_current_speaker_function()}"
)
except InvalidSpeakerFunctionError as e:
print(f"Error: {e}")
except Exception as e:
print(f"An error occurred: {e}")
continue
if not user_input:
continue
# Process the task and get responses
try:
self.run(user_input)
print("\nChat:")
# print(response)
except NoMentionedAgentsError:
print(
"\nError: Please mention at least one agent using @agent_name"
)
except AgentNotFoundError as e:
print(f"\nError: {str(e)}")
except Exception as e:
print(f"\nAn error occurred: {str(e)}")
except KeyboardInterrupt:
print("\nSession terminated by user. Goodbye!")
break
except Exception as e:
print(f"\nAn unexpected error occurred: {str(e)}")
print(
"The session will continue. You can type 'exit' to end it."
)
def _validate_speaker_function(self) -> None: def _validate_speaker_function(self) -> None:
""" """
Validates the speaker function. Validates the speaker function.
@ -287,6 +621,7 @@ IMPORTANT: You are part of a collaborative group chat where you can interact wit
2. ACKNOWLEDGE: Reference and acknowledge what other agents have said 2. ACKNOWLEDGE: Reference and acknowledge what other agents have said
3. BUILD UPON: Add your perspective while building upon their insights 3. BUILD UPON: Add your perspective while building upon their insights
4. MENTION: Use @agent_name to call on other agents when needed 4. MENTION: Use @agent_name to call on other agents when needed
5. COMPLETE: Acknowledge when your part is done and what still needs to be done
HOW TO MENTION OTHER AGENTS: HOW TO MENTION OTHER AGENTS:
- Use @agent_name to mention another agent in your response - Use @agent_name to mention another agent in your response
@ -325,24 +660,33 @@ COLLABORATION GUIDELINES:
- ASK CLARIFYING QUESTIONS if you need more information from other agents - ASK CLARIFYING QUESTIONS if you need more information from other agents
- DELEGATE appropriately: "Let me ask @expert_agent to verify this" or "@specialist, can you elaborate on this point?" - DELEGATE appropriately: "Let me ask @expert_agent to verify this" or "@specialist, can you elaborate on this point?"
TASK COMPLETION GUIDELINES:
- ACKNOWLEDGE when you are done with your part of the task
- CLEARLY STATE what still needs to be done before the overall task is finished
- If you mention other agents, explain what specific input you need from them
- Use phrases like "I have completed [specific part]" or "The task still requires [specific actions]"
- Provide a clear status update: "My analysis is complete. The task now needs @writer to create content and @reviewer to validate the approach."
RESPONSE STRUCTURE: RESPONSE STRUCTURE:
1. ACKNOWLEDGE: "I've reviewed the responses from @agent1 and @agent2..." 1. ACKNOWLEDGE: "I've reviewed the responses from @agent1 and @agent2..."
2. BUILD: "Building on @agent1's analysis of the data..." 2. BUILD: "Building on @agent1's analysis of the data..."
3. CONTRIBUTE: "From my perspective, I would add..." 3. CONTRIBUTE: "From my perspective, I would add..."
4. COLLABORATE: "To get a complete picture, let me ask @agent3 to..." 4. COLLABORATE: "To get a complete picture, let me ask @agent3 to..."
5. SYNTHESIZE: "Combining our insights, the key findings are..." 5. COMPLETE: "I have completed [my part]. The task still requires [specific next steps]"
6. SYNTHESIZE: "Combining our insights, the key findings are..."
EXAMPLES OF GOOD COLLABORATION: EXAMPLES OF GOOD COLLABORATION:
- "I've reviewed @analyst's data analysis and @researcher's market insights. The data shows strong growth potential, and I agree with @researcher that we should focus on emerging markets. Let me add that from a content perspective, we should @writer to create targeted messaging for these markets." - "I've reviewed @analyst's data analysis and @researcher's market insights. The data shows strong growth potential, and I agree with @researcher that we should focus on emerging markets. Let me add that from a content perspective, we should @writer to create targeted messaging for these markets. I have completed my market analysis. The task now requires @writer to develop content and @reviewer to validate our approach."
- "Building on @researcher's findings about customer behavior, I can see that @analyst's data supports this trend. To get a complete understanding, let me ask @writer to help us craft messaging that addresses these specific customer needs." - "Building on @researcher's findings about customer behavior, I can see that @analyst's data supports this trend. To get a complete understanding, let me ask @writer to help us craft messaging that addresses these specific customer needs. My data analysis is complete. The task still needs @writer to create messaging and @reviewer to approve the final strategy."
AVOID: AVOID:
- Ignoring other agents' responses - Ignoring other agents' responses
- Repeating what others have already said - Repeating what others have already said
- Making assumptions without consulting relevant experts - Making assumptions without consulting relevant experts
- Responding in isolation without considering the group's collective knowledge - Responding in isolation without considering the group's collective knowledge
- Not acknowledging task completion status
Remember: You are part of a team. Your response should reflect that you've read, understood, and built upon the contributions of others. Remember: You are part of a team. Your response should reflect that you've read, understood, and are building upon the contributions of others, and clearly communicate your task completion status.
""" """
# Update the agent's system prompt # Update the agent's system prompt
@ -438,6 +782,12 @@ Remember: You are part of a team. Your response should reflect that you've read,
) )
return sorted_agents return sorted_agents
elif self.speaker_function == random_dynamic_speaker:
# For dynamic speaker, we need to handle it differently
# The dynamic speaker will be called during the run method
# For now, just return the original order
return mentioned_agents
else: else:
# Custom speaker function # Custom speaker function
# For custom functions, we'll use the first agent returned # For custom functions, we'll use the first agent returned
@ -460,120 +810,12 @@ Remember: You are part of a team. Your response should reflect that you've read,
# Fallback to original order # Fallback to original order
return mentioned_agents return mentioned_agents
def set_speaker_function( def run(
self, self,
speaker_function: Callable, task: str,
speaker_state: Optional[dict] = None, img: Optional[str] = None,
) -> None: imgs: Optional[List[str]] = None,
""" ) -> str:
Set a custom speaker function and optional state.
Args:
speaker_function: Function that determines speaking order
speaker_state: Optional state for the speaker function
"""
self.speaker_function = speaker_function
if speaker_state:
self.speaker_state.update(speaker_state)
self._validate_speaker_function()
logger.info(
f"Speaker function updated to: {speaker_function.__name__}"
)
def set_priorities(self, priorities: dict) -> None:
"""
Set agent priorities for priority-based speaking order.
Args:
priorities: Dictionary mapping agent names to priority weights
"""
self.speaker_state["priorities"] = priorities
logger.info(f"Agent priorities set: {priorities}")
def start_interactive_session(self):
"""
Start an interactive terminal session for chatting with agents.
This method creates a REPL (Read-Eval-Print Loop) that allows users to:
- Chat with agents using @mentions
- See available agents and their descriptions
- Exit the session using 'exit' or 'quit'
- Get help using 'help' or '?'
"""
if not self.interactive:
raise InteractiveGroupChatError(
"Interactive mode is not enabled. Initialize with interactive=True"
)
print(f"\nWelcome to {self.name}!")
print(f"Description: {self.description}")
print("\nAvailable agents:")
for name, agent in self.agent_map.items():
if isinstance(agent, Agent):
print(
f"- @{name}: {agent.system_prompt.splitlines()[0]}"
)
else:
print(f"- @{name}: Custom callable function")
print("\nCommands:")
print("- Type 'help' or '?' for help")
print("- Type 'exit' or 'quit' to end the session")
print("- Use @agent_name to mention agents")
print("\nStart chatting:")
while True:
try:
# Get user input
user_input = input("\nYou: ").strip()
# Handle special commands
if user_input.lower() in ["exit", "quit"]:
print("Goodbye!")
break
if user_input.lower() in ["help", "?"]:
print("\nHelp:")
print("1. Mention agents using @agent_name")
print(
"2. You can mention multiple agents in one task"
)
print("3. Available agents:")
for name in self.agent_map:
print(f" - @{name}")
print(
"4. Type 'exit' or 'quit' to end the session"
)
continue
if not user_input:
continue
# Process the task and get responses
try:
self.run(user_input)
print("\nChat:")
# print(response)
except NoMentionedAgentsError:
print(
"\nError: Please mention at least one agent using @agent_name"
)
except AgentNotFoundError as e:
print(f"\nError: {str(e)}")
except Exception as e:
print(f"\nAn error occurred: {str(e)}")
except KeyboardInterrupt:
print("\nSession terminated by user. Goodbye!")
break
except Exception as e:
print(f"\nAn unexpected error occurred: {str(e)}")
print(
"The session will continue. You can type 'exit' to end it."
)
def run(self, task: str) -> str:
""" """
Process a task and get responses from mentioned agents. Process a task and get responses from mentioned agents.
If interactive mode is enabled, this will be called by start_interactive_session(). If interactive mode is enabled, this will be called by start_interactive_session().
@ -591,31 +833,171 @@ Remember: You are part of a team. Your response should reflect that you've read,
# Add user task to conversation # Add user task to conversation
self.conversation.add(role="User", content=task) self.conversation.add(role="User", content=task)
# Determine speaking order using speaker function # Handle dynamic speaker function differently
speaking_order = self._get_speaking_order( if self.speaker_function == random_dynamic_speaker:
mentioned_agents # Get strategy from speaker state (default to sequential)
) strategy = self.speaker_state.get(
logger.info( "strategy", "sequential"
f"Speaking order determined: {speaking_order}" )
)
# Get responses from mentioned agents in the determined order # For dynamic speaker, we'll determine the next speaker after each response
for agent_name in speaking_order: # Track which agents have spoken to ensure all get a chance
agent = self.agent_map.get(agent_name) spoken_agents = set()
if not agent: last_response = ""
raise AgentNotFoundError( max_iterations = (
f"Agent '{agent_name}' not found" len(mentioned_agents) * 3
) # Allow more iterations for parallel
iteration = 0
while iteration < max_iterations and len(
spoken_agents
) < len(mentioned_agents):
# Determine next speaker(s) using dynamic function
next_speakers = self.speaker_function(
mentioned_agents, # Use all mentioned agents, not remaining_agents
last_response,
strategy=strategy,
**self.speaker_state,
) )
try: # Handle both single agent and multiple agents
# Get the complete conversation history if isinstance(next_speakers, str):
context = ( next_speakers = [next_speakers]
self.conversation.return_history_as_string()
# Filter out invalid agents
valid_next_speakers = [
agent
for agent in next_speakers
if agent in mentioned_agents
]
if not valid_next_speakers:
# If no valid mentions found, randomly select from unspoken agents
unspoken_agents = [
agent
for agent in mentioned_agents
if agent not in spoken_agents
]
if unspoken_agents:
valid_next_speakers = [
random.choice(unspoken_agents)
]
else:
# All agents have spoken, break the loop
break
# Process agents based on strategy
if strategy == "sequential":
# Process one agent at a time
for next_speaker in valid_next_speakers:
if next_speaker in spoken_agents:
continue # Skip if already spoken
response = self._get_agent_response(
next_speaker, img, imgs
)
if response:
last_response = response
spoken_agents.add(next_speaker)
break # Only process one agent in sequential mode
elif strategy == "parallel":
# Process all mentioned agents in parallel
import concurrent.futures
# Get responses from all valid agents
responses = []
with concurrent.futures.ThreadPoolExecutor() as executor:
future_to_agent = {
executor.submit(
self._get_agent_response,
agent,
img,
imgs,
): agent
for agent in valid_next_speakers
if agent not in spoken_agents
}
for (
future
) in concurrent.futures.as_completed(
future_to_agent
):
agent = future_to_agent[future]
try:
response = future.result()
if response:
responses.append(response)
spoken_agents.add(agent)
except Exception as e:
logger.error(
f"Error getting response from {agent}: {e}"
)
# Combine responses for next iteration
if responses:
last_response = "\n\n".join(responses)
iteration += 1
else:
# For non-dynamic speaker functions, use the original logic
speaking_order = self._get_speaking_order(
mentioned_agents
)
logger.info(
f"Speaking order determined: {speaking_order}"
)
# Get responses from mentioned agents in the determined order
for agent_name in speaking_order:
response = self._get_agent_response(
agent_name, img, imgs
) )
# Get response from agent return history_output_formatter(
if isinstance(agent, Agent): self.conversation, self.output_type
collaborative_task = f"""{context} )
except InteractiveGroupChatError as e:
logger.error(f"GroupChat error: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error: {e}")
raise InteractiveGroupChatError(
f"Unexpected error occurred: {str(e)}"
)
def _get_agent_response(
self,
agent_name: str,
img: Optional[str] = None,
imgs: Optional[List[str]] = None,
) -> Optional[str]:
"""
Get response from a specific agent.
Args:
agent_name: Name of the agent to get response from
img: Optional image for the task
imgs: Optional list of images for the task
Returns:
The agent's response or None if error
"""
agent = self.agent_map.get(agent_name)
if not agent:
raise AgentNotFoundError(
f"Agent '{agent_name}' not found"
)
try:
# Get the complete conversation history
context = self.conversation.return_history_as_string()
# Get response from agent
if isinstance(agent, Agent):
collaborative_task = f"""{context}
COLLABORATIVE TASK: Please respond to the latest task as {agent_name}. COLLABORATIVE TASK: Please respond to the latest task as {agent_name}.
@ -626,38 +1008,56 @@ IMPORTANT INSTRUCTIONS:
4. If you need input from other agents, mention them using @agent_name 4. If you need input from other agents, mention them using @agent_name
5. Provide your unique expertise while showing you understand the group's collective knowledge 5. Provide your unique expertise while showing you understand the group's collective knowledge
Remember: You are part of a collaborative team. Your response should demonstrate that you've read, understood, and are building upon the contributions of others.""" TASK COMPLETION GUIDELINES:
- Acknowledge when you are done with your part of the task
- Clearly state what still needs to be done before the overall task is finished
- If you mention other agents, explain what specific input you need from them
- Use phrases like "I have completed [specific part]" or "The task still requires [specific actions]"
response = agent.run(task=collaborative_task) Remember: You are part of a collaborative team. Your response should demonstrate that you've read, understood, and are building upon the contributions of others."""
else:
# For callable functions
response = agent(context)
# Add response to conversation response = agent.run(
if response and not response.isspace(): task=collaborative_task,
self.conversation.add( img=img,
role=agent_name, content=response imgs=imgs,
) )
logger.info(f"Agent {agent_name} responded") else:
# For callable functions
response = agent(context)
except Exception as e: # Add response to conversation
logger.error( if response and not response.isspace():
f"Error getting response from {agent_name}: {e}" self.conversation.add(
) role=agent_name, content=response
self.conversation.add( )
role=agent_name, logger.info(f"Agent {agent_name} responded")
content=f"Error: Unable to generate response - {str(e)}", return response
)
return history_output_formatter( except Exception as e:
self.conversation, self.output_type logger.error(
f"Error getting response from {agent_name}: {e}"
) )
self.conversation.add(
role=agent_name,
content=f"Error: Unable to generate response - {str(e)}",
)
return f"Error: Unable to generate response - {str(e)}"
except InteractiveGroupChatError as e: return None
logger.error(f"GroupChat error: {e}")
raise def set_dynamic_strategy(self, strategy: str) -> None:
except Exception as e: """
logger.error(f"Unexpected error: {e}") Set the strategy for the random-dynamic-speaker function.
raise InteractiveGroupChatError(
f"Unexpected error occurred: {str(e)}" Args:
strategy: Either "sequential" or "parallel"
- "sequential": Process one agent at a time based on @mentions
- "parallel": Process all mentioned agents simultaneously
"""
if strategy not in ["sequential", "parallel"]:
raise ValueError(
"Strategy must be either 'sequential' or 'parallel'"
) )
self.speaker_state["strategy"] = strategy
logger.info(f"Dynamic speaker strategy set to: {strategy}")

@ -86,6 +86,7 @@ models = [
"o4-mini", "o4-mini",
"o3", "o3",
"gpt-4.1", "gpt-4.1",
"groq/llama-3.1-8b-instant",
"gpt-4.1-nano", "gpt-4.1-nano",
] ]

@ -182,6 +182,7 @@ class SwarmRouter:
list_all_agents: bool = False, list_all_agents: bool = False,
conversation: Any = None, conversation: Any = None,
agents_config: Optional[Dict[Any, Any]] = None, agents_config: Optional[Dict[Any, Any]] = None,
speaker_function: str = None,
*args, *args,
**kwargs, **kwargs,
): ):
@ -208,6 +209,7 @@ class SwarmRouter:
self.list_all_agents = list_all_agents self.list_all_agents = list_all_agents
self.conversation = conversation self.conversation = conversation
self.agents_config = agents_config self.agents_config = agents_config
self.speaker_function = speaker_function
# Reliability check # Reliability check
self.reliability_check() self.reliability_check()
@ -358,6 +360,7 @@ class SwarmRouter:
agents=self.agents, agents=self.agents,
max_loops=self.max_loops, max_loops=self.max_loops,
output_type=self.output_type, output_type=self.output_type,
speaker_function=self.speaker_function,
) )
elif self.swarm_type == "DeepResearchSwarm": elif self.swarm_type == "DeepResearchSwarm":

@ -179,7 +179,9 @@ class Formatter:
panel_style = ( panel_style = (
f"bold {random_color}" if style is None else style f"bold {random_color}" if style is None else style
) )
text_style = random_color text_style = (
"white" # Make text white instead of random color
)
def create_streaming_panel(text_obj, is_complete=False): def create_streaming_panel(text_obj, is_complete=False):
"""Create panel with proper text wrapping using Rich's built-in capabilities""" """Create panel with proper text wrapping using Rich's built-in capabilities"""

@ -151,6 +151,8 @@ class LiteLLM:
retries # Add retries for better reliability retries # Add retries for better reliability
) )
litellm.drop_params = True
def output_for_tools(self, response: any): def output_for_tools(self, response: any):
if self.mcp_call is True: if self.mcp_call is True:
out = response.choices[0].message.tool_calls[0].function out = response.choices[0].message.tool_calls[0].function

Loading…
Cancel
Save