Isaac Quarenta
fix: openrouter nemotron→gemini-2.0, mistral timeout 20s+connect, groq payload truncate, factual question gate
628112f Download modules/api_training_bridge.py from akra35567/Akiragpu: direct link, hf CLI and curl.
- Browser
- Download file 25.3 kB
-
https://huggingface.co/spaces/akra35567/Akiragpu/resolve/main/modules/api_training_bridge.py
- Command line
-
hf download hf://spaces/akra35567/Akiragpu/modules/api_training_bridge.py
-
curl -L -o api_training_bridge.py https://huggingface.co/spaces/akra35567/Akiragpu/resolve/main/modules/api_training_bridge.py
25.3 kB
| # api_training_bridge.py | |
| # ================================================================ | |
| # PONTE DE TREINAMENTO VIA API - AKIRA IA V21 | |
| # ================================================================ | |
| # Conecta os provedores API (Cerebras, OpenRouter, Mistral, etc.) | |
| # ao sistema de treinamento, permitindo que todos os 5 níveis | |
| # usem APIs para distilar conhecimento. | |
| # ================================================================ | |
| import os | |
| import time | |
| import json | |
| from typing import Optional, List, Dict, Any, Tuple | |
| from loguru import logger | |
| try: | |
| from . import config | |
| except (ImportError, SystemError): | |
| try: | |
| import modules.config as config | |
| except ImportError: | |
| config = None | |
| class APITrainingBridge: | |
| """ | |
| Ponte entre provedores API e sistema de treinamento. | |
| Cria clientes API leves para uso exclusivo de treinamento, | |
| reutilizando as mesmas chaves de API e rotações do LLMManager. | |
| Uso: | |
| bridge = APITrainingBridge() | |
| resposta, provider = bridge.generate("Analise a emoção desta mensagem: ...") | |
| """ | |
| def __init__(self): | |
| self._clients = {} | |
| self._providers = [] | |
| self._rotation = {} | |
| self._initialized = False | |
| self._stats = {} | |
| self._init_clients() | |
| def _init_clients(self): | |
| """Inicializa clientes API leves para treinamento.""" | |
| if self._initialized: | |
| return | |
| # 1. Cerebras (OpenAI SDK compat) | |
| self._setup_cerebras() | |
| # 2. OpenRouter (OpenAI SDK compat) | |
| self._setup_openrouter() | |
| # 3. Mistral (requests direto) | |
| self._setup_mistral() | |
| # 4. Groq (Groq SDK) | |
| self._setup_groq() | |
| # 5. Gemini (Google GenAI) | |
| self._setup_gemini() | |
| # 6. Cohere | |
| self._setup_cohere() | |
| # 7. HF Inference | |
| self._setup_hf_inference() | |
| self._initialized = True | |
| if self._providers: | |
| logger.info( | |
| f"🔗 [API-TRAINING] Bridge inicializado: " | |
| f"{', '.join(self._providers)} ({len(self._providers)} providers)" | |
| ) | |
| else: | |
| logger.warning("🔗 [API-TRAINING] Nenhum provider disponível para treinamento") | |
| def _setup_cerebras(self): | |
| """Cerebras via rotação multi-conta.""" | |
| try: | |
| from .cerebras_rotation import get_cerebras_rotation | |
| rotation = get_cerebras_rotation() | |
| if rotation.account_names: | |
| import openai | |
| key = rotation.get_current_api_key() | |
| if key: | |
| client = openai.OpenAI( | |
| api_key=key, | |
| base_url="https://api.cerebras.ai/v1", | |
| timeout=30.0, | |
| max_retries=0, | |
| ) | |
| self._clients["cerebras"] = client | |
| self._rotation["cerebras"] = rotation | |
| self._providers.append("cerebras") | |
| logger.debug(f"✅ [API-TRAINING] Cerebras OK ({rotation.get_current_account_name()})") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] Cerebras skip: {e}") | |
| def _setup_openrouter(self): | |
| """OpenRouter via rotação multi-conta.""" | |
| try: | |
| from .openrouter_rotation import get_openrouter_rotation | |
| rotation = get_openrouter_rotation() | |
| if rotation.api_keys: | |
| import openai | |
| key = rotation.get_current_key() | |
| if key: | |
| client = openai.OpenAI( | |
| api_key=key, | |
| base_url="https://openrouter.ai/api/v1", | |
| timeout=30.0, | |
| max_retries=0, | |
| ) | |
| self._clients["openrouter"] = client | |
| self._rotation["openrouter"] = rotation | |
| self._providers.append("openrouter") | |
| logger.debug(f"✅ [API-TRAINING] OpenRouter OK ({rotation.get_current_account_name()})") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] OpenRouter skip: {e}") | |
| def _setup_mistral(self): | |
| """Mistral via API key direta ou rotação.""" | |
| try: | |
| from .mistral_rotation import get_mistral_rotation | |
| rotation = get_mistral_rotation(config) | |
| if rotation: | |
| self._rotation["mistral"] = rotation | |
| self._providers.append("mistral") | |
| logger.debug(f"✅ [API-TRAINING] Mistral OK ({rotation.get_current_account_name()})") | |
| elif config and getattr(config, "MISTRAL_API_KEY", ""): | |
| self._providers.append("mistral") | |
| logger.debug("✅ [API-TRAINING] Mistral OK (chave única)") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] Mistral skip: {e}") | |
| def _setup_groq(self): | |
| """Groq SDK.""" | |
| try: | |
| api_key = getattr(config, "GROQ_API_KEY", "") if config else "" | |
| if api_key and len(api_key) > 5: | |
| from groq import Groq | |
| client = Groq(api_key=api_key) | |
| self._clients["groq"] = client | |
| self._providers.append("groq") | |
| logger.debug("✅ [API-TRAINING] Groq OK") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] Groq skip: {e}") | |
| def _setup_gemini(self): | |
| """Google Gemini via genai.""" | |
| try: | |
| if config: | |
| gemini_key = getattr(config, "GEMINI_API_KEY", "") or getattr(config, "GOOGLE_API_KEY", "") | |
| if gemini_key and len(gemini_key) > 5: | |
| import google.generativeai as genai | |
| genai.configure(api_key=gemini_key) | |
| model_name = getattr(config, "GEMINI_MODEL", "gemini-2.5-flash") | |
| self._clients["gemini"] = genai.GenerativeModel(model_name) | |
| self._providers.append("gemini") | |
| logger.debug("✅ [API-TRAINING] Gemini OK") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] Gemini skip: {e}") | |
| def _setup_cohere(self): | |
| """Cohere SDK.""" | |
| try: | |
| api_key = getattr(config, "COHERE_API_KEY", "") if config else "" | |
| if api_key and len(api_key) > 5: | |
| from cohere import Client | |
| client = Client(api_key=api_key) | |
| self._clients["cohere"] = client | |
| self._providers.append("cohere") | |
| logger.debug("✅ [API-TRAINING] Cohere OK") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] Cohere skip: {e}") | |
| def _setup_hf_inference(self): | |
| """HF Inference via rotação multi-conta.""" | |
| try: | |
| from .hf_inference_rotation import get_hf_inference_rotation | |
| rotation = get_hf_inference_rotation() | |
| configured = [ | |
| acc for acc in rotation.account_order | |
| if os.getenv(rotation.accounts[acc]) | |
| ] | |
| if configured: | |
| from huggingface_hub import InferenceClient | |
| token = rotation.get_current_api_token() | |
| if token: | |
| client = InferenceClient(token=token, timeout=30.0) | |
| self._clients["hf_inference"] = client | |
| self._rotation["hf_inference"] = rotation | |
| self._providers.append("hf_inference") | |
| logger.debug(f"✅ [API-TRAINING] HF Inference OK ({len(configured)} contas)") | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] HF Inference skip: {e}") | |
| # ============================================================ | |
| # INTERFACE PÚBLICA | |
| # ============================================================ | |
| def generate( | |
| self, | |
| prompt: str, | |
| system: str = "Responda de forma clara e direta.", | |
| max_tokens: int = 2048, | |
| temperature: float = 0.7, | |
| preferred_provider: Optional[str] = None, | |
| ) -> Tuple[str, str]: | |
| """ | |
| Gera resposta via API para treinamento. | |
| Returns: | |
| (resposta_texto, provider_name) ou ("", "") se todos falharem | |
| """ | |
| providers = self._providers[:] | |
| if preferred_provider and preferred_provider in providers: | |
| providers.remove(preferred_provider) | |
| providers.insert(0, preferred_provider) | |
| for provider in providers: | |
| try: | |
| text = self._call_provider(provider, prompt, system, max_tokens, temperature) | |
| if text and len(text.strip()) > 5: | |
| self._record_call(provider, True) | |
| return text.strip(), provider | |
| self._record_call(provider, False) | |
| except Exception as e: | |
| self._record_call(provider, False) | |
| err_str = str(e) | |
| logger.debug(f"[API-TRAINING] {provider} falhou: {e}") | |
| # 429 (rate limit temporário) → rotaciona conta | |
| if "429" in err_str or "rate" in err_str.lower(): | |
| self._handle_rate_limit(provider) | |
| # 402 (quota/crédito esgotado) → já tratado em _call_cerebras; | |
| # para outros providers, tenta rotacionar também. | |
| elif "402" in err_str or "payment" in err_str.lower(): | |
| self._handle_rate_limit(provider) | |
| return "", "" | |
| def generate_batch( | |
| self, | |
| prompts: List[Dict[str, str]], | |
| max_concurrent: int = 1, | |
| delay_between: float = 1.0, | |
| ) -> List[Dict[str, Any]]: | |
| """ | |
| Gera respostas para batch de prompts. | |
| Args: | |
| prompts: Lista de {"prompt": str, "system": str (opcional)} | |
| max_concurrent: Máximo de chamadas simultâneas | |
| delay_between: Delay entre chamadas (rate limiting) | |
| Returns: | |
| Lista de {"prompt": str, "response": str, "provider": str, "success": bool} | |
| """ | |
| results = [] | |
| for i, item in enumerate(prompts): | |
| prompt = item.get("prompt", "") | |
| system = item.get("system", "Responda de forma clara e direta.") | |
| text, provider = self.generate(prompt, system) | |
| results.append({ | |
| "prompt": prompt, | |
| "response": text, | |
| "provider": provider, | |
| "success": bool(text), | |
| }) | |
| if delay_between > 0 and i < len(prompts) - 1: | |
| time.sleep(delay_between) | |
| return results | |
| def get_best_provider(self) -> str: | |
| """Retorna o provider com melhor taxa de sucesso.""" | |
| best = "" | |
| best_score = -1 | |
| for provider, stats in self._stats.items(): | |
| total = stats.get("success", 0) + stats.get("failure", 0) | |
| if total < 3: | |
| continue | |
| rate = stats["success"] / total | |
| if rate > best_score: | |
| best_score = rate | |
| best = provider | |
| return best or (self._providers[0] if self._providers else "") | |
| def get_stats(self) -> Dict[str, Any]: | |
| """Retorna estatísticas de uso.""" | |
| return { | |
| "providers_available": self._providers[:], | |
| "stats": dict(self._stats), | |
| } | |
| def is_available(self) -> bool: | |
| """Retorna se há providers disponíveis.""" | |
| return bool(self._providers) | |
| def get_mac_training_context(self) -> Dict[str, Any]: | |
| """ | |
| Retorna contexto de treinamento com estado do drive MAC. | |
| Inclui informações de saúde, uso e performance do disco | |
| para contextualizar dados de treinamento em sistemas Apple. | |
| """ | |
| import subprocess | |
| context = { | |
| "platform": "unknown", | |
| "drives": [], | |
| "timestamp": time.time(), | |
| } | |
| try: | |
| import platform as plat | |
| system = plat.system() | |
| if system == "Darwin": | |
| context["platform"] = "macos" | |
| context["drives"] = self._get_mac_drive_states() | |
| else: | |
| context["platform"] = system.lower() | |
| context["drives"] = self._get_generic_drive_states() | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] MAC context error: {e}") | |
| return context | |
| def _get_mac_drive_states(self) -> List[Dict[str, Any]]: | |
| """Coleta estado dos drives em macOS via diskutil.""" | |
| drives = [] | |
| try: | |
| result = subprocess.run( | |
| ["diskutil", "list", "-plist"], | |
| capture_output=True, text=True, timeout=10 | |
| ) | |
| if result.returncode == 0 and result.stdout: | |
| plist_data = __import__("plistlib").loads(result.stdout.encode()) | |
| for disk in plist_data.get("AllDisks", []): | |
| info = plist_data.get("AllDiskInformation", {}).get(disk, {}) | |
| drives.append({ | |
| "name": disk, | |
| "volume_name": info.get("VolumeName", ""), | |
| "mount_point": info.get("MountPoint", ""), | |
| "total_bytes": info.get("TotalSize", 0), | |
| "free_bytes": info.get("FreeSpace", 0), | |
| "filesystem": info.get("Filesystem", ""), | |
| "protocol": info.get("BusProtocol", ""), | |
| }) | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] diskutil error: {e}") | |
| return drives | |
| def _get_generic_drive_states(self) -> List[Dict[str, Any]]: | |
| """Coleta estado dos drives em sistemas genéricos (Windows/Linux).""" | |
| drives = [] | |
| try: | |
| import shutil | |
| usage = shutil.disk_usage("/") | |
| drives.append({ | |
| "name": "/", | |
| "total_bytes": usage.total, | |
| "free_bytes": usage.free, | |
| "used_bytes": usage.used, | |
| }) | |
| except Exception as e: | |
| logger.debug(f"[API-TRAINING] disk usage error: {e}") | |
| return drives | |
| # ============================================================ | |
| # CHAMADAS POR PROVIDER | |
| # ============================================================ | |
| def _call_provider( | |
| self, provider: str, prompt: str, system: str, | |
| max_tokens: int, temperature: float | |
| ) -> Optional[str]: | |
| """Roteia chamada para o provider correto.""" | |
| if provider == "cerebras": | |
| return self._call_cerebras(prompt, system, max_tokens, temperature) | |
| elif provider == "openrouter": | |
| return self._call_openrouter(prompt, system, max_tokens, temperature) | |
| elif provider == "mistral": | |
| return self._call_mistral(prompt, system, max_tokens, temperature) | |
| elif provider == "groq": | |
| return self._call_groq(prompt, system, max_tokens, temperature) | |
| elif provider == "gemini": | |
| return self._call_gemini(prompt, system, max_tokens, temperature) | |
| elif provider == "cohere": | |
| return self._call_cohere(prompt, system, max_tokens, temperature) | |
| elif provider == "hf_inference": | |
| return self._call_hf_inference(prompt, system, max_tokens, temperature) | |
| return None | |
| def _call_cerebras(self, prompt, system, max_tokens, temperature): | |
| client = self._clients.get("cerebras") | |
| if not client: | |
| return None | |
| # Modelo Cerebras serverless default: gpt-oss-120b (120B params, 65k contexto, production). | |
| # Sobrescreve via config.CEREBRAS_MODEL ou env CEREBRAS_MODEL. | |
| model = getattr(config, "CEREBRAS_MODEL", "gpt-oss-120b") if config else "gpt-oss-120b" | |
| try: | |
| response = client.chat.completions.create( | |
| model=model, | |
| messages=[ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| ) | |
| return response.choices[0].message.content if response.choices else None | |
| except Exception as e: | |
| err_str = str(e) | |
| # 402 Payment Required → quota esgotada (não é rate limit temporário). | |
| # Blacklist 24h da conta atual e rotaciona, igual ao api.py. | |
| if "402" in err_str or "payment_required" in err_str.lower(): | |
| rotation = self._rotation.get("cerebras") | |
| if rotation and hasattr(rotation, "handle_quota_402"): | |
| try: | |
| current_name = rotation.get_current_account_name() | |
| rotation.handle_quota_402(current_name) | |
| # Atualiza o cliente para a próxima conta disponível | |
| new_key = rotation.get_current_api_key() | |
| if new_key and new_key != client.api_key: | |
| import openai | |
| self._clients["cerebras"] = openai.OpenAI( | |
| api_key=new_key, | |
| base_url="https://api.cerebras.ai/v1", | |
| timeout=30.0, | |
| max_retries=0, | |
| ) | |
| logger.info(f"🔄 [API-TRAINING] Cerebras 402 → rotacionado para '{rotation.get_current_account_name()}'") | |
| except Exception as rotate_e: | |
| logger.debug(f"[API-TRAINING] Cerebras 402 rotate error: {rotate_e}") | |
| raise | |
| def _call_openrouter(self, prompt, system, max_tokens, temperature, rotation=None): | |
| rotation = rotation or self._rotation.get("openrouter") | |
| client = self._clients.get("openrouter") | |
| if not client: | |
| return None | |
| # Atualiza chave se tem rotação | |
| if rotation: | |
| key = rotation.get_current_key() | |
| if key: | |
| client.api_key = key | |
| model = getattr(config, "OPENROUTER_MODEL", "nvidia/nemotron-3-ultra-550b-a55b:free") if config else "nvidia/nemotron-3-ultra-550b-a55b:free" | |
| try: | |
| response = client.chat.completions.create( | |
| model=model, | |
| messages=[ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| ) | |
| return response.choices[0].message.content if response.choices else None | |
| except Exception as e: | |
| err_str = str(e) | |
| # 429 → rotaciona conta OpenRouter | |
| if "429" in err_str or "rate" in err_str.lower(): | |
| if rotation and hasattr(rotation, "handle_429_error"): | |
| try: | |
| rotation.handle_429_error() | |
| new_key = rotation.get_current_key() | |
| if new_key: | |
| client.api_key = new_key | |
| except Exception: | |
| pass | |
| raise | |
| def _call_mistral(self, prompt, system, max_tokens, temperature): | |
| import requests | |
| rotation = self._rotation.get("mistral") | |
| api_key = "" | |
| if rotation: | |
| api_key = rotation.get_current_key() | |
| elif config: | |
| api_key = getattr(config, "MISTRAL_API_KEY", "") | |
| if not api_key: | |
| return None | |
| model = getattr(config, "MISTRAL_MODEL", "mistral-large-latest") if config else "mistral-large-latest" | |
| response = requests.post( | |
| "https://api.mistral.ai/v1/chat/completions", | |
| headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}, | |
| json={ | |
| "model": model, | |
| "messages": [ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| "max_tokens": max_tokens, | |
| "temperature": temperature, | |
| }, | |
| timeout=30, | |
| ) | |
| if response.status_code == 200: | |
| data = response.json() | |
| choices = data.get("choices", []) | |
| return choices[0]["message"]["content"] if choices else None | |
| elif response.status_code == 429: | |
| # Rate limit → rotaciona conta Mistral e levanta para o generate() tratar | |
| rotation = self._rotation.get("mistral") | |
| if rotation and hasattr(rotation, "handle_429_error"): | |
| try: | |
| rotation.handle_429_error() | |
| except Exception: | |
| pass | |
| raise Exception("429 Rate Limit") | |
| elif response.status_code == 402: | |
| # Payment Required → quota esgotada | |
| raise Exception("402 Payment Required") | |
| return None | |
| def _call_groq(self, prompt, system, max_tokens, temperature): | |
| client = self._clients.get("groq") | |
| if not client: | |
| return None | |
| if len(system or '') + len(prompt or '') > 7500: | |
| return None | |
| model = getattr(config, "GROQ_MODEL", "groq/compound") if config else "groq/compound" | |
| response = client.chat.completions.create( | |
| model=model, | |
| messages=[ | |
| {"role": "system", "content": system}, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| ) | |
| return response.choices[0].message.content if response.choices else None | |
| def _call_gemini(self, prompt, system, max_tokens, temperature): | |
| model = self._clients.get("gemini") | |
| if not model: | |
| return None | |
| full_prompt = f"{system}\n\n{prompt}" | |
| response = model.generate_content( | |
| full_prompt, | |
| generation_config={ | |
| "max_output_tokens": max_tokens, | |
| "temperature": temperature, | |
| }, | |
| ) | |
| return response.text if response.text else None | |
| def _call_cohere(self, prompt, system, max_tokens, temperature): | |
| client = self._clients.get("cohere") | |
| if not client: | |
| return None | |
| model = getattr(config, "COHERE_MODEL", "command-r-plus-08-2024") if config else "command-r-plus-08-2024" | |
| response = client.chat( | |
| model=model, | |
| message=prompt, | |
| preamble=system, | |
| max_tokens=max_tokens, | |
| temperature=temperature, | |
| ) | |
| return response.text if response.text else None | |
| def _call_hf_inference(self, prompt, system, max_tokens, temperature): | |
| client = self._clients.get("hf_inference") | |
| if not client: | |
| return None | |
| rotation = self._rotation.get("hf_inference") | |
| model = getattr(config, "MISTRAL_MODEL_HF", "mistralai/Mistral-7B-Instruct-v0.2") if config else "mistralai/Mistral-7B-Instruct-v0.2" | |
| # Atualiza token se tem rotação | |
| if rotation: | |
| token = rotation.get_current_api_token() | |
| if token: | |
| client.token = token | |
| full_prompt = f"{system}\n\n{prompt}" | |
| response = client.text_generation( | |
| full_prompt, | |
| model=model, | |
| max_new_tokens=max_tokens, | |
| temperature=temperature, | |
| ) | |
| return response if isinstance(response, str) else None | |
| # ============================================================ | |
| # UTILITÁRIOS | |
| # ============================================================ | |
| def _record_call(self, provider: str, success: bool): | |
| """Registra resultado de chamada.""" | |
| if provider not in self._stats: | |
| self._stats[provider] = {"success": 0, "failure": 0} | |
| if success: | |
| self._stats[provider]["success"] += 1 | |
| else: | |
| self._stats[provider]["failure"] += 1 | |
| def _handle_rate_limit(self, provider: str): | |
| """Trata rate limit rotacionando para próxima conta.""" | |
| rotation = self._rotation.get(provider) | |
| if not rotation: | |
| return | |
| # Diferentes rotações usam nomes diferentes de método para 429. | |
| # Cerebras: handle_rate_limit_error(); OpenRouter/Mistral: handle_429_error(). | |
| handler = getattr(rotation, "handle_429_error", None) or getattr(rotation, "handle_rate_limit_error", None) | |
| if handler: | |
| try: | |
| handler() | |
| logger.info(f"🔄 [API-TRAINING] {provider} rotacionado após rate limit") | |
| except Exception: | |
| pass | |
| # Singleton | |
| _bridge_instance = None | |
| def get_api_training_bridge() -> APITrainingBridge: | |
| """Obtém ou cria singleton do bridge.""" | |
| global _bridge_instance | |
| if _bridge_instance is None: | |
| _bridge_instance = APITrainingBridge() | |
| return _bridge_instance | |