Phase 3: Unified provider tools system for all LLM providers

This commit is contained in:
truetype committed 2026-03-20 17:45:03 +01:00
1 parent 9f3298fa13
commit aba2fc11f4
4 files changed
+735 -19

No files matched your search

+56 -5
View File
@@ -9,6 +9,7 @@ Un agente configurabile basato su DeepAgents che supporta più provider LLM.
- **Configurazione semplice**: Imposta le variabili d'ambiente per scegliere il tuo provider e modello
- **Configurazione persistente**: Gestisci e conserva le configurazioni tramite CLI o TUI
- **Estensibile**: Aggiungi strumenti personalizzati, modifica i prompt e configura i sub-agenti secondo le tue esigenze
- **Strumenti unificati**: Gestisci qualsiasi provider con la stessa interfaccia
- **Strumenti personalizzati**: Include strumenti per gestire Proxmox e Ollama
## Installazione
@@ -117,9 +118,61 @@ python main.py
Puoi quindi interagire con l'agente nel terminale. Digita 'exit' per uscire.
## Strumenti Unificati per Provider
MaxClaw Agent ora fornisce un'interfaccia unificata per gestire qualsiasi provider LLM. Una volta configurato un provider, puoi usare gli stessi comandi per tutti.
### Strumenti Disponibili per Ogni Provider
Per ogni provider configurato (Ollama, OpenAI, OpenRouter), hai accesso a:
- `{provider}_list_models`: Elenca tutti i modelli disponibili
- `{provider}_model_info`: Ottieni informazioni dettagliate su un modello
- `{provider}_test_connection`: Testa la connessione al provider
- `{provider}_get_default_model`: Mostra il modello predefinito
- `{provider}_set_default_model`: Imposta un modello predefinito
- `{provider}_info`: Mostra informazioni sulla configurazione del provider
### Esempi di Utilizzo
```bash
# Con Ollama configurato:
"List available Ollama models"
"Test connection to Ollama"
"What's the default model for Ollama?"
# Con OpenAI configurato:
"List available OpenAI models"
"Test connection to OpenAI"
"Show me OpenAI configuration"
# Con OpenRouter configurato:
"List available OpenRouter models"
"Test connection to OpenRouter"
"Show me OpenRouter configuration"
```
### Configurazione Automatica
Gli strumenti vengono caricati automaticamente in base alle variabili d'ambiente configurate:
```bash
# Se hai configurato Ollama:
export OLLAMA_BASE_URL="http://192.168.0.50:11434"
# → Verranno caricati automaticamente gli strumenti Ollama
# Se hai configurato OpenAI:
export OPENAI_API_KEY="sk-..."
# → Verranno caricati automaticamente gli strumenti OpenAI
# Se hai configurato OpenRouter:
export OPENROUTER_API_KEY="sk-or-..."
# → Verranno caricati automaticamente gli strumenti OpenRouter
```
## Strumenti Personalizzati
MaxClaw Agent include strumenti personalizzati per gestire il tuo ambiente:
MaxClaw Agent include anche strumenti personalizzati per gestire il tuo ambiente:
### Strumenti Proxmox
Configura le variabili d'ambiente per abilitare gli strumenti Proxmox:
@@ -139,10 +192,8 @@ Strumenti disponibili:
- `proxmox_create_snapshot`: Crea uno snapshot
- `proxmox_list_snapshots`: Elenca gli snapshot di una VM
### Strumenti Ollama
Gli strumenti Ollama sono sempre disponibili e utilizzano l'URL configurato in `OLLAMA_BASE_URL`.
Strumenti disponibili:
### Strumenti Ollama (Legacy)
Gli strumenti Ollama specifici sono ancora disponibili per retrocompatibilità:
- `ollama_list_models`: Elenca tutti i modelli disponibili
- `ollama_model_info`: Ottieni informazioni dettagliate su un modello
- `ollama_pull_model`: Scarica un nuovo modello
+28 -12
View File
@@ -2,7 +2,7 @@
"""
MaxClaw Agent - A configurable agent harness using DeepAgents
Supports multiple LLM providers via LangChain's init_chat_model
Enhanced with custom tools for Proxmox and Ollama management
Enhanced with unified provider tools and custom tools
"""
import os
import sys
@@ -14,7 +14,8 @@ from langchain.tools import Tool
# Import custom tools
try:
from tools import get_proxmox_tools, get_ollama_tools
from tools import get_proxmox_tools
from tools.provider_tools import get_provider_tools, get_all_provider_tools
TOOLS_AVAILABLE = True
except ImportError:
TOOLS_AVAILABLE = False
@@ -94,14 +95,13 @@ def get_custom_tools() -> List[Tool]:
except Exception as e:
print(f"⚠️ Could not load Proxmox tools: {e}")
# Add Ollama tools if configured
ollama_url = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
# Add unified provider tools for all configured providers
try:
ollama_tools = get_ollama_tools(base_url=ollama_url)
custom_tools.extend(ollama_tools)
print(f"✅ Added {len(ollama_tools)} Ollama tools")
provider_tools = get_all_provider_tools()
custom_tools.extend(provider_tools)
print(f"✅ Added {len(provider_tools)} provider tools")
except Exception as e:
print(f"⚠️ Could not load Ollama tools: {e}")
print(f"⚠️ Could not load provider tools: {e}")
return custom_tools
@@ -115,8 +115,15 @@ def create_maxclaw_agent(system_prompt: Optional[str] = None):
manage context, and delegate to subagents. You are designed to be configurable
to work with various LLM providers.
You have access to custom tools for managing Proxmox virtual machines and Ollama models.
Use these tools when asked about system administration, virtualization, or AI model management."""
You have access to:
1. Unified provider tools for managing LLM providers (list models, test connections, etc.)
2. Custom tools for managing Proxmox virtual machines
3. Standard DeepAgents tools (planning, filesystem, shell, subagents)
Use these tools when asked about:
- LLM provider management (listing models, testing connections, setting defaults)
- System administration and virtualization
- AI model management and configuration"""
model = load_model_from_config()
@@ -143,9 +150,18 @@ def main():
agent = create_maxclaw_agent()
print("✅ Agent initialized successfully!")
# Simple test interaction
# Show available tools
print("\n🔧 Available tools:")
print(" • Provider tools: list models, test connections, set defaults")
print(" • Proxmox tools: manage VMs, snapshots, resources")
print(" • Standard tools: planning, filesystem, shell, subagents")
print("\n💬 You can now interact with the agent. Type 'exit' to quit.")
print("💡 Try asking about Proxmox VMs or Ollama models!")
print("💡 Try asking:")
print(" - 'List available models for my provider'")
print(" - 'Test connection to Ollama'")
print(" - 'Show me Proxmox VMs'")
print(" - 'What's the default model for OpenAI?'")
while True:
user_input = input("\n👤 You: ").strip()
+21 -2
View File
@@ -1,14 +1,33 @@
"""
MaxClaw Agent Tools Package
Provides custom tools for Proxmox and Ollama management
Provides custom tools for Proxmox, Ollama, and unified provider management
"""
from .proxmox_tools import get_proxmox_tools, ProxmoxTools
from .ollama_tools import get_ollama_tools, OllamaTools
from .provider_tools import (
get_provider_tools,
get_all_provider_tools,
BaseProviderTools,
OllamaProviderTools,
OpenAIProviderTools,
OpenRouterProviderTools
)
__all__ = [
# Proxmox tools
'get_proxmox_tools',
'ProxmoxTools',
# Ollama tools (legacy)
'get_ollama_tools',
'OllamaTools'
'OllamaTools',
# Unified provider tools
'get_provider_tools',
'get_all_provider_tools',
'BaseProviderTools',
'OllamaProviderTools',
'OpenAIProviderTools',
'OpenRouterProviderTools'
]
+630
View File
@@ -0,0 +1,630 @@
"""
Unified Provider Tools for MaxClaw Agent
Provides a consistent interface for managing different LLM providers
"""
import os
import json
import requests
from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Any, Tuple
from langchain.tools import Tool
class BaseProviderTools(ABC):
"""Base class for provider-specific tools"""
def __init__(self, provider_name: str, **kwargs):
"""
Initialize provider tools
Args:
provider_name: Name of the provider (e.g., "ollama", "openai", "openrouter")
**kwargs: Provider-specific configuration
"""
self.provider_name = provider_name
self.config = kwargs
@abstractmethod
def list_models(self) -> str:
"""List available models for this provider"""
pass
@abstractmethod
def get_model_info(self, model_name: str) -> str:
"""Get detailed information about a specific model"""
pass
@abstractmethod
def test_connection(self) -> str:
"""Test connection to the provider"""
pass
@abstractmethod
def get_default_model(self) -> str:
"""Get the default model for this provider"""
pass
@abstractmethod
def set_default_model(self, model_name: str) -> str:
"""Set the default model for this provider"""
pass
@abstractmethod
def get_provider_info(self) -> str:
"""Get information about the provider configuration"""
pass
def get_tools(self) -> List[Tool]:
"""
Get a list of LangChain tools for this provider
Returns:
List of tools
"""
return [
Tool(
name=f"{self.provider_name}_list_models",
func=self.list_models,
description=f"List all available models for {self.provider_name}"
),
Tool(
name=f"{self.provider_name}_model_info",
func=self.get_model_info,
description=f"Get detailed information about a specific {self.provider_name} model"
),
Tool(
name=f"{self.provider_name}_test_connection",
func=self.test_connection,
description=f"Test connection to {self.provider_name} provider"
),
Tool(
name=f"{self.provider_name}_get_default_model",
func=self.get_default_model,
description=f"Get the default model for {self.provider_name}"
),
Tool(
name=f"{self.provider_name}_set_default_model",
func=self.set_default_model,
description=f"Set the default model for {self.provider_name}"
),
Tool(
name=f"{self.provider_name}_info",
func=self.get_provider_info,
description=f"Get information about {self.provider_name} configuration"
)
]
class OllamaProviderTools(BaseProviderTools):
"""Ollama provider tools implementation"""
def __init__(self, base_url: str = "http://localhost:11434", **kwargs):
"""
Initialize Ollama tools
Args:
base_url: Base URL of the Ollama instance
**kwargs: Additional configuration
"""
super().__init__("ollama", base_url=base_url, **kwargs)
self.base_url = base_url.rstrip('/')
self.api_url = f"{self.base_url}/api"
def _make_request(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
"""Make a request to the Ollama API"""
try:
url = f"{self.api_url}/{endpoint}"
if method.upper() == "GET":
response = requests.get(url, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, json=data, timeout=30)
else:
return {"success": False, "error": f"Unsupported method: {method}"}
if response.status_code == 200:
try:
return {"success": True, "data": response.json()}
except json.JSONDecodeError:
return {"success": True, "data": response.text}
else:
return {"success": False, "error": f"HTTP {response.status_code}: {response.text}"}
except requests.exceptions.Timeout:
return {"success": False, "error": "Request timed out"}
except requests.exceptions.ConnectionError:
return {"success": False, "error": f"Connection failed to {self.base_url}"}
except Exception as e:
return {"success": False, "error": str(e)}
def list_models(self) -> str:
"""List all available models on the Ollama instance"""
result = self._make_request("tags")
if not result["success"]:
return f"Error listing models: {result['error']}"
models = result["data"].get("models", [])
if not models:
return "No models found on the Ollama instance."
output = f"Available Ollama Models ({self.base_url}):\n"
output += "=" * 50 + "\n"
for model in models:
name = model.get("name", "Unknown")
size = model.get("size", 0)
# Convert size to human readable format
if size > 1024 * 1024 * 1024:
size_str = f"{size / (1024 * 1024 * 1024):.1f} GB"
elif size > 1024 * 1024:
size_str = f"{size / (1024 * 1024):.1f} MB"
else:
size_str = f"{size / 1024:.1f} KB"
output += f"• {name} ({size_str})\n"
return output
def get_model_info(self, model_name: str) -> str:
"""Get detailed information about a specific model"""
result = self._make_request("show", method="POST", data={"name": model_name})
if not result["success"]:
return f"Error getting model info: {result['error']}"
model_info = result["data"]
output = f"Model Information: {model_name}\n"
output += "=" * 50 + "\n"
if "license" in model_info:
output += f"License: {model_info['license']}\n"
if "parameters" in model_info:
output += f"Parameters: {model_info['parameters']}\n"
if "details" in model_info:
details = model_info["details"]
if "parameter_size" in details:
output += f"Parameter Size: {details['parameter_size']}\n"
if "quantization_level" in details:
output += f"Quantization Level: {details['quantization_level']}\n"
return output
def test_connection(self) -> str:
"""Test connection to Ollama"""
try:
response = requests.get(f"{self.base_url}/", timeout=5)
if response.status_code == 200:
return f"✅ Ollama instance at {self.base_url} is running and responsive."
else:
return f"⚠️ Ollama instance at {self.base_url} returned status {response.status_code}."
except requests.exceptions.ConnectionError:
return f"❌ Cannot connect to Ollama instance at {self.base_url}."
except requests.exceptions.Timeout:
return f"❌ Connection to Ollama instance at {self.base_url} timed out."
except Exception as e:
return f"❌ Error checking Ollama status: {str(e)}"
def get_default_model(self) -> str:
"""Get the default model for Ollama"""
# Ollama doesn't have a built-in default model concept
# We'll return the first available model or a message
result = self._make_request("tags")
if not result["success"]:
return f"Error getting models: {result['error']}"
models = result["data"].get("models", [])
if not models:
return "No models available on Ollama instance."
# Return the first model as default
first_model = models[0].get("name", "Unknown")
return f"Default model (first available): {first_model}"
def set_default_model(self, model_name: str) -> str:
"""Set the default model for Ollama"""
# Ollama doesn't have a built-in default model concept
# We'll just verify the model exists
result = self._make_request("show", method="POST", data={"name": model_name})
if result["success"]:
return f"✅ Model '{model_name}' exists and can be used as default."
else:
return f"❌ Model '{model_name}' not found on Ollama instance."
def get_provider_info(self) -> str:
"""Get information about Ollama configuration"""
output = "Ollama Provider Configuration:\n"
output += "=" * 50 + "\n"
output += f"Base URL: {self.base_url}\n"
output += f"API URL: {self.api_url}\n"
# Test connection
try:
response = requests.get(f"{self.base_url}/", timeout=5)
if response.status_code == 200:
output += "Status: ✅ Connected\n"
else:
output += f"Status: ⚠️ HTTP {response.status_code}\n"
except:
output += "Status: ❌ Not connected\n"
# Get model count
result = self._make_request("tags")
if result["success"]:
models = result["data"].get("models", [])
output += f"Available Models: {len(models)}\n"
else:
output += "Available Models: Unknown\n"
return output
class OpenAIProviderTools(BaseProviderTools):
"""OpenAI provider tools implementation"""
def __init__(self, api_key: str, **kwargs):
"""
Initialize OpenAI tools
Args:
api_key: OpenAI API key
**kwargs: Additional configuration
"""
super().__init__("openai", api_key=api_key, **kwargs)
self.api_key = api_key
self.api_url = "https://api.openai.com/v1"
def _make_request(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
"""Make a request to the OpenAI API"""
try:
url = f"{self.api_url}/{endpoint}"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
if method.upper() == "GET":
response = requests.get(url, headers=headers, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, headers=headers, json=data, timeout=30)
else:
return {"success": False, "error": f"Unsupported method: {method}"}
if response.status_code == 200:
try:
return {"success": True, "data": response.json()}
except json.JSONDecodeError:
return {"success": True, "data": response.text}
else:
return {"success": False, "error": f"HTTP {response.status_code}: {response.text}"}
except requests.exceptions.Timeout:
return {"success": False, "error": "Request timed out"}
except requests.exceptions.ConnectionError:
return {"success": False, "error": "Connection failed to OpenAI API"}
except Exception as e:
return {"success": False, "error": str(e)}
def list_models(self) -> str:
"""List all available models on OpenAI"""
result = self._make_request("models")
if not result["success"]:
return f"Error listing models: {result['error']}"
models = result["data"].get("data", [])
if not models:
return "No models found on OpenAI."
# Filter to show only common models
common_models = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo", "text-embedding-ada-002"]
output = "Available OpenAI Models:\n"
output += "=" * 50 + "\n"
for model in models:
model_id = model.get("id", "")
if model_id in common_models or "gpt" in model_id or "text-embedding" in model_id:
output += f"• {model_id}\n"
return output
def get_model_info(self, model_name: str) -> str:
"""Get detailed information about a specific model"""
result = self._make_request(f"models/{model_name}")
if not result["success"]:
return f"Error getting model info: {result['error']}"
model_info = result["data"]
output = f"Model Information: {model_name}\n"
output += "=" * 50 + "\n"
output += f"ID: {model_info.get('id', 'Unknown')}\n"
output += f"Object: {model_info.get('object', 'Unknown')}\n"
output += f"Created: {model_info.get('created', 'Unknown')}\n"
output += f"Owned by: {model_info.get('owned_by', 'Unknown')}\n"
return output
def test_connection(self) -> str:
"""Test connection to OpenAI"""
try:
# Try to list models as a connection test
result = self._make_request("models")
if result["success"]:
return "✅ OpenAI API connection successful."
else:
return f"❌ OpenAI API connection failed: {result['error']}"
except Exception as e:
return f"❌ Error connecting to OpenAI: {str(e)}"
def get_default_model(self) -> str:
"""Get the default model for OpenAI"""
# OpenAI doesn't have a built-in default model concept
# Return a common default
return "Default model: gpt-4o (recommended for most tasks)"
def set_default_model(self, model_name: str) -> str:
"""Set the default model for OpenAI"""
# OpenAI doesn't have a built-in default model concept
# Just verify the model exists
result = self._make_request(f"models/{model_name}")
if result["success"]:
return f"✅ Model '{model_name}' exists and can be used as default."
else:
return f"❌ Model '{model_name}' not found on OpenAI."
def get_provider_info(self) -> str:
"""Get information about OpenAI configuration"""
output = "OpenAI Provider Configuration:\n"
output += "=" * 50 + "\n"
output += f"API URL: {self.api_url}\n"
output += f"API Key: {'Set' if self.api_key else 'Not set'}\n"
# Test connection
try:
result = self._make_request("models")
if result["success"]:
output += "Status: ✅ Connected\n"
else:
output += f"Status: ❌ Connection failed\n"
except:
output += "Status: ❌ Not connected\n"
return output
class OpenRouterProviderTools(BaseProviderTools):
"""OpenRouter provider tools implementation"""
def __init__(self, api_key: str, base_url: str = "https://openrouter.ai/api/v1", **kwargs):
"""
Initialize OpenRouter tools
Args:
api_key: OpenRouter API key
base_url: Base URL for OpenRouter API
**kwargs: Additional configuration
"""
super().__init__("openrouter", api_key=api_key, base_url=base_url, **kwargs)
self.api_key = api_key
self.base_url = base_url.rstrip('/')
self.api_url = f"{self.base_url}"
def _make_request(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict[str, Any]:
"""Make a request to the OpenRouter API"""
try:
url = f"{self.api_url}/{endpoint}"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
if method.upper() == "GET":
response = requests.get(url, headers=headers, timeout=10)
elif method.upper() == "POST":
response = requests.post(url, headers=headers, json=data, timeout=30)
else:
return {"success": False, "error": f"Unsupported method: {method}"}
if response.status_code == 200:
try:
return {"success": True, "data": response.json()}
except json.JSONDecodeError:
return {"success": True, "data": response.text}
else:
return {"success": False, "error": f"HTTP {response.status_code}: {response.text}"}
except requests.exceptions.Timeout:
return {"success": False, "error": "Request timed out"}
except requests.exceptions.ConnectionError:
return {"success": False, "error": f"Connection failed to {self.base_url}"}
except Exception as e:
return {"success": False, "error": str(e)}
def list_models(self) -> str:
"""List all available models on OpenRouter"""
result = self._make_request("models")
if not result["success"]:
return f"Error listing models: {result['error']}"
models = result["data"].get("data", [])
if not models:
return "No models found on OpenRouter."
output = "Available OpenRouter Models:\n"
output += "=" * 50 + "\n"
for model in models[:20]: # Limit to first 20 models
model_id = model.get("id", "")
name = model.get("name", "")
description = model.get("description", "")
output += f"• {model_id}\n"
if name:
output += f" Name: {name}\n"
if description:
output += f" Description: {description[:100]}...\n"
output += "\n"
return output
def get_model_info(self, model_name: str) -> str:
"""Get detailed information about a specific model"""
result = self._make_request(f"models/{model_name}")
if not result["success"]:
return f"Error getting model info: {result['error']}"
model_info = result["data"]
output = f"Model Information: {model_name}\n"
output += "=" * 50 + "\n"
output += f"ID: {model_info.get('id', 'Unknown')}\n"
output += f"Name: {model_info.get('name', 'Unknown')}\n"
output += f"Description: {model_info.get('description', 'Unknown')}\n"
if "pricing" in model_info:
pricing = model_info["pricing"]
output += f"Pricing: {pricing}\n"
return output
def test_connection(self) -> str:
"""Test connection to OpenRouter"""
try:
# Try to list models as a connection test
result = self._make_request("models")
if result["success"]:
return "✅ OpenRouter API connection successful."
else:
return f"❌ OpenRouter API connection failed: {result['error']}"
except Exception as e:
return f"❌ Error connecting to OpenRouter: {str(e)}"
def get_default_model(self) -> str:
"""Get the default model for OpenRouter"""
# OpenRouter doesn't have a built-in default model concept
# Return a common default
return "Default model: anthropic/claude-3.5-sonnet (recommended for most tasks)"
def set_default_model(self, model_name: str) -> str:
"""Set the default model for OpenRouter"""
# OpenRouter doesn't have a built-in default model concept
# Just verify the model exists
result = self._make_request(f"models/{model_name}")
if result["success"]:
return f"✅ Model '{model_name}' exists and can be used as default."
else:
return f"❌ Model '{model_name}' not found on OpenRouter."
def get_provider_info(self) -> str:
"""Get information about OpenRouter configuration"""
output = "OpenRouter Provider Configuration:\n"
output += "=" * 50 + "\n"
output += f"API URL: {self.api_url}\n"
output += f"API Key: {'Set' if self.api_key else 'Not set'}\n"
# Test connection
try:
result = self._make_request("models")
if result["success"]:
output += "Status: ✅ Connected\n"
else:
output += f"Status: ❌ Connection failed\n"
except:
output += "Status: ❌ Not connected\n"
return output
def get_provider_tools(provider: str, **kwargs) -> List[Tool]:
"""
Get tools for a specific provider
Args:
provider: Provider name ("ollama", "openai", "openrouter")
**kwargs: Provider-specific configuration
Returns:
List of tools for the provider
"""
provider = provider.lower()
if provider == "ollama":
base_url = kwargs.get("base_url", os.getenv("OLLAMA_BASE_URL", "http://localhost:11434"))
tools = OllamaProviderTools(base_url=base_url)
return tools.get_tools()
elif provider == "openai":
api_key = kwargs.get("api_key", os.getenv("OPENAI_API_KEY"))
if not api_key:
print("Warning: OpenAI API key not provided")
return []
tools = OpenAIProviderTools(api_key=api_key)
return tools.get_tools()
elif provider == "openrouter":
api_key = kwargs.get("api_key", os.getenv("OPENROUTER_API_KEY"))
base_url = kwargs.get("base_url", os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1"))
if not api_key:
print("Warning: OpenRouter API key not provided")
return []
tools = OpenRouterProviderTools(api_key=api_key, base_url=base_url)
return tools.get_tools()
else:
print(f"Warning: Unknown provider '{provider}'")
return []
def get_all_provider_tools() -> List[Tool]:
"""
Get tools for all configured providers
Returns:
List of tools for all configured providers
"""
all_tools = []
# Check for Ollama configuration
ollama_url = os.getenv("OLLAMA_BASE_URL")
if ollama_url:
try:
ollama_tools = get_provider_tools("ollama", base_url=ollama_url)
all_tools.extend(ollama_tools)
print(f"✅ Added {len(ollama_tools)} Ollama tools")
except Exception as e:
print(f"⚠️ Could not load Ollama tools: {e}")
# Check for OpenAI configuration
openai_key = os.getenv("OPENAI_API_KEY")
if openai_key:
try:
openai_tools = get_provider_tools("openai", api_key=openai_key)
all_tools.extend(openai_tools)
print(f"✅ Added {len(openai_tools)} OpenAI tools")
except Exception as e:
print(f"⚠️ Could not load OpenAI tools: {e}")
# Check for OpenRouter configuration
openrouter_key = os.getenv("OPENROUTER_API_KEY")
if openrouter_key:
try:
openrouter_tools = get_provider_tools("openrouter", api_key=openrouter_key)
all_tools.extend(openrouter_tools)
print(f"✅ Added {len(openrouter_tools)} OpenRouter tools")
except Exception as e:
print(f"⚠️ Could not load OpenRouter tools: {e}")
return all_tools