CoolFace
Apppublic

bbqddt2/Antigravity

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
llm_engine.py541 linesDownload Raw Back to root
1# -*- coding: utf-8 -*-2"""3Antigravity LLM创新引擎 V3.0 — 全渠道AI协同4 5支持的所有AI供应商:60. CC Switch (最高优先级) - 本地代理 http://127.0.0.1:15721,路由Claude/Gemini/Codex71. OpenRouter (已接入) - 免费模型: qwen/gemma/llama等82. Gemini (已接入) - 免费层配额用完,自动降级93. DeepSeek (已接入) - 余额不足,自动降级104. Ollama (本地) - 需要安装,支持gemma/llama等115. Together AI (预留) - 需要API Key126. Perplexity (预留) - 需要API Key137. Groq (预留) - 需要API Key148. HuggingFace (预留) - 通过HF_TOKEN访问15 16自动故障转移: 按优先级依次尝试,一个失败自动换下一个17"""18import os19import json20import time21from pathlib import Path22from typing import Optional, List, Dict, Any23from datetime import datetime24 25_PROJECT_ROOT = Path(__file__).resolve().parent.parent26 27 28class LLMEngine:29    """全渠道LLM引擎 — 多供应商自动故障转移"""30 31    def __init__(self):32        self.providers = {}33        self.active_provider = None34        self.available = False35        self.call_log = []  # 记录每次调用36 37        # 从.env加载密钥38        self._load_env()39 40        # 初始化所有可用供应商 — CC Switch最高优先级41        self._init_cc_switch()   # ★ 最高优先级:CC Switch本地代理42        self._init_ollama()43        self._init_openrouter()44        self._init_deeprouter()45        self._init_sensenova()46        self._init_gemini()47        self._init_deepseek()48        self._init_together()49        self._init_perplexity()50        self._init_groq()51        self._init_huggingface()52 53        # 重新排序:CC Switch > Ollama54        if "cc_switch" in self.providers:55            self.providers["cc_switch"]["priority"] = -1  # 最高优先56        if "ollama" in self.providers:57            self.providers["ollama"]["priority"] = 058 59        # 确定可用供应商60        self._build_provider_list()61 62    def _load_env(self):63        """从.env文件加载所有密钥"""64        env_path = _PROJECT_ROOT / ".env"65        if env_path.exists():66            with open(env_path, "r", encoding="utf-8") as f:67                for line in f:68                    line = line.strip()69                    if line.startswith("#") or "=" not in line:70                        continue71                    key, _, value = line.partition("=")72                    key = key.strip()73                    value = value.strip()74                    if value and not os.environ.get(key):75                        os.environ[key] = value76 77    def _init_openrouter(self):78        """初始化OpenRouter"""79        or_key = os.environ.get("OPENROUTER_API_KEY")80        if or_key:81            try:82                from openai import OpenAI83                self.providers["openrouter"] = {84                    "client": OpenAI(base_url="https://openrouter.ai/api/v1", api_key=or_key),85                    "models": [86                        "qwen/qwen-2.5-72b-instruct",87                        "meta-llama/llama-3.3-70b-instruct",88                        "google/gemma-2-27b-it",89                        "mistralai/mistral-7b-instruct",90                    ],91                    "priority": 1,92                }93                self.available = True94            except Exception:95                pass96 97    def _init_deeprouter(self):98        """初始化DeepRouter — 多key轮换"""99        for i in range(1, 5):100            key = os.environ.get(f"DEEPRouter_API_KEY_{i}")101            if key:102                try:103                    from openai import OpenAI104                    self.providers["deeprouter"] = {105                        "client": OpenAI(api_key=key, base_url="https://www.deeprouter.top/v1"),106                        "models": ["gpt-4o-mini", "gpt-3.5-turbo", "claude-3-haiku-20240307"],107                        "priority": 2,108                    }109                    if not self.available:110                        self.available = True111                    break112                except Exception:113                    continue114 115    def _init_sensenova(self):116        """初始化SenseNova — 多key轮换"""117        for i in range(1, 5):118            key = os.environ.get(f"SENSENOVA_API_KEY_{i}")119            if key:120                try:121                    from openai import OpenAI122                    self.providers["sensenova"] = {123                        "client": OpenAI(api_key=key, base_url="https://api.sensenova.cn/v1"),124                        "models": ["yi-lightning", "yi-large"],125                        "priority": 4,126                    }127                    if not self.available:128                        self.available = True129                    break130                except Exception:131                    pass132 133    def _init_gemini(self):134        """初始化Gemini"""135        try:136            from llm_innovation.llm_client import GeminiClient137            gc = GeminiClient()138            if gc.available:139                self.providers["gemini"] = {140                    "client": gc,141                    "priority": 2,142                }143                if not self.available:144                    self.available = True145        except Exception:146            pass147 148    def _init_deepseek(self):149        """初始化DeepSeek"""150        ds_key = os.environ.get("DEEPSEEK_API_KEY")151        if ds_key:152            try:153                from openai import OpenAI154                self.providers["deepseek"] = {155                    "client": OpenAI(api_key=ds_key, base_url="https://api.deepseek.com/v1"),156                    "models": ["deepseek-chat", "deepseek-coder"],157                    "priority": 3,158                }159                if not self.available:160                    self.available = True161            except Exception:162                pass163 164    def _init_together(self):165        """初始化Together AI"""166        tg_key = os.environ.get("TOGETHER_API_KEY")167        if tg_key:168            try:169                from openai import OpenAI170                self.providers["together"] = {171                    "client": OpenAI(api_key=tg_key, base_url="https://api.together.xyz/v1"),172                    "models": [173                        "meta-llama/Llama-3-70b-chat-hf",174                        "mistralai/Mixtral-8x7B-Instruct-v0.1",175                        "Qwen/Qwen2.5-72B-Instruct",176                    ],177                    "priority": 4,178                }179                if not self.available:180                    self.available = True181            except Exception:182                pass183 184    def _init_perplexity(self):185        """初始化Perplexity"""186        px_key = os.environ.get("PERPLEXITY_API_KEY")187        if px_key:188            try:189                from openai import OpenAI190                self.providers["perplexity"] = {191                    "client": OpenAI(api_key=px_key, base_url="https://api.perplexity.ai"),192                    "models": ["sonar", "sonar-pro"],193                    "priority": 5,194                }195                if not self.available:196                    self.available = True197            except Exception:198                pass199 200    def _init_groq(self):201        """初始化Groq"""202        gx_key = os.environ.get("GROQ_API_KEY")203        if gx_key:204            try:205                from openai import OpenAI206                self.providers["groq"] = {207                    "client": OpenAI(api_key=gx_key, base_url="https://api.groq.com/openai/v1"),208                    "models": [209                        "llama-3.1-70b-versatile",210                        "mixtral-8x7b-32768",211                        "gemma2-9b-it",212                    ],213                    "priority": 6,214                }215                if not self.available:216                    self.available = True217            except Exception:218                pass219 220    def _init_cc_switch(self):221        """初始化 CC Switch 本地代理 — 路由Claude/Gemini/Codex,最高优先级"""222        cc_url = os.environ.get("CC_SWITCH_BASE_URL", "http://127.0.0.1:15721/v1")223        try:224            import requests225            # 探针:检查 CC Switch 是否在线226            probe = requests.get(cc_url.replace("/v1", "/"), timeout=3)227            if probe.status_code in (200, 404):  # 404 说明服务存在但路径不同,仍算在线228                from openai import OpenAI229                self.providers["cc_switch"] = {230                    "client": OpenAI(231                        api_key="cc-switch-local",  # 本地代理无需真实Key232                        base_url=cc_url,233                    ),234                    "models": [235                        "claude-sonnet-4-5",236                        "claude-3-5-haiku-20241022",237                        "gemini-2.0-flash",238                        "gemini-1.5-pro",239                    ],240                    "priority": -1,  # ★ 最高优先级241                    "name": "CC Switch",242                }243                self.available = True244        except Exception:245            pass  # CC Switch不在线,自动降级到下一个供应商246 247    def _init_ollama(self):248        """初始化Ollama本地模型 — 0延迟0费用"""249        try:250            import requests251            resp = requests.get("http://localhost:11434/api/tags", timeout=5)252            if resp.status_code == 200:253                models = resp.json().get("models", [])254                if models:255                    model_names = [m.get("name", "") for m in models]256                    self.providers["ollama"] = {257                        "client": requests.Session(),258                        "base_url": "http://localhost:11434",259                        "models": model_names,260                        "priority": 1,  # 本地最快最便宜261                    }262                    if not self.available:263                        self.available = True264        except Exception:265            pass266 267    def _init_huggingface(self):268        """初始化HuggingFace"""269        hf_token = os.environ.get("HF_TOKEN")270        if hf_token:271            try:272                from huggingface_hub import InferenceClient273                self.providers["huggingface"] = {274                    "client": InferenceClient(token=hf_token),275                    "models": ["meta-llama/Llama-3.3-70B-Instruct"],276                    "priority": 8,277                }278                if not self.available:279                    self.available = True280            except Exception:281                pass282 283    def _init_deeprouter(self):284        """初始化DeepRouter"""285        for i in range(1, 5):286            key = os.environ.get(f"DEEPRouter_API_KEY_{i}")287            if key:288                try:289                    from openai import OpenAI290                    self.providers["deeprouter"] = {291                        "client": OpenAI(api_key=key, base_url="https://www.deeprouter.top/v1"),292                        "models": ["gpt-4o-mini", "gpt-3.5-turbo", "claude-3-haiku-20240307"],293                        "priority": 2,  # 仅次于OpenRouter294                    }295                    if not self.available:296                        self.available = True297                    break298                except Exception:299                    continue300 301    def _init_sensenova(self):302        """初始化SenseNova"""303        for i in range(1, 5):304            key = os.environ.get(f"SENSENOVA_API_KEY_{i}")305            if key:306                try:307                    from openai import OpenAI308                    self.providers["sensenova"] = {309                        "client": OpenAI(api_key=key, base_url="https://api.sensenova.cn/v1"),310                        "models": ["yi-lightning", "yi-large"],311                        "priority": 5,312                    }313                    if not self.available:314                        self.available = True315                    break316                except Exception:317                    pass318 319    def _build_provider_list(self):320        """按优先级排序可用供应商"""321        self.provider_list = sorted(322            self.providers.values(),323            key=lambda p: p.get("priority", 999)324        )325 326    def generate(self, prompt: str, system_prompt: Optional[str] = None,327                 max_tokens: int = 4000, timeout: int = 60) -> Optional[str]:328        """329        尝试所有可用供应商,按优先级返回第一个成功的结果。330 331        Args:332            prompt: 用户提示333            system_prompt: 系统提示334            max_tokens: 最大token数335            timeout: 超时秒数336 337        Returns:338            生成的文本,所有供应商都失败返回None339        """340        start_time = time.time()341        errors = []342 343        for provider_name, provider in enumerate(self.provider_list):344            pname = list(self.providers.keys())[provider_name]345 346            try:347                result = self._call_provider(pname, provider, prompt, system_prompt, max_tokens, timeout)348                if result:349                    elapsed = time.time() - start_time350                    self.active_provider = pname351                    self.call_log.append({352                        "provider": pname,353                        "success": True,354                        "elapsed": round(elapsed, 2),355                        "timestamp": datetime.now().isoformat(),356                    })357                    return result358            except Exception as e:359                errors.append(f"{pname}: {str(e)[:100]}")360 361        # 所有供应商都失败362        self.call_log.append({363            "provider": "all_failed",364            "success": False,365            "errors": errors,366            "timestamp": datetime.now().isoformat(),367        })368        return None369 370    def _call_openrouter(self, provider, prompt, system_prompt, max_tokens):371        """调用OpenRouter"""372        client = provider["client"]373        messages = []374        if system_prompt:375            messages.append({"role": "system", "content": system_prompt})376        messages.append({"role": "user", "content": prompt})377 378        for model in provider.get("models", []):379            try:380                resp = client.chat.completions.create(381                    model=model,382                    messages=messages,383                    temperature=0.7,384                    max_tokens=max_tokens,385                    timeout=30,386                )387                return resp.choices[0].message.content388            except Exception:389                continue390        return None391 392    def _call_gemini(self, provider, prompt, system_prompt, max_tokens):393        """调用Gemini"""394        gc = provider["client"]395        if gc.available:396            return gc.generate_content(prompt, system_prompt)397        return None398 399    def _call_deepseek(self, provider, prompt, system_prompt, max_tokens):400        """调用DeepSeek"""401        client = provider["client"]402        messages = []403        if system_prompt:404            messages.append({"role": "system", "content": system_prompt})405        messages.append({"role": "user", "content": prompt})406 407        for model in provider.get("models", []):408            try:409                resp = client.chat.completions.create(410                    model=model,411                    messages=messages,412                    temperature=0.7,413                    max_tokens=max_tokens,414                )415                return resp.choices[0].message.content416            except Exception:417                continue418        return None419 420    def _call_together(self, provider, prompt, system_prompt, max_tokens):421        """调用Together AI"""422        client = provider["client"]423        messages = []424        if system_prompt:425            messages.append({"role": "system", "content": system_prompt})426        messages.append({"role": "user", "content": prompt})427 428        for model in provider.get("models", []):429            try:430                resp = client.chat.completions.create(431                    model=model,432                    messages=messages,433                    temperature=0.7,434                    max_tokens=max_tokens,435                )436                return resp.choices[0].message.content437            except Exception:438                continue439        return None440 441    def _call_ollama(self, provider, prompt, system_prompt, max_tokens):442        """调用Ollama本地模型"""443        client = provider["client"]444        base_url = provider.get("base_url", "http://localhost:11434")445        messages = []446        if system_prompt:447            messages.append({"role": "system", "content": system_prompt})448        messages.append({"role": "user", "content": prompt})449 450        # 优先尝试gemma2(content字段正常返回),gemma4的thinking模式导致content为空451        models = sorted(provider.get("models", []), key=lambda m: 0 if "gemma2" in m else 1)452 453        for model in models:454            try:455                resp = client.post(f"{base_url}/api/chat", json={456                    "model": model,457                    "messages": messages,458                    "stream": False,459                    "options": {"num_predict": max_tokens, "num_ctx": 4096},460                }, timeout=120)461                if resp.status_code == 200:462                    content = resp.json().get("message", {}).get("content", "").strip()463                    if content:464                        return content465            except Exception:466                continue467        return None468 469    def _call_deeprouter(self, provider, prompt, system_prompt, max_tokens):470        """调用DeepRouter"""471        client = provider["client"]472        messages = []473        if system_prompt:474            messages.append({"role": "system", "content": system_prompt})475        messages.append({"role": "user", "content": prompt})476 477        for model in provider.get("models", []):478            try:479                resp = client.chat.completions.create(480                    model=model,481                    messages=messages,482                    temperature=0.7,483                    max_tokens=max_tokens,484                )485                return resp.choices[0].message.content486            except Exception:487                continue488        return None489 490    def _call_sensenova(self, provider, prompt, system_prompt, max_tokens):491        """调用SenseNova"""492        client = provider["client"]493        messages = []494        if system_prompt:495            messages.append({"role": "system", "content": system_prompt})496        messages.append({"role": "user", "content": prompt})497 498        for model in provider.get("models", []):499            try:500                resp = client.chat.completions.create(501                    model=model,502                    messages=messages,503                    temperature=0.7,504                    max_tokens=max_tokens,505                )506                return resp.choices[0].message.content507            except Exception:508                continue509        return None510 511    def _call_provider(self, name, provider, prompt, system_prompt, max_tokens, timeout):512        """分发到具体供应商"""513        if name == "openrouter":514            return self._call_openrouter(provider, prompt, system_prompt, max_tokens)515        elif name == "deeprouter":516            return self._call_deeprouter(provider, prompt, system_prompt, max_tokens)517        elif name == "sensenova":518            return self._call_sensenova(provider, prompt, system_prompt, max_tokens)519        elif name == "gemini":520            return self._call_gemini(provider, prompt, system_prompt, max_tokens)521        elif name == "deepseek":522            return self._call_deepseek(provider, prompt, system_prompt, max_tokens)523        elif name == "together":524            return self._call_together(provider, prompt, system_prompt, max_tokens)525        elif name == "ollama":526            return self._call_ollama(provider, prompt, system_prompt, max_tokens)527        return None528 529    def get_status(self) -> Dict:530        """获取引擎状态"""531        status = {532            "available": self.available,533            "active_provider": self.active_provider,534            "providers_configured": list(self.providers.keys()),535            "call_log_last": self.call_log[-3:] if self.call_log else [],536        }537        return status538 539    def __repr__(self):540        return f"<LLMEngine(active: {self.active_provider}, providers: {list(self.providers.keys())})>"541