2026-02-16 11:00:48 +08:00
|
|
|
|
"""调度/候选子组件的协议接口。
|
|
|
|
|
|
|
|
|
|
|
|
目的:
|
|
|
|
|
|
- 用 `Protocol` 固化 CacheAwareScheduler 的子组件契约
|
|
|
|
|
|
- 便于单测注入 stub/mocks,减少对具体实现类的耦合
|
|
|
|
|
|
|
|
|
|
|
|
说明:这里的协议面向“调度器内部协作”,因此保留了部分 `_` 前缀方法。
|
|
|
|
|
|
后续如果要对外暴露更稳定的 API,可再抽出无下划线的 facade。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from typing import TYPE_CHECKING, Any, Protocol
|
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
|
|
from src.models.database import GlobalModel, Provider, ProviderAPIKey
|
|
|
|
|
|
from src.services.scheduling.affinity_manager import CacheAffinity
|
|
|
|
|
|
from src.services.scheduling.schemas import ConcurrencySnapshot, ProviderCandidate
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CandidateSorterProtocol(Protocol):
|
|
|
|
|
|
def _apply_priority_mode_sort(
|
|
|
|
|
|
self,
|
|
|
|
|
|
candidates: list[ProviderCandidate],
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
affinity_key: str | None = None,
|
|
|
|
|
|
api_format: str | None = None,
|
|
|
|
|
|
) -> list[ProviderCandidate]: ...
|
|
|
|
|
|
|
|
|
|
|
|
def _apply_load_balance(
|
|
|
|
|
|
self, candidates: list[ProviderCandidate], api_format: str | None = None
|
|
|
|
|
|
) -> list[ProviderCandidate]: ...
|
|
|
|
|
|
|
|
|
|
|
|
def shuffle_keys_by_internal_priority(
|
|
|
|
|
|
self,
|
|
|
|
|
|
keys: list[ProviderAPIKey],
|
|
|
|
|
|
affinity_key: str | None = None,
|
|
|
|
|
|
use_random: bool = False,
|
|
|
|
|
|
) -> list[ProviderAPIKey]: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CandidateBuilderProtocol(Protocol):
|
2026-03-09 22:57:23 +08:00
|
|
|
|
def _query_provider_refs(
|
|
|
|
|
|
self,
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
provider_offset: int = 0,
|
|
|
|
|
|
provider_limit: int | None = None,
|
|
|
|
|
|
) -> list[tuple[str, str]]: ...
|
|
|
|
|
|
|
2026-02-16 11:00:48 +08:00
|
|
|
|
def _query_providers(
|
|
|
|
|
|
self,
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
provider_offset: int = 0,
|
|
|
|
|
|
provider_limit: int | None = None,
|
2026-03-09 22:57:23 +08:00
|
|
|
|
allowed_providers: list[str] | None = None,
|
|
|
|
|
|
provider_ids: list[str] | None = None,
|
2026-02-16 11:00:48 +08:00
|
|
|
|
) -> list[Provider]: ...
|
|
|
|
|
|
|
|
|
|
|
|
async def _build_candidates(
|
|
|
|
|
|
self,
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
providers: list[Provider],
|
|
|
|
|
|
client_format: str,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
affinity_key: str | None,
|
|
|
|
|
|
model_mappings: list[str] | None = None,
|
|
|
|
|
|
max_candidates: int | None = None,
|
|
|
|
|
|
is_stream: bool = False,
|
|
|
|
|
|
capability_requirements: dict[str, bool] | None = None,
|
|
|
|
|
|
global_conversion_enabled: bool = True,
|
|
|
|
|
|
) -> list[ProviderCandidate]: ...
|
|
|
|
|
|
|
|
|
|
|
|
async def _check_model_support(
|
|
|
|
|
|
self,
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
provider: Provider,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
api_format: str | None = None,
|
|
|
|
|
|
is_stream: bool = False,
|
|
|
|
|
|
capability_requirements: dict[str, bool] | None = None,
|
|
|
|
|
|
) -> tuple[bool, str | None, list[str] | None, set[str] | None]: ...
|
|
|
|
|
|
|
|
|
|
|
|
async def _check_model_support_for_global_model(
|
|
|
|
|
|
self,
|
|
|
|
|
|
db: Session,
|
|
|
|
|
|
provider: Provider,
|
|
|
|
|
|
global_model: GlobalModel,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
api_format: str | None = None,
|
|
|
|
|
|
is_stream: bool = False,
|
|
|
|
|
|
capability_requirements: dict[str, bool] | None = None,
|
|
|
|
|
|
) -> tuple[bool, str | None, list[str] | None, set[str] | None]: ...
|
|
|
|
|
|
|
|
|
|
|
|
def _check_key_availability(
|
|
|
|
|
|
self,
|
|
|
|
|
|
key: ProviderAPIKey,
|
|
|
|
|
|
api_format: str | None,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
capability_requirements: dict[str, bool] | None = None,
|
|
|
|
|
|
model_mappings: list[str] | None = None,
|
|
|
|
|
|
candidate_models: set[str] | None = None,
|
|
|
|
|
|
*,
|
|
|
|
|
|
provider_type: str | None = None,
|
|
|
|
|
|
) -> tuple[bool, str | None, str | None]: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConcurrencyCheckerProtocol(Protocol):
|
|
|
|
|
|
async def check_available(
|
|
|
|
|
|
self,
|
|
|
|
|
|
key: ProviderAPIKey,
|
|
|
|
|
|
is_cached_user: bool = False,
|
|
|
|
|
|
) -> tuple[bool, ConcurrencySnapshot]: ...
|
|
|
|
|
|
|
|
|
|
|
|
def get_reservation_stats(self) -> dict[str, Any]: ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CacheAffinityManagerProtocol(Protocol):
|
|
|
|
|
|
async def get_affinity(
|
|
|
|
|
|
self, affinity_key: str, api_format: str, model_name: str
|
|
|
|
|
|
) -> CacheAffinity | None: ...
|
|
|
|
|
|
|
|
|
|
|
|
async def set_affinity(
|
|
|
|
|
|
self,
|
|
|
|
|
|
affinity_key: str,
|
|
|
|
|
|
provider_id: str,
|
|
|
|
|
|
endpoint_id: str,
|
|
|
|
|
|
key_id: str,
|
|
|
|
|
|
api_format: str,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
supports_caching: bool = True,
|
|
|
|
|
|
ttl: int | None = None,
|
|
|
|
|
|
) -> None: ...
|
|
|
|
|
|
|
|
|
|
|
|
async def invalidate_affinity(
|
|
|
|
|
|
self,
|
|
|
|
|
|
affinity_key: str,
|
|
|
|
|
|
api_format: str,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
key_id: str | None = None,
|
|
|
|
|
|
provider_id: str | None = None,
|
|
|
|
|
|
endpoint_id: str | None = None,
|
|
|
|
|
|
) -> None: ...
|
|
|
|
|
|
|
|
|
|
|
|
def get_stats(self) -> dict[str, Any]: ...
|