Merge pull request #145 from double2tea/feature/smart-token-routing

feat: smart token routing for video generation + deviceEnvInfo fix
This commit is contained in:
Chenyme authored and GitHub committed 2026-02-08 16:07:29 +08:00
commit 490ca21739
3 files changed
+112 -8

No files matched your search

+23 -8
View File
@@ -158,6 +158,14 @@ class VideoService:
"message": f"{prompt} {mode_flag}",
"toolOverrides": {"videoGen": True},
"enableSideBySide": True,
"deviceEnvInfo": {
"darkModeEnabled": False,
"devicePixelRatio": 2,
"screenWidth": 1920,
"screenHeight": 1080,
"viewportWidth": 1920,
"viewportHeight": 1080,
},
"responseMetadata": {
"experiments": [],
"modelConfigOverride": {
@@ -166,7 +174,7 @@ class VideoService:
"aspectRatio": aspect_ratio,
"parentPostId": post_id,
"resolutionName": resolution_name,
"videoLength": video_length
"videoLength": video_length,
}
}
},
@@ -293,17 +301,19 @@ class VideoService:
preset: str = "normal",
):
"""视频生成入口"""
# 获取 token
# 获取 token(使用智能路由)
token_mgr = await get_token_manager()
await token_mgr.reload_if_stale()
token = None
for pool_name in ModelService.pool_candidates_for_model(model):
token = token_mgr.get_token(pool_name)
if token:
break
# 使用智能路由选择 token(根据视频需求与候选池)
pool_candidates = ModelService.pool_candidates_for_model(model)
token_info = token_mgr.get_token_for_video(
resolution=resolution,
video_length=video_length,
pool_candidates=pool_candidates,
)
if not token:
if not token_info:
raise AppException(
message="No available tokens. Please try again later.",
error_type=ErrorType.RATE_LIMIT.value,
@@ -311,6 +321,11 @@ class VideoService:
status_code=429,
)
# 从 TokenInfo 对象中提取 token 字符串
token = token_info.token
if token.startswith("sso="):
token = token[4:]
think = {"enabled": True, "disabled": False}.get(thinking)
is_stream = stream if stream is not None else get_config("chat.stream")
+88
View File
@@ -210,6 +210,94 @@ class TokenManager:
return token[4:]
return token
def get_token_info(self, pool_name: str = "ssoBasic") -> Optional["TokenInfo"]:
"""
获取可用 Token 的完整信息
Args:
pool_name: Token 池名称
Returns:
TokenInfo 对象或 None
"""
pool = self.pools.get(pool_name)
if not pool:
logger.warning(f"Pool '{pool_name}' not found")
return None
token_info = pool.select()
if not token_info:
logger.warning(f"No available token in pool '{pool_name}'")
return None
return token_info
def get_token_for_video(
self,
resolution: str = "480p",
video_length: int = 6,
pool_candidates: Optional[List[str]] = None,
) -> Optional["TokenInfo"]:
"""
根据视频需求智能选择 Token 池
路由策略:
- 如果 resolution 是 "720p" 或 video_length > 6: 优先使用 "ssoSuper" 池
- 否则优先使用 "ssoBasic" 池
- 当提供 pool_candidates 时,按候选池顺序回退
Args:
resolution: 视频分辨率 ("480p" 或 "720p")
video_length: 视频时长(秒)
pool_candidates: 候选 Token 池(按优先级)
Returns:
TokenInfo 对象或 None(无可用 token)
"""
# 确定首选池
requires_super = resolution == "720p" or video_length > 6
primary_pool = SUPER_POOL_NAME if requires_super else BASIC_POOL_NAME
if pool_candidates:
ordered_pools = list(pool_candidates)
if primary_pool in ordered_pools:
ordered_pools.remove(primary_pool)
ordered_pools.insert(0, primary_pool)
else:
fallback_pool = BASIC_POOL_NAME if requires_super else SUPER_POOL_NAME
ordered_pools = [primary_pool, fallback_pool]
for idx, pool_name in enumerate(ordered_pools):
token_info = self.get_token_info(pool_name)
if token_info:
if idx == 0:
logger.info(
f"Video token routing: resolution={resolution}, length={video_length}s -> "
f"pool={pool_name} (token={token_info.token[:10]}...)"
)
else:
logger.info(
f"Video token routing: fallback from {ordered_pools[0]} -> {pool_name} "
f"(token={token_info.token[:10]}...)"
)
return token_info
if idx == 0 and requires_super and pool_name == primary_pool:
next_pool = ordered_pools[1] if len(ordered_pools) > 1 else None
if next_pool:
logger.warning(
f"Video token routing: {primary_pool} pool has no available token for "
f"resolution={resolution}, length={video_length}s. "
f"Falling back to {next_pool} pool."
)
# 两个池都没有可用 token
logger.warning(
f"Video token routing: no available token in any pool "
f"(resolution={resolution}, length={video_length}s)"
)
return None
async def consume(
self, token_str: str, effort: EffortType = EffortType.LOW
) -> bool:
+1
View File
@@ -137,3 +137,4 @@ nsfw_max_concurrent = 10
nsfw_batch_size = 50
# NSFW 单次最大数量
nsfw_max_tokens = 1000