feat: optimize initialization services and configurations

This commit is contained in:
Chenyme committed 2026-02-02 21:16:50 +08:00
1 parent 1dbe5d2525
commit 94582265c1
9 files changed
+208 -130

No files matched your search

+1
View File
@@ -47,6 +47,7 @@ logs/
# Data
data/*.json
data/tmp/
data/.locks/
# Testing
.pytest_cache/
+1
View File
@@ -20,6 +20,7 @@ COPY pyproject.toml uv.lock /app/
RUN uv sync --frozen --no-dev --no-install-project --active
COPY config.defaults.toml /app/config.defaults.toml
COPY app /app/app
COPY main.py /app/main.py
COPY scripts /app/scripts
+71 -11
View File
@@ -2,10 +2,46 @@
配置管理
- config.toml: 运行时配置
- config.defaults.toml: 默认配置基线
"""
from typing import Any
from app.core.storage import get_storage
from copy import deepcopy
from pathlib import Path
from typing import Any, Dict
import tomllib
from app.core.logger import logger
DEFAULT_CONFIG_FILE = Path(__file__).parent.parent.parent / "config.defaults.toml"
def _deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
"""深度合并字典: override 覆盖 base."""
if not isinstance(base, dict):
return deepcopy(override) if isinstance(override, dict) else deepcopy(base)
result = deepcopy(base)
if not isinstance(override, dict):
return result
for key, val in override.items():
if isinstance(val, dict) and isinstance(result.get(key), dict):
result[key] = _deep_merge(result[key], val)
else:
result[key] = val
return result
def _load_defaults() -> Dict[str, Any]:
"""加载默认配置文件"""
if not DEFAULT_CONFIG_FILE.exists():
return {}
try:
with DEFAULT_CONFIG_FILE.open("rb") as f:
return tomllib.load(f)
except Exception as e:
logger.warning(f"Failed to load defaults from {DEFAULT_CONFIG_FILE}: {e}")
return {}
class Config:
@@ -16,32 +52,53 @@ class Config:
def __init__(self):
self._config = {}
self._defaults = {}
self._defaults_loaded = False
def _ensure_defaults(self):
if self._defaults_loaded:
return
self._defaults = _load_defaults()
self._defaults_loaded = True
async def load(self):
"""显式加载配置"""
try:
from app.core.storage import get_storage, LocalStorage
import asyncio
self._ensure_defaults()
storage = get_storage()
config_data = await storage.load_config()
from_remote = True
# 从本地 data/config.toml 初始化后端
if config_data is None:
local_storage = LocalStorage()
from_remote = False
try:
# 尝试读取本地配置
config_data = await local_storage.load_config()
# 初始化后端
await storage.save_config(config_data)
logger.info(f"Initialized remote storage ({storage.__class__.__name__}) with local config.")
except Exception as e:
logger.info(f"Failed to auto-init config from local: {e}")
config_data = {}
self._config = config_data or {}
config_data = config_data or {}
merged = _deep_merge(self._defaults, config_data)
# 自动回填缺失配置到存储
should_persist = (not from_remote) or (merged != config_data)
if should_persist:
async with storage.acquire_lock("config_save", timeout=10):
await storage.save_config(merged)
if not from_remote:
logger.info(
f"Initialized remote storage ({storage.__class__.__name__}) with config baseline."
)
self._config = merged
except Exception as e:
print(f"Error loading config: {e}")
logger.error(f"Error loading config: {e}")
self._config = {}
def get(self, key: str, default: Any = None) -> Any:
@@ -66,8 +123,11 @@ class Config:
from app.core.storage import get_storage
storage = get_storage()
async with storage.acquire_lock("config_save", timeout=10):
await storage.save_config(new_config)
await self.load()
self._ensure_defaults()
base = _deep_merge(self._defaults, self._config or {})
merged = _deep_merge(base, new_config or {})
await storage.save_config(merged)
self._config = merged
# 全局配置实例
+37
View File
@@ -0,0 +1,37 @@
[grok]
temporary = true
stream = true
thinking = true
dynamic_statsig = true
filter_tags = ["xaiartifact","xai:tool_usage_card","grok:render"]
timeout = 120
base_proxy_url = ""
asset_proxy_url = ""
cf_clearance = ""
max_retry = 3
retry_status_codes = [401,429,403]
[app]
app_url = "http://127.0.0.1:8000"
app_key = "grok2api"
api_key = ""
image_format = "url"
video_format = "url"
[token]
auto_refresh = true
refresh_interval_hours = 8
fail_threshold = 5
save_delay_ms = 500
reload_interval_sec = 30
[cache]
enable_auto_clean = true
limit_mb = 1024
[performance]
assets_max_concurrent = 25
media_max_concurrent = 50
usage_max_concurrent = 25
assets_delete_batch_size = 10
admin_assets_batch_size = 10
+11 -2
View File
@@ -1,13 +1,22 @@
services:
grok2api:
build: .
container_name: grok2api
image: ghcr.io/chenyme/grok2api:latest
ports:
- "8000:8000"
environment:
TZ: Asia/Shanghai
SERVER_HOST: 0.0.0.0
LOG_LEVEL: INFO
SERVER_PORT: 8000
SERVER_WORKERS: 1
SERVER_STORAGE_TYPE: local
# SERVER_STORAGE_URL:
# SERVER_STORAGE_TYPE: (local, redis, mysql, pgsql) default: local
# SERVER_STORAGE_URL: (local mode is empty) default: empty
# Redis: redis://localhost:6379/0 or redis://:password@localhost:6379/0
# MySQL: mysql+aiomysql://user:pass@localhost/db
# PgSQL: postgresql+asyncpg://user:pass@localhost/db
volumes:
- ./data:/app/data
- ./logs:/app/logs
+11
View File
@@ -31,6 +31,17 @@ git clone https://github.com/chenyme/grok2api
docker compose up -d
```
### Environment variables
| Variable | Description | Default | Example |
| :--- | :--- | :--- | :--- |
| `LOG_LEVEL` | Log level | `INFO` | `DEBUG` |
| `SERVER_HOST` | Bind address | `0.0.0.0` | `0.0.0.0` |
| `SERVER_PORT` | Service port | `8000` | `8000` |
| `SERVER_WORKERS` | Uvicorn worker count | `1` | `2` |
| `SERVER_STORAGE_TYPE` | Storage type (`local`/`redis`/`mysql`/`pgsql`) | `local` | `pgsql` |
| `SERVER_STORAGE_URL` | Storage URL (empty for local) | `""` | `postgresql+asyncpg://user:password@host:5432/db` |
### Usage limits
- Basic account: 80 requests / 20h
-19
View File
@@ -1,19 +0,0 @@
# ============================================
# 启动配置
# ============================================
SERVER_HOST=0.0.0.0
SERVER_PORT=8000
SERVER_WORKERS=1
LOG_LEVEL=INFO
# ============================================
# 存储配置
# ============================================
# 存储类型: local, redis, mysql, pgsql
SERVER_STORAGE_TYPE=local
# 存储连接地址 (local 模式下留空)
# Redis: redis://localhost:6379/0 or redis://:password@localhost:6379/0
# MySQL: mysql+aiomysql://user:pass@localhost/db
# PgSQL: postgresql+asyncpg://user:pass@localhost/db
SERVER_STORAGE_URL=
+74 -59
View File
@@ -31,6 +31,19 @@ git clone https://github.com/chenyme/grok2api
docker compose up -d
```
### 环境变量
> 配置 `.env` 文件
| 变量名 | 说明 | 默认值 | 示例 |
| :---------------------- | :-------------------------------------------------- | :---------- | :-------------------------------------------------- |
| `LOG_LEVEL` | 日志级别 | `INFO` | `DEBUG` |
| `SERVER_HOST` | 服务监听地址 | `0.0.0.0` | `0.0.0.0` |
| `SERVER_PORT` | 服务端口 | `8000` | `8000` |
| `SERVER_WORKERS` | Uvicorn worker 数量 | `1` | `2` |
| `SERVER_STORAGE_TYPE` | 存储类型(`local`/`redis`/`mysql`/`pgsql`) | `local` | `pgsql` |
| `SERVER_STORAGE_URL` | 存储连接串(local 时可为空) | `""` | `postgresql+asyncpg://user:password@host:5432/db` |
### 可用次数
- Basic 账号:80 次 / 20h
@@ -38,24 +51,25 @@ docker compose up -d
### 可用模型
| 模型名 | 计次 | 可用账号 | 对话功能 | 图像功能 | 视频功能 |
| :--- | :---: | :--- | :---: | :---: | :---: |
| `grok-3` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-3-fast` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-mini` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-fast` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-heavy` | 4 | Super | 支持 | 支持 | - |
| `grok-4.1` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4.1-thinking` | 4 | Basic/Super | 支持 | 支持 | - |
| `grok-imagine-1.0` | 4 | Basic/Super | - | 支持 | - |
| `grok-imagine-1.0-video` | - | Basic/Super | - | - | 支持 |
| 模型名 | 计次 | 可用账号 | 对话功能 | 图像功能 | 视频功能 |
| :------------------------- | :--: | :---------- | :------: | :------: | :------: |
| `grok-3` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-3-fast` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-mini` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-fast` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4-heavy` | 4 | Super | 支持 | 支持 | - |
| `grok-4.1` | 1 | Basic/Super | 支持 | 支持 | - |
| `grok-4.1-thinking` | 4 | Basic/Super | 支持 | 支持 | - |
| `grok-imagine-1.0` | 4 | Basic/Super | - | 支持 | - |
| `grok-imagine-1.0-video` | - | Basic/Super | - | - | 支持 |
<br>
## 接口说明
### `POST /v1/chat/completions`
> 通用接口,支持对话聊天、图像生成、图像编辑、视频生成、视频超分
```bash
@@ -73,17 +87,17 @@ curl http://localhost:8000/v1/chat/completions \
<br>
| 字段 | 类型 | 说明 | 可用参数 |
| :--- | :--- | :--- | :--- |
| `model` | string | 模型名称 | - |
| `messages` | array | 消息列表 | `developer`, `system`, `user`, `assistant` |
| `stream` | boolean | 是否开启流式输出 | `true`, `false` |
| `thinking` | string | 思维链模式 | `enabled`, `disabled`, `null` |
| `video_config` | object | **视频模型专用配置对象** | - |
| └─ `aspect_ratio` | string | 视频宽高比 | `16:9`, `9:16`, `1:1`, `2:3`, `3:2` |
| └─ `video_length` | integer | 视频时长 (秒) | `5` - `15` |
| └─ `resolution` | string | 分辨率 | `SD`, `HD` |
| └─ `preset` | string | 风格预设 | `fun`, `normal`, `spicy` |
| 字段 | 类型 | 说明 | 可用参数 |
| :------------------- | :------ | :----------------------------- | :------------------------------------------------- |
| `model` | string | 模型名称 | - |
| `messages` | array | 消息列表 | `developer`, `system`, `user`, `assistant` |
| `stream` | boolean | 是否开启流式输出 | `true`, `false` |
| `thinking` | string | 思维链模式 | `enabled`, `disabled`, `null` |
| `video_config` | object | **视频模型专用配置对象** | - |
| └─`aspect_ratio` | string | 视频宽高比 | `16:9`, `9:16`, `1:1`, `2:3`, `3:2` |
| └─`video_length` | integer | 视频时长 (秒) | `5` - `15` |
| └─`resolution` | string | 分辨率 | `SD`, `HD` |
| └─`preset` | string | 风格预设 | `fun`, `normal`, `spicy` |
注:除上述外的其他参数将自动丢弃并忽略
@@ -92,6 +106,7 @@ curl http://localhost:8000/v1/chat/completions \
</details>
### `POST /v1/images/generations`
> 图像接口,支持图像生成、图像编辑
```bash
@@ -110,12 +125,12 @@ curl http://localhost:8000/v1/images/generations \
<br>
| 字段 | 类型 | 说明 | 可用参数 |
| :--- | :--- | :--- | :--- |
| `model` | string | 图像模型名 | `grok-imagine-1.0` |
| `prompt` | string | 图像描述提示词 | - |
| `n` | integer | 生成数量 | `1` - `10` (流式模式仅限 `1` 或 `2`) |
| `stream` | boolean | 是否开启流式输出 | `true`, `false` |
| 字段 | 类型 | 说明 | 可用参数 |
| :--------- | :------ | :--------------- | :------------------------------------------- |
| `model` | string | 图像模型名 | `grok-imagine-1.0` |
| `prompt` | string | 图像描述提示词 | - |
| `n` | integer | 生成数量 | `1` - `10` (流式模式仅限 `1` 或 `2`) |
| `stream` | boolean | 是否开启流式输出 | `true`, `false` |
注:除上述外的其他参数将自动丢弃并忽略
@@ -129,36 +144,36 @@ curl http://localhost:8000/v1/images/generations \
配置文件:`data/config.toml`
| 模块 | 字段 | 配置名 | 说明 | 默认值 |
| :--- | :--- | :--- | :--- | :--- |
| **app** | `app_url` | 应用地址 | 当前 Grok2API 服务的外部访问 URL,用于文件链接访问。 | `http://127.0.0.1:8000` |
| | `app_key` | 后台密码 | 登录 Grok2API 服务管理后台的密码,请妥善保管。 | `grok2api` |
| | `api_key` | API 密钥 | 调用 Grok2API 服务所需的 Bearer Token,请妥善保管。 | `""` |
| | `image_format` | 图片格式 | 生成的图片格式(url 或 base64)。 | `url` |
| | `video_format` | 视频格式 | 生成的视频格式(仅支持 url)。 | `url` |
| **grok** | `temporary` | 临时对话 | 是否启用临时对话模式。 | `true` |
| | `stream` | 流式响应 | 是否默认启用流式输出。 | `true` |
| | `thinking` | 思维链 | 是否启用模型思维链输出。 | `true` |
| | `dynamic_statsig` | 动态指纹 | 是否启用动态生成 Statsig 值。 | `true` |
| | `filter_tags` | 过滤标签 | 自动过滤 Grok 响应中的特殊标签。 | `["xaiartifact", "xai:tool_usage_card", "grok:render"]` |
| | `timeout` | 超时时间 | 请求 Grok 服务的超时时间(秒)。 | `120` |
| | `base_proxy_url` | 基础代理 URL | 代理请求到 Grok 官网的基础服务地址。 | `""` |
| | `asset_proxy_url` | 资源代理 URL | 代理请求到 Grok 官网的静态资源(图片/视频)地址。 | `""` |
| | `cf_clearance` | CF Clearance | Cloudflare 验证 Cookie,用于验证 Cloudflare 的验证。 | `""` |
| | `max_retry` | 最大重试 | 请求 Grok 服务失败时的最大重试次数。 | `3` |
| | `retry_status_codes` | 重试状态码 | 触发重试的 HTTP 状态码列表。 | `[401, 429, 403]` |
| **token** | `auto_refresh` | 自动刷新 | 是否开启 Token 自动刷新机制。 | `true` |
| | `refresh_interval_hours` | 刷新间隔 | Token 刷新的时间间隔(小时)。 | `8` |
| | `fail_threshold` | 失败阈值 | 单个 Token 连续失败多少次后被标记为不可用。 | `5` |
| | `save_delay_ms` | 保存延迟 | Token 变更合并写入的延迟(毫秒)。 | `500` |
| | `reload_interval_sec` | 一致性刷新 | 多 worker 场景下 Token 状态刷新间隔(秒)。 | `30` |
| **cache** | `enable_auto_clean` | 自动清理 | 是否启用缓存自动清理,开启后按上限自动回收。 | `true` |
| | `limit_mb` | 清理阈值 | 缓存大小阈值(MB),超过阈值会触发清理。 | `1024` |
| **performance** | `assets_max_concurrent` | 资产并发上限 | 资源上传/下载/列表的并发上限。推荐 25。 | `25` |
| | `media_max_concurrent` | 媒体并发上限 | 视频/媒体生成请求的并发上限。推荐 50。 | `50` |
| | `usage_max_concurrent` | 用量并发上限 | 用量查询请求的并发上限。推荐 25。 | `25` |
| | `assets_delete_batch_size` | 资产清理批量 | 在线资产删除单批并发数量。推荐 10。 | `10` |
| | `admin_assets_batch_size` | 管理端批量 | 管理端在线资产统计/清理批量并发数量。推荐 10。 | `10` |
| 模块 | 字段 | 配置名 | 说明 | 默认值 |
| :-------------------- | :--------------------------- | :----------- | :--------------------------------------------------- | :-------------------------------------------------------- |
| **app** | `app_url` | 应用地址 | 当前 Grok2API 服务的外部访问 URL,用于文件链接访问。 | `http://127.0.0.1:8000` |
| | `app_key` | 后台密码 | 登录 Grok2API 服务管理后台的密码,请妥善保管。 | `grok2api` |
| | `api_key` | API 密钥 | 调用 Grok2API 服务所需的 Bearer Token,请妥善保管。 | `""` |
| | `image_format` | 图片格式 | 生成的图片格式(url 或 base64)。 | `url` |
| | `video_format` | 视频格式 | 生成的视频格式(仅支持 url)。 | `url` |
| **grok** | `temporary` | 临时对话 | 是否启用临时对话模式。 | `true` |
| | `stream` | 流式响应 | 是否默认启用流式输出。 | `true` |
| | `thinking` | 思维链 | 是否启用模型思维链输出。 | `true` |
| | `dynamic_statsig` | 动态指纹 | 是否启用动态生成 Statsig 值。 | `true` |
| | `filter_tags` | 过滤标签 | 自动过滤 Grok 响应中的特殊标签。 | `["xaiartifact", "xai:tool_usage_card", "grok:render"]` |
| | `timeout` | 超时时间 | 请求 Grok 服务的超时时间(秒)。 | `120` |
| | `base_proxy_url` | 基础代理 URL | 代理请求到 Grok 官网的基础服务地址。 | `""` |
| | `asset_proxy_url` | 资源代理 URL | 代理请求到 Grok 官网的静态资源(图片/视频)地址。 | `""` |
| | `cf_clearance` | CF Clearance | Cloudflare 验证 Cookie,用于验证 Cloudflare 的验证。 | `""` |
| | `max_retry` | 最大重试 | 请求 Grok 服务失败时的最大重试次数。 | `3` |
| | `retry_status_codes` | 重试状态码 | 触发重试的 HTTP 状态码列表。 | `[401, 429, 403]` |
| **token** | `auto_refresh` | 自动刷新 | 是否开启 Token 自动刷新机制。 | `true` |
| | `refresh_interval_hours` | 刷新间隔 | Token 刷新的时间间隔(小时)。 | `8` |
| | `fail_threshold` | 失败阈值 | 单个 Token 连续失败多少次后被标记为不可用。 | `5` |
| | `save_delay_ms` | 保存延迟 | Token 变更合并写入的延迟(毫秒)。 | `500` |
| | `reload_interval_sec` | 一致性刷新 | 多 worker 场景下 Token 状态刷新间隔(秒)。 | `30` |
| **cache** | `enable_auto_clean` | 自动清理 | 是否启用缓存自动清理,开启后按上限自动回收。 | `true` |
| | `limit_mb` | 清理阈值 | 缓存大小阈值(MB),超过阈值会触发清理。 | `1024` |
| **performance** | `assets_max_concurrent` | 资产并发上限 | 资源上传/下载/列表的并发上限。推荐 25。 | `25` |
| | `media_max_concurrent` | 媒体并发上限 | 视频/媒体生成请求的并发上限。推荐 50。 | `50` |
| | `usage_max_concurrent` | 用量并发上限 | 用量查询请求的并发上限。推荐 25。 | `25` |
| | `assets_delete_batch_size` | 资产清理批量 | 在线资产删除单批并发数量。推荐 10。 | `10` |
| | `admin_assets_batch_size` | 管理端批量 | 管理端在线资产统计/清理批量并发数量。推荐 10。 | `10` |
<br>
+2 -39
View File
@@ -5,49 +5,12 @@ ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
DATA_DIR="$ROOT_DIR/data"
LOG_DIR="$ROOT_DIR/logs"
TMP_DIR="$DATA_DIR/tmp"
DEFAULT_CONFIG="$ROOT_DIR/config.defaults.toml"
mkdir -p "$DATA_DIR" "$LOG_DIR" "$TMP_DIR"
if [ ! -f "$DATA_DIR/config.toml" ]; then
cat > "$DATA_DIR/config.toml" <<'TOML'
[grok]
temporary = true
stream = true
thinking = true
dynamic_statsig = true
filter_tags = ["xaiartifact","xai:tool_usage_card","grok:render"]
timeout = 120
base_proxy_url = ""
asset_proxy_url = ""
cf_clearance = ""
max_retry = 3
retry_status_codes = [401,429,403]
[app]
app_url = "http://127.0.0.1:8000"
app_key = "grok2api"
api_key = ""
image_format = "url"
video_format = "url"
[token]
auto_refresh = true
refresh_interval_hours = 8
fail_threshold = 5
save_delay_ms = 500
reload_interval_sec = 30
[cache]
enable_auto_clean = true
limit_mb = 1024
[performance]
assets_max_concurrent = 25 # 推荐 25
media_max_concurrent = 50 # 推荐 50
usage_max_concurrent = 25 # 推荐 25
assets_delete_batch_size = 10 # 推荐 10
admin_assets_batch_size = 10 # 推荐 10
TOML
cp "$DEFAULT_CONFIG" "$DATA_DIR/config.toml"
fi
if [ ! -f "$DATA_DIR/token.json" ]; then