from pathlib import Path from pydantic_settings import BaseSettings, SettingsConfigDict from typing import Literal REPO_ROOT = Path(__file__).resolve().parents[2] ENV_FILE = REPO_ROOT / ".env" def _resolve_path(value: str) -> str: path = Path(value) if path.is_absolute(): return str(path) return str((REPO_ROOT / path).resolve()) class Settings(BaseSettings): model_config = SettingsConfigDict( env_file=str(ENV_FILE), env_file_encoding="utf-8", extra="ignore" ) # === 应用基础 === APP_NAME: str = "Jarvis" APP_VERSION: str = "0.1.0" DEBUG: bool = False HOST: str = "127.0.0.1" PORT: int = 9527 # === 安全 === SECRET_KEY: str = "change-me-in-production" ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # === 数据库 === DATABASE_URL: str = "sqlite+aiosqlite:///./data/jarvis.db" DATA_DIR: str = "data" # === ChromaDB === CHROMA_PERSIST_DIR: str = "data/chroma" # === LLM 配置 === # 支持: openai / claude / ollama / deepseek / custom LLM_PROVIDER: Literal["openai", "claude", "ollama", "deepseek", "custom"] = "openai" # OpenAI (默认) OPENAI_API_KEY: str = "" OPENAI_MODEL: str = "gpt-4o" OPENAI_BASE_URL: str = "https://api.openai.com/v1" # Claude ANTHROPIC_API_KEY: str = "" CLAUDE_MODEL: str = "claude-sonnet-4-20250514" CLAUDE_MAX_TOKENS: int = 8192 # Ollama (本地模型) OLLAMA_BASE_URL: str = "http://localhost:11434" OLLAMA_MODEL: str = "llama3" # === 定时任务 === SCHEDULER_ENABLED: bool = True DAILY_PLAN_TIME: str = "00:00" FORUM_SCAN_INTERVAL_MINUTES: int = 30 # === CORS === CORS_ORIGINS: list[str] = ["http://localhost:5173", "http://localhost:3000"] # === 文件上传 === UPLOAD_DIR: str = "data/uploads" MAX_UPLOAD_SIZE: int = 50 * 1024 * 1024 MINERU_LANGUAGE: Literal["ch", "en"] = "ch" # === 管理员 bootstrap === ADMIN: str = "" ADMIN_EMAIL: str = "" ADMIN_PASSWORD: str = "" ADMIN_FULL_NAME: str = "Administrator" # === 向量化 === EMBEDDING_MODEL: str = "text-embedding-3-small" EMBEDDING_BASE_URL: str = "https://api.openai.com/v1" EMBEDDING_API_KEY: str = "" CHUNK_SIZE: int = 500 CHUNK_OVERLAP: int = 50 # === LangSmith 可观测性 === LANGSMITH_TRACING: bool = False LANGSMITH_API_KEY: str = "" LANGSMITH_PROJECT: str = "jarvis-agent" # === NAS 部署 === NAS_DATA_ROOT: str = "/data/jarvis" # === Web Search / SearxNG === WEB_SEARCH_ENABLED: bool = False WEB_SEARCH_PROVIDER: str = "searxng" SEARXNG_BASE_URL: str = "" SEARXNG_AUTH_TYPE: Literal["none", "bearer", "basic"] = "none" SEARXNG_AUTH_TOKEN: str = "" SEARXNG_BASIC_USER: str = "" SEARXNG_BASIC_PASSWORD: str = "" WEB_SEARCH_DEFAULT_LIMIT: int = 5 WEB_SEARCH_TIMEOUT_SECONDS: int = 10 settings = Settings() settings.DATABASE_URL = settings.DATABASE_URL.replace("./data", _resolve_path("./data"), 1) settings.DATA_DIR = _resolve_path(settings.DATA_DIR) settings.CHROMA_PERSIST_DIR = _resolve_path(settings.CHROMA_PERSIST_DIR) settings.UPLOAD_DIR = _resolve_path(settings.UPLOAD_DIR)