mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
- 删除全部 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)
110 lines
3.9 KiB
Python
110 lines
3.9 KiB
Python
"""
|
||
调度器核心数据类型
|
||
|
||
从 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})"
|
||
)
|