""" GitHub Technology Trends Search Tool """ from .base_tool import BaseTool import requests import json from typing import Dict, List, Optional from datetime import datetime, timedelta class GitHubSearchTool(BaseTool): """Search GitHub for technology trends and adoption patterns""" def __init__(self): super().__init__("GitHub", "Search GitHub for technology adoption and development trends") self.base_url = "https://api.github.com" self.rate_limit_delay = 2.0 # GitHub has rate limits def search(self, technology: str, max_results: int = 5, **kwargs) -> str: """Search GitHub for technology trends and adoption""" self.rate_limit() try: # Search repositories repos_data = self._search_repositories(technology, max_results) if not repos_data or not repos_data.get('items'): return f"**GitHub Technology Research for: {technology}**\n\nNo relevant repositories found." result = f"**GitHub Technology Trends for: {technology}**\n\n" # Repository analysis result += self._format_repository_data(repos_data['items'], technology) # Trend analysis result += self._analyze_technology_trends(repos_data, technology) # Recent activity analysis result += self._analyze_recent_activity(repos_data['items'], technology) return result except requests.RequestException as e: return self.format_error_response(technology, f"Network error accessing GitHub: {str(e)}") except Exception as e: return self.format_error_response(technology, str(e)) def _search_repositories(self, technology: str, max_results: int) -> Optional[Dict]: """Search GitHub repositories for the technology""" repos_url = f"{self.base_url}/search/repositories" # Create comprehensive search query search_query = f'{technology} language:python OR language:javascript OR language:typescript OR language:go OR language:rust' params = { 'q': search_query, 'sort': 'stars', 'order': 'desc', 'per_page': max_results } response = requests.get(repos_url, params=params, timeout=15) response.raise_for_status() return response.json() def _format_repository_data(self, repositories: List[Dict], technology: str) -> str: """Format repository information""" result = f"**Top {len(repositories)} Repositories:**\n" for i, repo in enumerate(repositories, 1): stars = repo.get('stargazers_count', 0) forks = repo.get('forks_count', 0) language = repo.get('language', 'Unknown') updated = repo.get('updated_at', '')[:10] # YYYY-MM-DD result += f"**{i}. {repo['name']}** ({stars:,} ⭐, {forks:,} 🍴)\n" result += f" Language: {language} | Updated: {updated}\n" description = repo.get('description', 'No description') if description and len(description) > 100: description = description[:100] + "..." result += f" Description: {description}\n" result += f" URL: {repo.get('html_url', 'N/A')}\n\n" return result def _analyze_technology_trends(self, repos_data: Dict, technology: str) -> str: """Analyze technology adoption trends""" total_count = repos_data.get('total_count', 0) items = repos_data.get('items', []) if not items: return "" # Calculate adoption metrics total_stars = sum(repo.get('stargazers_count', 0) for repo in items) total_forks = sum(repo.get('forks_count', 0) for repo in items) avg_stars = total_stars / len(items) if items else 0 # Determine adoption level if total_count > 50000: adoption_level = "Very High" elif total_count > 10000: adoption_level = "High" elif total_count > 1000: adoption_level = "Moderate" elif total_count > 100: adoption_level = "Emerging" else: adoption_level = "Niche" # Language analysis languages = {} for repo in items: lang = repo.get('language') if lang: languages[lang] = languages.get(lang, 0) + 1 result = f"**Technology Adoption Analysis:**\n" result += f"• Total repositories: {total_count:,}\n" result += f"• Adoption level: {adoption_level}\n" result += f"• Average stars (top repos): {avg_stars:,.0f}\n" result += f"• Total community engagement: {total_stars:,} stars, {total_forks:,} forks\n" if languages: top_languages = sorted(languages.items(), key=lambda x: x[1], reverse=True)[:3] result += f"• Popular languages: {', '.join(f'{lang} ({count})' for lang, count in top_languages)}\n" result += "\n" return result def _analyze_recent_activity(self, repositories: List[Dict], technology: str) -> str: """Analyze recent development activity""" if not repositories: return "" # Check update recency current_date = datetime.now() recent_updates = 0 very_recent_updates = 0 for repo in repositories: updated_str = repo.get('updated_at', '') if updated_str: try: updated_date = datetime.fromisoformat(updated_str.replace('Z', '+00:00')) days_ago = (current_date - updated_date.replace(tzinfo=None)).days if days_ago <= 30: very_recent_updates += 1 if days_ago <= 90: recent_updates += 1 except: pass result = f"**Development Activity:**\n" result += f"• Recently updated (30 days): {very_recent_updates}/{len(repositories)} repositories\n" result += f"• Active projects (90 days): {recent_updates}/{len(repositories)} repositories\n" # Activity assessment if very_recent_updates / len(repositories) > 0.7: activity_level = "Very Active" elif recent_updates / len(repositories) > 0.5: activity_level = "Active" elif recent_updates / len(repositories) > 0.3: activity_level = "Moderate" else: activity_level = "Low" result += f"• Overall activity level: {activity_level}\n" result += f"• Community health: {'Strong' if activity_level in ['Very Active', 'Active'] else 'Moderate'} developer engagement\n\n" return result def should_use_for_query(self, query: str) -> bool: """GitHub is good for technology, framework, and development-related queries""" tech_indicators = [ 'technology', 'framework', 'library', 'software', 'programming', 'development', 'developer', 'code', 'github', 'open source', 'javascript', 'python', 'react', 'nodejs', 'django', 'flask', 'vue', 'angular', 'typescript', 'rust', 'go', 'kotlin', 'adoption', 'popular', 'trending', 'tools', 'stack' ] query_lower = query.lower() return any(indicator in query_lower for indicator in tech_indicators) def extract_key_info(self, text: str) -> dict: """Extract key information from GitHub results""" base_info = super().extract_key_info(text) if text: # Look for GitHub-specific patterns base_info.update({ 'repo_count': text.count('repositories'), 'has_stars': '⭐' in text, 'has_forks': '🍴' in text, 'has_recent_activity': any(year in text for year in ['2024', '2025']), 'adoption_mentioned': any(term in text.lower() for term in ['adoption', 'popular', 'trending']), 'languages_analyzed': 'Popular languages:' in text }) return base_info