From 1c36a1340e36c66c7c0c4fc46b65bdd42648f145 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Mon, 9 Jun 2025 13:00:16 -0700 Subject: [PATCH] [DOCS][FIX] [interactive group chat] --- docs/swarms/structs/interactive_groupchat.md | 2 + swarms/structs/deep_research_swarm.py | 188 +++++++++++++------ 2 files changed, 129 insertions(+), 61 deletions(-) diff --git a/docs/swarms/structs/interactive_groupchat.md b/docs/swarms/structs/interactive_groupchat.md index 207e2a50..2404af02 100644 --- a/docs/swarms/structs/interactive_groupchat.md +++ b/docs/swarms/structs/interactive_groupchat.md @@ -25,6 +25,7 @@ pip install swarms Initializes a new InteractiveGroupChat instance with the specified configuration. **Arguments:** + | Parameter | Type | Description | Default | |-----------|------|-------------|---------| | `id` | str | Unique identifier for the chat | auto-generated key | @@ -36,6 +37,7 @@ Initializes a new InteractiveGroupChat instance with the specified configuration | `interactive` | bool | Whether to enable interactive mode | False | **Example:** + ```python from swarms import Agent, InteractiveGroupChat diff --git a/swarms/structs/deep_research_swarm.py b/swarms/structs/deep_research_swarm.py index 530dc2ff..c2ae096b 100644 --- a/swarms/structs/deep_research_swarm.py +++ b/swarms/structs/deep_research_swarm.py @@ -5,13 +5,12 @@ import os from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List import aiohttp from dotenv import load_dotenv from rich.console import Console from rich.panel import Panel -from rich.text import Text from rich.tree import Tree from swarms.agents.reasoning_duo import ReasoningDuo @@ -122,12 +121,12 @@ async def _async_exa_search( ) -> Dict[str, Any]: """Asynchronous helper function for Exa.ai API requests""" api_url = "https://api.exa.ai/search" - + # Check if API key is available api_key = os.getenv("EXA_API_KEY") if not api_key: return {"error": "EXA_API_KEY environment variable not set"} - + headers = { "x-api-key": api_key, "Content-Type": "application/json", @@ -135,7 +134,8 @@ async def _async_exa_search( # Filter out None keys AND None values from kwargs safe_kwargs = { - str(k): v for k, v in kwargs.items() + str(k): v + for k, v in kwargs.items() if k is not None and v is not None and str(k) != "None" } @@ -148,10 +148,15 @@ async def _async_exa_search( "highlights": {"numSentences": 2}, }, } - + # Only add safe_kwargs if they don't conflict with existing keys for key, value in safe_kwargs.items(): - if key not in payload and key not in ["query", "useAutoprompt", "numResults", "contents"]: + if key not in payload and key not in [ + "query", + "useAutoprompt", + "numResults", + "contents", + ]: payload[key] = value try: @@ -420,10 +425,14 @@ class DeepResearchSwarm: try: # Get all the queries to process queries = self.get_queries(query) - + if not queries: - error_msg = "No queries generated. Please check your input." - self.conversation.add(role="System", content=error_msg) + error_msg = ( + "No queries generated. Please check your input." + ) + self.conversation.add( + role="System", content=error_msg + ) return history_output_formatter( self.conversation, type=self.output_type ) @@ -448,7 +457,9 @@ class DeepResearchSwarm: except Exception as e: # Handle any errors in the thread - error_msg = f"Error processing query '{q}': {str(e)}" + error_msg = ( + f"Error processing query '{q}': {str(e)}" + ) console.print(f"[bold red]{error_msg}[/bold red]") self.conversation.add( role="System", @@ -466,7 +477,9 @@ class DeepResearchSwarm: content=final_summary, ) except Exception as e: - error_msg = f"Error generating final summary: {str(e)}" + error_msg = ( + f"Error generating final summary: {str(e)}" + ) console.print(f"[bold red]{error_msg}[/bold red]") self.conversation.add( role="System", @@ -477,25 +490,32 @@ class DeepResearchSwarm: result = history_output_formatter( self.conversation, type=self.output_type ) - + # If output type is JSON, ensure it's properly formatted if self.output_type.lower() == "json": try: import json + if isinstance(result, str): # Try to parse and reformat for pretty printing parsed = json.loads(result) - return json.dumps(parsed, indent=2, ensure_ascii=False) + return json.dumps( + parsed, indent=2, ensure_ascii=False + ) except (json.JSONDecodeError, TypeError): # If parsing fails, return as-is pass - + return result - + except Exception as e: error_msg = f"Critical error in step execution: {str(e)}" console.print(f"[bold red]{error_msg}[/bold red]") - return {"error": error_msg} if self.output_type.lower() == "json" else error_msg + return ( + {"error": error_msg} + if self.output_type.lower() == "json" + else error_msg + ) def run(self, task: str): return self.step(task) @@ -515,10 +535,12 @@ class DeepResearchSwarm: future = self.executor.submit(self.step, task) futures.append((task, future)) - def parse_and_display_results(self, json_result: str, export_markdown: bool = True): + def parse_and_display_results( + self, json_result: str, export_markdown: bool = True + ): """ Parse JSON results and display in rich format with optional markdown export. - + Args: json_result (str): JSON string containing conversation results export_markdown (bool): Whether to export to markdown file @@ -526,86 +548,130 @@ class DeepResearchSwarm: try: # Parse JSON data = json.loads(json_result) - + # Create rich display - console.print("\n" + "="*100, style="cyan") - console.print("šŸ”¬ DEEP RESEARCH RESULTS", style="bold cyan", justify="center") - console.print("="*100, style="cyan") - + console.print("\n" + "=" * 100, style="cyan") + console.print( + "šŸ”¬ DEEP RESEARCH RESULTS", + style="bold cyan", + justify="center", + ) + console.print("=" * 100, style="cyan") + # Create conversation tree tree = Tree("šŸ—£ļø Research Conversation", style="bold blue") - markdown_content = ["# Deep Research Results\n", f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"] - + markdown_content = [ + "# Deep Research Results\n", + f"**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n", + ] + for i, entry in enumerate(data, 1): if isinstance(entry, dict): - role = entry.get('role', 'Unknown') - content = entry.get('content', '') - timestamp = entry.get('timestamp', '') - + role = entry.get("role", "Unknown") + content = entry.get("content", "") + timestamp = entry.get("timestamp", "") + # Get role info for display role_info = self._get_role_display_info(role) - + # Create tree branch branch_text = f"{role_info['emoji']} {role}" if timestamp: - time_part = timestamp.split()[-1] if ' ' in timestamp else timestamp[-8:] + time_part = ( + timestamp.split()[-1] + if " " in timestamp + else timestamp[-8:] + ) branch_text += f" ({time_part})" - - branch = tree.add(branch_text, style=role_info['style']) - + + branch = tree.add( + branch_text, style=role_info["style"] + ) + # Add content preview to tree - content_preview = content[:150] + "..." if len(content) > 150 else content - content_preview = content_preview.replace('\n', ' ') + content_preview = ( + content[:150] + "..." + if len(content) > 150 + else content + ) + content_preview = content_preview.replace( + "\n", " " + ) branch.add(content_preview, style="dim") - + # Add to markdown markdown_content.append(f"\n## {i}. {role}") if timestamp: - markdown_content.append(f"**Timestamp:** {timestamp}") + markdown_content.append( + f"**Timestamp:** {timestamp}" + ) markdown_content.append(f"\n{content}\n") - + # Display full content for important entries - if role.lower() in ['reasoning-agent-01'] and len(content) > 300: - console.print(f"\nšŸ“‹ {role} Full Response:", style="bold green") - console.print(Panel(content, border_style="green", title=f"{role} Analysis")) - + if ( + role.lower() in ["reasoning-agent-01"] + and len(content) > 300 + ): + console.print( + f"\nšŸ“‹ {role} Full Response:", + style="bold green", + ) + console.print( + Panel( + content, + border_style="green", + title=f"{role} Analysis", + ) + ) + # Display the tree console.print(tree) - + # Export to markdown if requested if export_markdown: # Create deepsearch_results directory results_dir = Path("deepsearch_results") results_dir.mkdir(exist_ok=True) - + # Generate filename with timestamp timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = results_dir / f"research_results_{timestamp}.md" - + filename = ( + results_dir / f"research_results_{timestamp}.md" + ) + # Write markdown file - with open(filename, 'w', encoding='utf-8') as f: - f.write('\n'.join(markdown_content)) - - console.print(f"\nšŸ’¾ Results exported to: {filename}", style="bold green") - - console.print("\nāœ… Research analysis complete!", style="bold cyan") - + with open(filename, "w", encoding="utf-8") as f: + f.write("\n".join(markdown_content)) + + console.print( + f"\nšŸ’¾ Results exported to: {filename}", + style="bold green", + ) + + console.print( + "\nāœ… Research analysis complete!", style="bold cyan" + ) + except json.JSONDecodeError as e: console.print(f"āŒ Error parsing JSON: {e}", style="red") except Exception as e: - console.print(f"āŒ Error displaying results: {e}", style="red") + console.print( + f"āŒ Error displaying results: {e}", style="red" + ) def _get_role_display_info(self, role: str) -> Dict[str, str]: """Get display information for different conversation roles.""" role_map = { "user": {"emoji": "šŸ‘¤", "style": "cyan"}, - "deep-research-agent": {"emoji": "šŸ”", "style": "blue"}, + "deep-research-agent": {"emoji": "šŸ”", "style": "blue"}, "reasoning-agent-01": {"emoji": "🧠", "style": "magenta"}, "system": {"emoji": "āš™ļø", "style": "yellow"}, } - + role_lower = role.lower() - return role_map.get(role_lower, {"emoji": "šŸ¤–", "style": "white"}) + return role_map.get( + role_lower, {"emoji": "šŸ¤–", "style": "white"} + ) # Example usage @@ -617,10 +683,10 @@ class DeepResearchSwarm: # result = swarm.step( # "What is the active tariff situation with mexico? Only create 2 queries" # ) - + # # Parse and display results in rich format with markdown export # swarm.parse_and_display_results(result, export_markdown=True) - + # except Exception as e: # print(f"Error running deep research swarm: {str(e)}") # import traceback