Spaces:
Sleeping
Sleeping
| """ | |
| LLM Connector - Multi-Provider Intelligence | |
| ============================================= | |
| Connects to available LLM APIs for content generation and reasoning. | |
| Falls back gracefully between providers. | |
| """ | |
| import json | |
| import logging | |
| import urllib.request | |
| import urllib.error | |
| from typing import Optional | |
| logger = logging.getLogger("openclaw.llm") | |
| class LLMConnector: | |
| """Multi-provider LLM connector.""" | |
| PROVIDERS = { | |
| "groq": { | |
| "url": "https://api.groq.com/openai/v1/chat/completions", | |
| "model": "llama-3.3-70b-versatile", | |
| "header_key": "Authorization", | |
| "header_prefix": "Bearer ", | |
| }, | |
| "gemini": { | |
| "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent", | |
| "model": "gemini-2.0-flash", | |
| "header_key": "x-goog-api-key", | |
| "header_prefix": "", | |
| }, | |
| "nvidia": { | |
| "url": "https://integrate.api.nvidia.com/v1/chat/completions", | |
| "model": "meta/llama-3.1-70b-instruct", | |
| "header_key": "Authorization", | |
| "header_prefix": "Bearer ", | |
| }, | |
| } | |
| def __init__(self, provider: str, api_key: str): | |
| self.provider = provider | |
| self.api_key = api_key | |
| self.config = self.PROVIDERS.get(provider, {}) | |
| def generate(self, prompt: str, system: str = "", max_tokens: int = 1024, temperature: float = 0.7) -> Optional[str]: | |
| """Generate text using the configured LLM.""" | |
| if not self.api_key or not self.config: | |
| logger.warning(f"LLM provider '{self.provider}' not configured") | |
| return None | |
| try: | |
| if self.provider == "gemini": | |
| return self._generate_gemini(prompt, system, max_tokens, temperature) | |
| else: | |
| return self._generate_openai_compat(prompt, system, max_tokens, temperature) | |
| except Exception as e: | |
| logger.error(f"LLM generation failed ({self.provider}): {e}") | |
| return None | |
| def _generate_openai_compat(self, prompt: str, system: str, max_tokens: int, temperature: float) -> Optional[str]: | |
| """Generate using OpenAI-compatible API (Groq, NVIDIA).""" | |
| messages = [] | |
| if system: | |
| messages.append({"role": "system", "content": system}) | |
| messages.append({"role": "user", "content": prompt}) | |
| data = json.dumps({ | |
| "model": self.config["model"], | |
| "messages": messages, | |
| "max_tokens": max_tokens, | |
| "temperature": temperature, | |
| }).encode() | |
| headers = { | |
| "Content-Type": "application/json", | |
| self.config["header_key"]: f"{self.config['header_prefix']}{self.api_key}", | |
| } | |
| req = urllib.request.Request(self.config["url"], data=data, headers=headers, method="POST") | |
| with urllib.request.urlopen(req, timeout=60) as resp: | |
| result = json.loads(resp.read().decode()) | |
| return result["choices"][0]["message"]["content"] | |
| def _generate_gemini(self, prompt: str, system: str, max_tokens: int, temperature: float) -> Optional[str]: | |
| """Generate using Google Gemini API.""" | |
| url = f"{self.config['url']}?key={self.api_key}" | |
| parts = [] | |
| if system: | |
| parts.append({"text": f"System: {system}\n\nUser: {prompt}"}) | |
| else: | |
| parts.append({"text": prompt}) | |
| data = json.dumps({ | |
| "contents": [{"parts": parts}], | |
| "generationConfig": { | |
| "maxOutputTokens": max_tokens, | |
| "temperature": temperature, | |
| } | |
| }).encode() | |
| headers = {"Content-Type": "application/json"} | |
| req = urllib.request.Request(url, data=data, headers=headers, method="POST") | |
| with urllib.request.urlopen(req, timeout=60) as resp: | |
| result = json.loads(resp.read().decode()) | |
| return result["candidates"][0]["content"]["parts"][0]["text"] | |
| class MultiLLM: | |
| """Try multiple LLM providers in order, with key rotation.""" | |
| def __init__(self, providers: dict[str, str]): | |
| """providers: dict of {provider_name: api_key} or {provider_name: 'key1,key2,key3'}""" | |
| self.connectors = [] | |
| # Priority order: nvidia (working), groq (fast), gemini (free) | |
| for name in ["nvidia", "groq", "gemini"]: | |
| if name in providers and providers[name]: | |
| # Support comma-separated multiple keys | |
| keys = [k.strip() for k in providers[name].split(",") if k.strip()] | |
| for key in keys: | |
| self.connectors.append(LLMConnector(name, key)) | |
| def generate(self, prompt: str, system: str = "", max_tokens: int = 1024, temperature: float = 0.7) -> str: | |
| """Try each provider until one works.""" | |
| for connector in self.connectors: | |
| try: | |
| result = connector.generate(prompt, system, max_tokens, temperature) | |
| if result: | |
| logger.info(f"LLM response from {connector.provider}") | |
| return result | |
| except Exception as e: | |
| logger.warning(f"Provider {connector.provider} failed: {e}") | |
| continue | |
| logger.warning("All LLM providers failed, using template") | |
| return "" | |
| def available(self) -> bool: | |
| return len(self.connectors) > 0 | |