Merge pull request #145 from double2tea/feature/smart-token-routing
feat: smart token routing for video generation + deviceEnvInfo fix
This commit is contained in:
3 files changed
+112
-8
No files matched your search
@@ -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")
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -137,3 +137,4 @@ nsfw_max_concurrent = 10
|
||||
nsfw_batch_size = 50
|
||||
# NSFW 单次最大数量
|
||||
nsfw_max_tokens = 1000
|
||||
|
||||
Reference in new issue
Block a user