| 285 | |
| 286 | |
| 287 | def select_model( |
| 288 | tier: Tier, |
| 289 | mode: RoutingMode, |
| 290 | confidence: float, |
| 291 | method: str, |
| 292 | reasoning: str, |
| 293 | tier_configs: dict[Tier, TierConfig], |
| 294 | estimated_input_tokens: int, |
| 295 | max_output_tokens: int, |
| 296 | prompt: str = "", |
| 297 | pricing: dict[str, ModelPricing] | None = None, |
| 298 | model_capabilities: dict[str, ModelCapabilities] | None = None, |
| 299 | request_requirements: RequestRequirements | None = None, |
| 300 | constraints: RoutingConstraints | None = None, |
| 301 | workload_hints: WorkloadHints | None = None, |
| 302 | routing_features: RoutingFeatures | None = None, |
| 303 | answer_depth: AnswerDepth = AnswerDepth.STANDARD, |
| 304 | answer_depth_multiplier: float = 1.0, |
| 305 | agentic_score: float = 0.0, |
| 306 | user_keyed_models: set[str] | None = None, |
| 307 | selection_weights: SelectionWeights | None = None, |
| 308 | bandit_config: BanditConfig | None = None, |
| 309 | model_experience: object | None = None, |
| 310 | ) -> RoutingDecision: |
| 311 | pricing = DEFAULT_MODEL_PRICING if pricing is None else pricing |
| 312 | capabilities = {} if model_capabilities is None else model_capabilities |
| 313 | requirements = request_requirements or RequestRequirements() |
| 314 | hard_constraints = constraints or RoutingConstraints() |
| 315 | hints = workload_hints or WorkloadHints() |
| 316 | weights = selection_weights or SelectionWeights() |
| 317 | effective_features = routing_features or RoutingFeatures() |
| 318 | effective_bandit = bandit_config or BanditConfig() |
| 319 | step_stable = _stabilize_agent_step_selection(effective_features) |
| 320 | if step_stable and effective_bandit.enabled: |
| 321 | effective_bandit = replace(effective_bandit, enabled=False) |
| 322 | lane = request_capability_lane(effective_features) |
| 323 | tc = tier_configs.get(tier, TierConfig()) |
| 324 | configured_candidates = [candidate for candidate in [ |
| 325 | tc.primary, *tc.fallback] if candidate] |
| 326 | if not configured_candidates: |
| 327 | configured_candidates = list(pricing.keys()) |
| 328 | if not configured_candidates: |
| 329 | _raise_no_available_models() |
| 330 | |
| 331 | filtered_candidates, excluded = _filter_candidates( |
| 332 | configured_candidates, requirements, capabilities) |
| 333 | if not filtered_candidates: |
| 334 | _raise_capability_infeasible( |
| 335 | available_models=configured_candidates, |
| 336 | candidate_count=len(configured_candidates), |
| 337 | requirements=requirements, |
| 338 | excluded=excluded, |
| 339 | constraints=hard_constraints, |
| 340 | ) |
| 341 | |
| 342 | candidates, failed_constraint, applied_constraints = _apply_constraints( |
| 343 | filtered_candidates, |
| 344 | hard_constraints, |