1 line
106 KiB
JSON
1 line
106 KiB
JSON
{"repo": "allenygy/gagent", "files": {".env.example": {"51052fdf77c8e32c76578a4efdd8884cb4f03e97": {"t": 1785746731.1381478, "n": 3349, "h": "ad966ee07f16154b", "c": "# Copy this file to `.env`, fill in the secrets you intend to use locally,\n# and NEVER commit the populated file.\n\n# =============================================================================\n# LLM Provider selection + global retry/backoff\n# =============================================================================\n# e.g. glm | qwen | perplexity | doubao | moonshot | deepseek | grok | gemini\nLLM_PROVIDER=glm\nLLM_RETRIES=3\nLLM_BACKOFF_BASE=0.5\nLLM_REQUEST_TIMEOUT=60\n\n# Task-specific model defaults (used by decomposer/executor if not overridden)\nDECOMP_MODEL=qwen3-max\nPLAN_EXECUTOR_MODEL=qwen3-max\n\n# =============================================================================\n# Provider-specific credentials\n# Only fill the ones you actually use. Leave others blank.\n# =============================================================================\n\n## Qwen (high quota / high concurrency)\nQWEN_API_KEY=\nQWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\nQWEN_MODEL=qwen3-max\n\n## GLM (fallback /-compatible default)\nGLM_API_KEY=\nGLM_API_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions\nGLM_MODEL=glm-4-flash\n\n## Perplexity\nPERPLEXITY_API_KEY=\nPERPLEXITY_API_URL=https://api.perplexity.ai/chat/completions\nPERPLEXITY_MODEL=sonar-reasoning-pro\n\n## Doubao (Volcengine Ark)\nDOUBAO_API_KEY=\nDOUBAO_API_URL=https://ark.cn-beijing.volces.com/api/v3\nDOUBAO_MODEL=doubao-seed-1-6-251015\n\n## Moonshot / Kimi\nMOONSHOT_API_KEY=\nMOONSHOT_API_URL=https://api.moonshot.cn/v1\nMOONSHOT_MODEL=kimi-k2-turbo-preview\n\n## DeepSeek\nDEEPSEEK_API_KEY=\nDEEPSEEK_API_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n\n## Grok / xAI\nGROK_API_KEY=\nGROK_API_URL=https://api.x.ai/v1\nGROK_MODEL=grok-4\n\n## Gemini (OpenAI-compatible endpoint)\nGEMINI_API_KEY=\nGEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/openai/\nGEMINI_MODEL=gemini-2.5-flash\n\n## Other optional providers already supported elsewhere\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=\nTAVILY_API_KEY=\n\n# =============================================================================\n# Springer Nature\n# =============================================================================\nSPRINGER_META_API_KEY=\nSPRINGER_OPENACCESS_API_KEY=\n\n# =============================================================================\n# MultiRAG\n# =============================================================================\nMULTIRAG_BASE_URL=\nMULTIRAG_API_KEY=\nMULTIRAG_QUERY_TIMEOUT_SECONDS=420\nMULTIRAG_HEALTH_TIMEOUT_SECONDS=5\nMULTIRAG_HEALTH_CACHE_TTL=60\nGRAPH_RAG_CACHE_TTL=900\n\n\n# =============================================================================\n# Core application configuration\n# =============================================================================\nBACKEND_HOST=0.0.0.0\nBACKEND_PORT=9000\nBASE_URL=http://0.0.0.0:9000\nDATABASE_URL=sqlite:///./tasks.db\nLOG_LEVEL=INFO\n\n# Frontend dev server defaults (used for local Vite dev/proxy hints)\nVITE_DEV_SERVER_HOST=0.0.0.0\nVITE_DEV_SERVER_PORT=3001\n# =============================================================================\n# Backend service tuning (timeouts, etc.) can be overridden below as needed.\n# =============================================================================\n# CORS_ORIGINS=http://localhost:3001\n# VITE_API_BASE_URL=http://localhost:9000\n# VITE_WS_BASE_URL=ws://localhost:9000\n"}, "16888d3a5bf21e372b37cd02e3050f738085328d": {"t": 1785767363.8207796, "n": 3349, "h": "ad966ee07f16154b", "c": "# Copy this file to `.env`, fill in the secrets you intend to use locally,\n# and NEVER commit the populated file.\n\n# =============================================================================\n# LLM Provider selection + global retry/backoff\n# =============================================================================\n# e.g. glm | qwen | perplexity | doubao | moonshot | deepseek | grok | gemini\nLLM_PROVIDER=glm\nLLM_RETRIES=3\nLLM_BACKOFF_BASE=0.5\nLLM_REQUEST_TIMEOUT=60\n\n# Task-specific model defaults (used by decomposer/executor if not overridden)\nDECOMP_MODEL=qwen3-max\nPLAN_EXECUTOR_MODEL=qwen3-max\n\n# =============================================================================\n# Provider-specific credentials\n# Only fill the ones you actually use. Leave others blank.\n# =============================================================================\n\n## Qwen (high quota / high concurrency)\nQWEN_API_KEY=\nQWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\nQWEN_MODEL=qwen3-max\n\n## GLM (fallback /-compatible default)\nGLM_API_KEY=\nGLM_API_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions\nGLM_MODEL=glm-4-flash\n\n## Perplexity\nPERPLEXITY_API_KEY=\nPERPLEXITY_API_URL=https://api.perplexity.ai/chat/completions\nPERPLEXITY_MODEL=sonar-reasoning-pro\n\n## Doubao (Volcengine Ark)\nDOUBAO_API_KEY=\nDOUBAO_API_URL=https://ark.cn-beijing.volces.com/api/v3\nDOUBAO_MODEL=doubao-seed-1-6-251015\n\n## Moonshot / Kimi\nMOONSHOT_API_KEY=\nMOONSHOT_API_URL=https://api.moonshot.cn/v1\nMOONSHOT_MODEL=kimi-k2-turbo-preview\n\n## DeepSeek\nDEEPSEEK_API_KEY=\nDEEPSEEK_API_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n\n## Grok / xAI\nGROK_API_KEY=\nGROK_API_URL=https://api.x.ai/v1\nGROK_MODEL=grok-4\n\n## Gemini (OpenAI-compatible endpoint)\nGEMINI_API_KEY=\nGEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/openai/\nGEMINI_MODEL=gemini-2.5-flash\n\n## Other optional providers already supported elsewhere\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=\nTAVILY_API_KEY=\n\n# =============================================================================\n# Springer Nature\n# =============================================================================\nSPRINGER_META_API_KEY=\nSPRINGER_OPENACCESS_API_KEY=\n\n# =============================================================================\n# MultiRAG\n# =============================================================================\nMULTIRAG_BASE_URL=\nMULTIRAG_API_KEY=\nMULTIRAG_QUERY_TIMEOUT_SECONDS=420\nMULTIRAG_HEALTH_TIMEOUT_SECONDS=5\nMULTIRAG_HEALTH_CACHE_TTL=60\nGRAPH_RAG_CACHE_TTL=900\n\n\n# =============================================================================\n# Core application configuration\n# =============================================================================\nBACKEND_HOST=0.0.0.0\nBACKEND_PORT=9000\nBASE_URL=http://0.0.0.0:9000\nDATABASE_URL=sqlite:///./tasks.db\nLOG_LEVEL=INFO\n\n# Frontend dev server defaults (used for local Vite dev/proxy hints)\nVITE_DEV_SERVER_HOST=0.0.0.0\nVITE_DEV_SERVER_PORT=3001\n# =============================================================================\n# Backend service tuning (timeouts, etc.) can be overridden below as needed.\n# =============================================================================\n# CORS_ORIGINS=http://localhost:3001\n# VITE_API_BASE_URL=http://localhost:9000\n# VITE_WS_BASE_URL=ws://localhost:9000\n"}, "406b44d8ba6b5eb491429d7009d7771b9ed2d346": {"t": 1785767365.0047827, "n": 3018, "h": "aa7e63a994ab7dec", "c": "# Copy this file to `.env`, fill in the secrets you intend to use locally,\n# and NEVER commit the populated file.\n\n# =============================================================================\n# LLM Provider selection + global retry/backoff\n# =============================================================================\n# e.g. glm | qwen | perplexity | doubao | moonshot | deepseek | grok | gemini\nLLM_PROVIDER=glm\nLLM_RETRIES=3\nLLM_BACKOFF_BASE=0.5\nLLM_REQUEST_TIMEOUT=60\n\n# Task-specific model defaults (used by decomposer/executor if not overridden)\nDECOMP_MODEL=qwen3-max\nPLAN_EXECUTOR_MODEL=qwen3-max\n\n# =============================================================================\n# Provider-specific credentials\n# Only fill the ones you actually use. Leave others blank.\n# =============================================================================\n\n## Qwen (high quota / high concurrency)\nQWEN_API_KEY=\nQWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\nQWEN_MODEL=qwen3-max\n\n## GLM (fallback /-compatible default)\nGLM_API_KEY=\nGLM_API_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions\nGLM_MODEL=glm-4-flash\n\n## Perplexity\nPERPLEXITY_API_KEY=\nPERPLEXITY_API_URL=https://api.perplexity.ai/chat/completions\nPERPLEXITY_MODEL=sonar-reasoning-pro\n\n## Doubao (Volcengine Ark)\nDOUBAO_API_KEY=\nDOUBAO_API_URL=https://ark.cn-beijing.volces.com/api/v3\nDOUBAO_MODEL=doubao-seed-1-6-251015\n\n## Moonshot / Kimi\nMOONSHOT_API_KEY=\nMOONSHOT_API_URL=https://api.moonshot.cn/v1\nMOONSHOT_MODEL=kimi-k2-turbo-preview\n\n## DeepSeek\nDEEPSEEK_API_KEY=\nDEEPSEEK_API_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n\n## Grok / xAI\nGROK_API_KEY=\nGROK_API_URL=https://api.x.ai/v1\nGROK_MODEL=grok-4\n\n## Gemini (OpenAI-compatible endpoint)\nGEMINI_API_KEY=\nGEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/openai/\nGEMINI_MODEL=gemini-2.5-flash\n\n## Other optional providers already supported elsewhere\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=\nTAVILY_API_KEY=\n\n# =============================================================================\n# Springer Nature\n# =============================================================================\nSPRINGER_META_API_KEY=\nSPRINGER_OPENACCESS_API_KEY=\n\n\n# =============================================================================\n# Core application configuration\n# =============================================================================\nBACKEND_HOST=0.0.0.0\nBACKEND_PORT=9000\nBASE_URL=http://0.0.0.0:9000\nDATABASE_URL=sqlite:///./tasks.db\nLOG_LEVEL=INFO\n\n# Frontend dev server defaults (used for local Vite dev/proxy hints)\nVITE_DEV_SERVER_HOST=0.0.0.0\nVITE_DEV_SERVER_PORT=3001\n# =============================================================================\n# Backend service tuning (timeouts, etc.) can be overridden below as needed.\n# =============================================================================\n# CORS_ORIGINS=http://localhost:3001\n# VITE_API_BASE_URL=http://localhost:9000\n# VITE_WS_BASE_URL=ws://localhost:9000\n"}, "2755eec0bd9fdf5e244cc01348129e1b4b89df12": {"t": 1785767366.4603398, "n": 2786, "h": "7b2fd52eb9be1bdc", "c": "# Copy this file to `.env`, fill in the secrets you intend to use locally,\n# and NEVER commit the populated file.\n\n# =============================================================================\n# LLM Provider selection + global retry/backoff\n# =============================================================================\n# e.g. glm | qwen | perplexity | doubao | moonshot | deepseek | grok | gemini\nLLM_PROVIDER=glm\nLLM_RETRIES=3\nLLM_BACKOFF_BASE=0.5\nLLM_REQUEST_TIMEOUT=60\n\n# Task-specific model defaults (used by decomposer/executor if not overridden)\nDECOMP_MODEL=qwen3-max\nPLAN_EXECUTOR_MODEL=qwen3-max\n\n# =============================================================================\n# Provider-specific credentials\n# Only fill the ones you actually use. Leave others blank.\n# =============================================================================\n\n## Qwen (high quota / high concurrency)\nQWEN_API_KEY=\nQWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\nQWEN_MODEL=qwen3-max\n\n## GLM (fallback /-compatible default)\nGLM_API_KEY=\nGLM_API_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions\nGLM_MODEL=glm-4-flash\n\n## Perplexity\nPERPLEXITY_API_KEY=\nPERPLEXITY_API_URL=https://api.perplexity.ai/chat/completions\nPERPLEXITY_MODEL=sonar-reasoning-pro\n\n## Doubao (Volcengine Ark)\nDOUBAO_API_KEY=\nDOUBAO_API_URL=https://ark.cn-beijing.volces.com/api/v3\nDOUBAO_MODEL=doubao-seed-1-6-251015\n\n## Moonshot / Kimi\nMOONSHOT_API_KEY=\nMOONSHOT_API_URL=https://api.moonshot.cn/v1\nMOONSHOT_MODEL=kimi-k2-turbo-preview\n\n## DeepSeek\nDEEPSEEK_API_KEY=\nDEEPSEEK_API_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n\n## Grok / xAI\nGROK_API_KEY=\nGROK_API_URL=https://api.x.ai/v1\nGROK_MODEL=grok-4\n\n## Gemini (OpenAI-compatible endpoint)\nGEMINI_API_KEY=\nGEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/openai/\nGEMINI_MODEL=gemini-2.5-flash\n\n## Other optional providers already supported elsewhere\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=\nTAVILY_API_KEY=\n\n\n# =============================================================================\n# Core application configuration\n# =============================================================================\nBACKEND_HOST=0.0.0.0\nBACKEND_PORT=9000\nBASE_URL=http://0.0.0.0:9000\nDATABASE_URL=sqlite:///./tasks.db\nLOG_LEVEL=INFO\n\n# Frontend dev server defaults (used for local Vite dev/proxy hints)\nVITE_DEV_SERVER_HOST=0.0.0.0\nVITE_DEV_SERVER_PORT=3001\n# =============================================================================\n# Backend service tuning (timeouts, etc.) can be overridden below as needed.\n# =============================================================================\n# CORS_ORIGINS=http://localhost:3001\n# VITE_API_BASE_URL=http://localhost:9000\n# VITE_WS_BASE_URL=ws://localhost:9000"}, "8ba709eface0542608e5320aa934ba9ed155a74f": {"t": 1785767367.668171, "n": 2787, "h": "325e476dd584782a", "c": "# Copy this file to `.env`, fill in the secrets you intend to use locally,\n# and NEVER commit the populated file.\n\n# =============================================================================\n# Core application configuration\n# =============================================================================\nBACKEND_HOST=0.0.0.0\nBACKEND_PORT=9000\nBASE_URL=http://0.0.0.0:9000\nDATABASE_URL=sqlite:///./tasks.db\nLOG_LEVEL=INFO\n\n# Frontend dev server defaults (used for local Vite dev/proxy hints)\nVITE_DEV_SERVER_HOST=0.0.0.0\nVITE_DEV_SERVER_PORT=3001\n\n\n# =============================================================================\n# LLM Provider selection + global retry/backoff\n# =============================================================================\n# e.g. glm | qwen | perplexity | doubao | moonshot | deepseek | grok | gemini\nLLM_PROVIDER=glm\nLLM_RETRIES=3\nLLM_BACKOFF_BASE=0.5\nLLM_REQUEST_TIMEOUT=60\n\n# Task-specific model defaults (used by decomposer/executor if not overridden)\nDECOMP_MODEL=qwen3-max\nPLAN_EXECUTOR_MODEL=qwen3-max\n\n# =============================================================================\n# Provider-specific credentials\n# Only fill the ones you actually use. Leave others blank.\n# =============================================================================\n\n## Qwen (high quota / high concurrency)\nQWEN_API_KEY=\nQWEN_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\nQWEN_MODEL=qwen3-max\n\n## GLM (fallback /-compatible default)\nGLM_API_KEY=\nGLM_API_URL=https://open.bigmodel.cn/api/paas/v4/chat/completions\nGLM_MODEL=glm-4-flash\n\n## Perplexity\nPERPLEXITY_API_KEY=\nPERPLEXITY_API_URL=https://api.perplexity.ai/chat/completions\nPERPLEXITY_MODEL=sonar-reasoning-pro\n\n## Doubao (Volcengine Ark)\nDOUBAO_API_KEY=\nDOUBAO_API_URL=https://ark.cn-beijing.volces.com/api/v3\nDOUBAO_MODEL=doubao-seed-1-6-251015\n\n## Moonshot / Kimi\nMOONSHOT_API_KEY=\nMOONSHOT_API_URL=https://api.moonshot.cn/v1\nMOONSHOT_MODEL=kimi-k2-turbo-preview\n\n## DeepSeek\nDEEPSEEK_API_KEY=\nDEEPSEEK_API_URL=https://api.deepseek.com\nDEEPSEEK_MODEL=deepseek-chat\n\n## Grok / xAI\nGROK_API_KEY=\nGROK_API_URL=https://api.x.ai/v1\nGROK_MODEL=grok-4\n\n## Gemini (OpenAI-compatible endpoint)\nGEMINI_API_KEY=\nGEMINI_API_URL=https://generativelanguage.googleapis.com/v1beta/openai/\nGEMINI_MODEL=gemini-2.5-flash\n\n## Other optional providers already supported elsewhere\nOPENAI_API_KEY=\nANTHROPIC_API_KEY=\nTAVILY_API_KEY=\n\n# =============================================================================\n# Backend service tuning (timeouts, etc.) can be overridden below as needed.\n# =============================================================================\n# CORS_ORIGINS=http://localhost:3001\n# VITE_API_BASE_URL=http://localhost:9000\n# VITE_WS_BASE_URL=ws://localhost:9000"}}, "app/services/foundation/settings.py": {"51052fdf77c8e32c76578a4efdd8884cb4f03e97": {"t": 1785746917.7038214, "n": 9024, "h": "bde996858aac09bb", "c": "#!/usr/bin/env python3\n\"\"\"Centralized application settings loaded from environment variables.\"\"\"\n\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n _skip = os.getenv(\"SKIP_DOTENV\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\", \"y\"}\n if not _skip:\n load_dotenv(override=True)\nexcept Exception:\n pass\n\n\ndef _env_first(names: list[str], default: Optional[str] = None) -> Optional[str]:\n for name in names:\n if not name:\n continue\n value = os.getenv(name)\n if value:\n return value\n return default\n\n\ndef _env_str(name: str, default: str) -> str:\n return os.getenv(name, default)\n\n\ndef _env_bool(name: str, default: bool = False) -> bool:\n value = os.getenv(name)\n if value is None:\n return default\n return value.strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n\ndef _env_int(name: str, default: int) -> int:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return int(value)\n except Exception:\n return default\n\n\ndef _env_float(name: str, default: float) -> float:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return float(value)\n except Exception:\n return default\n\n\nclass AppSettings:\n \"\"\"Lightweight settings loaded from environment variables.\"\"\"\n\n def __init__(self) -> None:\n # Logging\n self.log_level: str = _env_str(\"LOG_LEVEL\", \"INFO\")\n self.log_format: str = _env_str(\"LOG_FORMAT\", \"json\")\n\n # Database\n self.database_url: str = _env_str(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url: Optional[str] = os.getenv(\"BASE_URL\")\n\n # GLM / LLM\n self.glm_api_key: Optional[str] = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url: str = _env_str(\n \"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n )\n self.glm_model: str = _env_str(\"GLM_MODEL\", \"glm-4-flash\")\n self.glm_request_timeout: int = _env_int(\"GLM_REQUEST_TIMEOUT\", 60)\n self.llm_request_timeout: int = _env_int(\"LLM_REQUEST_TIMEOUT\", 60)\n self.llm_mock: bool = _env_bool(\"LLM_MOCK\", False)\n self.llm_retries: int = _env_int(\"LLM_RETRIES\", 2)\n self.llm_backoff_base: float = _env_float(\"LLM_BACKOFF_BASE\", 0.5)\n\n # Perplexity\n self.perplexity_api_key: Optional[str] = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url: str = _env_str(\n \"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\"\n )\n self.perplexity_model: str = _env_str(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n\n # Doubao\n self.doubao_api_key: Optional[str] = os.getenv(\"DOUBAO_API_KEY\")\n self.doubao_api_url: str = _env_str(\n \"DOUBAO_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\"\n )\n self.doubao_model: str = _env_str(\"DOUBAO_MODEL\", \"doubao-seed-1-6-251015\")\n\n # Moonshot / Kimi\n self.moonshot_api_key: Optional[str] = os.getenv(\"MOONSHOT_API_KEY\")\n self.moonshot_api_url: str = _env_str(\"MOONSHOT_API_URL\", \"https://api.moonshot.cn/v1\")\n self.moonshot_model: str = _env_str(\"MOONSHOT_MODEL\", \"kimi-k2-turbo-preview\")\n\n # DeepSeek\n self.deepseek_api_key: Optional[str] = os.getenv(\"DEEPSEEK_API_KEY\")\n self.deepseek_api_url: str = _env_str(\"DEEPSEEK_API_URL\", \"https://api.deepseek.com\")\n self.deepseek_model: str = _env_str(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n\n # Grok / xAI\n self.grok_api_key: Optional[str] = os.getenv(\"GROK_API_KEY\")\n self.grok_api_url: str = _env_str(\"GROK_API_URL\", \"https://api.x.ai/v1\")\n self.grok_model: str = _env_str(\"GROK_MODEL\", \"grok-4\")\n\n # Gemini\n self.gemini_api_key: Optional[str] = os.getenv(\"GEMINI_API_KEY\")\n self.gemini_api_url: str = _env_str(\n \"GEMINI_API_URL\", \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n )\n self.gemini_model: str = _env_str(\"GEMINI_MODEL\", \"gemini-2.5-flash\")\n\n # Custom OpenAI-compatible gateway\n self.custom_wxx_api_key: Optional[str] = _env_first(\n [\"CUSTOM_WXX_API_KEY\", \"OPENAI_COMPAT_API_KEY\"],\n \"sk-5cc515423d064bfda6f7e18ff433a6c1\",\n )\n self.custom_wxx_api_url: str = (\n _env_first(\n [\"CUSTOM_WXX_API_URL\", \"OPENAI_COMPAT_API_URL\"],\n \"https://trial-units-sms-leeds.trycloudflare.com/v1\",\n )\n or \"https://trial-units-sms-leeds.trycloudflare.com/v1\"\n )\n self.custom_wxx_model: str = (\n _env_first([\"CUSTOM_WXX_MODEL\", \"OPENAI_COMPAT_MODEL\"], \"gemini-3-flash\")\n or \"gemini-3-flash\"\n )\n # Backward compatibility alias\n self.openai_compat_api_key: Optional[str] = self.custom_wxx_api_key\n self.openai_compat_api_url: str = self.custom_wxx_api_url\n self.openai_compat_model: str = self.custom_wxx_model\n\n # QWEN\n self.qwen_api_key: Optional[str] = os.getenv(\"QWEN_API_KEY\")\n self.qwen_api_url: str = _env_str(\n \"QWEN_API_URL\",\n \"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n )\n self.qwen_model: str = _env_str(\"QWEN_MODEL\", \"qwen-turbo\")\n\n # Provider selection\n self.llm_provider: str = _env_str(\"LLM_PROVIDER\", \"glm\")\n\n # Embeddings\n self.glm_embeddings_api_url: Optional[str] = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model: str = _env_str(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n self.glm_embedding_dimension: int = _env_int(\"GLM_EMBEDDING_DIM\", 1536)\n self.glm_batch_size: int = _env_int(\"GLM_BATCH_SIZE\", 25)\n self.semantic_default_k: int = _env_int(\"SEMANTIC_DEFAULT_K\", 5)\n self.semantic_min_similarity: float = _env_float(\"SEMANTIC_MIN_SIMILARITY\", 0.3)\n self.glm_max_retries: int = _env_int(\"GLM_MAX_RETRIES\", 3)\n self.glm_retry_delay: float = _env_float(\"GLM_RETRY_DELAY\", 1.0)\n self.glm_debug: bool = _env_bool(\"GLM_DEBUG\", False)\n\n # Embedding cache\n self.embedding_cache_size: int = _env_int(\"EMBEDDING_CACHE_SIZE\", 10000)\n self.embedding_cache_persistent: bool = _env_bool(\"EMBEDDING_CACHE_PERSISTENT\", True)\n\n # Context/debug flags\n self.ctx_debug: bool = _env_bool(\"CTX_DEBUG\", False) or _env_bool(\"CONTEXT_DEBUG\", False)\n self.budget_debug: bool = _env_bool(\"BUDGET_DEBUG\", False)\n self.decomp_debug: bool = _env_bool(\"DECOMP_DEBUG\", False)\n self.global_index_path: str = _env_str(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # External providers\n self.openai_api_key: Optional[str] = _env_first([\"OPENAI_API_KEY\", \"GPT_API_KEY\"])\n self.xai_api_key: Optional[str] = _env_first([\"XAI_API_KEY\", \"GROK_API_KEY\"])\n self.anthropic_api_key: Optional[str] = _env_first([\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"])\n self.tavily_api_key: Optional[str] = os.getenv(\"TAVILY_API_KEY\")\n\n # Server\n self.backend_host: str = _env_str(\"BACKEND_HOST\", \"0.0.0.0\")\n self.backend_port: int = _env_int(\"BACKEND_PORT\", 9000)\n self.cors_origins: str = _env_str(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n )\n self.chat_include_action_summary: bool = _env_bool(\"CHAT_INCLUDE_ACTION_SUMMARY\", True)\n self.job_log_retention_days: int = _env_int(\"JOB_LOG_RETENTION_DAYS\", 30)\n self.job_log_max_rows: int = _env_int(\"JOB_LOG_MAX_ROWS\", 10000)\n\n # Simulation\n self.sim_user_model: str = _env_str(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model: str = _env_str(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n self.sim_default_turns: int = _env_int(\"SIM_DEFAULT_TURNS\", 5)\n self.sim_max_turns: int = _env_int(\"SIM_MAX_TURNS\", 10)\n self.sim_default_goal: str = _env_str(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n\n # Memory\n self.memory_auto_save_enabled: bool = _env_bool(\"MEMORY_AUTO_SAVE_ENABLED\", True)\n self.memory_retrieve_enabled: bool = _env_bool(\"MEMORY_RETRIEVE_ENABLED\", True)\n self.memory_query_limit: int = _env_int(\"MEMORY_QUERY_LIMIT\", 5)\n self.memory_min_similarity: float = _env_float(\"MEMORY_MIN_SIMILARITY\", 0.6)\n self.memory_text_similarity: float = _env_float(\"MEMORY_TEXT_SIMILARITY\", 1.0)\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"Return cached application settings.\"\"\"\n settings = AppSettings()\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n if not settings.base_url:\n settings.base_url = f\"http://{settings.backend_host}:{settings.backend_port}\"\n\n return settings\n"}, "16888d3a5bf21e372b37cd02e3050f738085328d": {"t": 1785767364.887629, "n": 9024, "h": "bde996858aac09bb", "c": "#!/usr/bin/env python3\n\"\"\"Centralized application settings loaded from environment variables.\"\"\"\n\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n _skip = os.getenv(\"SKIP_DOTENV\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\", \"y\"}\n if not _skip:\n load_dotenv(override=True)\nexcept Exception:\n pass\n\n\ndef _env_first(names: list[str], default: Optional[str] = None) -> Optional[str]:\n for name in names:\n if not name:\n continue\n value = os.getenv(name)\n if value:\n return value\n return default\n\n\ndef _env_str(name: str, default: str) -> str:\n return os.getenv(name, default)\n\n\ndef _env_bool(name: str, default: bool = False) -> bool:\n value = os.getenv(name)\n if value is None:\n return default\n return value.strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n\ndef _env_int(name: str, default: int) -> int:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return int(value)\n except Exception:\n return default\n\n\ndef _env_float(name: str, default: float) -> float:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return float(value)\n except Exception:\n return default\n\n\nclass AppSettings:\n \"\"\"Lightweight settings loaded from environment variables.\"\"\"\n\n def __init__(self) -> None:\n # Logging\n self.log_level: str = _env_str(\"LOG_LEVEL\", \"INFO\")\n self.log_format: str = _env_str(\"LOG_FORMAT\", \"json\")\n\n # Database\n self.database_url: str = _env_str(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url: Optional[str] = os.getenv(\"BASE_URL\")\n\n # GLM / LLM\n self.glm_api_key: Optional[str] = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url: str = _env_str(\n \"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n )\n self.glm_model: str = _env_str(\"GLM_MODEL\", \"glm-4-flash\")\n self.glm_request_timeout: int = _env_int(\"GLM_REQUEST_TIMEOUT\", 60)\n self.llm_request_timeout: int = _env_int(\"LLM_REQUEST_TIMEOUT\", 60)\n self.llm_mock: bool = _env_bool(\"LLM_MOCK\", False)\n self.llm_retries: int = _env_int(\"LLM_RETRIES\", 2)\n self.llm_backoff_base: float = _env_float(\"LLM_BACKOFF_BASE\", 0.5)\n\n # Perplexity\n self.perplexity_api_key: Optional[str] = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url: str = _env_str(\n \"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\"\n )\n self.perplexity_model: str = _env_str(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n\n # Doubao\n self.doubao_api_key: Optional[str] = os.getenv(\"DOUBAO_API_KEY\")\n self.doubao_api_url: str = _env_str(\n \"DOUBAO_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\"\n )\n self.doubao_model: str = _env_str(\"DOUBAO_MODEL\", \"doubao-seed-1-6-251015\")\n\n # Moonshot / Kimi\n self.moonshot_api_key: Optional[str] = os.getenv(\"MOONSHOT_API_KEY\")\n self.moonshot_api_url: str = _env_str(\"MOONSHOT_API_URL\", \"https://api.moonshot.cn/v1\")\n self.moonshot_model: str = _env_str(\"MOONSHOT_MODEL\", \"kimi-k2-turbo-preview\")\n\n # DeepSeek\n self.deepseek_api_key: Optional[str] = os.getenv(\"DEEPSEEK_API_KEY\")\n self.deepseek_api_url: str = _env_str(\"DEEPSEEK_API_URL\", \"https://api.deepseek.com\")\n self.deepseek_model: str = _env_str(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n\n # Grok / xAI\n self.grok_api_key: Optional[str] = os.getenv(\"GROK_API_KEY\")\n self.grok_api_url: str = _env_str(\"GROK_API_URL\", \"https://api.x.ai/v1\")\n self.grok_model: str = _env_str(\"GROK_MODEL\", \"grok-4\")\n\n # Gemini\n self.gemini_api_key: Optional[str] = os.getenv(\"GEMINI_API_KEY\")\n self.gemini_api_url: str = _env_str(\n \"GEMINI_API_URL\", \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n )\n self.gemini_model: str = _env_str(\"GEMINI_MODEL\", \"gemini-2.5-flash\")\n\n # Custom OpenAI-compatible gateway\n self.custom_wxx_api_key: Optional[str] = _env_first(\n [\"CUSTOM_WXX_API_KEY\", \"OPENAI_COMPAT_API_KEY\"],\n \"sk-5cc515423d064bfda6f7e18ff433a6c1\",\n )\n self.custom_wxx_api_url: str = (\n _env_first(\n [\"CUSTOM_WXX_API_URL\", \"OPENAI_COMPAT_API_URL\"],\n \"https://trial-units-sms-leeds.trycloudflare.com/v1\",\n )\n or \"https://trial-units-sms-leeds.trycloudflare.com/v1\"\n )\n self.custom_wxx_model: str = (\n _env_first([\"CUSTOM_WXX_MODEL\", \"OPENAI_COMPAT_MODEL\"], \"gemini-3-flash\")\n or \"gemini-3-flash\"\n )\n # Backward compatibility alias\n self.openai_compat_api_key: Optional[str] = self.custom_wxx_api_key\n self.openai_compat_api_url: str = self.custom_wxx_api_url\n self.openai_compat_model: str = self.custom_wxx_model\n\n # QWEN\n self.qwen_api_key: Optional[str] = os.getenv(\"QWEN_API_KEY\")\n self.qwen_api_url: str = _env_str(\n \"QWEN_API_URL\",\n \"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n )\n self.qwen_model: str = _env_str(\"QWEN_MODEL\", \"qwen-turbo\")\n\n # Provider selection\n self.llm_provider: str = _env_str(\"LLM_PROVIDER\", \"glm\")\n\n # Embeddings\n self.glm_embeddings_api_url: Optional[str] = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model: str = _env_str(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n self.glm_embedding_dimension: int = _env_int(\"GLM_EMBEDDING_DIM\", 1536)\n self.glm_batch_size: int = _env_int(\"GLM_BATCH_SIZE\", 25)\n self.semantic_default_k: int = _env_int(\"SEMANTIC_DEFAULT_K\", 5)\n self.semantic_min_similarity: float = _env_float(\"SEMANTIC_MIN_SIMILARITY\", 0.3)\n self.glm_max_retries: int = _env_int(\"GLM_MAX_RETRIES\", 3)\n self.glm_retry_delay: float = _env_float(\"GLM_RETRY_DELAY\", 1.0)\n self.glm_debug: bool = _env_bool(\"GLM_DEBUG\", False)\n\n # Embedding cache\n self.embedding_cache_size: int = _env_int(\"EMBEDDING_CACHE_SIZE\", 10000)\n self.embedding_cache_persistent: bool = _env_bool(\"EMBEDDING_CACHE_PERSISTENT\", True)\n\n # Context/debug flags\n self.ctx_debug: bool = _env_bool(\"CTX_DEBUG\", False) or _env_bool(\"CONTEXT_DEBUG\", False)\n self.budget_debug: bool = _env_bool(\"BUDGET_DEBUG\", False)\n self.decomp_debug: bool = _env_bool(\"DECOMP_DEBUG\", False)\n self.global_index_path: str = _env_str(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # External providers\n self.openai_api_key: Optional[str] = _env_first([\"OPENAI_API_KEY\", \"GPT_API_KEY\"])\n self.xai_api_key: Optional[str] = _env_first([\"XAI_API_KEY\", \"GROK_API_KEY\"])\n self.anthropic_api_key: Optional[str] = _env_first([\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"])\n self.tavily_api_key: Optional[str] = os.getenv(\"TAVILY_API_KEY\")\n\n # Server\n self.backend_host: str = _env_str(\"BACKEND_HOST\", \"0.0.0.0\")\n self.backend_port: int = _env_int(\"BACKEND_PORT\", 9000)\n self.cors_origins: str = _env_str(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n )\n self.chat_include_action_summary: bool = _env_bool(\"CHAT_INCLUDE_ACTION_SUMMARY\", True)\n self.job_log_retention_days: int = _env_int(\"JOB_LOG_RETENTION_DAYS\", 30)\n self.job_log_max_rows: int = _env_int(\"JOB_LOG_MAX_ROWS\", 10000)\n\n # Simulation\n self.sim_user_model: str = _env_str(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model: str = _env_str(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n self.sim_default_turns: int = _env_int(\"SIM_DEFAULT_TURNS\", 5)\n self.sim_max_turns: int = _env_int(\"SIM_MAX_TURNS\", 10)\n self.sim_default_goal: str = _env_str(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n\n # Memory\n self.memory_auto_save_enabled: bool = _env_bool(\"MEMORY_AUTO_SAVE_ENABLED\", True)\n self.memory_retrieve_enabled: bool = _env_bool(\"MEMORY_RETRIEVE_ENABLED\", True)\n self.memory_query_limit: int = _env_int(\"MEMORY_QUERY_LIMIT\", 5)\n self.memory_min_similarity: float = _env_float(\"MEMORY_MIN_SIMILARITY\", 0.6)\n self.memory_text_similarity: float = _env_float(\"MEMORY_TEXT_SIMILARITY\", 1.0)\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"Return cached application settings.\"\"\"\n settings = AppSettings()\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n if not settings.base_url:\n settings.base_url = f\"http://{settings.backend_host}:{settings.backend_port}\"\n\n return settings\n"}, "b7e0c7324694978138e0fd5e0b5b11d7c7a56f15": {"t": 1785767366.1321206, "n": 7981, "h": "427dccf61fd902c9", "c": "#!/usr/bin/env python3\n\"\"\"Centralized application settings loaded from environment variables.\"\"\"\n\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv(override=True)\nexcept Exception:\n pass\n\n\ndef _env_first(names: list[str], default: Optional[str] = None) -> Optional[str]:\n for name in names:\n if not name:\n continue\n value = os.getenv(name)\n if value:\n return value\n return default\n\n\ndef _env_str(name: str, default: str) -> str:\n return os.getenv(name, default)\n\n\ndef _env_bool(name: str, default: bool = False) -> bool:\n value = os.getenv(name)\n if value is None:\n return default\n return value.strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n\ndef _env_int(name: str, default: int) -> int:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return int(value)\n except Exception:\n return default\n\n\ndef _env_float(name: str, default: float) -> float:\n value = os.getenv(name)\n if value is None:\n return default\n try:\n return float(value)\n except Exception:\n return default\n\n\nclass AppSettings:\n \"\"\"Lightweight settings loaded from environment variables.\"\"\"\n\n def __init__(self) -> None:\n # Logging\n self.log_level: str = _env_str(\"LOG_LEVEL\", \"INFO\")\n self.log_format: str = _env_str(\"LOG_FORMAT\", \"json\")\n\n # Database\n self.database_url: str = _env_str(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url: Optional[str] = os.getenv(\"BASE_URL\")\n\n # GLM / LLM\n self.glm_api_key: Optional[str] = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url: str = _env_str(\n \"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n )\n self.glm_model: str = _env_str(\"GLM_MODEL\", \"glm-4-flash\")\n self.glm_request_timeout: int = _env_int(\"GLM_REQUEST_TIMEOUT\", 60)\n self.llm_request_timeout: int = _env_int(\"LLM_REQUEST_TIMEOUT\", 60)\n self.llm_mock: bool = _env_bool(\"LLM_MOCK\", False)\n self.llm_retries: int = _env_int(\"LLM_RETRIES\", 2)\n self.llm_backoff_base: float = _env_float(\"LLM_BACKOFF_BASE\", 0.5)\n\n # Perplexity\n self.perplexity_api_key: Optional[str] = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url: str = _env_str(\n \"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\"\n )\n self.perplexity_model: str = _env_str(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n\n # Doubao\n self.doubao_api_key: Optional[str] = os.getenv(\"DOUBAO_API_KEY\")\n self.doubao_api_url: str = _env_str(\n \"DOUBAO_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\"\n )\n self.doubao_model: str = _env_str(\"DOUBAO_MODEL\", \"doubao-seed-1-6-251015\")\n\n # Moonshot / Kimi\n self.moonshot_api_key: Optional[str] = os.getenv(\"MOONSHOT_API_KEY\")\n self.moonshot_api_url: str = _env_str(\"MOONSHOT_API_URL\", \"https://api.moonshot.cn/v1\")\n self.moonshot_model: str = _env_str(\"MOONSHOT_MODEL\", \"kimi-k2-turbo-preview\")\n\n # DeepSeek\n self.deepseek_api_key: Optional[str] = os.getenv(\"DEEPSEEK_API_KEY\")\n self.deepseek_api_url: str = _env_str(\"DEEPSEEK_API_URL\", \"https://api.deepseek.com\")\n self.deepseek_model: str = _env_str(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n\n # Grok / xAI\n self.grok_api_key: Optional[str] = os.getenv(\"GROK_API_KEY\")\n self.grok_api_url: str = _env_str(\"GROK_API_URL\", \"https://api.x.ai/v1\")\n self.grok_model: str = _env_str(\"GROK_MODEL\", \"grok-4\")\n\n # Gemini\n self.gemini_api_key: Optional[str] = os.getenv(\"GEMINI_API_KEY\")\n self.gemini_api_url: str = _env_str(\n \"GEMINI_API_URL\", \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n )\n self.gemini_model: str = _env_str(\"GEMINI_MODEL\", \"gemini-2.5-flash\")\n\n # QWEN\n self.qwen_api_key: Optional[str] = os.getenv(\"QWEN_API_KEY\")\n self.qwen_api_url: str = _env_str(\n \"QWEN_API_URL\",\n \"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n )\n self.qwen_model: str = _env_str(\"QWEN_MODEL\", \"qwen-turbo\")\n\n # Provider selection\n self.llm_provider: str = _env_str(\"LLM_PROVIDER\", \"glm\")\n\n # Embeddings\n self.glm_embeddings_api_url: Optional[str] = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model: str = _env_str(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n self.glm_embedding_dimension: int = _env_int(\"GLM_EMBEDDING_DIM\", 1536)\n self.glm_batch_size: int = _env_int(\"GLM_BATCH_SIZE\", 25)\n self.semantic_default_k: int = _env_int(\"SEMANTIC_DEFAULT_K\", 5)\n self.semantic_min_similarity: float = _env_float(\"SEMANTIC_MIN_SIMILARITY\", 0.3)\n self.glm_max_retries: int = _env_int(\"GLM_MAX_RETRIES\", 3)\n self.glm_retry_delay: float = _env_float(\"GLM_RETRY_DELAY\", 1.0)\n self.glm_debug: bool = _env_bool(\"GLM_DEBUG\", False)\n\n # Embedding cache\n self.embedding_cache_size: int = _env_int(\"EMBEDDING_CACHE_SIZE\", 10000)\n self.embedding_cache_persistent: bool = _env_bool(\"EMBEDDING_CACHE_PERSISTENT\", True)\n\n # Context/debug flags\n self.ctx_debug: bool = _env_bool(\"CTX_DEBUG\", False) or _env_bool(\"CONTEXT_DEBUG\", False)\n self.budget_debug: bool = _env_bool(\"BUDGET_DEBUG\", False)\n self.decomp_debug: bool = _env_bool(\"DECOMP_DEBUG\", False)\n self.global_index_path: str = _env_str(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # External providers\n self.openai_api_key: Optional[str] = _env_first([\"OPENAI_API_KEY\", \"GPT_API_KEY\"])\n self.xai_api_key: Optional[str] = _env_first([\"XAI_API_KEY\", \"GROK_API_KEY\"])\n self.anthropic_api_key: Optional[str] = _env_first([\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"])\n self.tavily_api_key: Optional[str] = os.getenv(\"TAVILY_API_KEY\")\n\n # Server\n self.backend_host: str = _env_str(\"BACKEND_HOST\", \"0.0.0.0\")\n self.backend_port: int = _env_int(\"BACKEND_PORT\", 9000)\n self.cors_origins: str = _env_str(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n )\n self.chat_include_action_summary: bool = _env_bool(\"CHAT_INCLUDE_ACTION_SUMMARY\", True)\n self.job_log_retention_days: int = _env_int(\"JOB_LOG_RETENTION_DAYS\", 30)\n self.job_log_max_rows: int = _env_int(\"JOB_LOG_MAX_ROWS\", 10000)\n\n # Simulation\n self.sim_user_model: str = _env_str(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model: str = _env_str(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n self.sim_default_turns: int = _env_int(\"SIM_DEFAULT_TURNS\", 5)\n self.sim_max_turns: int = _env_int(\"SIM_MAX_TURNS\", 10)\n self.sim_default_goal: str = _env_str(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n\n # Memory\n self.memory_auto_save_enabled: bool = _env_bool(\"MEMORY_AUTO_SAVE_ENABLED\", True)\n self.memory_retrieve_enabled: bool = _env_bool(\"MEMORY_RETRIEVE_ENABLED\", True)\n self.memory_query_limit: int = _env_int(\"MEMORY_QUERY_LIMIT\", 5)\n self.memory_min_similarity: float = _env_float(\"MEMORY_MIN_SIMILARITY\", 0.6)\n self.memory_text_similarity: float = _env_float(\"MEMORY_TEXT_SIMILARITY\", 1.0)\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"Return cached application settings.\"\"\"\n settings = AppSettings()\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n if not settings.base_url:\n settings.base_url = f\"http://{settings.backend_host}:{settings.backend_port}\"\n\n return settings\n"}, "6fb763618cba2812abc27694ccc23d6cd549b202": {"t": 1785767367.5564086, "n": 17909, "h": "a31a69d7d9a5e920", "c": "#!/usr/bin/env python3\n\"\"\"\n集中化应用配置(AppSettings)\n\n目标:\n- 统一管理日志、数据库、GLM 与外部 LLM 提供商密钥等配置\n- 合并嵌入(Embeddings)与聊天(Chat)相关参数,避免在 services 下分散读取环境变量\n- 默认从环境变量加载,支持 .env(如存在)\n- 为后续扩展结构化日志与指标埋点提供稳定入口\n\"\"\"\n\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n_USE_PYDANTIC = False\n_DOTENV_LOADED = False\ntry:\n # Pydantic v2: BaseSettings 已迁移到 pydantic_settings 包\n from pydantic import Field # type: ignore\n from pydantic_settings import BaseSettings # type: ignore\n\n _USE_PYDANTIC = True\nexcept Exception:\n try:\n # Pydantic v1 回退\n from pydantic import BaseSettings, Field # type: ignore\n\n _USE_PYDANTIC = True\n except Exception:\n # 本地回退:无 pydantic 时提供轻量实现\n class BaseSettings: # type: ignore\n pass\n\n def Field(default=None, **kwargs): # type: ignore\n return default\n\n _USE_PYDANTIC = False\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv(override=True) # ensure .env values override exported vars\n _DOTENV_LOADED = True\nexcept Exception:\n _DOTENV_LOADED = False\n\n\nif _USE_PYDANTIC:\n\n class AppSettings(BaseSettings):\n # 日志配置\n log_level: str = Field(default=\"INFO\", env=\"LOG_LEVEL\")\n # json|plain\n log_format: str = Field(default=\"json\", env=\"LOG_FORMAT\")\n\n # 数据库配置(保留扩展位)\n database_url: str = Field(default=\"sqlite:///./tasks.db\", env=\"DATABASE_URL\")\n\n # API服务配置\n base_url: Optional[str] = Field(default=None, env=\"BASE_URL\")\n\n # GLM / LLM 配置\n glm_api_key: Optional[str] = Field(default=None, env=\"GLM_API_KEY\")\n glm_api_url: str = Field(\n default=\"https://open.bigmodel.cn/api/paas/v4/chat/completions\",\n env=\"GLM_API_URL\",\n )\n glm_model: str = Field(default=\"glm-4-flash\", env=\"GLM_MODEL\")\n glm_request_timeout: int = Field(default=60, env=\"GLM_REQUEST_TIMEOUT\")\n llm_request_timeout: int = Field(default=60, env=\"LLM_REQUEST_TIMEOUT\")\n llm_mock: bool = Field(default=False, env=\"LLM_MOCK\")\n llm_retries: int = Field(default=2, env=\"LLM_RETRIES\")\n llm_backoff_base: float = Field(default=0.5, env=\"LLM_BACKOFF_BASE\")\n\n # Perplexity API 配置\n perplexity_api_key: Optional[str] = Field(\n default=None, env=\"PERPLEXITY_API_KEY\"\n )\n perplexity_api_url: str = Field(\n default=\"https://api.perplexity.ai/chat/completions\",\n env=\"PERPLEXITY_API_URL\",\n )\n perplexity_model: str = Field(\n default=\"sonar-reasoning-pro\", env=\"PERPLEXITY_MODEL\"\n )\n\n # Doubao (Volcengine Ark)\n doubao_api_key: Optional[str] = Field(default=None, env=\"DOUBAO_API_KEY\")\n doubao_api_url: str = Field(\n default=\"https://ark.cn-beijing.volces.com/api/v3\",\n env=\"DOUBAO_API_URL\",\n )\n doubao_model: str = Field(default=\"doubao-seed-1-6-251015\", env=\"DOUBAO_MODEL\")\n\n # Moonshot / Kimi\n moonshot_api_key: Optional[str] = Field(default=None, env=\"MOONSHOT_API_KEY\")\n moonshot_api_url: str = Field(\n default=\"https://api.moonshot.cn/v1\",\n env=\"MOONSHOT_API_URL\",\n )\n moonshot_model: str = Field(\n default=\"kimi-k2-turbo-preview\", env=\"MOONSHOT_MODEL\"\n )\n\n # DeepSeek\n deepseek_api_key: Optional[str] = Field(default=None, env=\"DEEPSEEK_API_KEY\")\n deepseek_api_url: str = Field(\n default=\"https://api.deepseek.com\",\n env=\"DEEPSEEK_API_URL\",\n )\n deepseek_model: str = Field(default=\"deepseek-chat\", env=\"DEEPSEEK_MODEL\")\n\n # Grok / xAI\n grok_api_key: Optional[str] = Field(default=None, env=\"GROK_API_KEY\")\n grok_api_url: str = Field(\n default=\"https://api.x.ai/v1\",\n env=\"GROK_API_URL\",\n )\n grok_model: str = Field(default=\"grok-4\", env=\"GROK_MODEL\")\n\n # Gemini (OpenAI-compatible endpoint)\n gemini_api_key: Optional[str] = Field(default=None, env=\"GEMINI_API_KEY\")\n gemini_api_url: str = Field(\n default=\"https://generativelanguage.googleapis.com/v1beta/openai/\",\n env=\"GEMINI_API_URL\",\n )\n gemini_model: str = Field(default=\"gemini-2.5-flash\", env=\"GEMINI_MODEL\")\n\n # QWEN API 配置\n qwen_api_key: Optional[str] = Field(default=None, env=\"QWEN_API_KEY\")\n qwen_api_url: str = Field(\n default=\"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n env=\"QWEN_API_URL\",\n )\n qwen_model: str = Field(default=\"qwen-turbo\", env=\"QWEN_MODEL\")\n\n # 通用LLM配置(用于选择提供商)\n llm_provider: str = Field(\n default=\"glm\", env=\"LLM_PROVIDER\"\n ) # glm, perplexity, qwen, openai, etc.\n\n # GLM Embeddings 专用配置(集中到此,供 app.services.config 使用)\n glm_embeddings_api_url: Optional[str] = Field(\n default=None, env=\"GLM_EMBEDDINGS_API_URL\"\n )\n glm_embedding_model: str = Field(\n default=\"embedding-3\", env=\"GLM_EMBEDDING_MODEL\"\n )\n glm_embedding_dimension: int = Field(default=1536, env=\"GLM_EMBEDDING_DIM\")\n glm_batch_size: int = Field(default=25, env=\"GLM_BATCH_SIZE\")\n semantic_default_k: int = Field(default=5, env=\"SEMANTIC_DEFAULT_K\")\n semantic_min_similarity: float = Field(\n default=0.3, env=\"SEMANTIC_MIN_SIMILARITY\"\n )\n glm_max_retries: int = Field(default=3, env=\"GLM_MAX_RETRIES\")\n glm_retry_delay: float = Field(default=1.0, env=\"GLM_RETRY_DELAY\")\n glm_debug: bool = Field(default=False, env=\"GLM_DEBUG\")\n\n # 嵌入缓存配置(避免在多个模块中直接读取环境变量)\n embedding_cache_size: int = Field(default=10000, env=\"EMBEDDING_CACHE_SIZE\")\n embedding_cache_persistent: bool = Field(\n default=True, env=\"EMBEDDING_CACHE_PERSISTENT\"\n )\n\n # 调试与上下文配置\n ctx_debug: bool = Field(default=False, env=[\"CTX_DEBUG\", \"CONTEXT_DEBUG\"])\n budget_debug: bool = Field(default=False, env=\"BUDGET_DEBUG\")\n decomp_debug: bool = Field(default=False, env=\"DECOMP_DEBUG\")\n global_index_path: str = Field(default=\"INDEX.md\", env=\"GLOBAL_INDEX_PATH\")\n\n # 外部提供商(可选)\n openai_api_key: Optional[str] = Field(\n default=None, env=[\"OPENAI_API_KEY\", \"GPT_API_KEY\"]\n )\n xai_api_key: Optional[str] = Field(\n default=None, env=[\"XAI_API_KEY\", \"GROK_API_KEY\"]\n )\n anthropic_api_key: Optional[str] = Field(\n default=None, env=[\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"]\n )\n tavily_api_key: Optional[str] = Field(default=None, env=\"TAVILY_API_KEY\")\n\n # 服务器配置\n backend_host: str = Field(default=\"0.0.0.0\", env=\"BACKEND_HOST\")\n backend_port: int = Field(default=9000, env=\"BACKEND_PORT\")\n cors_origins: str = Field(\n default=\"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n env=\"CORS_ORIGINS\",\n )\n chat_include_action_summary: bool = Field(\n default=True, env=\"CHAT_INCLUDE_ACTION_SUMMARY\"\n )\n # Memory 相似度下限、文本匹配的默认相似度\n memory_min_similarity: float = Field(default=0.6, env=\"MEMORY_MIN_SIMILARITY\")\n memory_text_similarity: float = Field(default=1.0, env=\"MEMORY_TEXT_SIMILARITY\")\n job_log_retention_days: int = Field(default=30, env=\"JOB_LOG_RETENTION_DAYS\")\n job_log_max_rows: int = Field(default=10000, env=\"JOB_LOG_MAX_ROWS\")\n sim_user_model: str = Field(default=\"qwen3-max\", env=\"SIM_USER_MODEL\")\n sim_judge_model: str = Field(default=\"qwen3-max\", env=\"SIM_JUDGE_MODEL\")\n sim_default_turns: int = Field(default=5, env=\"SIM_DEFAULT_TURNS\")\n sim_max_turns: int = Field(default=10, env=\"SIM_MAX_TURNS\")\n memory_auto_save_enabled: bool = Field(default=True, env=\"MEMORY_AUTO_SAVE_ENABLED\")\n memory_retrieve_enabled: bool = Field(default=True, env=\"MEMORY_RETRIEVE_ENABLED\")\n memory_query_limit: int = Field(default=5, env=\"MEMORY_QUERY_LIMIT\")\n memory_min_similarity: float = Field(default=0.6, env=\"MEMORY_MIN_SIMILARITY\")\n sim_default_goal: str = Field(\n default=\"Refine the currently bound plan to better achieve the user's objectives.\",\n env=\"SIM_DEFAULT_GOAL\",\n )\n\n class Config:\n env_file = \".env\"\n case_sensitive = False\n\nelse:\n\n class AppSettings: # 轻量回退实现\n def __init__(self) -> None:\n # Ensure .env is loaded in pure os.getenv path as well\n if not _DOTENV_LOADED:\n try:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv(override=True)\n except Exception:\n pass\n self.log_level = os.getenv(\"LOG_LEVEL\", \"INFO\")\n self.log_format = os.getenv(\"LOG_FORMAT\", \"json\")\n self.database_url = os.getenv(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url = os.getenv(\"BASE_URL\")\n self.glm_api_key = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url = os.getenv(\n \"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n )\n self.glm_model = os.getenv(\"GLM_MODEL\", \"glm-4-flash\")\n try:\n self.glm_request_timeout = int(os.getenv(\"GLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.glm_request_timeout = 60\n try:\n self.llm_request_timeout = int(os.getenv(\"LLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.llm_request_timeout = 60\n self.llm_mock = os.getenv(\"LLM_MOCK\", \"\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n\n # Perplexity API 配置\n self.perplexity_api_key = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url = os.getenv(\n \"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\"\n )\n self.perplexity_model = os.getenv(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n self.doubao_api_key = os.getenv(\"DOUBAO_API_KEY\")\n self.doubao_api_url = os.getenv(\n \"DOUBAO_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\"\n )\n self.doubao_model = os.getenv(\"DOUBAO_MODEL\", \"doubao-seed-1-6-251015\")\n self.moonshot_api_key = os.getenv(\"MOONSHOT_API_KEY\")\n self.moonshot_api_url = os.getenv(\n \"MOONSHOT_API_URL\", \"https://api.moonshot.cn/v1\"\n )\n self.moonshot_model = os.getenv(\"MOONSHOT_MODEL\", \"kimi-k2-turbo-preview\")\n self.deepseek_api_key = os.getenv(\"DEEPSEEK_API_KEY\")\n self.deepseek_api_url = os.getenv(\n \"DEEPSEEK_API_URL\", \"https://api.deepseek.com\"\n )\n self.deepseek_model = os.getenv(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n self.grok_api_key = os.getenv(\"GROK_API_KEY\")\n self.grok_api_url = os.getenv(\"GROK_API_URL\", \"https://api.x.ai/v1\")\n self.grok_model = os.getenv(\"GROK_MODEL\", \"grok-4\")\n self.gemini_api_key = os.getenv(\"GEMINI_API_KEY\")\n self.gemini_api_url = os.getenv(\n \"GEMINI_API_URL\",\n \"https://generativelanguage.googleapis.com/v1beta/openai/\",\n )\n self.gemini_model = os.getenv(\"GEMINI_MODEL\", \"gemini-2.5-flash\")\n\n # 通用LLM配置\n self.llm_provider = os.getenv(\"LLM_PROVIDER\", \"glm\")\n try:\n self.llm_retries = int(os.getenv(\"LLM_RETRIES\", \"2\"))\n except Exception:\n self.llm_retries = 2\n try:\n self.llm_backoff_base = float(os.getenv(\"LLM_BACKOFF_BASE\", \"0.5\"))\n except Exception:\n self.llm_backoff_base = 0.5\n self.openai_api_key = os.getenv(\"OPENAI_API_KEY\") or os.getenv(\n \"GPT_API_KEY\"\n )\n self.xai_api_key = os.getenv(\"XAI_API_KEY\") or os.getenv(\"GROK_API_KEY\")\n self.anthropic_api_key = os.getenv(\"ANTHROPIC_API_KEY\") or os.getenv(\n \"CLAUDE_API_KEY\"\n )\n self.tavily_api_key = os.getenv(\"TAVILY_API_KEY\")\n self.sim_user_model = os.getenv(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model = os.getenv(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n try:\n self.sim_default_turns = int(os.getenv(\"SIM_DEFAULT_TURNS\", \"5\"))\n except Exception:\n self.sim_default_turns = 5\n try:\n self.sim_max_turns = int(os.getenv(\"SIM_MAX_TURNS\", \"10\"))\n except Exception:\n self.sim_max_turns = 10\n self.sim_default_goal = os.getenv(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n self.memory_auto_save_enabled = os.getenv(\"MEMORY_AUTO_SAVE_ENABLED\", \"true\").lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n self.memory_retrieve_enabled = os.getenv(\"MEMORY_RETRIEVE_ENABLED\", \"true\").lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n try:\n self.memory_query_limit = int(os.getenv(\"MEMORY_QUERY_LIMIT\", \"5\"))\n except Exception:\n self.memory_query_limit = 5\n try:\n self.memory_min_similarity = float(\n os.getenv(\"MEMORY_MIN_SIMILARITY\", \"0.6\")\n )\n except Exception:\n self.memory_min_similarity = 0.6\n\n # Embeddings 专用配置\n self.glm_embeddings_api_url = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model = os.getenv(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n try:\n self.glm_embedding_dimension = int(\n os.getenv(\"GLM_EMBEDDING_DIM\", \"1536\")\n )\n except Exception:\n self.glm_embedding_dimension = 1536\n try:\n self.glm_batch_size = int(os.getenv(\"GLM_BATCH_SIZE\", \"25\"))\n except Exception:\n self.glm_batch_size = 25\n try:\n self.semantic_default_k = int(os.getenv(\"SEMANTIC_DEFAULT_K\", \"5\"))\n except Exception:\n self.semantic_default_k = 5\n try:\n self.semantic_min_similarity = float(\n os.getenv(\"SEMANTIC_MIN_SIMILARITY\", \"0.3\")\n )\n except Exception:\n self.semantic_min_similarity = 0.3\n try:\n self.glm_max_retries = int(os.getenv(\"GLM_MAX_RETRIES\", \"3\"))\n except Exception:\n self.glm_max_retries = 3\n try:\n self.glm_retry_delay = float(os.getenv(\"GLM_RETRY_DELAY\", \"1.0\"))\n except Exception:\n self.glm_retry_delay = 1.0\n self.glm_debug = os.getenv(\"GLM_DEBUG\", \"\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n\n # 嵌入缓存配置\n try:\n self.embedding_cache_size = int(\n os.getenv(\"EMBEDDING_CACHE_SIZE\", \"10000\")\n )\n except Exception:\n self.embedding_cache_size = 10000\n self.embedding_cache_persistent = (\n os.getenv(\"EMBEDDING_CACHE_PERSISTENT\", \"1\").strip() == \"1\"\n )\n\n # 调试与上下文配置\n def _truthy(v: str) -> bool:\n return str(v).strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n self.ctx_debug = _truthy(os.getenv(\"CTX_DEBUG\", \"\")) or _truthy(\n os.getenv(\"CONTEXT_DEBUG\", \"\")\n )\n self.budget_debug = _truthy(os.getenv(\"BUDGET_DEBUG\", \"\"))\n self.decomp_debug = _truthy(os.getenv(\"DECOMP_DEBUG\", \"\"))\n self.global_index_path = os.getenv(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # 服务器配置\n self.backend_host = os.getenv(\"BACKEND_HOST\", \"0.0.0.0\")\n try:\n self.backend_port = int(os.getenv(\"BACKEND_PORT\", \"9000\"))\n except Exception:\n self.backend_port = 9000\n self.cors_origins = os.getenv(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n )\n self.chat_include_action_summary = os.getenv(\n \"CHAT_INCLUDE_ACTION_SUMMARY\", \"1\"\n ).strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n try:\n self.job_log_retention_days = int(\n os.getenv(\"JOB_LOG_RETENTION_DAYS\", \"30\")\n )\n except Exception:\n self.job_log_retention_days = 30\n try:\n self.job_log_max_rows = int(os.getenv(\"JOB_LOG_MAX_ROWS\", \"10000\"))\n except Exception:\n self.job_log_max_rows = 10000\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"获取全局应用配置(带缓存)\"\"\"\n settings = AppSettings() # 自动从环境变量与 .env 读取\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n base_url = getattr(settings, \"base_url\", None)\n if not base_url:\n host = getattr(settings, \"backend_host\", \"0.0.0.0\")\n port = getattr(settings, \"backend_port\", 9000)\n settings.base_url = f\"http://{host}:{port}\"\n\n return settings\n"}, "8ba709eface0542608e5320aa934ba9ed155a74f": {"t": 1785767368.8109381, "n": 16536, "h": "a1db7b776e7b2e20", "c": "#!/usr/bin/env python3\n\"\"\"\n集中化应用配置(AppSettings)\n\n目标:\n- 统一管理日志、数据库、GLM 与外部 LLM 提供商密钥等配置\n- 合并嵌入(Embeddings)与聊天(Chat)相关参数,避免在 services 下分散读取环境变量\n- 默认从环境变量加载,支持 .env(如存在)\n- 为后续扩展结构化日志与指标埋点提供稳定入口\n\"\"\"\n\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n_USE_PYDANTIC = False\n_DOTENV_LOADED = False\ntry:\n # Pydantic v2: BaseSettings 已迁移到 pydantic_settings 包\n from pydantic import Field # type: ignore\n from pydantic_settings import BaseSettings # type: ignore\n\n _USE_PYDANTIC = True\nexcept Exception:\n try:\n # Pydantic v1 回退\n from pydantic import BaseSettings, Field # type: ignore\n\n _USE_PYDANTIC = True\n except Exception:\n # 本地回退:无 pydantic 时提供轻量实现\n class BaseSettings: # type: ignore\n pass\n\n def Field(default=None, **kwargs): # type: ignore\n return default\n\n _USE_PYDANTIC = False\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv(override=True) # ensure .env values override exported vars\n _DOTENV_LOADED = True\nexcept Exception:\n _DOTENV_LOADED = False\n\n\nif _USE_PYDANTIC:\n\n class AppSettings(BaseSettings):\n # 日志配置\n log_level: str = Field(default=\"INFO\", env=\"LOG_LEVEL\")\n # json|plain\n log_format: str = Field(default=\"json\", env=\"LOG_FORMAT\")\n\n # 数据库配置(保留扩展位)\n database_url: str = Field(default=\"sqlite:///./tasks.db\", env=\"DATABASE_URL\")\n\n # API服务配置\n base_url: Optional[str] = Field(default=None, env=\"BASE_URL\")\n\n # GLM / LLM 配置\n glm_api_key: Optional[str] = Field(default=None, env=\"GLM_API_KEY\")\n glm_api_url: str = Field(\n default=\"https://open.bigmodel.cn/api/paas/v4/chat/completions\",\n env=\"GLM_API_URL\",\n )\n glm_model: str = Field(default=\"glm-4-flash\", env=\"GLM_MODEL\")\n glm_request_timeout: int = Field(default=60, env=\"GLM_REQUEST_TIMEOUT\")\n llm_request_timeout: int = Field(default=60, env=\"LLM_REQUEST_TIMEOUT\")\n llm_mock: bool = Field(default=False, env=\"LLM_MOCK\")\n llm_retries: int = Field(default=2, env=\"LLM_RETRIES\")\n llm_backoff_base: float = Field(default=0.5, env=\"LLM_BACKOFF_BASE\")\n\n # Perplexity API 配置\n perplexity_api_key: Optional[str] = Field(\n default=None, env=\"PERPLEXITY_API_KEY\"\n )\n perplexity_api_url: str = Field(\n default=\"https://api.perplexity.ai/chat/completions\",\n env=\"PERPLEXITY_API_URL\",\n )\n perplexity_model: str = Field(\n default=\"sonar-reasoning-pro\", env=\"PERPLEXITY_MODEL\"\n )\n\n # Doubao (Volcengine Ark)\n doubao_api_key: Optional[str] = Field(default=None, env=\"DOUBAO_API_KEY\")\n doubao_api_url: str = Field(\n default=\"https://ark.cn-beijing.volces.com/api/v3\",\n env=\"DOUBAO_API_URL\",\n )\n doubao_model: str = Field(default=\"doubao-seed-1-6-251015\", env=\"DOUBAO_MODEL\")\n\n # Moonshot / Kimi\n moonshot_api_key: Optional[str] = Field(default=None, env=\"MOONSHOT_API_KEY\")\n moonshot_api_url: str = Field(\n default=\"https://api.moonshot.cn/v1\",\n env=\"MOONSHOT_API_URL\",\n )\n moonshot_model: str = Field(\n default=\"kimi-k2-turbo-preview\", env=\"MOONSHOT_MODEL\"\n )\n\n # DeepSeek\n deepseek_api_key: Optional[str] = Field(default=None, env=\"DEEPSEEK_API_KEY\")\n deepseek_api_url: str = Field(\n default=\"https://api.deepseek.com\",\n env=\"DEEPSEEK_API_URL\",\n )\n deepseek_model: str = Field(default=\"deepseek-chat\", env=\"DEEPSEEK_MODEL\")\n\n # Grok / xAI\n grok_api_key: Optional[str] = Field(default=None, env=\"GROK_API_KEY\")\n grok_api_url: str = Field(\n default=\"https://api.x.ai/v1\",\n env=\"GROK_API_URL\",\n )\n grok_model: str = Field(default=\"grok-4\", env=\"GROK_MODEL\")\n\n # Gemini (OpenAI-compatible endpoint)\n gemini_api_key: Optional[str] = Field(default=None, env=\"GEMINI_API_KEY\")\n gemini_api_url: str = Field(\n default=\"https://generativelanguage.googleapis.com/v1beta/openai/\",\n env=\"GEMINI_API_URL\",\n )\n gemini_model: str = Field(default=\"gemini-2.5-flash\", env=\"GEMINI_MODEL\")\n\n # QWEN API 配置\n qwen_api_key: Optional[str] = Field(default=None, env=\"QWEN_API_KEY\")\n qwen_api_url: str = Field(\n default=\"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n env=\"QWEN_API_URL\",\n )\n qwen_model: str = Field(default=\"qwen-turbo\", env=\"QWEN_MODEL\")\n\n # 通用LLM配置(用于选择提供商)\n llm_provider: str = Field(\n default=\"glm\", env=\"LLM_PROVIDER\"\n ) # glm, perplexity, qwen, openai, etc.\n\n # GLM Embeddings 专用配置(集中到此,供 app.services.config 使用)\n glm_embeddings_api_url: Optional[str] = Field(\n default=None, env=\"GLM_EMBEDDINGS_API_URL\"\n )\n glm_embedding_model: str = Field(\n default=\"embedding-3\", env=\"GLM_EMBEDDING_MODEL\"\n )\n glm_embedding_dimension: int = Field(default=1536, env=\"GLM_EMBEDDING_DIM\")\n glm_batch_size: int = Field(default=25, env=\"GLM_BATCH_SIZE\")\n semantic_default_k: int = Field(default=5, env=\"SEMANTIC_DEFAULT_K\")\n semantic_min_similarity: float = Field(\n default=0.3, env=\"SEMANTIC_MIN_SIMILARITY\"\n )\n glm_max_retries: int = Field(default=3, env=\"GLM_MAX_RETRIES\")\n glm_retry_delay: float = Field(default=1.0, env=\"GLM_RETRY_DELAY\")\n glm_debug: bool = Field(default=False, env=\"GLM_DEBUG\")\n\n # 嵌入缓存配置(避免在多个模块中直接读取环境变量)\n embedding_cache_size: int = Field(default=10000, env=\"EMBEDDING_CACHE_SIZE\")\n embedding_cache_persistent: bool = Field(\n default=True, env=\"EMBEDDING_CACHE_PERSISTENT\"\n )\n\n # 调试与上下文配置\n ctx_debug: bool = Field(default=False, env=[\"CTX_DEBUG\", \"CONTEXT_DEBUG\"])\n budget_debug: bool = Field(default=False, env=\"BUDGET_DEBUG\")\n decomp_debug: bool = Field(default=False, env=\"DECOMP_DEBUG\")\n global_index_path: str = Field(default=\"INDEX.md\", env=\"GLOBAL_INDEX_PATH\")\n\n # 外部提供商(可选)\n openai_api_key: Optional[str] = Field(\n default=None, env=[\"OPENAI_API_KEY\", \"GPT_API_KEY\"]\n )\n xai_api_key: Optional[str] = Field(\n default=None, env=[\"XAI_API_KEY\", \"GROK_API_KEY\"]\n )\n anthropic_api_key: Optional[str] = Field(\n default=None, env=[\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"]\n )\n tavily_api_key: Optional[str] = Field(default=None, env=\"TAVILY_API_KEY\")\n\n # 服务器配置\n backend_host: str = Field(default=\"0.0.0.0\", env=\"BACKEND_HOST\")\n backend_port: int = Field(default=9000, env=\"BACKEND_PORT\")\n cors_origins: str = Field(\n default=\"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n env=\"CORS_ORIGINS\",\n )\n chat_include_action_summary: bool = Field(\n default=True, env=\"CHAT_INCLUDE_ACTION_SUMMARY\"\n )\n job_log_retention_days: int = Field(default=30, env=\"JOB_LOG_RETENTION_DAYS\")\n job_log_max_rows: int = Field(default=10000, env=\"JOB_LOG_MAX_ROWS\")\n sim_user_model: str = Field(default=\"qwen3-max\", env=\"SIM_USER_MODEL\")\n sim_judge_model: str = Field(default=\"qwen3-max\", env=\"SIM_JUDGE_MODEL\")\n sim_default_turns: int = Field(default=5, env=\"SIM_DEFAULT_TURNS\")\n sim_max_turns: int = Field(default=10, env=\"SIM_MAX_TURNS\")\n sim_default_goal: str = Field(\n default=\"Refine the currently bound plan to better achieve the user's objectives.\",\n env=\"SIM_DEFAULT_GOAL\",\n )\n\n class Config:\n env_file = \".env\"\n case_sensitive = False\n\nelse:\n\n class AppSettings: # 轻量回退实现\n def __init__(self) -> None:\n # Ensure .env is loaded in pure os.getenv path as well\n if not _DOTENV_LOADED:\n try:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv(override=True)\n except Exception:\n pass\n self.log_level = os.getenv(\"LOG_LEVEL\", \"INFO\")\n self.log_format = os.getenv(\"LOG_FORMAT\", \"json\")\n self.database_url = os.getenv(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url = os.getenv(\"BASE_URL\")\n self.glm_api_key = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url = os.getenv(\n \"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\"\n )\n self.glm_model = os.getenv(\"GLM_MODEL\", \"glm-4-flash\")\n try:\n self.glm_request_timeout = int(os.getenv(\"GLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.glm_request_timeout = 60\n try:\n self.llm_request_timeout = int(os.getenv(\"LLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.llm_request_timeout = 60\n self.llm_mock = os.getenv(\"LLM_MOCK\", \"\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n\n # Perplexity API 配置\n self.perplexity_api_key = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url = os.getenv(\n \"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\"\n )\n self.perplexity_model = os.getenv(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n self.doubao_api_key = os.getenv(\"DOUBAO_API_KEY\")\n self.doubao_api_url = os.getenv(\n \"DOUBAO_API_URL\", \"https://ark.cn-beijing.volces.com/api/v3\"\n )\n self.doubao_model = os.getenv(\"DOUBAO_MODEL\", \"doubao-seed-1-6-251015\")\n self.moonshot_api_key = os.getenv(\"MOONSHOT_API_KEY\")\n self.moonshot_api_url = os.getenv(\n \"MOONSHOT_API_URL\", \"https://api.moonshot.cn/v1\"\n )\n self.moonshot_model = os.getenv(\"MOONSHOT_MODEL\", \"kimi-k2-turbo-preview\")\n self.deepseek_api_key = os.getenv(\"DEEPSEEK_API_KEY\")\n self.deepseek_api_url = os.getenv(\n \"DEEPSEEK_API_URL\", \"https://api.deepseek.com\"\n )\n self.deepseek_model = os.getenv(\"DEEPSEEK_MODEL\", \"deepseek-chat\")\n self.grok_api_key = os.getenv(\"GROK_API_KEY\")\n self.grok_api_url = os.getenv(\"GROK_API_URL\", \"https://api.x.ai/v1\")\n self.grok_model = os.getenv(\"GROK_MODEL\", \"grok-4\")\n self.gemini_api_key = os.getenv(\"GEMINI_API_KEY\")\n self.gemini_api_url = os.getenv(\n \"GEMINI_API_URL\",\n \"https://generativelanguage.googleapis.com/v1beta/openai/\",\n )\n self.gemini_model = os.getenv(\"GEMINI_MODEL\", \"gemini-2.5-flash\")\n\n # 通用LLM配置\n self.llm_provider = os.getenv(\"LLM_PROVIDER\", \"glm\")\n try:\n self.llm_retries = int(os.getenv(\"LLM_RETRIES\", \"2\"))\n except Exception:\n self.llm_retries = 2\n try:\n self.llm_backoff_base = float(os.getenv(\"LLM_BACKOFF_BASE\", \"0.5\"))\n except Exception:\n self.llm_backoff_base = 0.5\n self.openai_api_key = os.getenv(\"OPENAI_API_KEY\") or os.getenv(\n \"GPT_API_KEY\"\n )\n self.xai_api_key = os.getenv(\"XAI_API_KEY\") or os.getenv(\"GROK_API_KEY\")\n self.anthropic_api_key = os.getenv(\"ANTHROPIC_API_KEY\") or os.getenv(\n \"CLAUDE_API_KEY\"\n )\n self.tavily_api_key = os.getenv(\"TAVILY_API_KEY\")\n self.sim_user_model = os.getenv(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model = os.getenv(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n try:\n self.sim_default_turns = int(os.getenv(\"SIM_DEFAULT_TURNS\", \"5\"))\n except Exception:\n self.sim_default_turns = 5\n try:\n self.sim_max_turns = int(os.getenv(\"SIM_MAX_TURNS\", \"10\"))\n except Exception:\n self.sim_max_turns = 10\n self.sim_default_goal = os.getenv(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n\n # Embeddings 专用配置\n self.glm_embeddings_api_url = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model = os.getenv(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n try:\n self.glm_embedding_dimension = int(\n os.getenv(\"GLM_EMBEDDING_DIM\", \"1536\")\n )\n except Exception:\n self.glm_embedding_dimension = 1536\n try:\n self.glm_batch_size = int(os.getenv(\"GLM_BATCH_SIZE\", \"25\"))\n except Exception:\n self.glm_batch_size = 25\n try:\n self.semantic_default_k = int(os.getenv(\"SEMANTIC_DEFAULT_K\", \"5\"))\n except Exception:\n self.semantic_default_k = 5\n try:\n self.semantic_min_similarity = float(\n os.getenv(\"SEMANTIC_MIN_SIMILARITY\", \"0.3\")\n )\n except Exception:\n self.semantic_min_similarity = 0.3\n try:\n self.glm_max_retries = int(os.getenv(\"GLM_MAX_RETRIES\", \"3\"))\n except Exception:\n self.glm_max_retries = 3\n try:\n self.glm_retry_delay = float(os.getenv(\"GLM_RETRY_DELAY\", \"1.0\"))\n except Exception:\n self.glm_retry_delay = 1.0\n self.glm_debug = os.getenv(\"GLM_DEBUG\", \"\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n\n # 嵌入缓存配置\n try:\n self.embedding_cache_size = int(\n os.getenv(\"EMBEDDING_CACHE_SIZE\", \"10000\")\n )\n except Exception:\n self.embedding_cache_size = 10000\n self.embedding_cache_persistent = (\n os.getenv(\"EMBEDDING_CACHE_PERSISTENT\", \"1\").strip() == \"1\"\n )\n\n # 调试与上下文配置\n def _truthy(v: str) -> bool:\n return str(v).strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n self.ctx_debug = _truthy(os.getenv(\"CTX_DEBUG\", \"\")) or _truthy(\n os.getenv(\"CONTEXT_DEBUG\", \"\")\n )\n self.budget_debug = _truthy(os.getenv(\"BUDGET_DEBUG\", \"\"))\n self.decomp_debug = _truthy(os.getenv(\"DECOMP_DEBUG\", \"\"))\n self.global_index_path = os.getenv(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # 服务器配置\n self.backend_host = os.getenv(\"BACKEND_HOST\", \"0.0.0.0\")\n try:\n self.backend_port = int(os.getenv(\"BACKEND_PORT\", \"9000\"))\n except Exception:\n self.backend_port = 9000\n self.cors_origins = os.getenv(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n )\n self.chat_include_action_summary = os.getenv(\n \"CHAT_INCLUDE_ACTION_SUMMARY\", \"1\"\n ).strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n try:\n self.job_log_retention_days = int(\n os.getenv(\"JOB_LOG_RETENTION_DAYS\", \"30\")\n )\n except Exception:\n self.job_log_retention_days = 30\n try:\n self.job_log_max_rows = int(os.getenv(\"JOB_LOG_MAX_ROWS\", \"10000\"))\n except Exception:\n self.job_log_max_rows = 10000\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"获取全局应用配置(带缓存)\"\"\"\n settings = AppSettings() # 自动从环境变量与 .env 读取\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n base_url = getattr(settings, \"base_url\", None)\n if not base_url:\n host = getattr(settings, \"backend_host\", \"0.0.0.0\")\n port = getattr(settings, \"backend_port\", 9000)\n settings.base_url = f\"http://{host}:{port}\"\n\n return settings\n"}, "9b5f67f493e17475c4c43c5fcb93664a4f9036f0": {"t": 1785767370.327938, "n": 12818, "h": "9803bb4827a4f280", "c": "#!/usr/bin/env python3\n\"\"\"\n集中化应用配置(AppSettings)\n\n目标:\n- 统一管理日志、数据库、GLM 与外部 LLM 提供商密钥等配置\n- 合并嵌入(Embeddings)与聊天(Chat)相关参数,避免在 services 下分散读取环境变量\n- 默认从环境变量加载,支持 .env(如存在)\n- 为后续扩展结构化日志与指标埋点提供稳定入口\n\"\"\"\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n_USE_PYDANTIC = False\n_DOTENV_LOADED = False\ntry:\n # Pydantic v2: BaseSettings 已迁移到 pydantic_settings 包\n from pydantic import Field # type: ignore\n from pydantic_settings import BaseSettings # type: ignore\n\n _USE_PYDANTIC = True\nexcept Exception:\n try:\n # Pydantic v1 回退\n from pydantic import BaseSettings, Field # type: ignore\n\n _USE_PYDANTIC = True\n except Exception:\n # 本地回退:无 pydantic 时提供轻量实现\n class BaseSettings: # type: ignore\n pass\n\n def Field(default=None, **kwargs): # type: ignore\n return default\n\n _USE_PYDANTIC = False\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv() # no-op if file missing; respects current working dir\n _DOTENV_LOADED = True\nexcept Exception:\n _DOTENV_LOADED = False\n\n\nif _USE_PYDANTIC:\n\n class AppSettings(BaseSettings):\n # 日志配置\n log_level: str = Field(default=\"INFO\", env=\"LOG_LEVEL\")\n # json|plain\n log_format: str = Field(default=\"json\", env=\"LOG_FORMAT\")\n\n # 数据库配置(保留扩展位)\n database_url: str = Field(default=\"sqlite:///./tasks.db\", env=\"DATABASE_URL\")\n \n # API服务配置\n base_url: Optional[str] = Field(default=None, env=\"BASE_URL\")\n\n # GLM / LLM 配置\n glm_api_key: Optional[str] = Field(default=None, env=\"GLM_API_KEY\")\n glm_api_url: str = Field(\n default=\"https://open.bigmodel.cn/api/paas/v4/chat/completions\",\n env=\"GLM_API_URL\",\n )\n glm_model: str = Field(default=\"glm-4-flash\", env=\"GLM_MODEL\")\n glm_request_timeout: int = Field(default=60, env=\"GLM_REQUEST_TIMEOUT\")\n llm_request_timeout: int = Field(default=60, env=\"LLM_REQUEST_TIMEOUT\")\n llm_mock: bool = Field(default=False, env=\"LLM_MOCK\")\n llm_retries: int = Field(default=2, env=\"LLM_RETRIES\")\n llm_backoff_base: float = Field(default=0.5, env=\"LLM_BACKOFF_BASE\")\n\n # Perplexity API 配置\n perplexity_api_key: Optional[str] = Field(default=None, env=\"PERPLEXITY_API_KEY\")\n perplexity_api_url: str = Field(\n default=\"https://api.perplexity.ai/chat/completions\",\n env=\"PERPLEXITY_API_URL\",\n )\n perplexity_model: str = Field(default=\"sonar-reasoning-pro\", env=\"PERPLEXITY_MODEL\")\n\n # QWEN API 配置\n qwen_api_key: Optional[str] = Field(default=None, env=\"QWEN_API_KEY\")\n qwen_api_url: str = Field(\n default=\"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n env=\"QWEN_API_URL\",\n )\n qwen_model: str = Field(default=\"qwen-turbo\", env=\"QWEN_MODEL\")\n\n # 通用LLM配置(用于选择提供商)\n llm_provider: str = Field(default=\"glm\", env=\"LLM_PROVIDER\") # glm, perplexity, qwen, openai, etc.\n\n # GLM Embeddings 专用配置(集中到此,供 app.services.config 使用)\n glm_embeddings_api_url: Optional[str] = Field(default=None, env=\"GLM_EMBEDDINGS_API_URL\")\n glm_embedding_model: str = Field(default=\"embedding-3\", env=\"GLM_EMBEDDING_MODEL\")\n glm_embedding_dimension: int = Field(default=1536, env=\"GLM_EMBEDDING_DIM\")\n glm_batch_size: int = Field(default=25, env=\"GLM_BATCH_SIZE\")\n semantic_default_k: int = Field(default=5, env=\"SEMANTIC_DEFAULT_K\")\n semantic_min_similarity: float = Field(default=0.3, env=\"SEMANTIC_MIN_SIMILARITY\")\n glm_max_retries: int = Field(default=3, env=\"GLM_MAX_RETRIES\")\n glm_retry_delay: float = Field(default=1.0, env=\"GLM_RETRY_DELAY\")\n glm_debug: bool = Field(default=False, env=\"GLM_DEBUG\")\n\n # 嵌入缓存配置(避免在多个模块中直接读取环境变量)\n embedding_cache_size: int = Field(default=10000, env=\"EMBEDDING_CACHE_SIZE\")\n embedding_cache_persistent: bool = Field(default=True, env=\"EMBEDDING_CACHE_PERSISTENT\")\n\n # 调试与上下文配置\n ctx_debug: bool = Field(default=False, env=[\"CTX_DEBUG\", \"CONTEXT_DEBUG\"])\n budget_debug: bool = Field(default=False, env=\"BUDGET_DEBUG\")\n decomp_debug: bool = Field(default=False, env=\"DECOMP_DEBUG\")\n global_index_path: str = Field(default=\"INDEX.md\", env=\"GLOBAL_INDEX_PATH\")\n\n # 外部提供商(可选)\n openai_api_key: Optional[str] = Field(default=None, env=[\"OPENAI_API_KEY\", \"GPT_API_KEY\"])\n xai_api_key: Optional[str] = Field(default=None, env=[\"XAI_API_KEY\", \"GROK_API_KEY\"])\n anthropic_api_key: Optional[str] = Field(default=None, env=[\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"])\n tavily_api_key: Optional[str] = Field(default=None, env=\"TAVILY_API_KEY\")\n\n # 服务器配置\n backend_host: str = Field(default=\"0.0.0.0\", env=\"BACKEND_HOST\")\n backend_port: int = Field(default=9000, env=\"BACKEND_PORT\")\n cors_origins: str = Field(\n default=\"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n env=\"CORS_ORIGINS\"\n )\n chat_include_action_summary: bool = Field(\n default=True, env=\"CHAT_INCLUDE_ACTION_SUMMARY\"\n )\n job_log_retention_days: int = Field(\n default=30, env=\"JOB_LOG_RETENTION_DAYS\"\n )\n job_log_max_rows: int = Field(\n default=10000, env=\"JOB_LOG_MAX_ROWS\"\n )\n sim_user_model: str = Field(default=\"qwen3-max\", env=\"SIM_USER_MODEL\")\n sim_judge_model: str = Field(default=\"qwen3-max\", env=\"SIM_JUDGE_MODEL\")\n sim_default_turns: int = Field(default=5, env=\"SIM_DEFAULT_TURNS\")\n sim_max_turns: int = Field(default=10, env=\"SIM_MAX_TURNS\")\n sim_default_goal: str = Field(\n default=\"Refine the currently bound plan to better achieve the user's objectives.\",\n env=\"SIM_DEFAULT_GOAL\",\n )\n\n class Config:\n env_file = \".env\"\n case_sensitive = False\n\nelse:\n\n class AppSettings: # 轻量回退实现\n def __init__(self) -> None:\n # Ensure .env is loaded in pure os.getenv path as well\n if not _DOTENV_LOADED:\n try:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv()\n except Exception:\n pass\n self.log_level = os.getenv(\"LOG_LEVEL\", \"INFO\")\n self.log_format = os.getenv(\"LOG_FORMAT\", \"json\")\n self.database_url = os.getenv(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url = os.getenv(\"BASE_URL\")\n self.glm_api_key = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url = os.getenv(\"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\")\n self.glm_model = os.getenv(\"GLM_MODEL\", \"glm-4-flash\")\n try:\n self.glm_request_timeout = int(os.getenv(\"GLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.glm_request_timeout = 60\n try:\n self.llm_request_timeout = int(os.getenv(\"LLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.llm_request_timeout = 60\n self.llm_mock = os.getenv(\"LLM_MOCK\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n \n # Perplexity API 配置\n self.perplexity_api_key = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url = os.getenv(\"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\")\n self.perplexity_model = os.getenv(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n \n # 通用LLM配置\n self.llm_provider = os.getenv(\"LLM_PROVIDER\", \"glm\")\n try:\n self.llm_retries = int(os.getenv(\"LLM_RETRIES\", \"2\"))\n except Exception:\n self.llm_retries = 2\n try:\n self.llm_backoff_base = float(os.getenv(\"LLM_BACKOFF_BASE\", \"0.5\"))\n except Exception:\n self.llm_backoff_base = 0.5\n self.openai_api_key = os.getenv(\"OPENAI_API_KEY\") or os.getenv(\"GPT_API_KEY\")\n self.xai_api_key = os.getenv(\"XAI_API_KEY\") or os.getenv(\"GROK_API_KEY\")\n self.anthropic_api_key = os.getenv(\"ANTHROPIC_API_KEY\") or os.getenv(\"CLAUDE_API_KEY\")\n self.tavily_api_key = os.getenv(\"TAVILY_API_KEY\")\n self.sim_user_model = os.getenv(\"SIM_USER_MODEL\", \"qwen3-max\")\n self.sim_judge_model = os.getenv(\"SIM_JUDGE_MODEL\", \"qwen3-max\")\n try:\n self.sim_default_turns = int(os.getenv(\"SIM_DEFAULT_TURNS\", \"5\"))\n except Exception:\n self.sim_default_turns = 5\n try:\n self.sim_max_turns = int(os.getenv(\"SIM_MAX_TURNS\", \"10\"))\n except Exception:\n self.sim_max_turns = 10\n self.sim_default_goal = os.getenv(\n \"SIM_DEFAULT_GOAL\",\n \"Refine the currently bound plan to better achieve the user's objectives.\",\n )\n\n # Embeddings 专用配置\n self.glm_embeddings_api_url = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model = os.getenv(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n try:\n self.glm_embedding_dimension = int(os.getenv(\"GLM_EMBEDDING_DIM\", \"1536\"))\n except Exception:\n self.glm_embedding_dimension = 1536\n try:\n self.glm_batch_size = int(os.getenv(\"GLM_BATCH_SIZE\", \"25\"))\n except Exception:\n self.glm_batch_size = 25\n try:\n self.semantic_default_k = int(os.getenv(\"SEMANTIC_DEFAULT_K\", \"5\"))\n except Exception:\n self.semantic_default_k = 5\n try:\n self.semantic_min_similarity = float(os.getenv(\"SEMANTIC_MIN_SIMILARITY\", \"0.3\"))\n except Exception:\n self.semantic_min_similarity = 0.3\n try:\n self.glm_max_retries = int(os.getenv(\"GLM_MAX_RETRIES\", \"3\"))\n except Exception:\n self.glm_max_retries = 3\n try:\n self.glm_retry_delay = float(os.getenv(\"GLM_RETRY_DELAY\", \"1.0\"))\n except Exception:\n self.glm_retry_delay = 1.0\n self.glm_debug = os.getenv(\"GLM_DEBUG\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n # 嵌入缓存配置\n try:\n self.embedding_cache_size = int(os.getenv(\"EMBEDDING_CACHE_SIZE\", \"10000\"))\n except Exception:\n self.embedding_cache_size = 10000\n self.embedding_cache_persistent = (os.getenv(\"EMBEDDING_CACHE_PERSISTENT\", \"1\").strip() == \"1\")\n\n # 调试与上下文配置\n def _truthy(v: str) -> bool:\n return str(v).strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n self.ctx_debug = _truthy(os.getenv(\"CTX_DEBUG\", \"\")) or _truthy(os.getenv(\"CONTEXT_DEBUG\", \"\"))\n self.budget_debug = _truthy(os.getenv(\"BUDGET_DEBUG\", \"\"))\n self.decomp_debug = _truthy(os.getenv(\"DECOMP_DEBUG\", \"\"))\n self.global_index_path = os.getenv(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # 服务器配置\n self.backend_host = os.getenv(\"BACKEND_HOST\", \"0.0.0.0\")\n try:\n self.backend_port = int(os.getenv(\"BACKEND_PORT\", \"9000\"))\n except Exception:\n self.backend_port = 9000\n self.cors_origins = os.getenv(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\"\n )\n self.chat_include_action_summary = os.getenv(\"CHAT_INCLUDE_ACTION_SUMMARY\", \"1\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n try:\n self.job_log_retention_days = int(os.getenv(\"JOB_LOG_RETENTION_DAYS\", \"30\"))\n except Exception:\n self.job_log_retention_days = 30\n try:\n self.job_log_max_rows = int(os.getenv(\"JOB_LOG_MAX_ROWS\", \"10000\"))\n except Exception:\n self.job_log_max_rows = 10000\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"获取全局应用配置(带缓存)\"\"\"\n settings = AppSettings() # 自动从环境变量与 .env 读取\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n base_url = getattr(settings, \"base_url\", None)\n if not base_url:\n host = getattr(settings, \"backend_host\", \"0.0.0.0\")\n port = getattr(settings, \"backend_port\", 9000)\n settings.base_url = f\"http://{host}:{port}\"\n\n return settings\n"}, "9c5b3281e37f7e9ac0d23459fec5b0a4b3833f71": {"t": 1785767371.7474387, "n": 11659, "h": "224feaa3da3fe2c0", "c": "#!/usr/bin/env python3\n\"\"\"\n集中化应用配置(AppSettings)\n\n目标:\n- 统一管理日志、数据库、GLM 与外部 LLM 提供商密钥等配置\n- 合并嵌入(Embeddings)与聊天(Chat)相关参数,避免在 services 下分散读取环境变量\n- 默认从环境变量加载,支持 .env(如存在)\n- 为后续扩展结构化日志与指标埋点提供稳定入口\n\"\"\"\nimport os\nfrom functools import lru_cache\nfrom typing import Optional\n\n_USE_PYDANTIC = False\n_DOTENV_LOADED = False\ntry:\n # Pydantic v2: BaseSettings 已迁移到 pydantic_settings 包\n from pydantic import Field # type: ignore\n from pydantic_settings import BaseSettings # type: ignore\n\n _USE_PYDANTIC = True\nexcept Exception:\n try:\n # Pydantic v1 回退\n from pydantic import BaseSettings, Field # type: ignore\n\n _USE_PYDANTIC = True\n except Exception:\n # 本地回退:无 pydantic 时提供轻量实现\n class BaseSettings: # type: ignore\n pass\n\n def Field(default=None, **kwargs): # type: ignore\n return default\n\n _USE_PYDANTIC = False\n\n# Best-effort: load .env even in fallback or mixed environments\ntry:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv() # no-op if file missing; respects current working dir\n _DOTENV_LOADED = True\nexcept Exception:\n _DOTENV_LOADED = False\n\n\nif _USE_PYDANTIC:\n\n class AppSettings(BaseSettings):\n # 日志配置\n log_level: str = Field(default=\"INFO\", env=\"LOG_LEVEL\")\n # json|plain\n log_format: str = Field(default=\"json\", env=\"LOG_FORMAT\")\n\n # 数据库配置(保留扩展位)\n database_url: str = Field(default=\"sqlite:///./tasks.db\", env=\"DATABASE_URL\")\n \n # API服务配置\n base_url: Optional[str] = Field(default=None, env=\"BASE_URL\")\n\n # GLM / LLM 配置\n glm_api_key: Optional[str] = Field(default=None, env=\"GLM_API_KEY\")\n glm_api_url: str = Field(\n default=\"https://open.bigmodel.cn/api/paas/v4/chat/completions\",\n env=\"GLM_API_URL\",\n )\n glm_model: str = Field(default=\"glm-4-flash\", env=\"GLM_MODEL\")\n glm_request_timeout: int = Field(default=60, env=\"GLM_REQUEST_TIMEOUT\")\n llm_request_timeout: int = Field(default=60, env=\"LLM_REQUEST_TIMEOUT\")\n llm_mock: bool = Field(default=False, env=\"LLM_MOCK\")\n llm_retries: int = Field(default=2, env=\"LLM_RETRIES\")\n llm_backoff_base: float = Field(default=0.5, env=\"LLM_BACKOFF_BASE\")\n\n # Perplexity API 配置\n perplexity_api_key: Optional[str] = Field(default=None, env=\"PERPLEXITY_API_KEY\")\n perplexity_api_url: str = Field(\n default=\"https://api.perplexity.ai/chat/completions\",\n env=\"PERPLEXITY_API_URL\",\n )\n perplexity_model: str = Field(default=\"sonar-reasoning-pro\", env=\"PERPLEXITY_MODEL\")\n\n # QWEN API 配置\n qwen_api_key: Optional[str] = Field(default=None, env=\"QWEN_API_KEY\")\n qwen_api_url: str = Field(\n default=\"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\",\n env=\"QWEN_API_URL\",\n )\n qwen_model: str = Field(default=\"qwen-turbo\", env=\"QWEN_MODEL\")\n\n # 通用LLM配置(用于选择提供商)\n llm_provider: str = Field(default=\"glm\", env=\"LLM_PROVIDER\") # glm, perplexity, qwen, openai, etc.\n\n # GLM Embeddings 专用配置(集中到此,供 app.services.config 使用)\n glm_embeddings_api_url: Optional[str] = Field(default=None, env=\"GLM_EMBEDDINGS_API_URL\")\n glm_embedding_model: str = Field(default=\"embedding-3\", env=\"GLM_EMBEDDING_MODEL\")\n glm_embedding_dimension: int = Field(default=1536, env=\"GLM_EMBEDDING_DIM\")\n glm_batch_size: int = Field(default=25, env=\"GLM_BATCH_SIZE\")\n semantic_default_k: int = Field(default=5, env=\"SEMANTIC_DEFAULT_K\")\n semantic_min_similarity: float = Field(default=0.3, env=\"SEMANTIC_MIN_SIMILARITY\")\n glm_max_retries: int = Field(default=3, env=\"GLM_MAX_RETRIES\")\n glm_retry_delay: float = Field(default=1.0, env=\"GLM_RETRY_DELAY\")\n glm_debug: bool = Field(default=False, env=\"GLM_DEBUG\")\n\n # 嵌入缓存配置(避免在多个模块中直接读取环境变量)\n embedding_cache_size: int = Field(default=10000, env=\"EMBEDDING_CACHE_SIZE\")\n embedding_cache_persistent: bool = Field(default=True, env=\"EMBEDDING_CACHE_PERSISTENT\")\n\n # 调试与上下文配置\n ctx_debug: bool = Field(default=False, env=[\"CTX_DEBUG\", \"CONTEXT_DEBUG\"])\n budget_debug: bool = Field(default=False, env=\"BUDGET_DEBUG\")\n decomp_debug: bool = Field(default=False, env=\"DECOMP_DEBUG\")\n global_index_path: str = Field(default=\"INDEX.md\", env=\"GLOBAL_INDEX_PATH\")\n\n # 外部提供商(可选)\n openai_api_key: Optional[str] = Field(default=None, env=[\"OPENAI_API_KEY\", \"GPT_API_KEY\"])\n xai_api_key: Optional[str] = Field(default=None, env=[\"XAI_API_KEY\", \"GROK_API_KEY\"])\n anthropic_api_key: Optional[str] = Field(default=None, env=[\"ANTHROPIC_API_KEY\", \"CLAUDE_API_KEY\"])\n tavily_api_key: Optional[str] = Field(default=None, env=\"TAVILY_API_KEY\")\n\n # 服务器配置\n backend_host: str = Field(default=\"0.0.0.0\", env=\"BACKEND_HOST\")\n backend_port: int = Field(default=9000, env=\"BACKEND_PORT\")\n cors_origins: str = Field(\n default=\"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\",\n env=\"CORS_ORIGINS\"\n )\n chat_include_action_summary: bool = Field(\n default=True, env=\"CHAT_INCLUDE_ACTION_SUMMARY\"\n )\n job_log_retention_days: int = Field(\n default=30, env=\"JOB_LOG_RETENTION_DAYS\"\n )\n job_log_max_rows: int = Field(\n default=10000, env=\"JOB_LOG_MAX_ROWS\"\n )\n\n class Config:\n env_file = \".env\"\n case_sensitive = False\n\nelse:\n\n class AppSettings: # 轻量回退实现\n def __init__(self) -> None:\n # Ensure .env is loaded in pure os.getenv path as well\n if not _DOTENV_LOADED:\n try:\n from dotenv import load_dotenv # type: ignore\n\n load_dotenv()\n except Exception:\n pass\n self.log_level = os.getenv(\"LOG_LEVEL\", \"INFO\")\n self.log_format = os.getenv(\"LOG_FORMAT\", \"json\")\n self.database_url = os.getenv(\"DATABASE_URL\", \"sqlite:///./tasks.db\")\n self.base_url = os.getenv(\"BASE_URL\")\n self.glm_api_key = os.getenv(\"GLM_API_KEY\")\n self.glm_api_url = os.getenv(\"GLM_API_URL\", \"https://open.bigmodel.cn/api/paas/v4/chat/completions\")\n self.glm_model = os.getenv(\"GLM_MODEL\", \"glm-4-flash\")\n try:\n self.glm_request_timeout = int(os.getenv(\"GLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.glm_request_timeout = 60\n try:\n self.llm_request_timeout = int(os.getenv(\"LLM_REQUEST_TIMEOUT\", \"60\"))\n except Exception:\n self.llm_request_timeout = 60\n self.llm_mock = os.getenv(\"LLM_MOCK\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n \n # Perplexity API 配置\n self.perplexity_api_key = os.getenv(\"PERPLEXITY_API_KEY\")\n self.perplexity_api_url = os.getenv(\"PERPLEXITY_API_URL\", \"https://api.perplexity.ai/chat/completions\")\n self.perplexity_model = os.getenv(\"PERPLEXITY_MODEL\", \"sonar-reasoning-pro\")\n \n # 通用LLM配置\n self.llm_provider = os.getenv(\"LLM_PROVIDER\", \"glm\")\n try:\n self.llm_retries = int(os.getenv(\"LLM_RETRIES\", \"2\"))\n except Exception:\n self.llm_retries = 2\n try:\n self.llm_backoff_base = float(os.getenv(\"LLM_BACKOFF_BASE\", \"0.5\"))\n except Exception:\n self.llm_backoff_base = 0.5\n self.openai_api_key = os.getenv(\"OPENAI_API_KEY\") or os.getenv(\"GPT_API_KEY\")\n self.xai_api_key = os.getenv(\"XAI_API_KEY\") or os.getenv(\"GROK_API_KEY\")\n self.anthropic_api_key = os.getenv(\"ANTHROPIC_API_KEY\") or os.getenv(\"CLAUDE_API_KEY\")\n self.tavily_api_key = os.getenv(\"TAVILY_API_KEY\")\n\n # Embeddings 专用配置\n self.glm_embeddings_api_url = os.getenv(\"GLM_EMBEDDINGS_API_URL\")\n self.glm_embedding_model = os.getenv(\"GLM_EMBEDDING_MODEL\", \"embedding-3\")\n try:\n self.glm_embedding_dimension = int(os.getenv(\"GLM_EMBEDDING_DIM\", \"1536\"))\n except Exception:\n self.glm_embedding_dimension = 1536\n try:\n self.glm_batch_size = int(os.getenv(\"GLM_BATCH_SIZE\", \"25\"))\n except Exception:\n self.glm_batch_size = 25\n try:\n self.semantic_default_k = int(os.getenv(\"SEMANTIC_DEFAULT_K\", \"5\"))\n except Exception:\n self.semantic_default_k = 5\n try:\n self.semantic_min_similarity = float(os.getenv(\"SEMANTIC_MIN_SIMILARITY\", \"0.3\"))\n except Exception:\n self.semantic_min_similarity = 0.3\n try:\n self.glm_max_retries = int(os.getenv(\"GLM_MAX_RETRIES\", \"3\"))\n except Exception:\n self.glm_max_retries = 3\n try:\n self.glm_retry_delay = float(os.getenv(\"GLM_RETRY_DELAY\", \"1.0\"))\n except Exception:\n self.glm_retry_delay = 1.0\n self.glm_debug = os.getenv(\"GLM_DEBUG\", \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n # 嵌入缓存配置\n try:\n self.embedding_cache_size = int(os.getenv(\"EMBEDDING_CACHE_SIZE\", \"10000\"))\n except Exception:\n self.embedding_cache_size = 10000\n self.embedding_cache_persistent = (os.getenv(\"EMBEDDING_CACHE_PERSISTENT\", \"1\").strip() == \"1\")\n\n # 调试与上下文配置\n def _truthy(v: str) -> bool:\n return str(v).strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n self.ctx_debug = _truthy(os.getenv(\"CTX_DEBUG\", \"\")) or _truthy(os.getenv(\"CONTEXT_DEBUG\", \"\"))\n self.budget_debug = _truthy(os.getenv(\"BUDGET_DEBUG\", \"\"))\n self.decomp_debug = _truthy(os.getenv(\"DECOMP_DEBUG\", \"\"))\n self.global_index_path = os.getenv(\"GLOBAL_INDEX_PATH\", \"INDEX.md\")\n\n # 服务器配置\n self.backend_host = os.getenv(\"BACKEND_HOST\", \"0.0.0.0\")\n try:\n self.backend_port = int(os.getenv(\"BACKEND_PORT\", \"9000\"))\n except Exception:\n self.backend_port = 9000\n self.cors_origins = os.getenv(\n \"CORS_ORIGINS\",\n \"http://localhost:3000,http://127.0.0.1:3000,http://localhost:3001,http://127.0.0.1:3001\"\n )\n self.chat_include_action_summary = os.getenv(\"CHAT_INCLUDE_ACTION_SUMMARY\", \"1\").strip().lower() in {\n \"1\",\n \"true\",\n \"yes\",\n \"on\",\n }\n try:\n self.job_log_retention_days = int(os.getenv(\"JOB_LOG_RETENTION_DAYS\", \"30\"))\n except Exception:\n self.job_log_retention_days = 30\n try:\n self.job_log_max_rows = int(os.getenv(\"JOB_LOG_MAX_ROWS\", \"10000\"))\n except Exception:\n self.job_log_max_rows = 10000\n\n\n@lru_cache(maxsize=1)\ndef get_settings() -> AppSettings:\n \"\"\"获取全局应用配置(带缓存)\"\"\"\n settings = AppSettings() # 自动从环境变量与 .env 读取\n\n # Ensure BASE_URL fallback stays in sync with BACKEND_HOST/BACKEND_PORT\n base_url = getattr(settings, \"base_url\", None)\n if not base_url:\n host = getattr(settings, \"backend_host\", \"0.0.0.0\")\n port = getattr(settings, \"backend_port\", 9000)\n settings.base_url = f\"http://{host}:{port}\"\n\n return settings\n"}}}} |