Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/bamper0051-cell_black-whiteai.json

1 line
49 KiB
JSON

{"repo": "bamper0051-cell/black-whiteai", "files": {"final/config.py": {"4e4c9b4c0abae2677a1f3ca964356909fd94720b": {"t": 1785674550.432129, "n": 15054, "h": "367c359df7e60151", "c": "import os\nfrom dotenv import load_dotenv, set_key\n\n# Всегда ищем .env рядом с этим файлом, не зависит от cwd\nBASE_DIR = os.path.dirname(os.path.abspath(__file__))\nENV_PATH = os.path.join(BASE_DIR, '.env')\n# DATA_DIR — отдельная папка для БД (монтируется как volume в Docker)\nDATA_DIR = os.path.join(BASE_DIR, 'data')\nos.makedirs(DATA_DIR, exist_ok=True)\n\ndef _load():\n load_dotenv(dotenv_path=ENV_PATH, override=True)\n\n_load()\n\ndef get(key, default=''):\n return os.environ.get(key, default)\n\ndef _set_env(key, value):\n \"\"\"Пишет в .env и сразу в os.environ (runtime-изменение).\"\"\"\n os.environ[key] = str(value)\n if os.path.exists(ENV_PATH):\n set_key(ENV_PATH, key, str(value))\n\nclass _Config:\n # ── Read-only (из env) ────────────────────────────────────\n @property\n def BASE_DIR(self): return BASE_DIR\n @property\n def DATA_DIR(self): return DATA_DIR\n @property\n def ENV_PATH(self): return ENV_PATH\n @property\n def TELEGRAM_BOT_TOKEN(self): return get('TELEGRAM_BOT_TOKEN', '')\n @property\n def TELEGRAM_CHAT_ID(self): return get('TELEGRAM_CHAT_ID', '')\n @property\n def OLLAMA_BASE_URL(self): return get('OLLAMA_BASE_URL', 'http://localhost:11434')\n @property\n def ELEVEN_API_KEY(self): return get('ELEVEN_API_KEY', '')\n @property\n def ELEVEN_VOICE_ID(self): return get('ELEVEN_VOICE_ID', '')\n @property\n def ELEVEN_MODEL_ID(self): return get('ELEVEN_MODEL_ID', 'eleven_multilingual_v2').strip()\n @property\n def PARSE_INTERVAL_HOURS(self): return int(get('PARSE_INTERVAL_HOURS', '12'))\n @property\n def GEMINI_API_KEY(self): return get('GEMINI_API_KEY', '')\n @GEMINI_API_KEY.setter\n def GEMINI_API_KEY(self, v): _set_env('GEMINI_API_KEY', v)\n\n # ── Ключи по провайдерам (опционально) ───────────────────\n # ── API ключи провайдеров ─────────────────────────────────\n # Логика: специфичный ключ → LLM_API_KEY как fallback\n @property\n def OPENAI_API_KEY(self): return get('OPENAI_API_KEY') or self.LLM_API_KEY\n @property\n def ANTHROPIC_API_KEY(self): return get('ANTHROPIC_API_KEY') or self.LLM_API_KEY\n @property\n def MISTRAL_API_KEY(self): return get('MISTRAL_API_KEY') or self.LLM_API_KEY\n @property\n def DEEPSEEK_API_KEY(self): return get('DEEPSEEK_API_KEY') or self.LLM_API_KEY\n @property\n def GROQ_API_KEY(self): return get('GROQ_API_KEY') or self.LLM_API_KEY\n @property\n def TOGETHER_API_KEY(self): return get('TOGETHER_API_KEY') or self.LLM_API_KEY\n @property\n def FIREWORKS_API_KEY(self): return get('FIREWORKS_API_KEY') or self.LLM_API_KEY\n @property\n def CEREBRAS_API_KEY(self): return get('CEREBRAS_API_KEY') or self.LLM_API_KEY\n @property\n def SAMBANOVA_API_KEY(self): return get('SAMBANOVA_API_KEY') or self.LLM_API_KEY\n @property\n def NOVITA_API_KEY(self): return get('NOVITA_API_KEY') or self.LLM_API_KEY\n @property\n def DEEPINFRA_API_KEY(self): return get('DEEPINFRA_API_KEY') or self.LLM_API_KEY\n @property\n def LAMBDA_API_KEY(self): return get('LAMBDA_API_KEY') or self.LLM_API_KEY\n @property\n def LEPTON_API_KEY(self): return get('LEPTON_API_KEY') or self.LLM_API_KEY\n @property\n def SCALEWAY_API_KEY(self): return get('SCALEWAY_API_KEY') or self.LLM_API_KEY\n @property\n def FEATHERLESS_API_KEY(self): return get('FEATHERLESS_API_KEY') or self.LLM_API_KEY\n @property\n def CHUTES_API_KEY(self): return get('CHUTES_API_KEY') or self.LLM_API_KEY\n @property\n def NEETS_API_KEY(self): return get('NEETS_API_KEY') or self.LLM_API_KEY\n @property\n def XAI_API_KEY(self): return get('XAI_API_KEY') or self.LLM_API_KEY\n @property\n def OPENROUTER_API_KEY(self): return get('OPENROUTER_API_KEY') or self.LLM_API_KEY\n @property\n def KIMI_API_KEY(self): return get('KIMI_API_KEY') or self.LLM_API_KEY\n @property\n def COHERE_API_KEY(self): return get('COHERE_API_KEY') or self.LLM_API_KEY\n @property\n def KLUSTER_API_KEY(self): return get('KLUSTER_API_KEY') or self.LLM_API_KEY\n @property\n def LLAMA_API_KEY(self): return get('LLAMA_API_KEY') or self.LLM_API_KEY\n @property\n def PERPLEXITY_API_KEY(self): return get('PERPLEXITY_API_KEY') or self.LLM_API_KEY\n @property\n def HYPERBOLIC_API_KEY(self): return get('HYPERBOLIC_API_KEY') or self.LLM_API_KEY\n @property\n def ANYSCALE_API_KEY(self): return get('ANYSCALE_API_KEY') or self.LLM_API_KEY\n @property\n def NVIDIA_API_KEY(self): return get('NVIDIA_API_KEY') or self.LLM_API_KEY\n @property\n def ALIBABA_API_KEY(self): return get('ALIBABA_API_KEY') or self.LLM_API_KEY\n @property\n def ZHIPU_API_KEY(self): return get('ZHIPU_API_KEY') or self.LLM_API_KEY\n @property\n def STEPFUN_API_KEY(self): return get('STEPFUN_API_KEY') or self.LLM_API_KEY\n @property\n def SILICONFLOW_API_KEY(self): return get('SILICONFLOW_API_KEY') or self.LLM_API_KEY\n @property\n def YI_API_KEY(self): return get('YI_API_KEY') or self.LLM_API_KEY\n @property\n def MOONSHOT_API_KEY(self): return get('MOONSHOT_API_KEY') or self.LLM_API_KEY\n @property\n def MINIMAX_API_KEY(self): return get('MINIMAX_API_KEY') or self.LLM_API_KEY\n @property\n def WRITER_API_KEY(self): return get('WRITER_API_KEY') or self.LLM_API_KEY\n @property\n def AI21_API_KEY(self): return get('AI21_API_KEY') or self.LLM_API_KEY\n @property\n def REKA_API_KEY(self): return get('REKA_API_KEY') or self.LLM_API_KEY\n @property\n def UPSTAGE_API_KEY(self): return get('UPSTAGE_API_KEY') or self.LLM_API_KEY\n\n def get_key(self, provider: str) -> str:\n \"\"\"Возвращает ключ для конкретного провайдера.\"\"\"\n mapping = {\n 'openai': 'OPENAI_API_KEY',\n 'mistral': 'MISTRAL_API_KEY',\n 'groq': 'GROQ_API_KEY',\n 'deepseek': 'DEEPSEEK_API_KEY',\n 'claude': 'ANTHROPIC_API_KEY',\n 'anthropic': 'ANTHROPIC_API_KEY',\n 'openrouter': 'OPENROUTER_API_KEY',\n 'xai': 'XAI_API_KEY',\n 'grok': 'XAI_API_KEY',\n 'gemini': 'GEMINI_API_KEY',\n }\n attr = mapping.get(provider.lower())\n if attr:\n specific = get(attr, '')\n if specific:\n return specific\n return self.LLM_API_KEY # fallback\n\n # ── Settable properties (runtime + .env) ─────────────────\n @property\n def LLM_PROVIDER(self): return get('LLM_PROVIDER', 'openai').lower().strip()\n @LLM_PROVIDER.setter\n def LLM_PROVIDER(self, v): _set_env('LLM_PROVIDER', v)\n\n @property\n def LLM_MODEL(self): return get('LLM_MODEL', 'gpt-4o-mini').strip()\n @LLM_MODEL.setter\n def LLM_MODEL(self, v): _set_env('LLM_MODEL', v)\n\n @property\n def LLM_API_KEY(self): return get('LLM_API_KEY', '')\n @LLM_API_KEY.setter\n def LLM_API_KEY(self, v): _set_env('LLM_API_KEY', v)\n\n # ── Специализированные провайдеры ─────────────────────────\n # Если не заданы — фоллбэк на основной LLM\n @property\n def CODE_PROVIDER(self):\n return get('CODE_PROVIDER', '') or self.LLM_PROVIDER\n @CODE_PROVIDER.setter\n def CODE_PROVIDER(self, v): _set_env('CODE_PROVIDER', v)\n\n @property\n def CODE_MODEL(self):\n return get('CODE_MODEL', '') or self.LLM_MODEL\n @CODE_MODEL.setter\n def CODE_MODEL(self, v): _set_env('CODE_MODEL', v)\n\n @property\n def AGENT_PROVIDER(self):\n return get('AGENT_PROVIDER', '') or self.LLM_PROVIDER\n @AGENT_PROVIDER.setter\n def AGENT_PROVIDER(self, v): _set_env('AGENT_PROVIDER', v)\n\n @property\n def AGENT_MODEL(self):\n return get('AGENT_MODEL', '') or self.LLM_MODEL\n @AGENT_MODEL.setter\n def AGENT_MODEL(self, v): _set_env('AGENT_MODEL', v)\n\n @property\n def IMAGE_PROVIDER(self):\n return get('IMAGE_PROVIDER', 'auto').lower().strip()\n @IMAGE_PROVIDER.setter\n def IMAGE_PROVIDER(self, v): _set_env('IMAGE_PROVIDER', v)\n\n @property\n def HF_API_KEY(self): return get('HF_API_KEY', '') or self.LLM_API_KEY\n @HF_API_KEY.setter\n def HF_API_KEY(self, v): _set_env('HF_API_KEY', v)\n\n @property\n def DEEPGRAM_API_KEY(self): return get('DEEPGRAM_API_KEY', '') or self.LLM_API_KEY\n @property\n def STABILITY_API_KEY(self): return get('STABILITY_API_KEY', '')\n\n @property\n def TTS_PROVIDER(self): return get('TTS_PROVIDER', 'edge').lower().strip()\n @TTS_PROVIDER.setter\n def TTS_PROVIDER(self, v): _set_env('TTS_PROVIDER', v)\n\n @property\n def TTS_VOICE(self): return get('TTS_VOICE', 'ru-RU-DmitryNeural')\n @TTS_VOICE.setter\n def TTS_VOICE(self, v): _set_env('TTS_VOICE', v)\n\n def reload(self):\n _load()\n print(f\" 🔄 Config reloaded: {self.LLM_PROVIDER} / {self.LLM_MODEL}\", flush=True)\n\n def set_llm(self, provider: str, model: str, api_key: str = ''):\n \"\"\"Удобный метод: меняет провайдер + модель + ключ одним вызовом.\"\"\"\n self.LLM_PROVIDER = provider.lower().strip()\n self.LLM_MODEL = model.strip()\n if api_key:\n self.LLM_API_KEY = api_key.strip()\n print(f\" ✅ LLM: {self.LLM_PROVIDER} / {self.LLM_MODEL}\", flush=True)\n\n # ════════════════════════════════════════════════════════════\n # BlackBugsAI — Brand / Proxy / Billing / Plans\n # ════════════════════════════════════════════════════════════\n\n BRAND = {\n 'name': 'BlackBugsAI',\n 'tagline': 'Autonomous AI Agent Platform',\n 'version': '1.0.0',\n 'emoji': '🖤🐛',\n 'url': 'https://blackbugsai.com',\n 'support': '@blackbugsai_support',\n }\n\n # ── Proxy / Tor ──────────────────────────────────────────────\n @property\n def PROXY(self) -> dict:\n return {\n 'enabled': get('PROXY_ENABLED', '').lower() == 'true',\n 'url': get('PROXY_URL', ''),\n 'tor': get('TOR_ENABLED', '').lower() == 'true',\n 'tor_port': int(get('TOR_PORT', '9050')),\n 'rotate': get('PROXY_ROTATE', '').lower() == 'true',\n }\n\n @property\n def PROXY_ENABLED(self) -> bool:\n return get('PROXY_ENABLED', '').lower() == 'true'\n\n # ── Admin Web ────────────────────────────────────────────────\n @property\n def ADMIN_WEB_PORT(self) -> int:\n return int(get('ADMIN_WEB_PORT', '8080'))\n\n @property\n def ADMIN_WEB_TOKEN(self) -> str:\n return get('ADMIN_WEB_TOKEN', 'changeme_secret_token')\n\n # ── Auth / JWT ───────────────────────────────────────────────\n @property\n def JWT_SECRET(self) -> str:\n return get('JWT_SECRET', 'change_me_in_production')\n\n @property\n def JWT_EXPIRE_HOURS(self) -> int:\n return int(get('JWT_EXPIRE_HOURS', '24'))\n\n # ── Billing ──────────────────────────────────────────────────\n @property\n def BILLING_ENABLED(self) -> bool:\n return get('BILLING_ENABLED', 'false').lower() == 'true'\n\n @property\n def BILLING_PROVIDER(self) -> str:\n return get('BILLING_PROVIDER', 'telegram_stars')\n\n # ── Plans ────────────────────────────────────────────────────\n PLANS = {\n 'free': {\n 'name': '🆓 Free',\n 'price': 0,\n 'tasks_per_day': 10,\n 'tools': ['tts', 'pollinations_image', 'web_search',\n 'python_sandbox', 'chat', 'fetch_url'],\n 'file_mb': 10,\n 'agent_rounds': 3,\n 'history_msgs': 20,\n },\n 'pro': {\n 'name': '⭐ Pro',\n 'price': 9.99,\n 'tasks_per_day': 100,\n 'tools': ['*'],\n 'file_mb': 500,\n 'agent_rounds': 10,\n 'history_msgs': 50,\n },\n 'business': {\n 'name': '🚀 Business',\n 'price': 49.99,\n 'tasks_per_day': -1,\n 'tools': ['*'],\n 'file_mb': -1,\n 'agent_rounds': 20,\n 'history_msgs': 100,\n 'api_access': True,\n 'white_label': True,\n },\n 'enterprise': {\n 'name': '🏢 Enterprise',\n 'price': 0,\n 'tasks_per_day': -1,\n 'tools': ['*'],\n 'file_mb': -1,\n 'agent_rounds': -1,\n 'history_msgs': 200,\n 'dedicated': True,\n 'sla': True,\n },\n }\n\n # ── Sandbox ──────────────────────────────────────────────────\n @property\n def SANDBOX(self) -> dict:\n return {\n 'timeout_default': int(get('SANDBOX_TIMEOUT', '30')),\n 'timeout_max': int(get('SANDBOX_TIMEOUT_MAX', '300')),\n 'docker_enabled': get('DOCKER_SANDBOX', '').lower() == 'true',\n }\n\n # ── Queue ────────────────────────────────────────────────────\n @property\n def QUEUE_WORKERS(self) -> int:\n return int(get('QUEUE_WORKERS', '2'))\n\n @property\n def REDIS_URL(self) -> str:\n return get('REDIS_URL', '')\n\n def get_plan(self, plan_name: str) -> dict:\n \"\"\"Возвращает конфиг тарифного плана.\"\"\"\n return self.PLANS.get(plan_name, self.PLANS['free'])\n\n def plan_allows_tool(self, plan_name: str, tool_name: str) -> bool:\n \"\"\"Проверяет доступность инструмента для тарифа.\"\"\"\n plan = self.get_plan(plan_name)\n allowed = plan.get('tools', [])\n return '*' in allowed or tool_name in allowed\n\n def startup_banner(self) -> str:\n \"\"\"ASCII-баннер при запуске.\"\"\"\n b = self.BRAND\n lines = [\n \"\",\n \" ██████╗ ██╗ █████╗ ██████╗██╗ ██╗██████╗ ██╗ ██╗ ██████╗ ███████╗\",\n \" ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔══██╗██║ ██║██╔════╝ ██╔════╝\",\n \" ██████╔╝██║ ███████║██║ █████╔╝ ██████╔╝██║ ██║██║ ███╗███████╗ \",\n \" ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ██╔══██╗██║ ██║██║ ██║╚════██║\",\n \" ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗██████╔╝╚██████╔╝╚██████╔╝███████║\",\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝╚══════╝\",\n \"\",\n f\" {b['emoji']} {b['name']} — {b['tagline']} v{b['version']}\",\n \"\",\n ]\n return \"\\n\".join(lines)\n\n def __setattr__(self, key, value):\n \"\"\"Fallback setattr — на случай если @property.setter не сработал.\"\"\"\n _set_env(key, str(value))\n\nimport sys\nsys.modules[__name__] = _Config()\n"}}, "config.py": {"4e4c9b4c0abae2677a1f3ca964356909fd94720b": {"t": 1785674550.630162, "n": 15054, "h": "367c359df7e60151", "c": "import os\nfrom dotenv import load_dotenv, set_key\n\n# Всегда ищем .env рядом с этим файлом, не зависит от cwd\nBASE_DIR = os.path.dirname(os.path.abspath(__file__))\nENV_PATH = os.path.join(BASE_DIR, '.env')\n# DATA_DIR — отдельная папка для БД (монтируется как volume в Docker)\nDATA_DIR = os.path.join(BASE_DIR, 'data')\nos.makedirs(DATA_DIR, exist_ok=True)\n\ndef _load():\n load_dotenv(dotenv_path=ENV_PATH, override=True)\n\n_load()\n\ndef get(key, default=''):\n return os.environ.get(key, default)\n\ndef _set_env(key, value):\n \"\"\"Пишет в .env и сразу в os.environ (runtime-изменение).\"\"\"\n os.environ[key] = str(value)\n if os.path.exists(ENV_PATH):\n set_key(ENV_PATH, key, str(value))\n\nclass _Config:\n # ── Read-only (из env) ────────────────────────────────────\n @property\n def BASE_DIR(self): return BASE_DIR\n @property\n def DATA_DIR(self): return DATA_DIR\n @property\n def ENV_PATH(self): return ENV_PATH\n @property\n def TELEGRAM_BOT_TOKEN(self): return get('TELEGRAM_BOT_TOKEN', '')\n @property\n def TELEGRAM_CHAT_ID(self): return get('TELEGRAM_CHAT_ID', '')\n @property\n def OLLAMA_BASE_URL(self): return get('OLLAMA_BASE_URL', 'http://localhost:11434')\n @property\n def ELEVEN_API_KEY(self): return get('ELEVEN_API_KEY', '')\n @property\n def ELEVEN_VOICE_ID(self): return get('ELEVEN_VOICE_ID', '')\n @property\n def ELEVEN_MODEL_ID(self): return get('ELEVEN_MODEL_ID', 'eleven_multilingual_v2').strip()\n @property\n def PARSE_INTERVAL_HOURS(self): return int(get('PARSE_INTERVAL_HOURS', '12'))\n @property\n def GEMINI_API_KEY(self): return get('GEMINI_API_KEY', '')\n @GEMINI_API_KEY.setter\n def GEMINI_API_KEY(self, v): _set_env('GEMINI_API_KEY', v)\n\n # ── Ключи по провайдерам (опционально) ───────────────────\n # ── API ключи провайдеров ─────────────────────────────────\n # Логика: специфичный ключ → LLM_API_KEY как fallback\n @property\n def OPENAI_API_KEY(self): return get('OPENAI_API_KEY') or self.LLM_API_KEY\n @property\n def ANTHROPIC_API_KEY(self): return get('ANTHROPIC_API_KEY') or self.LLM_API_KEY\n @property\n def MISTRAL_API_KEY(self): return get('MISTRAL_API_KEY') or self.LLM_API_KEY\n @property\n def DEEPSEEK_API_KEY(self): return get('DEEPSEEK_API_KEY') or self.LLM_API_KEY\n @property\n def GROQ_API_KEY(self): return get('GROQ_API_KEY') or self.LLM_API_KEY\n @property\n def TOGETHER_API_KEY(self): return get('TOGETHER_API_KEY') or self.LLM_API_KEY\n @property\n def FIREWORKS_API_KEY(self): return get('FIREWORKS_API_KEY') or self.LLM_API_KEY\n @property\n def CEREBRAS_API_KEY(self): return get('CEREBRAS_API_KEY') or self.LLM_API_KEY\n @property\n def SAMBANOVA_API_KEY(self): return get('SAMBANOVA_API_KEY') or self.LLM_API_KEY\n @property\n def NOVITA_API_KEY(self): return get('NOVITA_API_KEY') or self.LLM_API_KEY\n @property\n def DEEPINFRA_API_KEY(self): return get('DEEPINFRA_API_KEY') or self.LLM_API_KEY\n @property\n def LAMBDA_API_KEY(self): return get('LAMBDA_API_KEY') or self.LLM_API_KEY\n @property\n def LEPTON_API_KEY(self): return get('LEPTON_API_KEY') or self.LLM_API_KEY\n @property\n def SCALEWAY_API_KEY(self): return get('SCALEWAY_API_KEY') or self.LLM_API_KEY\n @property\n def FEATHERLESS_API_KEY(self): return get('FEATHERLESS_API_KEY') or self.LLM_API_KEY\n @property\n def CHUTES_API_KEY(self): return get('CHUTES_API_KEY') or self.LLM_API_KEY\n @property\n def NEETS_API_KEY(self): return get('NEETS_API_KEY') or self.LLM_API_KEY\n @property\n def XAI_API_KEY(self): return get('XAI_API_KEY') or self.LLM_API_KEY\n @property\n def OPENROUTER_API_KEY(self): return get('OPENROUTER_API_KEY') or self.LLM_API_KEY\n @property\n def KIMI_API_KEY(self): return get('KIMI_API_KEY') or self.LLM_API_KEY\n @property\n def COHERE_API_KEY(self): return get('COHERE_API_KEY') or self.LLM_API_KEY\n @property\n def KLUSTER_API_KEY(self): return get('KLUSTER_API_KEY') or self.LLM_API_KEY\n @property\n def LLAMA_API_KEY(self): return get('LLAMA_API_KEY') or self.LLM_API_KEY\n @property\n def PERPLEXITY_API_KEY(self): return get('PERPLEXITY_API_KEY') or self.LLM_API_KEY\n @property\n def HYPERBOLIC_API_KEY(self): return get('HYPERBOLIC_API_KEY') or self.LLM_API_KEY\n @property\n def ANYSCALE_API_KEY(self): return get('ANYSCALE_API_KEY') or self.LLM_API_KEY\n @property\n def NVIDIA_API_KEY(self): return get('NVIDIA_API_KEY') or self.LLM_API_KEY\n @property\n def ALIBABA_API_KEY(self): return get('ALIBABA_API_KEY') or self.LLM_API_KEY\n @property\n def ZHIPU_API_KEY(self): return get('ZHIPU_API_KEY') or self.LLM_API_KEY\n @property\n def STEPFUN_API_KEY(self): return get('STEPFUN_API_KEY') or self.LLM_API_KEY\n @property\n def SILICONFLOW_API_KEY(self): return get('SILICONFLOW_API_KEY') or self.LLM_API_KEY\n @property\n def YI_API_KEY(self): return get('YI_API_KEY') or self.LLM_API_KEY\n @property\n def MOONSHOT_API_KEY(self): return get('MOONSHOT_API_KEY') or self.LLM_API_KEY\n @property\n def MINIMAX_API_KEY(self): return get('MINIMAX_API_KEY') or self.LLM_API_KEY\n @property\n def WRITER_API_KEY(self): return get('WRITER_API_KEY') or self.LLM_API_KEY\n @property\n def AI21_API_KEY(self): return get('AI21_API_KEY') or self.LLM_API_KEY\n @property\n def REKA_API_KEY(self): return get('REKA_API_KEY') or self.LLM_API_KEY\n @property\n def UPSTAGE_API_KEY(self): return get('UPSTAGE_API_KEY') or self.LLM_API_KEY\n\n def get_key(self, provider: str) -> str:\n \"\"\"Возвращает ключ для конкретного провайдера.\"\"\"\n mapping = {\n 'openai': 'OPENAI_API_KEY',\n 'mistral': 'MISTRAL_API_KEY',\n 'groq': 'GROQ_API_KEY',\n 'deepseek': 'DEEPSEEK_API_KEY',\n 'claude': 'ANTHROPIC_API_KEY',\n 'anthropic': 'ANTHROPIC_API_KEY',\n 'openrouter': 'OPENROUTER_API_KEY',\n 'xai': 'XAI_API_KEY',\n 'grok': 'XAI_API_KEY',\n 'gemini': 'GEMINI_API_KEY',\n }\n attr = mapping.get(provider.lower())\n if attr:\n specific = get(attr, '')\n if specific:\n return specific\n return self.LLM_API_KEY # fallback\n\n # ── Settable properties (runtime + .env) ─────────────────\n @property\n def LLM_PROVIDER(self): return get('LLM_PROVIDER', 'openai').lower().strip()\n @LLM_PROVIDER.setter\n def LLM_PROVIDER(self, v): _set_env('LLM_PROVIDER', v)\n\n @property\n def LLM_MODEL(self): return get('LLM_MODEL', 'gpt-4o-mini').strip()\n @LLM_MODEL.setter\n def LLM_MODEL(self, v): _set_env('LLM_MODEL', v)\n\n @property\n def LLM_API_KEY(self): return get('LLM_API_KEY', '')\n @LLM_API_KEY.setter\n def LLM_API_KEY(self, v): _set_env('LLM_API_KEY', v)\n\n # ── Специализированные провайдеры ─────────────────────────\n # Если не заданы — фоллбэк на основной LLM\n @property\n def CODE_PROVIDER(self):\n return get('CODE_PROVIDER', '') or self.LLM_PROVIDER\n @CODE_PROVIDER.setter\n def CODE_PROVIDER(self, v): _set_env('CODE_PROVIDER', v)\n\n @property\n def CODE_MODEL(self):\n return get('CODE_MODEL', '') or self.LLM_MODEL\n @CODE_MODEL.setter\n def CODE_MODEL(self, v): _set_env('CODE_MODEL', v)\n\n @property\n def AGENT_PROVIDER(self):\n return get('AGENT_PROVIDER', '') or self.LLM_PROVIDER\n @AGENT_PROVIDER.setter\n def AGENT_PROVIDER(self, v): _set_env('AGENT_PROVIDER', v)\n\n @property\n def AGENT_MODEL(self):\n return get('AGENT_MODEL', '') or self.LLM_MODEL\n @AGENT_MODEL.setter\n def AGENT_MODEL(self, v): _set_env('AGENT_MODEL', v)\n\n @property\n def IMAGE_PROVIDER(self):\n return get('IMAGE_PROVIDER', 'auto').lower().strip()\n @IMAGE_PROVIDER.setter\n def IMAGE_PROVIDER(self, v): _set_env('IMAGE_PROVIDER', v)\n\n @property\n def HF_API_KEY(self): return get('HF_API_KEY', '') or self.LLM_API_KEY\n @HF_API_KEY.setter\n def HF_API_KEY(self, v): _set_env('HF_API_KEY', v)\n\n @property\n def DEEPGRAM_API_KEY(self): return get('DEEPGRAM_API_KEY', '') or self.LLM_API_KEY\n @property\n def STABILITY_API_KEY(self): return get('STABILITY_API_KEY', '')\n\n @property\n def TTS_PROVIDER(self): return get('TTS_PROVIDER', 'edge').lower().strip()\n @TTS_PROVIDER.setter\n def TTS_PROVIDER(self, v): _set_env('TTS_PROVIDER', v)\n\n @property\n def TTS_VOICE(self): return get('TTS_VOICE', 'ru-RU-DmitryNeural')\n @TTS_VOICE.setter\n def TTS_VOICE(self, v): _set_env('TTS_VOICE', v)\n\n def reload(self):\n _load()\n print(f\" 🔄 Config reloaded: {self.LLM_PROVIDER} / {self.LLM_MODEL}\", flush=True)\n\n def set_llm(self, provider: str, model: str, api_key: str = ''):\n \"\"\"Удобный метод: меняет провайдер + модель + ключ одним вызовом.\"\"\"\n self.LLM_PROVIDER = provider.lower().strip()\n self.LLM_MODEL = model.strip()\n if api_key:\n self.LLM_API_KEY = api_key.strip()\n print(f\" ✅ LLM: {self.LLM_PROVIDER} / {self.LLM_MODEL}\", flush=True)\n\n # ════════════════════════════════════════════════════════════\n # BlackBugsAI — Brand / Proxy / Billing / Plans\n # ════════════════════════════════════════════════════════════\n\n BRAND = {\n 'name': 'BlackBugsAI',\n 'tagline': 'Autonomous AI Agent Platform',\n 'version': '1.0.0',\n 'emoji': '🖤🐛',\n 'url': 'https://blackbugsai.com',\n 'support': '@blackbugsai_support',\n }\n\n # ── Proxy / Tor ──────────────────────────────────────────────\n @property\n def PROXY(self) -> dict:\n return {\n 'enabled': get('PROXY_ENABLED', '').lower() == 'true',\n 'url': get('PROXY_URL', ''),\n 'tor': get('TOR_ENABLED', '').lower() == 'true',\n 'tor_port': int(get('TOR_PORT', '9050')),\n 'rotate': get('PROXY_ROTATE', '').lower() == 'true',\n }\n\n @property\n def PROXY_ENABLED(self) -> bool:\n return get('PROXY_ENABLED', '').lower() == 'true'\n\n # ── Admin Web ────────────────────────────────────────────────\n @property\n def ADMIN_WEB_PORT(self) -> int:\n return int(get('ADMIN_WEB_PORT', '8080'))\n\n @property\n def ADMIN_WEB_TOKEN(self) -> str:\n return get('ADMIN_WEB_TOKEN', 'changeme_secret_token')\n\n # ── Auth / JWT ───────────────────────────────────────────────\n @property\n def JWT_SECRET(self) -> str:\n return get('JWT_SECRET', 'change_me_in_production')\n\n @property\n def JWT_EXPIRE_HOURS(self) -> int:\n return int(get('JWT_EXPIRE_HOURS', '24'))\n\n # ── Billing ──────────────────────────────────────────────────\n @property\n def BILLING_ENABLED(self) -> bool:\n return get('BILLING_ENABLED', 'false').lower() == 'true'\n\n @property\n def BILLING_PROVIDER(self) -> str:\n return get('BILLING_PROVIDER', 'telegram_stars')\n\n # ── Plans ────────────────────────────────────────────────────\n PLANS = {\n 'free': {\n 'name': '🆓 Free',\n 'price': 0,\n 'tasks_per_day': 10,\n 'tools': ['tts', 'pollinations_image', 'web_search',\n 'python_sandbox', 'chat', 'fetch_url'],\n 'file_mb': 10,\n 'agent_rounds': 3,\n 'history_msgs': 20,\n },\n 'pro': {\n 'name': '⭐ Pro',\n 'price': 9.99,\n 'tasks_per_day': 100,\n 'tools': ['*'],\n 'file_mb': 500,\n 'agent_rounds': 10,\n 'history_msgs': 50,\n },\n 'business': {\n 'name': '🚀 Business',\n 'price': 49.99,\n 'tasks_per_day': -1,\n 'tools': ['*'],\n 'file_mb': -1,\n 'agent_rounds': 20,\n 'history_msgs': 100,\n 'api_access': True,\n 'white_label': True,\n },\n 'enterprise': {\n 'name': '🏢 Enterprise',\n 'price': 0,\n 'tasks_per_day': -1,\n 'tools': ['*'],\n 'file_mb': -1,\n 'agent_rounds': -1,\n 'history_msgs': 200,\n 'dedicated': True,\n 'sla': True,\n },\n }\n\n # ── Sandbox ──────────────────────────────────────────────────\n @property\n def SANDBOX(self) -> dict:\n return {\n 'timeout_default': int(get('SANDBOX_TIMEOUT', '30')),\n 'timeout_max': int(get('SANDBOX_TIMEOUT_MAX', '300')),\n 'docker_enabled': get('DOCKER_SANDBOX', '').lower() == 'true',\n }\n\n # ── Queue ────────────────────────────────────────────────────\n @property\n def QUEUE_WORKERS(self) -> int:\n return int(get('QUEUE_WORKERS', '2'))\n\n @property\n def REDIS_URL(self) -> str:\n return get('REDIS_URL', '')\n\n def get_plan(self, plan_name: str) -> dict:\n \"\"\"Возвращает конфиг тарифного плана.\"\"\"\n return self.PLANS.get(plan_name, self.PLANS['free'])\n\n def plan_allows_tool(self, plan_name: str, tool_name: str) -> bool:\n \"\"\"Проверяет доступность инструмента для тарифа.\"\"\"\n plan = self.get_plan(plan_name)\n allowed = plan.get('tools', [])\n return '*' in allowed or tool_name in allowed\n\n def startup_banner(self) -> str:\n \"\"\"ASCII-баннер при запуске.\"\"\"\n b = self.BRAND\n lines = [\n \"\",\n \" ██████╗ ██╗ █████╗ ██████╗██╗ ██╗██████╗ ██╗ ██╗ ██████╗ ███████╗\",\n \" ██╔══██╗██║ ██╔══██╗██╔════╝██║ ██╔╝██╔══██╗██║ ██║██╔════╝ ██╔════╝\",\n \" ██████╔╝██║ ███████║██║ █████╔╝ ██████╔╝██║ ██║██║ ███╗███████╗ \",\n \" ██╔══██╗██║ ██╔══██║██║ ██╔═██╗ ██╔══██╗██║ ██║██║ ██║╚════██║\",\n \" ██████╔╝███████╗██║ ██║╚██████╗██║ ██╗██████╔╝╚██████╔╝╚██████╔╝███████║\",\n \" ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝╚══════╝\",\n \"\",\n f\" {b['emoji']} {b['name']} — {b['tagline']} v{b['version']}\",\n \"\",\n ]\n return \"\\n\".join(lines)\n\n def __setattr__(self, key, value):\n \"\"\"Fallback setattr — на случай если @property.setter не сработал.\"\"\"\n _set_env(key, str(value))\n\nimport sys\nsys.modules[__name__] = _Config()\n"}}, "docker-compose.ymlBK": {"4e4c9b4c0abae2677a1f3ca964356909fd94720b": {"t": 1785746976.2042406, "n": 4569, "h": "498d3838f3a2125d", "c": "# ═══════════════════════════════════════════════════════════════════════════\n# BlackBugsAI — Docker Compose v3.0 (PRODUCTION)\n# \n# АРХИТЕКТУРА:\n# nginx:80 → admin_web:8080 (/)\n# nginx:80 → fish_web:5100 (/fish/*) ← изолирован, нет конфликта туннелей\n# Один туннель → nginx:80\n#\n# ЗАПУСК:\n# cp .env.example .env && nano .env\n# docker compose -f docker-compose.v3.yml up -d\n#\n# GCP SSH:\n# bash deploy_gcp.sh\n# ═══════════════════════════════════════════════════════════════════════════\n\n\nservices:\n\n # ── Nginx — единственная точка входа ─────────────────────────────────────\n nginx:\n image: nginx:alpine\n container_name: blackbugs-nginx\n restart: unless-stopped\n ports:\n - \"${HTTP_PORT:-80}:80\"\n volumes:\n - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro\n depends_on:\n blackbugs:\n condition: service_started\n networks:\n - blackbugs-net\n logging: &log-opts\n driver: \"json-file\"\n options: {max-size: \"20m\", max-file: \"3\"}\n\n # ── BlackBugsAI Main ─────────────────────────────────────────────────────\n blackbugs:\n build:\n context: .\n dockerfile: Dockerfile.v3\n image: blackbugsai:v3\n container_name: blackbugs-main\n restart: unless-stopped\n\n environment:\n # ── Telegram ──────────────────────────────────────────────────────────\n - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}\n - TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID:-}\n\n # ── LLM ───────────────────────────────────────────────────────────────\n - LLM_PROVIDER=${LLM_PROVIDER:-deepseek}\n - LLM_MODEL=${LLM_MODEL:-deepseek-coder}\n - LLM_API_KEY=${LLM_API_KEY:-}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-}\n - OPENAI_API_KEY=${OPENAI_API_KEY:-}\n - GROQ_API_KEY=${GROQ_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n\n # ── Admin Panel ───────────────────────────────────────────────────────\n - ADMIN_WEB_PORT=8080\n - ADMIN_WEB_TOKEN=${ADMIN_WEB_TOKEN:?ADMIN_WEB_TOKEN required}\n - SECRET_KEY=${SECRET_KEY:-blackbugs_change_in_prod}\n\n # ── Fish module ───────────────────────────────────────────────────────\n # Fish слушает на 5100 (НЕ 5000!), nginx проксирует /fish/*\n # TUNNEL управляется ТОЛЬКО admin_web — у fish нет прав на tunnel!\n - FISH_SERVER_PORT=5100\n - FISH_TUNNEL_DISABLED=true # ← Ключевой фикс конфликта\n\n # ── Tunnel ────────────────────────────────────────────────────────────\n - TUNNEL_PROVIDER=${TUNNEL_PROVIDER:-cloudflared}\n - TUNNEL_TARGET_PORT=80 # ← Туннель → nginx, не напрямую на 8080\n - AUTO_TUNNEL=${AUTO_TUNNEL:-false}\n - BORE_SERVER=${BORE_SERVER:-bore.pub}\n\n # ── TTS ───────────────────────────────────────────────────────────────\n - TTS_PROVIDER=${TTS_PROVIDER:-edge}\n - TTS_VOICE=${TTS_VOICE:-ru-RU-DmitryNeural}\n\n # ── Misc ──────────────────────────────────────────────────────────────\n - PYTHONUNBUFFERED=1\n - TZ=${TZ:-UTC}\n\n # Порты только для debug — в prod nginx → container\n expose:\n - \"8080\" # Admin\n - \"5100\" # Fish (только internal)\n\n # Прямой доступ к admin если nginx не нужен\n ports:\n - \"${ADMIN_DIRECT_PORT:-8080}:8080\"\n\n volumes:\n - blackbugs-data:/app/data\n - blackbugs-matrix:/app/matrix_workspace\n - blackbugs-neo:/app/neo_workspace\n - blackbugs-fish:/app/fish_data\n - blackbugs-logs:/app/logs\n - /var/run/docker.sock:/var/run/docker.sock:ro # для docker-in-docker тулзов\n\n networks:\n - blackbugs-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8080/health\"]\n interval: 20s\n timeout: 8s\n start_period: 180s\n retries: 5\n\n logging: *log-opts\n\n # ── Watchtower — авто-обновление образа ─────────────────────────────────\n # watchtower:\n # image: containrrr/watchtower\n # container_name: blackbugs-watcher\n # restart: unless-stopped\n # volumes:\n # - /var/run/docker.sock:/var/run/docker.sock\n # command: --interval 3600 --cleanup blackbugs-main\n # networks: [blackbugs-net]\n\n# ── Networks ──────────────────────────────────────────────────────────────\nnetworks:\n blackbugs-net:\n driver: bridge\n ipam:\n config:\n - subnet: 172.20.0.0/24\n\n# ── Volumes ───────────────────────────────────────────────────────────────\nvolumes:\n blackbugs-data: {name: blackbugs-data}\n blackbugs-matrix: {name: blackbugs-matrix}\n blackbugs-neo: {name: blackbugs-neo}\n blackbugs-fish: {name: blackbugs-fish}\n blackbugs-logs: {name: blackbugs-logs}\n"}, "7b69b5edc3fcf34eea3a26955767864cb6c853f5": {"t": 1785769476.797438, "n": 4569, "h": "498d3838f3a2125d", "c": "# ═══════════════════════════════════════════════════════════════════════════\n# BlackBugsAI — Docker Compose v3.0 (PRODUCTION)\n# \n# АРХИТЕКТУРА:\n# nginx:80 → admin_web:8080 (/)\n# nginx:80 → fish_web:5100 (/fish/*) ← изолирован, нет конфликта туннелей\n# Один туннель → nginx:80\n#\n# ЗАПУСК:\n# cp .env.example .env && nano .env\n# docker compose -f docker-compose.v3.yml up -d\n#\n# GCP SSH:\n# bash deploy_gcp.sh\n# ═══════════════════════════════════════════════════════════════════════════\n\n\nservices:\n\n # ── Nginx — единственная точка входа ─────────────────────────────────────\n nginx:\n image: nginx:alpine\n container_name: blackbugs-nginx\n restart: unless-stopped\n ports:\n - \"${HTTP_PORT:-80}:80\"\n volumes:\n - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro\n depends_on:\n blackbugs:\n condition: service_started\n networks:\n - blackbugs-net\n logging: &log-opts\n driver: \"json-file\"\n options: {max-size: \"20m\", max-file: \"3\"}\n\n # ── BlackBugsAI Main ─────────────────────────────────────────────────────\n blackbugs:\n build:\n context: .\n dockerfile: Dockerfile.v3\n image: blackbugsai:v3\n container_name: blackbugs-main\n restart: unless-stopped\n\n environment:\n # ── Telegram ──────────────────────────────────────────────────────────\n - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}\n - TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID:-}\n\n # ── LLM ───────────────────────────────────────────────────────────────\n - LLM_PROVIDER=${LLM_PROVIDER:-deepseek}\n - LLM_MODEL=${LLM_MODEL:-deepseek-coder}\n - LLM_API_KEY=${LLM_API_KEY:-}\n - DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY:-}\n - OPENAI_API_KEY=${OPENAI_API_KEY:-}\n - GROQ_API_KEY=${GROQ_API_KEY:-}\n - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}\n\n # ── Admin Panel ───────────────────────────────────────────────────────\n - ADMIN_WEB_PORT=8080\n - ADMIN_WEB_TOKEN=${ADMIN_WEB_TOKEN:?ADMIN_WEB_TOKEN required}\n - SECRET_KEY=${SECRET_KEY:-blackbugs_change_in_prod}\n\n # ── Fish module ───────────────────────────────────────────────────────\n # Fish слушает на 5100 (НЕ 5000!), nginx проксирует /fish/*\n # TUNNEL управляется ТОЛЬКО admin_web — у fish нет прав на tunnel!\n - FISH_SERVER_PORT=5100\n - FISH_TUNNEL_DISABLED=true # ← Ключевой фикс конфликта\n\n # ── Tunnel ────────────────────────────────────────────────────────────\n - TUNNEL_PROVIDER=${TUNNEL_PROVIDER:-cloudflared}\n - TUNNEL_TARGET_PORT=80 # ← Туннель → nginx, не напрямую на 8080\n - AUTO_TUNNEL=${AUTO_TUNNEL:-false}\n - BORE_SERVER=${BORE_SERVER:-bore.pub}\n\n # ── TTS ───────────────────────────────────────────────────────────────\n - TTS_PROVIDER=${TTS_PROVIDER:-edge}\n - TTS_VOICE=${TTS_VOICE:-ru-RU-DmitryNeural}\n\n # ── Misc ──────────────────────────────────────────────────────────────\n - PYTHONUNBUFFERED=1\n - TZ=${TZ:-UTC}\n\n # Порты только для debug — в prod nginx → container\n expose:\n - \"8080\" # Admin\n - \"5100\" # Fish (только internal)\n\n # Прямой доступ к admin если nginx не нужен\n ports:\n - \"${ADMIN_DIRECT_PORT:-8080}:8080\"\n\n volumes:\n - blackbugs-data:/app/data\n - blackbugs-matrix:/app/matrix_workspace\n - blackbugs-neo:/app/neo_workspace\n - blackbugs-fish:/app/fish_data\n - blackbugs-logs:/app/logs\n - /var/run/docker.sock:/var/run/docker.sock:ro # для docker-in-docker тулзов\n\n networks:\n - blackbugs-net\n healthcheck:\n test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8080/health\"]\n interval: 20s\n timeout: 8s\n start_period: 180s\n retries: 5\n\n logging: *log-opts\n\n # ── Watchtower — авто-обновление образа ─────────────────────────────────\n # watchtower:\n # image: containrrr/watchtower\n # container_name: blackbugs-watcher\n # restart: unless-stopped\n # volumes:\n # - /var/run/docker.sock:/var/run/docker.sock\n # command: --interval 3600 --cleanup blackbugs-main\n # networks: [blackbugs-net]\n\n# ── Networks ──────────────────────────────────────────────────────────────\nnetworks:\n blackbugs-net:\n driver: bridge\n ipam:\n config:\n - subnet: 172.20.0.0/24\n\n# ── Volumes ───────────────────────────────────────────────────────────────\nvolumes:\n blackbugs-data: {name: blackbugs-data}\n blackbugs-matrix: {name: blackbugs-matrix}\n blackbugs-neo: {name: blackbugs-neo}\n blackbugs-fish: {name: blackbugs-fish}\n blackbugs-logs: {name: blackbugs-logs}\n"}}}}