Files
Aether/_deprecated_py_src/services/scheduling/schemas.py
fawney19 1d9c77522a refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/
- 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层
- 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构
- 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations)
- 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image
- 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
2026-04-03 16:26:16 +08:00

110 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
调度器核心数据类型
从 CacheAwareScheduler 提取的共享数据结构,被 24+ 个模块使用。
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from src.models.database import (
Provider,
ProviderAPIKey,
ProviderEndpoint,
)
if TYPE_CHECKING:
from src.services.provider.pool.config import PoolConfig
@dataclass
class ProviderCandidate:
"""候选 provider 组合及是否命中缓存"""
provider: Provider
endpoint: ProviderEndpoint
key: ProviderAPIKey
is_cached: bool = False
is_skipped: bool = False # 是否被跳过
skip_reason: str | None = None # 跳过原因
mapping_matched_model: str | None = None # 通过映射匹配到的模型名(用于实际请求)
needs_conversion: bool = False # 是否需要格式转换
provider_api_format: str = "" # Provider 端点实际格式(用于健康度/熔断 bucket
output_limit: int | None = None # GlobalModel 配置的模型输出上限
capability_miss_count: int = 0 # COMPATIBLE 能力不匹配数0=完全匹配,用于排序)
def _stable_order_key(self) -> tuple[int, int, str, str, str]:
"""
为排序/优先队列提供稳定的比较键。
说明:
- 运行时偶发会出现对 ProviderCandidate 做 tuple 排序/heap 排序的场景;
当主键相同需要比较候选本身时,若候选不可比较会触发:
TypeError: '<' not supported between instances of 'ProviderCandidate' and 'ProviderCandidate'
- 这里提供一个与调度逻辑无关、但足够稳定且可比的兜底顺序。
"""
provider_priority_raw = getattr(self.provider, "provider_priority", None)
internal_priority_raw = getattr(self.key, "internal_priority", None)
try:
provider_priority = (
int(provider_priority_raw) if provider_priority_raw is not None else 999999
)
except Exception:
provider_priority = 999999
try:
internal_priority = (
int(internal_priority_raw) if internal_priority_raw is not None else 999999
)
except Exception:
internal_priority = 999999
provider_id = str(getattr(self.provider, "id", "") or "")
endpoint_id = str(getattr(self.endpoint, "id", "") or "")
key_id = str(getattr(self.key, "id", "") or "")
return (provider_priority, internal_priority, provider_id, endpoint_id, key_id)
def __lt__(self, other: object) -> bool:
if not isinstance(other, ProviderCandidate):
return NotImplemented
return self._stable_order_key() < other._stable_order_key()
@dataclass
class PoolCandidate(ProviderCandidate):
"""号池候选。
排序阶段作为单个候选参与;执行阶段再在 pool_keys 内部选择/切换 key。
"""
pool_keys: list[ProviderAPIKey] = field(default_factory=list)
pool_config: PoolConfig | None = None
pool_priority: int = 999999
_pool_key_index: int = 0
# 延迟可用性检查参数(号池优化:先排序再分页检查)
_deferred_check_params: dict[str, Any] | None = field(default=None, repr=False)
@dataclass
class ConcurrencySnapshot:
key_current: int
key_limit: int | None
is_cached_user: bool = False
# 动态预留信息
reservation_ratio: float = 0.0
reservation_phase: str = "unknown"
reservation_confidence: float = 0.0
load_factor: float = 0.0
def describe(self) -> str:
key_limit_text = str(self.key_limit) if self.key_limit is not None else "inf"
reservation_text = f"{self.reservation_ratio:.0%}" if self.reservation_ratio > 0 else "N/A"
return (
f"key={self.key_current}/{key_limit_text}, "
f"cached={self.is_cached_user}, "
f"reserve={reservation_text}({self.reservation_phase})"
)