feat: migrate configuration structure to improve organization and flexibility
This commit is contained in:
1 parent
5d369596c5
commit
bcd94069a7
22 files changed
+488
-253
No files matched your search
+5
-5
@@ -93,7 +93,7 @@ def _validate_common_request(
|
||||
# WS 流式仅支持 b64_json (base64 视为同义)
|
||||
if (
|
||||
request.stream
|
||||
and get_config("grok.image_ws")
|
||||
and get_config("image.image_ws")
|
||||
and request.response_format
|
||||
and request.response_format not in {"b64_json", "base64"}
|
||||
):
|
||||
@@ -319,13 +319,13 @@ async def create_image(request: ImageGenerationRequest):
|
||||
# 获取 token 和模型信息
|
||||
token_mgr, token = await _get_token(request.model)
|
||||
model_info = ModelService.get(request.model)
|
||||
use_ws = bool(get_config("grok.image_ws"))
|
||||
use_ws = bool(get_config("image.image_ws"))
|
||||
|
||||
# 流式模式
|
||||
if request.stream:
|
||||
if use_ws:
|
||||
aspect_ratio = resolve_aspect_ratio(request.size)
|
||||
enable_nsfw = bool(get_config("grok.image_ws_nsfw"))
|
||||
enable_nsfw = bool(get_config("image.image_ws_nsfw"))
|
||||
upstream = image_service.stream(
|
||||
token=token,
|
||||
prompt=request.prompt,
|
||||
@@ -372,7 +372,7 @@ async def create_image(request: ImageGenerationRequest):
|
||||
usage_override = None
|
||||
if use_ws:
|
||||
aspect_ratio = resolve_aspect_ratio(request.size)
|
||||
enable_nsfw = bool(get_config("grok.image_ws_nsfw"))
|
||||
enable_nsfw = bool(get_config("image.image_ws_nsfw"))
|
||||
all_images = []
|
||||
seen = set()
|
||||
expected_per_call = 6
|
||||
@@ -643,7 +643,7 @@ async def edit_image(
|
||||
] = parent_post_id
|
||||
|
||||
raw_payload = {
|
||||
"temporary": bool(get_config("grok.temporary")),
|
||||
"temporary": bool(get_config("chat.temporary")),
|
||||
"modelName": model_info.grok_model,
|
||||
"message": edit_request.prompt,
|
||||
"enableImageGeneration": True,
|
||||
|
||||
+86
-1
@@ -32,6 +32,81 @@ def _deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any
|
||||
return result
|
||||
|
||||
|
||||
def _migrate_deprecated_config(config: Dict[str, Any], valid_sections: set) -> tuple[Dict[str, Any], set]:
|
||||
"""
|
||||
迁移废弃的配置节到新配置结构
|
||||
|
||||
Returns:
|
||||
(迁移后的配置, 废弃的配置节集合)
|
||||
"""
|
||||
# 配置映射规则:旧配置 -> 新配置
|
||||
MIGRATION_MAP = {
|
||||
# grok.* -> 对应的新配置节
|
||||
"grok.temporary": "chat.temporary",
|
||||
"grok.disable_memory": "chat.disable_memory",
|
||||
"grok.stream": "chat.stream",
|
||||
"grok.thinking": "chat.thinking",
|
||||
"grok.dynamic_statsig": "chat.dynamic_statsig",
|
||||
"grok.filter_tags": "chat.filter_tags",
|
||||
|
||||
"grok.timeout": "network.timeout",
|
||||
"grok.base_proxy_url": "network.base_proxy_url",
|
||||
"grok.asset_proxy_url": "network.asset_proxy_url",
|
||||
|
||||
"grok.cf_clearance": "security.cf_clearance",
|
||||
"grok.browser": "security.browser",
|
||||
"grok.user_agent": "security.user_agent",
|
||||
|
||||
"grok.max_retry": "retry.max_retry",
|
||||
"grok.retry_status_codes": "retry.retry_status_codes",
|
||||
"grok.retry_backoff_base": "retry.retry_backoff_base",
|
||||
"grok.retry_backoff_factor": "retry.retry_backoff_factor",
|
||||
"grok.retry_backoff_max": "retry.retry_backoff_max",
|
||||
"grok.retry_budget": "retry.retry_budget",
|
||||
|
||||
"grok.stream_idle_timeout": "timeout.stream_idle_timeout",
|
||||
"grok.video_idle_timeout": "timeout.video_idle_timeout",
|
||||
|
||||
"grok.image_ws": "image.image_ws",
|
||||
"grok.image_ws_nsfw": "image.image_ws_nsfw",
|
||||
"grok.image_ws_blocked_seconds": "image.image_ws_blocked_seconds",
|
||||
"grok.image_ws_final_min_bytes": "image.image_ws_final_min_bytes",
|
||||
"grok.image_ws_medium_min_bytes": "image.image_ws_medium_min_bytes",
|
||||
}
|
||||
|
||||
deprecated_sections = set(config.keys()) - valid_sections
|
||||
if not deprecated_sections:
|
||||
return config, set()
|
||||
|
||||
result = {k: deepcopy(v) for k, v in config.items() if k in valid_sections}
|
||||
migrated_count = 0
|
||||
|
||||
# 处理废弃配置节中的配置项
|
||||
for old_section in deprecated_sections:
|
||||
if old_section not in config or not isinstance(config[old_section], dict):
|
||||
continue
|
||||
|
||||
for old_key, old_value in config[old_section].items():
|
||||
# 查找映射规则
|
||||
old_path = f"{old_section}.{old_key}"
|
||||
new_path = MIGRATION_MAP.get(old_path)
|
||||
|
||||
if new_path:
|
||||
new_section, new_key = new_path.split(".", 1)
|
||||
# 确保新配置节存在
|
||||
if new_section not in result:
|
||||
result[new_section] = {}
|
||||
# 迁移配置项(保留用户的自定义值)
|
||||
result[new_section][new_key] = old_value
|
||||
migrated_count += 1
|
||||
logger.debug(f"Migrated config: {old_path} -> {new_path} = {old_value}")
|
||||
|
||||
if migrated_count > 0:
|
||||
logger.info(f"Migrated {migrated_count} config items from deprecated sections")
|
||||
|
||||
return result, deprecated_sections
|
||||
|
||||
|
||||
def _load_defaults() -> Dict[str, Any]:
|
||||
"""加载默认配置文件"""
|
||||
if not DEFAULT_CONFIG_FILE.exists():
|
||||
@@ -91,10 +166,18 @@ class Config:
|
||||
config_data = {}
|
||||
|
||||
config_data = config_data or {}
|
||||
|
||||
# 检查是否有废弃的配置节
|
||||
valid_sections = set(self._defaults.keys())
|
||||
config_data, deprecated_sections = _migrate_deprecated_config(config_data, valid_sections)
|
||||
if deprecated_sections:
|
||||
logger.info(f"Cleaned deprecated config sections: {deprecated_sections}")
|
||||
|
||||
merged = _deep_merge(self._defaults, config_data)
|
||||
|
||||
# 自动回填缺失配置到存储
|
||||
should_persist = (not from_remote) or (merged != config_data)
|
||||
# 或迁移了配置后需要更新
|
||||
should_persist = (not from_remote) or (merged != config_data) or deprecated_sections
|
||||
if should_persist:
|
||||
async with storage.acquire_lock("config_save", timeout=10):
|
||||
await storage.save_config(merged)
|
||||
@@ -102,6 +185,8 @@ class Config:
|
||||
logger.info(
|
||||
f"Initialized remote storage ({storage.__class__.__name__}) with config baseline."
|
||||
)
|
||||
if deprecated_sections:
|
||||
logger.info("Configuration automatically migrated and cleaned.")
|
||||
|
||||
self._config = merged
|
||||
except Exception as e:
|
||||
|
||||
@@ -6,60 +6,84 @@ Grok 服务默认配置
|
||||
|
||||
# Grok 服务默认配置
|
||||
GROK_DEFAULTS = {
|
||||
"grok": {
|
||||
# 网络配置
|
||||
"browser": "chrome136",
|
||||
"app": {
|
||||
"app_url": "",
|
||||
"app_key": "grok2api",
|
||||
"api_key": "",
|
||||
"image_format": "url",
|
||||
"video_format": "html",
|
||||
},
|
||||
|
||||
"network": {
|
||||
"timeout": 120,
|
||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
"base_proxy_url": "",
|
||||
"asset_proxy_url": "",
|
||||
},
|
||||
|
||||
"security": {
|
||||
"cf_clearance": "",
|
||||
|
||||
# 对话配置
|
||||
"browser": "chrome136",
|
||||
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36",
|
||||
},
|
||||
|
||||
"chat": {
|
||||
"temporary": True,
|
||||
"disable_memory": True,
|
||||
"stream": True,
|
||||
"thinking": False,
|
||||
"dynamic_statsig": True,
|
||||
"filter_tags": ["grok:render", "xaiartifact", "xai:tool_usage_card"],
|
||||
|
||||
# 重试配置
|
||||
},
|
||||
|
||||
"retry": {
|
||||
"max_retry": 3,
|
||||
"retry_status_codes": [401, 429, 403],
|
||||
"retry_backoff_base": 0.5,
|
||||
"retry_backoff_factor": 2.0,
|
||||
"retry_backoff_max": 30.0,
|
||||
"retry_budget": 90.0,
|
||||
|
||||
# 超时配置
|
||||
},
|
||||
|
||||
"timeout": {
|
||||
"stream_idle_timeout": 45.0,
|
||||
"video_idle_timeout": 90.0,
|
||||
|
||||
# 图片配置
|
||||
},
|
||||
|
||||
"image": {
|
||||
"image_ws": True,
|
||||
"image_ws_nsfw": True,
|
||||
"image_ws_blocked_seconds": 15,
|
||||
"image_ws_final_min_bytes": 100000,
|
||||
"image_ws_medium_min_bytes": 30000,
|
||||
|
||||
# Statsig
|
||||
"dynamic_statsig": True,
|
||||
},
|
||||
|
||||
"app": {
|
||||
"app_url": "",
|
||||
"image_format": "url",
|
||||
"video_format": "html",
|
||||
},
|
||||
|
||||
"performance": {
|
||||
"assets_max_concurrent": 25,
|
||||
"assets_delete_batch_size": 10,
|
||||
"media_max_concurrent": 50,
|
||||
"usage_max_concurrent": 25,
|
||||
"token": {
|
||||
"auto_refresh": True,
|
||||
"refresh_interval_hours": 8,
|
||||
"super_refresh_interval_hours": 2,
|
||||
"fail_threshold": 5,
|
||||
"save_delay_ms": 500,
|
||||
"reload_interval_sec": 30,
|
||||
},
|
||||
|
||||
"cache": {
|
||||
"enable_auto_clean": True,
|
||||
"limit_mb": 1024,
|
||||
},
|
||||
|
||||
"performance": {
|
||||
"assets_max_concurrent": 25,
|
||||
"assets_delete_batch_size": 10,
|
||||
"assets_batch_size": 10,
|
||||
"assets_max_tokens": 1000,
|
||||
"media_max_concurrent": 50,
|
||||
"usage_max_concurrent": 25,
|
||||
"usage_batch_size": 50,
|
||||
"usage_max_tokens": 1000,
|
||||
"nsfw_max_concurrent": 10,
|
||||
"nsfw_batch_size": 50,
|
||||
"nsfw_max_tokens": 1000,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,13 +32,13 @@ class StreamProcessor(BaseProcessor):
|
||||
self.fingerprint: str = ""
|
||||
self.think_opened: bool = False
|
||||
self.role_sent: bool = False
|
||||
self.filter_tags = get_config("grok.filter_tags")
|
||||
self.filter_tags = get_config("chat.filter_tags")
|
||||
self.image_format = get_config("app.image_format")
|
||||
self._tag_buffer: str = ""
|
||||
self._in_filter_tag: bool = False
|
||||
|
||||
if think is None:
|
||||
self.show_think = get_config("grok.thinking")
|
||||
self.show_think = get_config("chat.thinking")
|
||||
else:
|
||||
self.show_think = think
|
||||
|
||||
@@ -114,7 +114,7 @@ class StreamProcessor(BaseProcessor):
|
||||
|
||||
async def process(self, response: AsyncIterable[bytes]) -> AsyncGenerator[str, None]:
|
||||
"""处理流式响应"""
|
||||
idle_timeout = get_config("grok.stream_idle_timeout")
|
||||
idle_timeout = get_config("timeout.stream_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
@@ -223,7 +223,7 @@ class CollectProcessor(BaseProcessor):
|
||||
def __init__(self, model: str, token: str = ""):
|
||||
super().__init__(model, token)
|
||||
self.image_format = get_config("app.image_format")
|
||||
self.filter_tags = get_config("grok.filter_tags")
|
||||
self.filter_tags = get_config("chat.filter_tags")
|
||||
|
||||
def _filter_content(self, content: str) -> str:
|
||||
"""过滤内容中的特殊标签"""
|
||||
@@ -242,7 +242,7 @@ class CollectProcessor(BaseProcessor):
|
||||
response_id = ""
|
||||
fingerprint = ""
|
||||
content = ""
|
||||
idle_timeout = get_config("grok.stream_idle_timeout")
|
||||
idle_timeout = get_config("timeout.stream_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
|
||||
@@ -45,7 +45,7 @@ class ImageStreamProcessor(BaseProcessor):
|
||||
async def process(self, response: AsyncIterable[bytes]) -> AsyncGenerator[str, None]:
|
||||
"""处理流式响应"""
|
||||
final_images = []
|
||||
idle_timeout = get_config("grok.stream_idle_timeout")
|
||||
idle_timeout = get_config("timeout.stream_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
@@ -160,7 +160,7 @@ class ImageCollectProcessor(BaseProcessor):
|
||||
async def process(self, response: AsyncIterable[bytes]) -> List[str]:
|
||||
"""处理并收集图片"""
|
||||
images = []
|
||||
idle_timeout = get_config("grok.stream_idle_timeout")
|
||||
idle_timeout = get_config("timeout.stream_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
|
||||
@@ -32,7 +32,7 @@ class VideoStreamProcessor(BaseProcessor):
|
||||
self.video_format = str(get_config("app.video_format")).lower()
|
||||
|
||||
if think is None:
|
||||
self.show_think = get_config("grok.thinking")
|
||||
self.show_think = get_config("chat.thinking")
|
||||
else:
|
||||
self.show_think = think
|
||||
|
||||
@@ -68,7 +68,7 @@ class VideoStreamProcessor(BaseProcessor):
|
||||
|
||||
async def process(self, response: AsyncIterable[bytes]) -> AsyncGenerator[str, None]:
|
||||
"""处理视频流式响应"""
|
||||
idle_timeout = get_config("grok.video_idle_timeout")
|
||||
idle_timeout = get_config("timeout.video_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
@@ -171,7 +171,7 @@ class VideoCollectProcessor(BaseProcessor):
|
||||
"""处理并收集视频响应"""
|
||||
response_id = ""
|
||||
content = ""
|
||||
idle_timeout = get_config("grok.video_idle_timeout")
|
||||
idle_timeout = get_config("timeout.video_idle_timeout")
|
||||
|
||||
try:
|
||||
async for line in _with_idle_timeout(response, idle_timeout, self.model):
|
||||
|
||||
@@ -111,10 +111,10 @@ class ServiceConfig:
|
||||
@classmethod
|
||||
def from_settings(cls, proxy: Optional[str] = None):
|
||||
return cls(
|
||||
proxy=proxy or get_config("grok.asset_proxy_url") or get_config("grok.base_proxy_url"),
|
||||
timeout=get_config("grok.timeout"),
|
||||
browser=get_config("grok.browser"),
|
||||
user_agent=get_config("grok.user_agent"),
|
||||
proxy=proxy or get_config("network.asset_proxy_url") or get_config("network.base_proxy_url"),
|
||||
timeout=get_config("network.timeout"),
|
||||
browser=get_config("security.browser"),
|
||||
user_agent=get_config("security.user_agent"),
|
||||
)
|
||||
|
||||
def get_proxies(self) -> Optional[dict]:
|
||||
|
||||
@@ -102,7 +102,7 @@ class ChatRequestBuilder:
|
||||
@staticmethod
|
||||
def build_headers(token: str) -> Dict[str, str]:
|
||||
"""构造请求头"""
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -142,7 +142,7 @@ class ChatRequestBuilder:
|
||||
merged_attachments.extend(image_attachments)
|
||||
|
||||
payload = {
|
||||
"temporary": get_config("grok.temporary"),
|
||||
"temporary": get_config("chat.temporary"),
|
||||
"modelName": model,
|
||||
"message": message,
|
||||
"fileAttachments": merged_attachments,
|
||||
@@ -160,7 +160,7 @@ class ChatRequestBuilder:
|
||||
"modelConfigOverride": {"modelMap": {}},
|
||||
"requestModelDetails": {"modelId": model},
|
||||
},
|
||||
"disableMemory": get_config("grok.disable_memory"),
|
||||
"disableMemory": get_config("chat.disable_memory"),
|
||||
"deviceEnvInfo": {
|
||||
"darkModeEnabled": False,
|
||||
"devicePixelRatio": 2,
|
||||
@@ -181,27 +181,27 @@ class GrokChatService:
|
||||
"""Grok API 调用服务"""
|
||||
|
||||
def __init__(self, proxy: str = None):
|
||||
self.proxy = proxy or get_config("grok.base_proxy_url")
|
||||
self.proxy = proxy or get_config("network.base_proxy_url")
|
||||
|
||||
async def chat(self, token: str, message: str, model: str = "grok-3", mode: str = None,
|
||||
stream: bool = None, file_attachments: List[str] = None,
|
||||
image_attachments: List[str] = None, raw_payload: Dict[str, Any] = None):
|
||||
"""发送聊天请求"""
|
||||
if stream is None:
|
||||
stream = get_config("grok.stream")
|
||||
stream = get_config("chat.stream")
|
||||
|
||||
headers = ChatRequestBuilder.build_headers(token)
|
||||
payload = raw_payload if raw_payload is not None else ChatRequestBuilder.build_payload(
|
||||
message, model, mode, file_attachments, image_attachments
|
||||
)
|
||||
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
|
||||
timeout = get_config("grok.timeout")
|
||||
timeout = get_config("network.timeout")
|
||||
|
||||
logger.debug(f"Chat request: model={model}, mode={mode}, stream={stream}, attachments={len(file_attachments or [])}")
|
||||
|
||||
# 建立连接
|
||||
async def establish_connection():
|
||||
browser = get_config("grok.browser")
|
||||
browser = get_config("security.browser")
|
||||
session = AsyncSession(impersonate=browser)
|
||||
try:
|
||||
response = await session.post(
|
||||
@@ -303,7 +303,7 @@ class GrokChatService:
|
||||
finally:
|
||||
await upload_service.close()
|
||||
|
||||
stream = request.stream if request.stream is not None else get_config("grok.stream")
|
||||
stream = request.stream if request.stream is not None else get_config("chat.stream")
|
||||
|
||||
response = await self.chat(
|
||||
token, message, grok_model, mode, stream,
|
||||
@@ -340,7 +340,7 @@ class ChatService:
|
||||
|
||||
# 解析参数
|
||||
think = {"enabled": True, "disabled": False}.get(thinking)
|
||||
is_stream = stream if stream is not None else get_config("grok.stream")
|
||||
is_stream = stream if stream is not None else get_config("chat.stream")
|
||||
|
||||
# 构造请求
|
||||
chat_request = ChatRequest(model=model, messages=messages, stream=is_stream, think=think)
|
||||
|
||||
@@ -32,7 +32,7 @@ class ImageService:
|
||||
self._url_pattern = re.compile(r"/images/([a-f0-9-]+)\.(png|jpg|jpeg)")
|
||||
|
||||
def _resolve_proxy(self) -> tuple[aiohttp.BaseConnector, Optional[str]]:
|
||||
proxy_url = get_config("grok.base_proxy_url")
|
||||
proxy_url = get_config("network.base_proxy_url")
|
||||
if not proxy_url:
|
||||
return aiohttp.TCPConnector(ssl=self._ssl_context), None
|
||||
|
||||
@@ -46,7 +46,7 @@ class ImageService:
|
||||
|
||||
def _get_ws_headers(self, token: str) -> Dict[str, str]:
|
||||
cookie = build_sso_cookie(token, include_rw=True)
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
return {
|
||||
"Cookie": cookie,
|
||||
"Origin": "https://grok.com",
|
||||
@@ -61,7 +61,7 @@ class ImageService:
|
||||
return match.group(1) if match else None
|
||||
|
||||
def _is_final_image(self, url: str, blob_size: int) -> bool:
|
||||
return (url or "").lower().endswith((".jpg", ".jpeg")) and blob_size > get_config("grok.image_ws_final_min_bytes")
|
||||
return (url or "").lower().endswith((".jpg", ".jpeg")) and blob_size > get_config("image.image_ws_final_min_bytes")
|
||||
|
||||
def _classify_image(self, url: str, blob: str) -> Optional[Dict[str, object]]:
|
||||
if not url or not blob:
|
||||
@@ -71,7 +71,7 @@ class ImageService:
|
||||
blob_size = len(blob)
|
||||
is_final = self._is_final_image(url, blob_size)
|
||||
|
||||
stage = "final" if is_final else ("medium" if blob_size > get_config("grok.image_ws_medium_min_bytes") else "preview")
|
||||
stage = "final" if is_final else ("medium" if blob_size > get_config("image.image_ws_medium_min_bytes") else "preview")
|
||||
|
||||
return {
|
||||
"type": "image",
|
||||
@@ -122,8 +122,8 @@ class ImageService:
|
||||
) -> AsyncGenerator[Dict[str, object], None]:
|
||||
request_id = str(uuid.uuid4())
|
||||
headers = self._get_ws_headers(token)
|
||||
timeout = float(get_config("grok.timeout"))
|
||||
blocked_seconds = float(get_config("grok.image_ws_blocked_seconds"))
|
||||
timeout = float(get_config("network.timeout"))
|
||||
blocked_seconds = float(get_config("image.image_ws_blocked_seconds"))
|
||||
|
||||
try:
|
||||
connector, proxy = self._resolve_proxy()
|
||||
|
||||
@@ -43,14 +43,14 @@ class VideoService:
|
||||
"""视频生成服务"""
|
||||
|
||||
def __init__(self, proxy: str = None):
|
||||
self.proxy = proxy or get_config("grok.base_proxy_url")
|
||||
self.timeout = get_config("grok.timeout")
|
||||
self.proxy = proxy or get_config("network.base_proxy_url")
|
||||
self.timeout = get_config("network.timeout")
|
||||
|
||||
def _build_headers(
|
||||
self, token: str, referer: str = "https://grok.com/imagine"
|
||||
) -> dict:
|
||||
"""构建请求头"""
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -103,7 +103,7 @@ class VideoService:
|
||||
CREATE_POST_API,
|
||||
headers=headers,
|
||||
json=payload,
|
||||
impersonate=get_config("grok.browser"),
|
||||
impersonate=get_config("security.browser"),
|
||||
timeout=30,
|
||||
proxies=self._build_proxies(),
|
||||
)
|
||||
@@ -172,7 +172,7 @@ class VideoService:
|
||||
headers = self._build_headers(token)
|
||||
payload = self._build_payload(prompt, post_id, aspect_ratio, video_length, resolution_name, preset)
|
||||
|
||||
session = AsyncSession(impersonate=get_config("grok.browser"))
|
||||
session = AsyncSession(impersonate=get_config("security.browser"))
|
||||
response = await session.post(
|
||||
CHAT_API,
|
||||
headers=headers,
|
||||
@@ -267,7 +267,7 @@ class VideoService:
|
||||
)
|
||||
|
||||
think = {"enabled": True, "disabled": False}.get(thinking)
|
||||
is_stream = stream if stream is not None else get_config("grok.stream")
|
||||
is_stream = stream if stream is not None else get_config("chat.stream")
|
||||
|
||||
# 提取内容
|
||||
from app.services.grok.services.chat import MessageExtractor
|
||||
|
||||
@@ -37,8 +37,8 @@ class NSFWService:
|
||||
"""NSFW 模式服务"""
|
||||
|
||||
def __init__(self, proxy: str = None):
|
||||
self.proxy = proxy or get_config("grok.base_proxy_url")
|
||||
self.timeout = float(get_config("grok.timeout"))
|
||||
self.proxy = proxy or get_config("network.base_proxy_url")
|
||||
self.timeout = float(get_config("network.timeout"))
|
||||
|
||||
def _build_proxies(self) -> Optional[dict]:
|
||||
"""构建代理配置"""
|
||||
@@ -60,7 +60,7 @@ class NSFWService:
|
||||
def _build_headers(self, token: str) -> dict:
|
||||
"""构造 gRPC-Web 请求头"""
|
||||
cookie = build_sso_cookie(token, include_rw=True)
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
return {
|
||||
"accept": "*/*",
|
||||
"content-type": "application/grpc-web+proto",
|
||||
@@ -75,7 +75,7 @@ class NSFWService:
|
||||
def _build_birth_headers(self, token: str) -> dict:
|
||||
"""构造设置出生日期请求头"""
|
||||
cookie = build_sso_cookie(token, include_rw=True)
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
return {
|
||||
"accept": "*/*",
|
||||
"content-type": "application/json",
|
||||
@@ -97,9 +97,7 @@ class NSFWService:
|
||||
protobuf = b"\x0a\x02\x10\x01\x12" + bytes([len(inner)]) + inner
|
||||
return encode_grpc_web_payload(protobuf)
|
||||
|
||||
async def _set_birth_date(
|
||||
self, session: AsyncSession, token: str
|
||||
) -> tuple[bool, int, Optional[str]]:
|
||||
async def _set_birth_date(self, session: AsyncSession, token: str) -> tuple[bool, int, Optional[str]]:
|
||||
"""设置出生日期"""
|
||||
headers = self._build_birth_headers(token)
|
||||
payload = {"birthDate": self._random_birth_date()}
|
||||
@@ -125,7 +123,7 @@ class NSFWService:
|
||||
logger.debug(f"NSFW payload: len={len(payload)} hex={payload.hex()}")
|
||||
|
||||
try:
|
||||
browser = get_config("grok.browser")
|
||||
browser = get_config("security.browser")
|
||||
async with AsyncSession(impersonate=browser) as session:
|
||||
# 先设置出生日期
|
||||
ok, birth_status, birth_err = await self._set_birth_date(session, token)
|
||||
|
||||
@@ -23,12 +23,12 @@ class UsageService:
|
||||
"""用量查询服务"""
|
||||
|
||||
def __init__(self, proxy: str = None):
|
||||
self.proxy = proxy or get_config("grok.base_proxy_url")
|
||||
self.timeout = get_config("grok.timeout")
|
||||
self.proxy = proxy or get_config("network.base_proxy_url")
|
||||
self.timeout = get_config("network.timeout")
|
||||
|
||||
def _build_headers(self, token: str) -> dict:
|
||||
"""构建请求头"""
|
||||
user_agent = get_config("grok.user_agent")
|
||||
user_agent = get_config("security.user_agent")
|
||||
headers = {
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
@@ -97,7 +97,7 @@ class UsageService:
|
||||
try:
|
||||
headers = self._build_headers(token)
|
||||
payload = {"requestKind": "DEFAULT", "modelName": model_name}
|
||||
browser = get_config("grok.browser")
|
||||
browser = get_config("security.browser")
|
||||
|
||||
async with AsyncSession() as session:
|
||||
response = await session.post(
|
||||
|
||||
@@ -18,7 +18,7 @@ class VoiceService:
|
||||
"""Voice Mode Service (LiveKit)"""
|
||||
|
||||
def __init__(self, proxy: str = None):
|
||||
self.proxy = proxy or get_config("grok.base_proxy_url")
|
||||
self.proxy = proxy or get_config("network.base_proxy_url")
|
||||
|
||||
async def get_token(
|
||||
self,
|
||||
@@ -42,8 +42,8 @@ class VoiceService:
|
||||
proxies = {"http": self.proxy, "https": self.proxy} if self.proxy else None
|
||||
|
||||
try:
|
||||
browser = get_config("grok.browser")
|
||||
timeout = get_config("grok.timeout")
|
||||
browser = get_config("security.browser")
|
||||
timeout = get_config("network.timeout")
|
||||
async with AsyncSession(impersonate=browser) as session:
|
||||
response = await session.post(
|
||||
LIVEKIT_TOKEN_API,
|
||||
|
||||
@@ -17,7 +17,7 @@ def _normalize_token(token: str) -> str:
|
||||
|
||||
def build_sso_cookie(token: str, include_rw: bool = False) -> str:
|
||||
token = _normalize_token(token)
|
||||
cf = get_config("grok.cf_clearance")
|
||||
cf = get_config("security.cf_clearance")
|
||||
cookie = f"sso={token}"
|
||||
if include_rw:
|
||||
cookie = f"{cookie}; sso-rw={token}"
|
||||
|
||||
@@ -23,17 +23,17 @@ class RetryContext:
|
||||
|
||||
def __init__(self):
|
||||
self.attempt = 0
|
||||
self.max_retry = int(get_config("grok.max_retry"))
|
||||
self.retry_codes = get_config("grok.retry_status_codes")
|
||||
self.max_retry = int(get_config("retry.max_retry"))
|
||||
self.retry_codes = get_config("retry.retry_status_codes")
|
||||
self.last_error = None
|
||||
self.last_status = None
|
||||
self.total_delay = 0.0
|
||||
self.retry_budget = float(get_config("grok.retry_budget"))
|
||||
self.retry_budget = float(get_config("retry.retry_budget"))
|
||||
|
||||
# 退避参数
|
||||
self.backoff_base = float(get_config("grok.retry_backoff_base"))
|
||||
self.backoff_factor = float(get_config("grok.retry_backoff_factor"))
|
||||
self.backoff_max = float(get_config("grok.retry_backoff_max"))
|
||||
self.backoff_base = float(get_config("retry.retry_backoff_base"))
|
||||
self.backoff_factor = float(get_config("retry.retry_backoff_factor"))
|
||||
self.backoff_max = float(get_config("retry.retry_backoff_max"))
|
||||
|
||||
# decorrelated jitter 状态
|
||||
self._last_delay = self.backoff_base
|
||||
|
||||
@@ -30,7 +30,7 @@ class StatsigService:
|
||||
Returns:
|
||||
Base64 编码的 ID
|
||||
"""
|
||||
dynamic = get_config("grok.dynamic_statsig")
|
||||
dynamic = get_config("chat.dynamic_statsig")
|
||||
|
||||
if not dynamic:
|
||||
return "ZTpUeXBlRXJyb3I6IENhbm5vdCByZWFkIHByb3BlcnRpZXMgb2YgdW5kZWZpbmVkIChyZWFkaW5nICdjaGlsZE5vZGVzJyk="
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<script src="/static/common/header.js?v=3"></script>
|
||||
<script src="/static/common/footer.js?v=1"></script>
|
||||
<script src="/static/common/toast.js"></script>
|
||||
<script src="/static/config/config.js?v=13"></script>
|
||||
<script src="/static/config/config.js?v=14"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+52
-54
@@ -26,6 +26,7 @@ const NUMERIC_FIELDS = new Set([
|
||||
'video_idle_timeout',
|
||||
'image_ws_blocked_seconds',
|
||||
'image_ws_final_min_bytes',
|
||||
'image_ws_medium_min_bytes',
|
||||
'nsfw_max_concurrent',
|
||||
'nsfw_batch_size',
|
||||
'nsfw_max_tokens'
|
||||
@@ -40,44 +41,60 @@ const LOCALE_MAP = {
|
||||
"image_format": { title: "图片格式", desc: "生成的图片格式(url 或 base64)。" },
|
||||
"video_format": { title: "视频格式", desc: "生成的视频格式(html 或 url,url 为处理后的链接)。" }
|
||||
},
|
||||
"grok": {
|
||||
"label": "Grok 设置",
|
||||
"network": {
|
||||
"label": "网络配置",
|
||||
"timeout": { title: "请求超时", desc: "请求 Grok 服务的超时时间(秒)。" },
|
||||
"base_proxy_url": { title: "基础代理 URL", desc: "代理请求到 Grok 官网的基础服务地址。" },
|
||||
"asset_proxy_url": { title: "资源代理 URL", desc: "代理请求到 Grok 官网的静态资源(图片/视频)地址。" }
|
||||
},
|
||||
"security": {
|
||||
"label": "反爬虫验证",
|
||||
"cf_clearance": { title: "CF Clearance", desc: "Cloudflare Clearance Cookie,用于绕过反爬虫验证。" },
|
||||
"browser": { title: "浏览器指纹", desc: "curl_cffi 浏览器指纹标识(如 chrome136)。" },
|
||||
"user_agent": { title: "User-Agent", desc: "HTTP 请求的 User-Agent 字符串,需与浏览器指纹匹配。" }
|
||||
},
|
||||
"chat": {
|
||||
"label": "对话配置",
|
||||
"temporary": { title: "临时对话", desc: "是否启用临时对话模式。" },
|
||||
"disable_memory": { title: "禁用记忆", desc: "禁用 Grok 记忆功能,以防止响应中出现不相关上下文" },
|
||||
"disable_memory": { title: "禁用记忆", desc: "禁用 Grok 记忆功能,以防止响应中出现不相关上下文。" },
|
||||
"stream": { title: "流式响应", desc: "是否默认启用流式输出。" },
|
||||
"thinking": { title: "思维链", desc: "是否启用模型思维链输出。" },
|
||||
"dynamic_statsig": { title: "动态指纹", desc: "是否启用动态生成 Statsig 值。" },
|
||||
"filter_tags": { title: "过滤标签", desc: "自动过滤 Grok 响应中的特殊标签。" },
|
||||
"timeout": { title: "超时时间", desc: "请求 Grok 服务的超时时间(秒)。" },
|
||||
"base_proxy_url": { title: "基础代理 URL", desc: "代理请求到 Grok 官网的基础服务地址。" },
|
||||
"asset_proxy_url": { title: "资源代理 URL", desc: "代理请求到 Grok 官网的静态资源(图片/视频)地址。" },
|
||||
"image_ws": { title: "Imagine WebSocket 生成", desc: "启用后 /v1/images/generations 走 WebSocket 直连。" },
|
||||
"image_ws_nsfw": { title: "Imagine NSFW", desc: "WS 请求里是否启用 NSFW。" },
|
||||
"image_ws_blocked_seconds": { title: "Imagine WebSocket Blocked 阈值", desc: "收到中等图后超过该秒数仍无最终图则判定 blocked。" },
|
||||
"image_ws_final_min_bytes": { title: "Imagine WebSocket 最终图最小字节", desc: "判定最终图的最小字节数(通常 JPG > 100KB)。" },
|
||||
"max_retry": { title: "最大重试", desc: "请求 Grok 服务失败时的最大重试次数。" },
|
||||
"filter_tags": { title: "过滤标签", desc: "自动过滤 Grok 响应中的特殊标签。" }
|
||||
},
|
||||
"retry": {
|
||||
"label": "重试策略",
|
||||
"max_retry": { title: "最大重试次数", desc: "请求 Grok 服务失败时的最大重试次数。" },
|
||||
"retry_status_codes": { title: "重试状态码", desc: "触发重试的 HTTP 状态码列表。" },
|
||||
"retry_backoff_base": { title: "退避基数", desc: "重试退避的基础延迟(秒)。" },
|
||||
"retry_backoff_factor": { title: "退避倍率", desc: "重试退避的指数放大系数。" },
|
||||
"retry_backoff_max": { title: "退避上限", desc: "单次重试等待的最大延迟(秒)。" },
|
||||
"retry_budget": { title: "退避预算", desc: "单次请求的最大重试总耗时(秒)。" },
|
||||
"retry_budget": { title: "退避预算", desc: "单次请求的最大重试总耗时(秒)。" }
|
||||
},
|
||||
"timeout": {
|
||||
"label": "超时配置",
|
||||
"stream_idle_timeout": { title: "流空闲超时", desc: "流式响应空闲超时(秒),超过将断开。" },
|
||||
"video_idle_timeout": { title: "视频空闲超时", desc: "视频生成空闲超时(秒),超过将断开。" },
|
||||
"cf_clearance": { title: "CF Clearance", desc: "" },
|
||||
"browser": { title: "curl_cffi", desc: "" },
|
||||
"user_agent": { title: "User-Agent", desc: "" }
|
||||
"video_idle_timeout": { title: "视频空闲超时", desc: "视频生成空闲超时(秒),超过将断开。" }
|
||||
},
|
||||
"image": {
|
||||
"label": "图片生成",
|
||||
"image_ws": { title: "WebSocket 生成", desc: "启用后 /v1/images/generations 走 WebSocket 直连。" },
|
||||
"image_ws_nsfw": { title: "NSFW 模式", desc: "WebSocket 请求是否启用 NSFW。" },
|
||||
"image_ws_blocked_seconds": { title: "Blocked 阈值", desc: "收到中等图后超过该秒数仍无最终图则判定 blocked。" },
|
||||
"image_ws_final_min_bytes": { title: "最终图最小字节", desc: "判定最终图的最小字节数(通常 JPG > 100KB)。" },
|
||||
"image_ws_medium_min_bytes": { title: "中等图最小字节", desc: "判定中等质量图的最小字节数。" }
|
||||
},
|
||||
"token": {
|
||||
"label": "Token 池设置",
|
||||
"label": "Token 池管理",
|
||||
"auto_refresh": { title: "自动刷新", desc: "是否开启 Token 自动刷新机制。" },
|
||||
"refresh_interval_hours": { title: "刷新间隔", desc: "Token 刷新的时间间隔(小时)。" },
|
||||
"refresh_interval_hours": { title: "刷新间隔", desc: "普通 Token 刷新的时间间隔(小时)。" },
|
||||
"super_refresh_interval_hours": { title: "Super 刷新间隔", desc: "Super Token 刷新的时间间隔(小时)。" },
|
||||
"fail_threshold": { title: "失败阈值", desc: "单个 Token 连续失败多少次后被标记为不可用。" },
|
||||
"save_delay_ms": { title: "保存延迟", desc: "Token 变更合并写入的延迟(毫秒)。" },
|
||||
"reload_interval_sec": { title: "一致性刷新", desc: "多 worker 场景下 Token 状态刷新间隔(秒)。" }
|
||||
"reload_interval_sec": { title: "同步间隔", desc: "多 worker 场景下 Token 状态刷新间隔(秒)。" }
|
||||
},
|
||||
"cache": {
|
||||
"label": "缓存设置",
|
||||
"label": "缓存管理",
|
||||
"enable_auto_clean": { title: "自动清理", desc: "是否启用缓存自动清理,开启后按上限自动回收。" },
|
||||
"limit_mb": { title: "清理阈值", desc: "缓存大小阈值(MB),超过阈值会触发清理。" }
|
||||
},
|
||||
@@ -97,11 +114,9 @@ const LOCALE_MAP = {
|
||||
}
|
||||
};
|
||||
|
||||
// 反爬虫验证字段(从 grok 中单独分组显示)
|
||||
const ANTIBOT_FIELDS = ['cf_clearance', 'browser', 'user_agent'];
|
||||
const ANTIBOT_GROUP = {
|
||||
label: "反爬虫验证",
|
||||
desc: "以下三个参数用于绕过 Cloudflare 反爬虫验证。配置不正确将导致 403 错误。服务首次请求 Grok 时的 IP 必须与获取 CF Clearance 时的 IP 一致,后续服务器请求 IP 变化不会导致403。"
|
||||
// 配置部分说明(可选)
|
||||
const SECTION_DESCRIPTIONS = {
|
||||
"security": "配置不正确将导致 403 错误。服务首次请求 Grok 时的 IP 必须与获取 CF Clearance 时的 IP 一致,后续服务器请求 IP 变化不会导致 403。"
|
||||
};
|
||||
|
||||
const SECTION_ORDER = new Map(Object.keys(LOCALE_MAP).map((key, index) => [key, index]));
|
||||
@@ -250,24 +265,29 @@ function renderConfig(data) {
|
||||
const localeSection = LOCALE_MAP[section];
|
||||
const keyOrder = localeSection ? new Map(Object.keys(localeSection).map((k, i) => [k, i])) : null;
|
||||
|
||||
// 分离反爬虫字段和普通字段
|
||||
const allKeys = sortByOrder(Object.keys(items), keyOrder);
|
||||
const normalKeys = section === 'grok' ? allKeys.filter(k => !ANTIBOT_FIELDS.includes(k)) : allKeys;
|
||||
const antibotKeys = section === 'grok' ? allKeys.filter(k => ANTIBOT_FIELDS.includes(k)) : [];
|
||||
|
||||
// 渲染普通字段
|
||||
if (normalKeys.length > 0) {
|
||||
if (allKeys.length > 0) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'config-section';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.innerHTML = `<div class="config-section-title">${getSectionLabel(section)}</div>`;
|
||||
|
||||
// 添加部分说明(如果有)
|
||||
if (SECTION_DESCRIPTIONS[section]) {
|
||||
const descP = document.createElement('p');
|
||||
descP.className = 'text-[var(--accents-4)] text-sm mt-1 mb-4';
|
||||
descP.textContent = SECTION_DESCRIPTIONS[section];
|
||||
header.appendChild(descP);
|
||||
}
|
||||
|
||||
card.appendChild(header);
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'config-grid';
|
||||
|
||||
normalKeys.forEach(key => {
|
||||
allKeys.forEach(key => {
|
||||
const fieldCard = buildFieldCard(section, key, items[key]);
|
||||
grid.appendChild(fieldCard);
|
||||
});
|
||||
@@ -277,28 +297,6 @@ function renderConfig(data) {
|
||||
fragment.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染反爬虫验证分组(仅 grok section)
|
||||
if (antibotKeys.length > 0) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'config-section';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.innerHTML = `<div class="config-section-title">${ANTIBOT_GROUP.label}</div>
|
||||
<p class="text-[var(--accents-4)] text-sm mt-1 mb-4">${ANTIBOT_GROUP.desc}</p>`;
|
||||
card.appendChild(header);
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'config-grid';
|
||||
|
||||
antibotKeys.forEach(key => {
|
||||
const fieldCard = buildFieldCard(section, key, items[key]);
|
||||
grid.appendChild(fieldCard);
|
||||
});
|
||||
|
||||
card.appendChild(grid);
|
||||
fragment.appendChild(card);
|
||||
}
|
||||
});
|
||||
|
||||
container.appendChild(fragment);
|
||||
|
||||
+110
-29
@@ -1,58 +1,139 @@
|
||||
[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 = ""
|
||||
# 浏览器指纹配置
|
||||
browser = "chrome136"
|
||||
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
disable_memory = true
|
||||
max_retry = 3
|
||||
retry_status_codes = [401,429,403]
|
||||
image_ws = true
|
||||
image_ws_blocked_seconds = 15
|
||||
image_ws_final_min_bytes = 100000
|
||||
image_ws_nsfw = true
|
||||
retry_backoff_base = 0.5
|
||||
retry_backoff_factor = 2.0
|
||||
retry_backoff_max = 30.0
|
||||
retry_budget = 90.0
|
||||
stream_idle_timeout = 120.0
|
||||
video_idle_timeout = 90.0
|
||||
|
||||
# ==================== 应用设置 ====================
|
||||
[app]
|
||||
# 应用访问地址(用于生成文件链接)
|
||||
app_url = "http://127.0.0.1:8000"
|
||||
# 后台管理密码
|
||||
app_key = "grok2api"
|
||||
# API 调用密钥(可选)
|
||||
api_key = ""
|
||||
# 生成图片的格式(url 或 base64)
|
||||
image_format = "url"
|
||||
# 生成视频的格式(html 或 url)
|
||||
video_format = "html"
|
||||
|
||||
|
||||
# ==================== 网络配置 ====================
|
||||
[network]
|
||||
# 请求超时时间(秒)
|
||||
timeout = 120
|
||||
# 基础代理地址(代理到 Grok 官网)
|
||||
base_proxy_url = ""
|
||||
# 资源代理地址(代理静态资源如图片/视频)
|
||||
asset_proxy_url = ""
|
||||
|
||||
|
||||
# ==================== 反爬虫验证 ====================
|
||||
[security]
|
||||
# Cloudflare Clearance Cookie
|
||||
cf_clearance = ""
|
||||
# curl_cffi 浏览器指纹
|
||||
browser = "chrome136"
|
||||
# User-Agent 字符串
|
||||
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
|
||||
|
||||
# ==================== 对话配置 ====================
|
||||
[chat]
|
||||
# 是否启用临时对话模式
|
||||
temporary = true
|
||||
# 是否禁用 Grok 记忆功能
|
||||
disable_memory = true
|
||||
# 是否默认启用流式响应
|
||||
stream = true
|
||||
# 是否启用思维链输出
|
||||
thinking = true
|
||||
# 是否动态生成 Statsig 指纹
|
||||
dynamic_statsig = true
|
||||
# 过滤的特殊标签列表
|
||||
filter_tags = ["xaiartifact","xai:tool_usage_card","grok:render"]
|
||||
|
||||
|
||||
# ==================== 重试策略 ====================
|
||||
[retry]
|
||||
# 最大重试次数
|
||||
max_retry = 3
|
||||
# 触发重试的 HTTP 状态码
|
||||
retry_status_codes = [401,429,403]
|
||||
# 退避基础延迟(秒)
|
||||
retry_backoff_base = 0.5
|
||||
# 退避倍率
|
||||
retry_backoff_factor = 2.0
|
||||
# 单次重试最大延迟(秒)
|
||||
retry_backoff_max = 30.0
|
||||
# 总重试预算时间(秒)
|
||||
retry_budget = 90.0
|
||||
|
||||
|
||||
# ==================== 超时配置 ====================
|
||||
[timeout]
|
||||
# 流式响应空闲超时(秒)
|
||||
stream_idle_timeout = 120.0
|
||||
# 视频生成空闲超时(秒)
|
||||
video_idle_timeout = 90.0
|
||||
|
||||
|
||||
# ==================== 图片生成 ====================
|
||||
[image]
|
||||
# 是否启用 WebSocket 直连生成图片
|
||||
image_ws = true
|
||||
# WebSocket 生成时是否启用 NSFW
|
||||
image_ws_nsfw = true
|
||||
# 收到中等质量图后等待最终图的超时秒数
|
||||
image_ws_blocked_seconds = 15
|
||||
# 判定为最终图的最小字节数
|
||||
image_ws_final_min_bytes = 100000
|
||||
# 判定为中等质量图的最小字节数
|
||||
image_ws_medium_min_bytes = 30000
|
||||
|
||||
|
||||
# ==================== Token 池管理 ====================
|
||||
[token]
|
||||
# 是否启用 Token 自动刷新
|
||||
auto_refresh = true
|
||||
# 普通 Token 刷新间隔(小时)
|
||||
refresh_interval_hours = 8
|
||||
# Super Token 刷新间隔(小时)
|
||||
super_refresh_interval_hours = 2
|
||||
# Token 连续失败阈值
|
||||
fail_threshold = 5
|
||||
# Token 变更保存延迟(毫秒)
|
||||
save_delay_ms = 500
|
||||
# 多 worker 状态同步间隔(秒)
|
||||
reload_interval_sec = 30
|
||||
|
||||
|
||||
# ==================== 缓存管理 ====================
|
||||
[cache]
|
||||
# 是否启用自动清理
|
||||
enable_auto_clean = true
|
||||
# 缓存大小上限(MB)
|
||||
limit_mb = 1024
|
||||
|
||||
|
||||
# ==================== 并发性能 ====================
|
||||
[performance]
|
||||
# Assets 批量处理并发上限
|
||||
assets_max_concurrent = 25
|
||||
media_max_concurrent = 50
|
||||
usage_max_concurrent = 25
|
||||
# Assets 批量删除大小
|
||||
assets_delete_batch_size = 10
|
||||
# Assets 单批处理数量
|
||||
assets_batch_size = 10
|
||||
# Assets 单次最大处理数量
|
||||
assets_max_tokens = 1000
|
||||
|
||||
# Media 生成并发上限
|
||||
media_max_concurrent = 50
|
||||
|
||||
# Token 用量刷新并发上限
|
||||
usage_max_concurrent = 25
|
||||
# Token 用量刷新批次大小
|
||||
usage_batch_size = 50
|
||||
# Token 用量单次最大数量
|
||||
usage_max_tokens = 1000
|
||||
|
||||
# NSFW 批量开启并发上限
|
||||
nsfw_max_concurrent = 10
|
||||
# NSFW 批量开启批次大小
|
||||
nsfw_batch_size = 50
|
||||
# NSFW 单次最大数量
|
||||
nsfw_max_tokens = 1000
|
||||
+63
-30
@@ -1,53 +1,46 @@
|
||||
[grok]
|
||||
browser = "chrome136"
|
||||
[app]
|
||||
app_url = "http://127.0.0.1:8000"
|
||||
app_key = "grok2api"
|
||||
api_key = ""
|
||||
image_format = "url"
|
||||
video_format = "html"
|
||||
|
||||
[network]
|
||||
timeout = 120
|
||||
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
base_proxy_url = ""
|
||||
asset_proxy_url = ""
|
||||
|
||||
[security]
|
||||
cf_clearance = ""
|
||||
browser = "chrome136"
|
||||
user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
|
||||
|
||||
[chat]
|
||||
temporary = true
|
||||
disable_memory = true
|
||||
stream = true
|
||||
thinking = true
|
||||
dynamic_statsig = true
|
||||
filter_tags = ["xaiartifact","xai:tool_usage_card","grok:render"]
|
||||
|
||||
[retry]
|
||||
max_retry = 3
|
||||
retry_status_codes = [401,429,403]
|
||||
retry_backoff_base = 0.5
|
||||
retry_backoff_factor = 2.0
|
||||
retry_backoff_max = 30.0
|
||||
retry_budget = 90.0
|
||||
|
||||
[timeout]
|
||||
stream_idle_timeout = 120.0
|
||||
video_idle_timeout = 90.0
|
||||
|
||||
[image]
|
||||
image_ws = true
|
||||
image_ws_nsfw = true
|
||||
image_ws_blocked_seconds = 15
|
||||
image_ws_final_min_bytes = 100000
|
||||
image_ws_medium_min_bytes = 30000
|
||||
dynamic_statsig = true
|
||||
image_ws = true
|
||||
image_ws_nsfw = true
|
||||
|
||||
[app]
|
||||
app_url = "http://127.0.0.1:8000"
|
||||
image_format = "url"
|
||||
video_format = "html"
|
||||
app_key = "grok2api"
|
||||
api_key = ""
|
||||
|
||||
[performance]
|
||||
assets_max_concurrent = 25
|
||||
media_max_concurrent = 50
|
||||
usage_max_concurrent = 25
|
||||
assets_delete_batch_size = 10
|
||||
assets_batch_size = 10
|
||||
assets_max_tokens = 1000
|
||||
usage_batch_size = 50
|
||||
usage_max_tokens = 1000
|
||||
nsfw_max_concurrent = 10
|
||||
nsfw_batch_size = 50
|
||||
nsfw_max_tokens = 1000
|
||||
|
||||
[cache]
|
||||
enable_auto_clean = true
|
||||
limit_mb = 1024
|
||||
|
||||
[token]
|
||||
auto_refresh = true
|
||||
@@ -56,3 +49,43 @@ super_refresh_interval_hours = 2
|
||||
fail_threshold = 5
|
||||
save_delay_ms = 200
|
||||
reload_interval_sec = 30
|
||||
|
||||
[cache]
|
||||
enable_auto_clean = true
|
||||
limit_mb = 1024
|
||||
|
||||
[performance]
|
||||
assets_max_concurrent = 25
|
||||
assets_delete_batch_size = 10
|
||||
assets_batch_size = 10
|
||||
assets_max_tokens = 1000
|
||||
media_max_concurrent = 50
|
||||
usage_max_concurrent = 25
|
||||
usage_batch_size = 50
|
||||
usage_max_tokens = 1000
|
||||
nsfw_max_concurrent = 10
|
||||
nsfw_batch_size = 50
|
||||
nsfw_max_tokens = 1000
|
||||
|
||||
[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]
|
||||
image_ws = true
|
||||
image_ws_blocked_seconds = 15
|
||||
image_ws_final_min_bytes = 100000
|
||||
image_ws_nsfw = true
|
||||
retry_backoff_base = 0.5
|
||||
retry_backoff_factor = 2.0
|
||||
retry_backoff_max = 30.0
|
||||
retry_budget = 90.0
|
||||
stream_idle_timeout = 120.0
|
||||
video_idle_timeout = 90.0
|
||||
+31
-23
@@ -1,6 +1,6 @@
|
||||
# Grok2API
|
||||
|
||||
[中文](README.md) | **English**
|
||||
[中文](../README.md) | **English**
|
||||
|
||||
> [!NOTE]
|
||||
> This project is for learning and research only. You must comply with Grok's Terms of Use and applicable laws. Do not use it for illegal purposes.
|
||||
@@ -194,6 +194,10 @@ Config file: `data/config.toml`
|
||||
> In production or behind a reverse proxy, make sure `app.app_url` is set to the public URL.
|
||||
> Otherwise file links may be incorrect or return 403.
|
||||
|
||||
> [!TIP]
|
||||
> **v2.0 Config Upgrade**: Existing users will have their config **auto-migrated** to the new structure upon update.
|
||||
> Custom values from the old `[grok]` section will be automatically mapped to the corresponding new sections.
|
||||
|
||||
| Module | Field | Key | Description | Default |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **app** | `app_url` | App URL | External access URL for Grok2API (used for file links). | `http://127.0.0.1:8000` |
|
||||
@@ -201,46 +205,50 @@ Config file: `data/config.toml`
|
||||
| | `api_key` | API key | Token for calling Grok2API (optional). | `""` |
|
||||
| | `image_format` | Image format | Output image format (`url` or `base64`). | `url` |
|
||||
| | `video_format` | Video format | Output video format (html tag or processed url). | `html` |
|
||||
| **grok** | `temporary` | Temporary chat | Enable temporary conversation mode. | `true` |
|
||||
| **network** | `timeout` | Request timeout | Timeout for Grok requests (seconds). | `120` |
|
||||
| | `base_proxy_url` | Base proxy URL | Base service address proxying Grok official site. | `""` |
|
||||
| | `asset_proxy_url` | Asset proxy URL | Proxy URL for Grok static assets (images/videos). | `""` |
|
||||
| **security** | `cf_clearance` | CF Clearance | Cloudflare clearance cookie for bypassing anti-bot. | `""` |
|
||||
| | `browser` | Browser fingerprint | curl_cffi browser fingerprint (e.g. chrome136). | `chrome136` |
|
||||
| | `user_agent` | User-Agent | HTTP User-Agent string. | `Mozilla/5.0 (Macintosh; ...)` |
|
||||
| **chat** | `temporary` | Temporary chat | Enable temporary conversation mode. | `true` |
|
||||
| | `disable_memory` | Disable memory | Disable Grok memory to prevent irrelevant context. | `true` |
|
||||
| | `stream` | Streaming | Enable streaming by default. | `true` |
|
||||
| | `thinking` | Thinking chain | Enable model thinking output. | `true` |
|
||||
| | `dynamic_statsig` | Dynamic fingerprint | Enable dynamic Statsig value generation. | `true` |
|
||||
| | `filter_tags` | Filter tags | Auto-filter special tags in Grok responses. | `["xaiartifact", "xai:tool_usage_card", "grok:render"]` |
|
||||
| | `timeout` | Timeout | Timeout for Grok requests (seconds). | `120` |
|
||||
| | `base_proxy_url` | Base proxy URL | Base service address proxying Grok official site. | `""` |
|
||||
| | `asset_proxy_url` | Asset proxy URL | Proxy URL for Grok static assets (images/videos). | `""` |
|
||||
| | `cf_clearance` | CF Clearance | Cloudflare clearance cookie for verification. | `""` |
|
||||
| | `max_retry` | Max retries | Max retries on Grok request failure. | `3` |
|
||||
| **retry** | `max_retry` | Max retries | Max retries on Grok request failure. | `3` |
|
||||
| | `retry_status_codes` | Retry status codes | HTTP status codes that trigger retry. | `[401, 429, 403]` |
|
||||
| | `retry_backoff_base` | Backoff base | Base delay for retry backoff (seconds). | `0.5` |
|
||||
| | `retry_backoff_factor` | Backoff factor | Exponential multiplier for retry backoff. | `2.0` |
|
||||
| | `retry_backoff_max` | Backoff max | Max wait per retry (seconds). | `30.0` |
|
||||
| | `retry_budget` | Backoff budget | Max total retry time per request (seconds). | `90.0` |
|
||||
| | `stream_idle_timeout` | Stream idle timeout | Idle timeout for streaming responses (seconds). | `45.0` |
|
||||
| **timeout** | `stream_idle_timeout` | Stream idle timeout | Idle timeout for streaming responses (seconds). | `120.0` |
|
||||
| | `video_idle_timeout` | Video idle timeout | Idle timeout for video generation (seconds). | `90.0` |
|
||||
| | `image_ws` | Image WS generation | When enabled, `/v1/images/generations` uses WebSocket. | `false` |
|
||||
| | `image_ws_blocked_seconds` | WS blocked threshold | Mark blocked if no final image after this many seconds post-medium. | `15` |
|
||||
| | `image_ws_final_min_bytes` | WS final min bytes | Minimum bytes to treat an image as final (JPG usually > 100KB). | `100000` |
|
||||
| | `image_ws_nsfw` | WS NSFW | Enable NSFW in WS requests. | `true` |
|
||||
| **image** | `image_ws` | WebSocket generation | When enabled, `/v1/images/generations` uses WebSocket. | `true` |
|
||||
| | `image_ws_nsfw` | NSFW mode | Enable NSFW in WebSocket requests. | `true` |
|
||||
| | `image_ws_blocked_seconds` | Blocked threshold | Mark blocked if no final image after this many seconds post-medium. | `15` |
|
||||
| | `image_ws_final_min_bytes` | Final min bytes | Minimum bytes to treat an image as final (JPG usually > 100KB). | `100000` |
|
||||
| | `image_ws_medium_min_bytes` | Medium min bytes | Minimum bytes for medium quality image. | `30000` |
|
||||
| **token** | `auto_refresh` | Auto refresh | Enable automatic token refresh. | `true` |
|
||||
| | `refresh_interval_hours` | Refresh interval | Token refresh interval (hours). | `8` |
|
||||
| | `refresh_interval_hours` | Refresh interval | Regular token refresh interval (hours). | `8` |
|
||||
| | `super_refresh_interval_hours` | Super refresh interval | Super token refresh interval (hours). | `2` |
|
||||
| | `fail_threshold` | Failure threshold | Consecutive failures before a token is disabled. | `5` |
|
||||
| | `save_delay_ms` | Save delay | Debounced save delay for token changes (ms). | `500` |
|
||||
| | `reload_interval_sec` | Consistency refresh | Token state refresh interval in multi-worker setups (sec). | `30` |
|
||||
| | `reload_interval_sec` | Sync interval | Token state refresh interval in multi-worker setups (sec). | `30` |
|
||||
| **cache** | `enable_auto_clean` | Auto clean | Enable cache auto clean; cleanup when exceeding limit. | `true` |
|
||||
| | `limit_mb` | Cleanup threshold | Cache size threshold (MB) that triggers cleanup. | `1024` |
|
||||
| **performance** | `nsfw_max_concurrent` | NSFW enable concurrency | Concurrency cap for enabling NSFW in batch. Recommended 10. | `10` |
|
||||
| **performance** | `media_max_concurrent` | Media concurrency | Concurrency cap for video/media generation. Recommended 50. | `50` |
|
||||
| | `assets_max_concurrent` | Assets concurrency | Concurrency cap for batch asset find/delete. Recommended 25. | `25` |
|
||||
| | `assets_batch_size` | Assets batch size | Batch size for asset find/delete. Recommended 10. | `10` |
|
||||
| | `assets_max_tokens` | Assets max tokens | Max tokens per asset find/delete batch. Recommended 1000. | `1000` |
|
||||
| | `assets_delete_batch_size` | Assets delete batch | Batch concurrency for single-account asset deletion. Recommended 10. | `10` |
|
||||
| | `usage_max_concurrent` | Token refresh concurrency | Concurrency cap for batch usage refresh. Recommended 25. | `25` |
|
||||
| | `usage_batch_size` | Token refresh batch size | Batch size for usage refresh. Recommended 50. | `50` |
|
||||
| | `usage_max_tokens` | Token refresh max tokens | Max tokens per usage refresh batch. Recommended 1000. | `1000` |
|
||||
| | `nsfw_max_concurrent` | NSFW enable concurrency | Concurrency cap for enabling NSFW in batch. Recommended 10. | `10` |
|
||||
| | `nsfw_batch_size` | NSFW enable batch size | Batch size for enabling NSFW. Recommended 50. | `50` |
|
||||
| | `nsfw_max_tokens` | NSFW enable max tokens | Max tokens per NSFW batch to avoid mistakes. Recommended 1000. | `1000` |
|
||||
| | `usage_max_concurrent` | Token usage refresh concurrency | Concurrency cap for batch usage refresh. Recommended 25. | `25` |
|
||||
| | `usage_batch_size` | Token usage refresh batch size | Batch size for usage refresh. Recommended 50. | `50` |
|
||||
| | `usage_max_tokens` | Token usage refresh max tokens | Max tokens per usage refresh batch. Recommended 1000. | `1000` |
|
||||
| | `assets_max_concurrent` | Online assets find/delete concurrency | Concurrency cap for online asset find/delete. Recommended 25. | `25` |
|
||||
| | `assets_batch_size` | Online assets find/delete batch size | Batch size for online asset find/delete. Recommended 10. | `10` |
|
||||
| | `assets_max_tokens` | Online assets find/delete max tokens | Max tokens per online asset find/delete batch. Recommended 1000. | `1000` |
|
||||
| | `assets_delete_batch_size` | Online assets delete batch | Batch concurrency for online asset deletion. Recommended 10. | `10` |
|
||||
| | `media_max_concurrent` | Media concurrency | Concurrency cap for video/media generation. Recommended 50. | `50` |
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Grok2API
|
||||
|
||||
**中文** | [English](README.en.md)
|
||||
**中文** | [English](docs/README.en.md)
|
||||
|
||||
> [!NOTE]
|
||||
> 本项目仅供学习与研究,使用者必须在遵循 Grok 的 **使用条款** 以及 **法律法规** 的情况下使用,不得用于非法用途。
|
||||
@@ -223,6 +223,10 @@ curl http://localhost:8000/v1/images/edits \
|
||||
> 生产环境或反向代理部署时,请确保 `app.app_url` 配置为对外可访问的完整 URL,
|
||||
> 否则可能出现文件访问链接不正确或 403 等问题。
|
||||
|
||||
> [!TIP]
|
||||
> **v2.0 配置结构升级**:旧版本用户更新后,配置会**自动迁移**到新结构,无需手动修改。
|
||||
> 旧的 `[grok]` 配置节中的自定义值会自动映射到对应的新配置节。
|
||||
|
||||
| 模块 | 字段 | 配置名 | 说明 | 默认值 |
|
||||
| :-------------------- | :--------------------------- | :----------- | :--------------------------------------------------- | :-------------------------------------------------------- |
|
||||
| **app** | `app_url` | 应用地址 | 当前 Grok2API 服务的外部访问 URL,用于文件链接访问。 | `http://127.0.0.1:8000` |
|
||||
@@ -230,46 +234,50 @@ curl http://localhost:8000/v1/images/edits \
|
||||
| | `api_key` | API 密钥 | 调用 Grok2API 服务的 Token(可选)。 | `""` |
|
||||
| | `image_format` | 图片格式 | 生成的图片格式(url 或 base64)。 | `url` |
|
||||
| | `video_format` | 视频格式 | 生成的视频格式(html 或 url,url 为处理后的链接)。 | `html` |
|
||||
| **grok** | `temporary` | 临时对话 | 是否启用临时对话模式。 | `true` |
|
||||
| **network** | `timeout` | 请求超时 | 请求 Grok 服务的超时时间(秒)。 | `120` |
|
||||
| | `base_proxy_url` | 基础代理 URL | 代理请求到 Grok 官网的基础服务地址。 | `""` |
|
||||
| | `asset_proxy_url` | 资源代理 URL | 代理请求到 Grok 官网的静态资源(图片/视频)地址。 | `""` |
|
||||
| **security** | `cf_clearance` | CF Clearance | Cloudflare 验证 Cookie,用于绕过反爬虫验证。 | `""` |
|
||||
| | `browser` | 浏览器指纹 | curl_cffi 浏览器指纹标识(如 chrome136)。 | `chrome136` |
|
||||
| | `user_agent` | User-Agent | HTTP 请求的 User-Agent 字符串。 | `Mozilla/5.0 (Macintosh; ...)` |
|
||||
| **chat** | `temporary` | 临时对话 | 是否启用临时对话模式。 | `true` |
|
||||
| | `disable_memory` | 禁用记忆 | 禁用 Grok 记忆功能,防止响应中出现不相关上下文。 | `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** | `max_retry` | 最大重试 | 请求 Grok 服务失败时的最大重试次数。 | `3` |
|
||||
| | `retry_status_codes` | 重试状态码 | 触发重试的 HTTP 状态码列表。 | `[401, 429, 403]` |
|
||||
| | `retry_backoff_base` | 退避基数 | 重试退避的基础延迟(秒)。 | `0.5` |
|
||||
| | `retry_backoff_factor` | 退避倍率 | 重试退避的指数放大系数。 | `2.0` |
|
||||
| | `retry_backoff_max` | 退避上限 | 单次重试等待的最大延迟(秒)。 | `30.0` |
|
||||
| | `retry_budget` | 退避预算 | 单次请求的最大重试总耗时(秒)。 | `90.0` |
|
||||
| | `stream_idle_timeout` | 流空闲超时 | 流式响应空闲超时(秒),超过将断开。 | `45.0` |
|
||||
| **timeout** | `stream_idle_timeout` | 流空闲超时 | 流式响应空闲超时(秒),超过将断开。 | `120.0` |
|
||||
| | `video_idle_timeout` | 视频空闲超时 | 视频生成空闲超时(秒),超过将断开。 | `90.0` |
|
||||
| | `image_ws` | 图片 WS 生成 | 启用后 `/v1/images/generations` 走 WebSocket 直连。 | `false` |
|
||||
| | `image_ws_blocked_seconds` | WS Blocked 阈值 | 收到中等图后超过该秒数仍无最终图则判定 blocked。 | `15` |
|
||||
| | `image_ws_final_min_bytes` | WS 最终图最小字节 | 判定最终图的最小字节数(通常 JPG > 100KB)。 | `100000` |
|
||||
| | `image_ws_nsfw` | WS NSFW | WS 请求里是否启用 NSFW。 | `true` |
|
||||
| **image** | `image_ws` | WebSocket 生成 | 启用后 `/v1/images/generations` 走 WebSocket 直连。| `true` |
|
||||
| | `image_ws_nsfw` | NSFW 模式 | WebSocket 请求是否启用 NSFW。 | `true` |
|
||||
| | `image_ws_blocked_seconds` | Blocked 阈值 | 收到中等图后超过该秒数仍无最终图则判定 blocked。 | `15` |
|
||||
| | `image_ws_final_min_bytes` | 最终图最小字节 | 判定最终图的最小字节数(通常 JPG > 100KB)。 | `100000` |
|
||||
| | `image_ws_medium_min_bytes`| 中等图最小字节 | 判定中等质量图的最小字节数。 | `30000` |
|
||||
| **token** | `auto_refresh` | 自动刷新 | 是否开启 Token 自动刷新机制。 | `true` |
|
||||
| | `refresh_interval_hours` | 刷新间隔 | Token 刷新的时间间隔(小时)。 | `8` |
|
||||
| | `super_refresh_interval_hours` | Super 刷新间隔 | Super Token 刷新的时间间隔(小时)。 | `2` |
|
||||
| | `refresh_interval_hours` | 刷新间隔 | 普通 Token 刷新的时间间隔(小时)。 | `8` |
|
||||
| | `super_refresh_interval_hours` | Super 刷新间隔 | Super Token 刷新的时间间隔(小时)。 | `2` |
|
||||
| | `fail_threshold` | 失败阈值 | 单个 Token 连续失败多少次后被标记为不可用。 | `5` |
|
||||
| | `save_delay_ms` | 保存延迟 | Token 变更合并写入的延迟(毫秒)。 | `500` |
|
||||
| | `reload_interval_sec` | 一致性刷新 | 多 worker 场景下 Token 状态刷新间隔(秒)。 | `30` |
|
||||
| | `reload_interval_sec` | 同步间隔 | 多 worker 场景下 Token 状态刷新间隔(秒)。 | `30` |
|
||||
| **cache** | `enable_auto_clean` | 自动清理 | 是否启用缓存自动清理,开启后按上限自动回收。 | `true` |
|
||||
| | `limit_mb` | 清理阈值 | 缓存大小阈值(MB),超过阈值会触发清理。 | `1024` |
|
||||
| **performance** | `nsfw_max_concurrent` | 开启 NSFW 模式并发上限 | 批量开启 NSFW 模式时的并发请求上限。推荐 10。 | `10` |
|
||||
| | `nsfw_batch_size` | 开启 NSFW 模式批次大小 | 批量开启 NSFW 模式的单批处理数量。推荐 50。 | `50` |
|
||||
| | `nsfw_max_tokens` | 开启 NSFW 模式最大数量 | 单次批量开启 NSFW 的 Token 数量上限,防止误操作。推荐 1000。 | `1000` |
|
||||
| | `usage_max_concurrent` | Token 用量刷新并发上限 | 批量刷新 Token 用量时的并发请求上限。推荐 25。 | `25` |
|
||||
| | `usage_batch_size` | Token 用量刷新批次大小 | 批量刷新 Token 用量的单批处理数量。推荐 50。 | `50` |
|
||||
| | `usage_max_tokens` | Token 用量刷新最大数量 | 批量刷新 Token 用量的单次处理数量上限。推荐 1000。 | `1000` |
|
||||
| | `assets_max_concurrent` | 在线资产查找/删除并发上限 | 在线资产查找/删除时的并发请求上限。推荐 25。 | `25` |
|
||||
| | `assets_batch_size` | 在线资产查找/删除批次大小 | 在线资产查找/删除的单批处理数量。推荐 10。 | `10` |
|
||||
| | `assets_max_tokens` | 在线资产查找/删除最大数量 | 在线资产查找/删除的单次处理数量上限。推荐 1000。 | `1000` |
|
||||
| | `assets_delete_batch_size` | 在线资产删除批量 | 在线资产删除单批并发数量。推荐 10。 | `10` |
|
||||
| | `media_max_concurrent` | 媒体并发上限 | 视频/媒体生成请求的并发上限。推荐 50。 | `50` |
|
||||
| **performance** | `media_max_concurrent` | Media 并发上限 | 视频/媒体生成请求的并发上限。推荐 50。 | `50` |
|
||||
| | `assets_max_concurrent` | Assets 并发上限 | 批量查找/删除资产时的并发请求上限。推荐 25。 | `25` |
|
||||
| | `assets_batch_size` | Assets 批次大小 | 批量查找/删除资产时的单批处理数量。推荐 10。 | `10` |
|
||||
| | `assets_max_tokens` | Assets 最大数量 | 单次批量查找/删除资产时的处理数量上限。推荐 1000。 | `1000` |
|
||||
| | `assets_delete_batch_size` | Assets 删除批量 | 单账号批量删除资产时的单批并发数量。推荐 10。 | `10` |
|
||||
| | `usage_max_concurrent` | Token 刷新并发上限 | 批量刷新 Token 用量时的并发请求上限。推荐 25。 | `25` |
|
||||
| | `usage_batch_size` | Token 刷新批次大小 | 批量刷新 Token 用量的单批处理数量。推荐 50。 | `50` |
|
||||
| | `usage_max_tokens` | Token 刷新最大数量 | 单次批量刷新 Token 用量时的处理数量上限。推荐 1000。| `1000` |
|
||||
| | `nsfw_max_concurrent` | NSFW 开启并发上限 | 批量开启 NSFW 模式时的并发请求上限。推荐 10。 | `10` |
|
||||
| | `nsfw_batch_size` | NSFW 开启批次大小 | 批量开启 NSFW 模式的单批处理数量。推荐 50。 | `50` |
|
||||
| | `nsfw_max_tokens` | NSFW 开启最大数量 | 单次批量开启 NSFW 的 Token 数量上限。推荐 1000。 | `1000` |
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
Reference in new issue
Block a user