-
-
Notifications
You must be signed in to change notification settings - Fork 441
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[FEAT][Agent][save][load] [FIX][openai assistants]
- Loading branch information
Showing
17 changed files
with
2,544 additions
and
465 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
import json | ||
import os | ||
import platform | ||
import sys | ||
import traceback | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from typing import Any, Dict, List, Optional, Tuple | ||
|
||
import psutil | ||
import requests | ||
from loguru import logger | ||
from swarm_models import OpenAIChat | ||
|
||
from swarms.structs.agent import Agent | ||
|
||
|
||
@dataclass | ||
class SwarmSystemInfo: | ||
"""System information for Swarms issue reports.""" | ||
|
||
os_name: str | ||
os_version: str | ||
python_version: str | ||
cpu_usage: float | ||
memory_usage: float | ||
disk_usage: float | ||
swarms_version: str # Added Swarms version tracking | ||
cuda_available: bool # Added CUDA availability check | ||
gpu_info: Optional[str] # Added GPU information | ||
|
||
|
||
class SwarmsIssueReporter: | ||
""" | ||
Production-grade GitHub issue reporter specifically designed for the Swarms library. | ||
Automatically creates detailed issues for the https://github.com/kyegomez/swarms repository. | ||
Features: | ||
- Swarms-specific error categorization | ||
- Automatic version and dependency tracking | ||
- CUDA and GPU information collection | ||
- Integration with Swarms logging system | ||
- Detailed environment information | ||
""" | ||
|
||
REPO_OWNER = "kyegomez" | ||
REPO_NAME = "swarms" | ||
ISSUE_CATEGORIES = { | ||
"agent": ["agent", "automation"], | ||
"memory": ["memory", "storage"], | ||
"tool": ["tools", "integration"], | ||
"llm": ["llm", "model"], | ||
"performance": ["performance", "optimization"], | ||
"compatibility": ["compatibility", "environment"], | ||
} | ||
|
||
def __init__( | ||
self, | ||
github_token: str, | ||
rate_limit: int = 10, | ||
rate_period: int = 3600, | ||
log_file: str = "swarms_issues.log", | ||
enable_duplicate_check: bool = True, | ||
): | ||
""" | ||
Initialize the Swarms Issue Reporter. | ||
Args: | ||
github_token (str): GitHub personal access token | ||
rate_limit (int): Maximum number of issues to create per rate_period | ||
rate_period (int): Time period for rate limiting in seconds | ||
log_file (str): Path to log file | ||
enable_duplicate_check (bool): Whether to check for duplicate issues | ||
""" | ||
self.github_token = github_token | ||
self.rate_limit = rate_limit | ||
self.rate_period = rate_period | ||
self.enable_duplicate_check = enable_duplicate_check | ||
self.github_token = os.getenv("GITHUB_API_KEY") | ||
|
||
# Initialize logging | ||
log_path = os.path.join(os.getcwd(), "logs", log_file) | ||
os.makedirs(os.path.dirname(log_path), exist_ok=True) | ||
logger.add( | ||
log_path, | ||
rotation="1 day", | ||
retention="1 month", | ||
compression="zip", | ||
) | ||
|
||
# Issue tracking | ||
self.issues_created = [] | ||
self.last_issue_time = datetime.now() | ||
|
||
# Validate GitHub token | ||
# self._validate_github_credentials() | ||
|
||
def _get_swarms_version(self) -> str: | ||
"""Get the installed version of Swarms.""" | ||
try: | ||
import swarms | ||
|
||
return swarms.__version__ | ||
except: | ||
return "Unknown" | ||
|
||
def _get_gpu_info(self) -> Tuple[bool, Optional[str]]: | ||
"""Get GPU information and CUDA availability.""" | ||
try: | ||
import torch | ||
|
||
cuda_available = torch.cuda.is_available() | ||
if cuda_available: | ||
gpu_info = torch.cuda.get_device_name(0) | ||
return cuda_available, gpu_info | ||
return False, None | ||
except: | ||
return False, None | ||
|
||
def _get_system_info(self) -> SwarmSystemInfo: | ||
"""Collect system and Swarms-specific information.""" | ||
cuda_available, gpu_info = self._get_gpu_info() | ||
|
||
return SwarmSystemInfo( | ||
os_name=platform.system(), | ||
os_version=platform.version(), | ||
python_version=sys.version, | ||
cpu_usage=psutil.cpu_percent(), | ||
memory_usage=psutil.virtual_memory().percent, | ||
disk_usage=psutil.disk_usage("/").percent, | ||
swarms_version=self._get_swarms_version(), | ||
cuda_available=cuda_available, | ||
gpu_info=gpu_info, | ||
) | ||
|
||
def _categorize_error( | ||
self, error: Exception, context: Dict | ||
) -> List[str]: | ||
"""Categorize the error and return appropriate labels.""" | ||
error_str = str(error).lower() | ||
type(error).__name__ | ||
|
||
labels = ["bug", "automated"] | ||
|
||
# Check error message and context for category keywords | ||
for ( | ||
category, | ||
category_labels, | ||
) in self.ISSUE_CATEGORIES.items(): | ||
if any( | ||
keyword in error_str for keyword in category_labels | ||
): | ||
labels.extend(category_labels) | ||
break | ||
|
||
# Add severity label based on error type | ||
if issubclass(type(error), (SystemError, MemoryError)): | ||
labels.append("severity:critical") | ||
elif issubclass(type(error), (ValueError, TypeError)): | ||
labels.append("severity:medium") | ||
else: | ||
labels.append("severity:low") | ||
|
||
return list(set(labels)) # Remove duplicates | ||
|
||
def _format_swarms_issue_body( | ||
self, | ||
error: Exception, | ||
system_info: SwarmSystemInfo, | ||
context: Dict, | ||
) -> str: | ||
"""Format the issue body with Swarms-specific information.""" | ||
return f""" | ||
## Swarms Error Report | ||
- **Error Type**: {type(error).__name__} | ||
- **Error Message**: {str(error)} | ||
- **Swarms Version**: {system_info.swarms_version} | ||
## Environment Information | ||
- **OS**: {system_info.os_name} {system_info.os_version} | ||
- **Python Version**: {system_info.python_version} | ||
- **CUDA Available**: {system_info.cuda_available} | ||
- **GPU**: {system_info.gpu_info or "N/A"} | ||
- **CPU Usage**: {system_info.cpu_usage}% | ||
- **Memory Usage**: {system_info.memory_usage}% | ||
- **Disk Usage**: {system_info.disk_usage}% | ||
## Stack Trace | ||
{traceback.format_exc()} | ||
## Context | ||
{json.dumps(context, indent=2)} | ||
## Dependencies | ||
{self._get_dependencies_info()} | ||
## Time of Occurrence | ||
{datetime.now().isoformat()} | ||
--- | ||
*This issue was automatically generated by SwarmsIssueReporter* | ||
""" | ||
|
||
def _get_dependencies_info(self) -> str: | ||
"""Get information about installed dependencies.""" | ||
try: | ||
import pkg_resources | ||
|
||
deps = [] | ||
for dist in pkg_resources.working_set: | ||
deps.append(f"- {dist.key} {dist.version}") | ||
return "\n".join(deps) | ||
except: | ||
return "Unable to fetch dependency information" | ||
|
||
# First, add this method to your SwarmsIssueReporter class | ||
def _check_rate_limit(self) -> bool: | ||
"""Check if we're within rate limits.""" | ||
now = datetime.now() | ||
time_diff = (now - self.last_issue_time).total_seconds() | ||
|
||
if ( | ||
len(self.issues_created) >= self.rate_limit | ||
and time_diff < self.rate_period | ||
): | ||
logger.warning("Rate limit exceeded for issue creation") | ||
return False | ||
|
||
# Clean up old issues from tracking | ||
self.issues_created = [ | ||
time | ||
for time in self.issues_created | ||
if (now - time).total_seconds() < self.rate_period | ||
] | ||
|
||
return True | ||
|
||
def report_swarms_issue( | ||
self, | ||
error: Exception, | ||
agent: Optional[Agent] = None, | ||
context: Dict[str, Any] = None, | ||
priority: str = "normal", | ||
) -> Optional[int]: | ||
""" | ||
Report a Swarms-specific issue to GitHub. | ||
Args: | ||
error (Exception): The exception to report | ||
agent (Optional[Agent]): The Swarms agent instance that encountered the error | ||
context (Dict[str, Any]): Additional context about the error | ||
priority (str): Issue priority ("low", "normal", "high", "critical") | ||
Returns: | ||
Optional[int]: Issue number if created successfully | ||
""" | ||
try: | ||
if not self._check_rate_limit(): | ||
logger.warning( | ||
"Skipping issue creation due to rate limit" | ||
) | ||
return None | ||
|
||
# Collect system information | ||
system_info = self._get_system_info() | ||
|
||
# Prepare context with agent information if available | ||
full_context = context or {} | ||
if agent: | ||
full_context.update( | ||
{ | ||
"agent_name": agent.agent_name, | ||
"agent_description": agent.agent_description, | ||
"max_loops": agent.max_loops, | ||
"context_length": agent.context_length, | ||
} | ||
) | ||
|
||
# Create issue title | ||
title = f"[{type(error).__name__}] {str(error)[:100]}" | ||
if agent: | ||
title = f"[Agent: {agent.agent_name}] {title}" | ||
|
||
# Get appropriate labels | ||
labels = self._categorize_error(error, full_context) | ||
labels.append(f"priority:{priority}") | ||
|
||
# Create the issue | ||
url = f"https://api.github.com/repos/{self.REPO_OWNER}/{self.REPO_NAME}/issues" | ||
data = { | ||
"title": title, | ||
"body": self._format_swarms_issue_body( | ||
error, system_info, full_context | ||
), | ||
"labels": labels, | ||
} | ||
|
||
response = requests.post( | ||
url, | ||
headers={ | ||
"Authorization": f"token {self.github_token}" | ||
}, | ||
json=data, | ||
) | ||
response.raise_for_status() | ||
|
||
issue_number = response.json()["number"] | ||
logger.info( | ||
f"Successfully created Swarms issue #{issue_number}" | ||
) | ||
|
||
return issue_number | ||
|
||
except Exception as e: | ||
logger.error(f"Error creating Swarms issue: {str(e)}") | ||
return None | ||
|
||
|
||
# from swarms import Agent | ||
# from swarm_models import OpenAIChat | ||
# from swarms.utils.issue_reporter import SwarmsIssueReporter | ||
# import os | ||
|
||
# Setup the reporter with your GitHub token | ||
reporter = SwarmsIssueReporter( | ||
github_token=os.getenv("GITHUB_API_KEY") | ||
) | ||
|
||
|
||
# Force an error to test the reporter | ||
try: | ||
# This will raise an error since the input isn't valid | ||
# Create an agent that might have issues | ||
model = OpenAIChat(model_name="gpt-4o") | ||
agent = Agent(agent_name="Test-Agent", max_loops=1) | ||
|
||
result = agent.run(None) | ||
|
||
raise ValueError("test") | ||
except Exception as e: | ||
# Report the issue | ||
issue_number = reporter.report_swarms_issue( | ||
error=e, | ||
agent=agent, | ||
context={"task": "test_run"}, | ||
priority="high", | ||
) | ||
print(f"Created issue number: {issue_number}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.