Select a token using load-aware balancing Args: for_image_generation: If True, only select tokens with image_enabled=True for_video_generation: If True, only select tokens with video_enabled=True model: Model name (used to filter tokens for speci
(
self,
for_image_generation: bool = False,
for_video_generation: bool = False,
model: Optional[str] = None,
reserve: bool = False,
enforce_concurrency_filter: bool = True,
track_pending: bool = False,
)
| 138 | return False, f"扩展路由检查失败: {exc}" |
| 139 | |
| 140 | async def select_token( |
| 141 | self, |
| 142 | for_image_generation: bool = False, |
| 143 | for_video_generation: bool = False, |
| 144 | model: Optional[str] = None, |
| 145 | reserve: bool = False, |
| 146 | enforce_concurrency_filter: bool = True, |
| 147 | track_pending: bool = False, |
| 148 | ) -> Optional[Token]: |
| 149 | """ |
| 150 | Select a token using load-aware balancing |
| 151 | |
| 152 | Args: |
| 153 | for_image_generation: If True, only select tokens with image_enabled=True |
| 154 | for_video_generation: If True, only select tokens with video_enabled=True |
| 155 | model: Model name (used to filter tokens for specific models) |
| 156 | reserve: Whether to atomically reserve one concurrency slot for the selected token |
| 157 | enforce_concurrency_filter: |
| 158 | Whether to pre-filter tokens by current inflight/remaining capacity. |
| 159 | For reserve=False generation paths, this should usually be False so |
| 160 | requests can enter the downstream wait queue instead of failing fast. |
| 161 | track_pending: |
| 162 | Whether to count the selected token as a queued request immediately. |
| 163 | This smooths burst distribution before the hard concurrency slot is acquired. |
| 164 | |
| 165 | Returns: |
| 166 | Selected token or None if no available tokens |
| 167 | """ |
| 168 | debug_logger.log_info( |
| 169 | f"[LOAD_BALANCER] 开始选择Token (图片生成={for_image_generation}, " |
| 170 | f"视频生成={for_video_generation}, 模型={model}, 预占槽位={reserve})" |
| 171 | ) |
| 172 | |
| 173 | active_tokens = await self.token_manager.get_active_tokens() |
| 174 | debug_logger.log_info(f"[LOAD_BALANCER] 获取到 {len(active_tokens)} 个活跃Token") |
| 175 | |
| 176 | if not active_tokens: |
| 177 | debug_logger.log_info(f"[LOAD_BALANCER] ❌ 没有活跃的Token") |
| 178 | return None |
| 179 | |
| 180 | available_tokens = [] |
| 181 | filtered_reasons = {} |
| 182 | required_tier = get_required_paygate_tier_for_model(model) |
| 183 | |
| 184 | for token in active_tokens: |
| 185 | normalized_tier = normalize_user_paygate_tier(token.user_paygate_tier) |
| 186 | if model and not supports_model_for_tier(model, normalized_tier): |
| 187 | filtered_reasons[token.id] = '账号等级不足,需要 ' + get_paygate_tier_label(required_tier) |
| 188 | continue |
| 189 | if for_image_generation: |
| 190 | if not token.image_enabled: |
| 191 | filtered_reasons[token.id] = "图片生成已禁用" |
| 192 | continue |
| 193 | |
| 194 | route_ok, route_reason = await self._check_extension_route(token) |
| 195 | if not route_ok: |
| 196 | filtered_reasons[token.id] = route_reason |
| 197 | continue |
no test coverage detected