Skip to main content
Glama
provider_selector_widget.py19.7 kB
"""Shared Provider Selector Widget - Reusable provider selection component""" from PySide2 import QtCore, QtWidgets class ProviderSelectorWidget(QtWidgets.QWidget): """Reusable widget for provider selection with status indicators.""" # Signals provider_changed = QtCore.Signal(str, str) # provider_name, model_name refresh_requested = QtCore.Signal() def __init__(self, parent=None): super(ProviderSelectorWidget, self).__init__(parent) # Services self.provider_service = None self.config_manager = None # Current selection self.current_provider = None self.current_model = None # Available providers and their models self.available_providers = {} self._setup_ui() self._setup_services() def _setup_ui(self): """Setup the user interface.""" layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(10) # Provider label layout.addWidget(QtWidgets.QLabel("Provider:")) # Provider selection dropdown self.provider_combo = QtWidgets.QComboBox() self.provider_combo.setMinimumWidth(120) self.provider_combo.setToolTip("Select AI provider") self.provider_combo.currentTextChanged.connect( self._on_provider_selection_changed ) layout.addWidget(self.provider_combo) # Model label layout.addWidget(QtWidgets.QLabel("Model:")) # Model selection dropdown self.model_combo = QtWidgets.QComboBox() self.model_combo.setMinimumWidth(150) self.model_combo.setToolTip("Select model for current provider") self.model_combo.currentTextChanged.connect(self._on_model_selection_changed) layout.addWidget(self.model_combo) # Status indicator self.status_label = QtWidgets.QLabel("●") self.status_label.setFixedSize(20, 20) self.status_label.setAlignment(QtCore.Qt.AlignCenter) self.status_label.setToolTip("Provider connection status") self._update_status_indicator("unknown", "Checking...") layout.addWidget(self.status_label) # Refresh button self.refresh_btn = QtWidgets.QPushButton("⟳") self.refresh_btn.setFixedSize(25, 25) self.refresh_btn.setToolTip("Refresh providers") self.refresh_btn.clicked.connect(self._on_refresh_clicked) layout.addWidget(self.refresh_btn) layout.addStretch() def _setup_services(self): """Setup service connections.""" # Services will be set via set_provider_service method pass def showEvent(self, event): """Handle show event to refresh providers when widget becomes visible.""" super().showEvent(event) # Refresh providers when shown for the first time or if empty if not self.available_providers: print("ProviderSelector: Widget shown, refreshing providers...") self._refresh_providers() def set_provider_service(self, provider_service): """Set the provider service instance.""" self.provider_service = provider_service # Connect to provider service signals if self.provider_service: # Connect to provider service signals if hasattr(self.provider_service, "provider_status_changed"): self.provider_service.provider_status_changed.connect( self._on_provider_status_changed ) if hasattr(self.provider_service, "providers_updated"): self.provider_service.providers_updated.connect( self._on_providers_updated ) if hasattr(self.provider_service, "provider_selection_changed"): self.provider_service.provider_selection_changed.connect( self._on_external_provider_selection_changed ) # Load current providers self._refresh_providers() # Restore current selection if available current_selection = self.provider_service.get_current_provider_selection() if current_selection["provider"]: self.set_current_selection( current_selection["provider"], current_selection["model"] ) def set_config_manager(self, config_manager): """Set the config manager instance.""" self.config_manager = config_manager # Refresh providers when config manager becomes available if config_manager and not self.available_providers: self._refresh_providers() def refresh_on_show(self): """Refresh providers when widget becomes visible (e.g., tab activation).""" if not self.available_providers: print("ProviderSelector: Refreshing on show") self._refresh_providers() def _refresh_providers(self): """Refresh the list of available providers.""" print("ProviderSelector: Refreshing providers...") if not self.provider_service: print("ProviderSelector: No provider service available") self._update_status_indicator("error", "Provider service not available") # Fall back to default providers if no service self._load_fallback_providers() return try: # Get active providers from service active_providers = self.provider_service.get_active_providers() all_providers = self.provider_service.get_all_providers() print(f"ProviderSelector: Active providers: {list(active_providers.keys()) if active_providers else 'None'}") print(f"ProviderSelector: All providers: {list(all_providers.keys()) if all_providers else 'None'}") # Update available providers dict self.available_providers = {} # Clear dropdowns self.provider_combo.clear() self.model_combo.clear() if not all_providers: print("ProviderSelector: No providers from service, using fallback") self._load_fallback_providers() return # Add providers to dropdown current_provider_found = False for provider_name, provider_info in all_providers.items(): self.provider_combo.addItem(provider_name) # Get available models for this provider models = self._get_provider_models(provider_name, provider_info) self.available_providers[provider_name] = { "models": models, "status": ( "active" if provider_name in active_providers else "inactive" ), "info": provider_info, } # Check if this was the previously selected provider if provider_name == self.current_provider: current_provider_found = True # Restore previous selection or select default if current_provider_found and self.current_provider: index = self.provider_combo.findText(self.current_provider) if index >= 0: self.provider_combo.setCurrentIndex(index) elif self.provider_combo.count() > 0: # Select first provider as default self.provider_combo.setCurrentIndex(0) # Update status active_count = len(active_providers) total_count = len(all_providers) if active_count > 0: self._update_status_indicator( "connected", f"{active_count}/{total_count} providers active" ) else: self._update_status_indicator("warning", "No active providers") except Exception as e: print(f"ProviderSelector: Error refreshing providers: {e}") self._update_status_indicator("error", f"Error: {str(e)}") # Fall back to default providers on error self._load_fallback_providers() def _load_fallback_providers(self): """Load fallback default providers when service is unavailable.""" print("ProviderSelector: Loading fallback providers") # Clear dropdowns self.provider_combo.clear() self.model_combo.clear() # Default providers that should always be available fallback_providers = { "Anthropic": { "type": "anthropic", "models": ["claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022"], "status": "inactive" }, "OpenAI": { "type": "openai", "models": ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"], "status": "inactive" }, "Google": { "type": "google", "models": ["gemini-1.5-pro", "gemini-1.5-flash"], "status": "inactive" }, "Google Vertex AI": { "type": "vertexai", "models": ["gemini-1.5-pro", "gemini-1.5-flash", "text-bison"], "status": "inactive" }, "OpenRouter": { "type": "openrouter", "models": ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"], "status": "inactive" } } self.available_providers = {} for provider_name, provider_data in fallback_providers.items(): self.provider_combo.addItem(provider_name) self.available_providers[provider_name] = { "models": provider_data["models"], "status": provider_data["status"], "info": {"type": provider_data["type"]} } # Select first provider as default if self.provider_combo.count() > 0: self.provider_combo.setCurrentIndex(0) self._update_status_indicator("warning", "Using default providers - configure in Providers tab") def _get_provider_models(self, provider_name, provider_info): """Get available models for a provider.""" try: # Try to get models from provider service first if self.provider_service and hasattr( self.provider_service, "get_provider_models" ): models = self.provider_service.get_provider_models(provider_name) if models: return models # Try to get models from provider service (alternative method) if hasattr(self.provider_service, "get_provider_models"): models = self.provider_service.get_provider_models(provider_name) if models: return models # Fallback to default models based on provider type provider_type = provider_info.get("type", "").lower() if not provider_type: # Try to infer from provider name provider_type = provider_name.lower() default_models = { "anthropic": [ "claude-4-20241120", "claude-3-5-sonnet-20241022", "claude-3-5-haiku-20241022", ], "openai": ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-3.5-turbo"], "google": ["gemini-1.5-pro", "gemini-1.5-flash", "gemini-pro"], "vertexai": [ "gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.0-pro", "text-bison", "code-bison" ], "openrouter": [ "anthropic/claude-3.5-sonnet", "openai/gpt-4o", "google/gemini-pro", ], } return default_models.get(provider_type, ["default-model"]) except Exception as e: print(f"ProviderSelector: Error getting models for {provider_name}: {e}") return ["default-model"] def _on_provider_selection_changed(self, provider_name): """Handle provider selection change.""" if not provider_name or provider_name == "No providers configured": return self.current_provider = provider_name # Update model dropdown self._update_model_dropdown(provider_name) # Update status for selected provider if provider_name in self.available_providers: provider_data = self.available_providers[provider_name] status = provider_data["status"] if status == "active": self._update_status_indicator("connected", f"{provider_name} active") else: self._update_status_indicator("warning", f"{provider_name} inactive") def _update_model_dropdown(self, provider_name): """Update the model dropdown for the selected provider.""" self.model_combo.clear() if provider_name not in self.available_providers: self.model_combo.addItem("No models available") return models = self.available_providers[provider_name]["models"] self.model_combo.addItems(models) # Try to restore previous model selection for this provider if self.current_provider == provider_name and self.current_model: index = self.model_combo.findText(self.current_model) if index >= 0: self.model_combo.setCurrentIndex(index) # If no previous selection, try to get default from config if self.config_manager and self.model_combo.currentText() == models[0]: try: provider_config = self.config_manager.get_provider_config(provider_name) if provider_config and "model" in provider_config: default_model = provider_config["model"] index = self.model_combo.findText(default_model) if index >= 0: self.model_combo.setCurrentIndex(index) except Exception as e: print(f"ProviderSelector: Error loading default model: {e}") def _on_model_selection_changed(self, model_name): """Handle model selection change.""" if not model_name or model_name == "No models available": return self.current_model = model_name # Save the model selection via provider service if self.provider_service and self.current_provider: try: if hasattr(self.provider_service, "update_provider_model"): success = self.provider_service.update_provider_model( self.current_provider, model_name ) if success: print( f"ProviderSelector: Updated model {model_name} for provider {self.current_provider}" ) else: print( "ProviderSelector: Failed to update model via provider service" ) else: print( "ProviderSelector: Provider service doesn't support model updates" ) except Exception as e: print( f"ProviderSelector: Error updating model via provider service: {e}" ) # Fallback: Save to config manager directly if self.config_manager and self.current_provider: try: provider_config = self.config_manager.get_provider_config( self.current_provider ) if not provider_config: provider_config = {} provider_config["model"] = model_name self.config_manager.set_provider_config( self.current_provider, provider_config ) print( f"ProviderSelector: Saved model {model_name} for provider {self.current_provider}" ) except Exception as e: print(f"ProviderSelector: Error saving model selection: {e}") # Emit signal for other components if self.current_provider: self.provider_changed.emit(self.current_provider, model_name) def _on_refresh_clicked(self): """Handle refresh button click.""" self._refresh_providers() self.refresh_requested.emit() def _on_provider_status_changed(self, provider_name, status, message): """Handle provider status change signal.""" # Update status if this is the currently selected provider if provider_name == self.current_provider: self._update_status_indicator(status, f"{provider_name}: {message}") # Update available providers data if provider_name in self.available_providers: self.available_providers[provider_name]["status"] = status def _on_providers_updated(self): """Handle providers list update signal.""" self._refresh_providers() def _on_external_provider_selection_changed(self, provider_name, model_name): """Handle provider selection change from external signal.""" if provider_name == self.current_provider and model_name == self.current_model: return # No change # Update provider selection self.set_current_selection(provider_name, model_name) def _update_status_indicator(self, status, tooltip): """Update the status indicator.""" status_colors = { "connected": "#4CAF50", # Green "active": "#4CAF50", # Green "warning": "#FF9800", # Orange "inactive": "#FF9800", # Orange "error": "#f44336", # Red "unknown": "#9E9E9E", # Gray } color = status_colors.get(status, "#9E9E9E") self.status_label.setStyleSheet(f"color: {color}; font-weight: bold;") self.status_label.setToolTip(tooltip) def get_current_selection(self): """Get the current provider and model selection.""" return {"provider": self.current_provider, "model": self.current_model} def set_current_selection(self, provider_name, model_name=None): """Set the current provider and model selection.""" # Set provider if provider_name: index = self.provider_combo.findText(provider_name) if index >= 0: self.provider_combo.setCurrentIndex(index) self.current_provider = provider_name # Update model dropdown self._update_model_dropdown(provider_name) # Set model if specified if model_name: model_index = self.model_combo.findText(model_name) if model_index >= 0: self.model_combo.setCurrentIndex(model_index) self.current_model = model_name def is_provider_available(self): """Check if a provider is currently available.""" return ( self.current_provider is not None and self.current_provider in self.available_providers and self.available_providers[self.current_provider]["status"] == "active" )

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/mcp-freecad'

If you have feedback or need assistance with the MCP directory API, please join our Discord server