diff --git a/.env.example b/.env.example index cfa8bc7..0aa465e 100644 --- a/.env.example +++ b/.env.example @@ -1,19 +1,18 @@ # NexusAI 部署环境变量模板 # 复制为 .env 并填入真实值;.env 已加入 .gitignore,切勿提交版本库。 -# ---- MySQL ---- -MYSQL_ROOT_PASSWORD=change_me_strong_password - -# ---- One-API ---- -# One-API 后台管理员登录后生成的对外聚合 Token(sk-xxxx),写入 LobeChat 使用 -ONE_API_TOKEN=sk-change_me_after_one_api_setup - -# ---- LobeChat ---- -# 前端访问密码(Access Code),自定义,建议高复杂度 -LOBE_ACCESS_CODE=change_me_access_code +# ---- LiteLLM 网关 ---- +# LiteLLM 后台管理 Key(可自定义,用于后台 API 认证,前端无需输入) +LITELLM_MASTER_KEY=sk-change_me_strong_key # ---- 各 AI 渠道 API Key ---- -# 在 One-API 后台添加渠道时手动填入,此处仅作为记录/备用,不由容器直接读取 +# 这些 Key 被 litellm-config.yaml 引用,容器启动时自动注入为环境变量 +# 获取方式: +# GEMINI_API_KEY: https://aistudio.google.com/apikey +# NVIDIA_API_KEY: https://build.nvidia.com/discover/llama2-70b +# GROQ_API_KEY: https://console.groq.com +# OPENROUTER_API_KEY: https://openrouter.ai/keys +# MISTRAL_API_KEY: https://console.mistral.ai/api-tokens GEMINI_API_KEY= NVIDIA_API_KEY= GROQ_API_KEY= diff --git a/docker-compose.yml b/docker-compose.yml index 1f5288a..f48a8e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,50 +1,26 @@ version: '3.8' services: - mysql: - image: mysql:8.0 - container_name: one-api-mysql + litellm: + image: ghcr.io/berriai/litellm:latest + container_name: litellm-proxy restart: always environment: - - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - - MYSQL_DATABASE=oneapi + - LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY:-sk-1234} volumes: - - ./mysql-data:/var/lib/mysql + - ./litellm-config.yaml:/home/litellm/config.yaml:ro networks: - ai-network + command: python -m litellm.proxy.proxy --config /home/litellm/config.yaml --port 4000 - one-api: - image: justsong/one-api:latest - container_name: one-api + openwebui: + image: ghcr.io/open-webui/open-webui:latest + container_name: openwebui restart: always - ports: - # 3000 已被 NAS 上的 Gitea 容器占用,改用 3001 - # NAS sshd 关闭了 AllowTcpForwarding,SSH 隧道方案不可行,改为仅绑定 NAS 局域网 IP(不经 frp,不映射公网) - - "192.168.50.64:3001:3000" - volumes: - - ./one-api-data:/data environment: - - SQL_DSN=root:${MYSQL_ROOT_PASSWORD}@tcp(mysql:3306)/oneapi - - TZ=Asia/Shanghai - depends_on: - - mysql - networks: - - ai-network - - lobe-chat: - image: lobehub/lobe-chat:latest - container_name: lobe-chat - restart: always - # 不发布主机端口:仅 Nginx 通过容器网络访问 lobe-chat:3210 - environment: - - OPENAI_API_KEY=${ONE_API_TOKEN} - - OPENAI_PROXY_URL=http://one-api:3000/v1 - - ACCESS_CODE=${LOBE_ACCESS_CODE} - - DEFAULT_LANG=zh-CN - # 只暴露聚合模型名,隐藏各厂商真实模型名(对应 SDD 3.2 模型映射设计) - - OPENAI_MODEL_LIST=-all,+auto-vision=Auto Vision,+auto-text=Auto Text - depends_on: - - one-api + - OPENAI_API_KEY=${LITELLM_MASTER_KEY:-sk-1234} + - OPENAI_API_BASE=http://litellm:4000/v1 + - WEBUI_AUTH=False networks: - ai-network @@ -53,14 +29,14 @@ services: container_name: nginx-proxy restart: always ports: - # NAS 本机 80/443 已被其他服务占用(DSM/其他容器),改用 8443 + # NAS 本机 80/443 已被其他服务占用,改用 8443 # 甲骨文 frp 隧道(frpc.toml [[proxies]] name=nexusai)将公网专属端口 3210 转发到 NAS 的 8443 - "8443:443" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro - ./certs:/etc/nginx/certs:ro depends_on: - - lobe-chat + - openwebui networks: - ai-network diff --git a/litellm-config.yaml b/litellm-config.yaml new file mode 100644 index 0000000..9dea0aa --- /dev/null +++ b/litellm-config.yaml @@ -0,0 +1,97 @@ +# LiteLLM 网关配置文件 +# 所有渠道和模型优先级、Failover 规则在此定义 +# API Key 通过环境变量注入,见 .env 文件 + +model_list: + # 聚合模型1: auto-vision (图文混合问答) + # 按优先级依次尝试: Gemini(1) → NVIDIA(1) → OpenRouter(2) → Mistral(3) + # (Groq 不支持视觉,故不在此列表) + - model_name: auto-vision + litellm_params: + model: gemini/gemini-flash-latest + api_key: ${GEMINI_API_KEY} + api_base: https://generativelanguage.googleapis.com/v1beta + fallbacks: + - nvidia-nim-vision + - openrouter-vision + - mistral-vision + tpm_limit: 10000 + rpm_limit: 100 + + - model_name: nvidia-nim-vision + litellm_params: + model: openai/meta/llama-3.2-11b-vision-instruct + api_key: ${NVIDIA_API_KEY} + api_base: https://integrate.api.nvidia.com/v1 + + - model_name: openrouter-vision + litellm_params: + model: openrouter/google/gemini-2.0-flash-exp:free + api_key: ${OPENROUTER_API_KEY} + api_base: https://openrouter.ai/api/v1 + + - model_name: mistral-vision + litellm_params: + model: openai/pixtral-12b-2409 + api_key: ${MISTRAL_API_KEY} + api_base: https://api.mistral.ai/v1 + + # 聚合模型2: auto-text (纯文本问答) + # 按优先级依次尝试: Gemini(1) → NVIDIA(1) → OpenRouter(2) → Groq(2) → Mistral(3) + - model_name: auto-text + litellm_params: + model: gemini/gemini-flash-latest + api_key: ${GEMINI_API_KEY} + api_base: https://generativelanguage.googleapis.com/v1beta + fallbacks: + - nvidia-nim-text + - openrouter-text + - groq-text + - mistral-text + tpm_limit: 10000 + rpm_limit: 100 + + - model_name: nvidia-nim-text + litellm_params: + model: openai/meta/llama-3.2-11b-vision-instruct + api_key: ${NVIDIA_API_KEY} + api_base: https://integrate.api.nvidia.com/v1 + + - model_name: openrouter-text + litellm_params: + model: openrouter/google/gemini-2.0-flash-exp:free + api_key: ${OPENROUTER_API_KEY} + api_base: https://openrouter.ai/api/v1 + + - model_name: groq-text + litellm_params: + model: openai/llama-3.3-70b-versatile + api_key: ${GROQ_API_KEY} + api_base: https://api.groq.com/openai/v1 + + - model_name: mistral-text + litellm_params: + model: openai/mistral-small-latest + api_key: ${MISTRAL_API_KEY} + api_base: https://api.mistral.ai/v1 + +# 网关全局配置 +router_settings: + enable_cooldowns: true + cooldown_threshold: 5 # 连续失败 5 次进入冷却 + cooldown_duration: 600 # 冷却期 10 分钟 + enable_retry: true + max_retries: 2 # 最多重试 2 次 + +# 日志级别 +logging: + level: INFO + +# Fallback 行为 +# 上游错误(429/5xx/timeout)时自动切换到下一个渠道 +fallback_error_codes: + - 429 + - 502 + - 503 + - 504 + - timeout diff --git a/nginx.conf b/nginx.conf index 758ad92..dd7b92e 100644 --- a/nginx.conf +++ b/nginx.conf @@ -21,7 +21,7 @@ http { sendfile on; keepalive_timeout 65; - # 域名/IP 未确定前,先用 _ 通配 server_name(见 SDD 8.4 待确认事项) + # 域名/IP 未确定前,先用 _ 通配 server_name server { listen 443 ssl; server_name _; @@ -30,14 +30,18 @@ http { ssl_certificate_key /etc/nginx/certs/privkey.pem; location / { - proxy_pass http://lobe-chat:3210; + proxy_pass http://openwebui:8080; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - # 关闭缓冲/缓存以支持流式输出(SSE) + # WebSocket 支持(Infinity Chat 可能使用 WebSocket) + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # 关闭缓冲/缓存以支持流式输出 proxy_buffering off; proxy_cache off; proxy_read_timeout 300s;