给出更明确的“无可用账号”原因,优先用于分辨率/tier 档位提示。
(
self,
*,
for_image_generation: bool = False,
for_video_generation: bool = False,
model: Optional[str] = None,
)
| 315 | ready_candidates = [ |
| 316 | item for item in available_tokens if not item["needs_refresh"] |
| 317 | ] |
| 318 | refresh_candidates = [ |
| 319 | item for item in available_tokens if item["needs_refresh"] |
| 320 | ] |
| 321 | if ready_candidates and refresh_candidates: |
| 322 | available_tokens = ready_candidates + refresh_candidates |
| 323 | |
| 324 | debug_logger.log_info("[LOAD_BALANCER] 候选Token负载:") |
| 325 | for item in available_tokens: |
| 326 | token = item["token"] |
| 327 | remaining = "unlimited" if item["remaining"] is None else item["remaining"] |
| 328 | debug_logger.log_info( |
| 329 | f"[LOAD_BALANCER] - Token {token.id} ({token.email}) " |
| 330 | f"inflight={item['inflight']}, remaining={remaining}, " |
| 331 | f"needs_refresh={item['needs_refresh']}, credits={token.credits}" |
| 332 | ) |
| 333 | |
| 334 | # 只为候选列表中真正尝试到的 token 做 AT 校验,避免每次请求把所有 token 全扫一遍 |
| 335 | for item in available_tokens: |
| 336 | token = item["token"] |
| 337 | token_id = token.id |
| 338 | |
| 339 | token = await self.token_manager.ensure_valid_token(token) |
| 340 | if not token: |
| 341 | debug_logger.log_info( |
| 342 | f"[LOAD_BALANCER] 跳过 Token {token_id}: AT无效或已过期" |
| 343 | ) |
| 344 | continue |
| 345 | |
| 346 | if reserve and not await self._reserve_slot( |
| 347 | token.id, for_image_generation, for_video_generation |
| 348 | ): |
| 349 | debug_logger.log_info( |
| 350 | f"[LOAD_BALANCER] 跳过 Token {token.id}: 预占槽位失败" |
| 351 | ) |
| 352 | continue |
| 353 | |
| 354 | if track_pending: |
| 355 | await self._add_pending( |
| 356 | token.id, for_image_generation, for_video_generation |
| 357 | ) |
| 358 |
no test coverage detected