Files
Aether/_deprecated_py_src/services/orchestration/request_dispatcher.py

171 lines
5.7 KiB
Python
Raw Normal View History

2025-12-10 20:52:44 +08:00
"""
请求分发器
负责执行单个候选请求
"""
from collections.abc import Callable
from typing import Any
2025-12-10 20:52:44 +08:00
from sqlalchemy.orm import Session
from src.core.logger import logger
from src.models.database import ApiKey
from src.services.request.candidate import RequestCandidateService
from src.services.request.executor import RequestExecutor
from src.services.scheduling.aware_scheduler import CacheAwareScheduler, ProviderCandidate
2025-12-10 20:52:44 +08:00
class RequestDispatcher:
"""
请求分发器 - 负责执行单个候选请求
职责
1. 执行请求并返回结果
2. 更新候选状态pending -> success/failed
3. 设置缓存亲和性成功时
"""
def __init__(
self,
db: Session,
request_executor: RequestExecutor,
cache_scheduler: CacheAwareScheduler | None = None,
2025-12-10 20:52:44 +08:00
) -> None:
"""
初始化请求分发器
Args:
db: 数据库会话
request_executor: 请求执行器
cache_scheduler: 缓存调度器可选
"""
self.db = db
self.request_executor = request_executor
self.cache_scheduler = cache_scheduler
async def dispatch(
self,
candidate: ProviderCandidate,
candidate_index: int,
retry_index: int,
candidate_record_id: str,
user_api_key: ApiKey | None,
user_id: str | None,
2025-12-10 20:52:44 +08:00
request_func: Callable[..., Any],
request_id: str | None,
api_format: str,
2025-12-10 20:52:44 +08:00
model_name: str,
affinity_key: str,
global_model_id: str,
attempt_counter: int,
max_attempts: int,
is_stream: bool = False,
) -> tuple[Any, str, str, str, str, str, int | None]:
2025-12-10 20:52:44 +08:00
"""
执行请求并返回结果
Args:
candidate: 候选对象
candidate_index: 候选索引
retry_index: 重试索引
candidate_record_id: 候选记录 ID
user_api_key: 用户 API Key
request_func: 请求函数
request_id: 请求 ID
api_format: API 格式
model_name: 模型名称
affinity_key: 亲和性标识符通常为API Key ID
global_model_id: GlobalModel ID规范化的模型标识用于缓存亲和性
attempt_counter: 尝试计数
max_attempts: 最大尝试次数
is_stream: 是否为流式请求
Returns:
(response, provider_name, candidate_record_id, provider_id, endpoint_id, key_id, ttfb_ms)
2025-12-10 20:52:44 +08:00
Raises:
ExecutionError: 执行失败时
"""
provider = candidate.provider
endpoint = candidate.endpoint
key = candidate.key
# 显式转换为 str
provider_id = str(provider.id)
provider_name = str(provider.name)
endpoint_id = str(endpoint.id)
key_id = str(key.id)
cache_ttl_minutes = int(key.cache_ttl_minutes or 0)
provider_supports_caching = cache_ttl_minutes > 0
provider_cache_ttl_seconds: int | None = (
2025-12-10 20:52:44 +08:00
cache_ttl_minutes * 60 if cache_ttl_minutes > 0 else None
)
# 更新状态为 pending
RequestCandidateService.update_candidate_status(
db=self.db, candidate_id=candidate_record_id, status="pending"
)
# 执行请求
execution_result = await self.request_executor.execute(
candidate=candidate,
candidate_id=candidate_record_id,
candidate_index=candidate_index,
user_api_key=user_api_key,
user_id=user_id,
2025-12-10 20:52:44 +08:00
request_func=request_func,
request_id=request_id,
api_format=api_format,
model_name=model_name,
is_stream=is_stream,
)
context = execution_result.context
elapsed_ms = context.elapsed_ms or 0
# 流式请求:标记为 streaming 状态(请求尚未完成)
# 非流式请求:标记为 success 状态
# 注意executor.execute() 内部已经处理了状态标记,这里不再重复
# 流式请求的 success 状态会在流完成后由 _record_stream_stats 方法标记
# 设置缓存亲和性
if provider_supports_caching and self.cache_scheduler is not None:
try:
await self.cache_scheduler.set_cache_affinity(
affinity_key=affinity_key,
provider_id=provider_id,
endpoint_id=endpoint_id,
key_id=key_id,
api_format=api_format,
2025-12-10 20:52:44 +08:00
global_model_id=global_model_id,
ttl=provider_cache_ttl_seconds,
)
except Exception as cache_exc:
logger.warning(f" [{request_id}] 设置缓存亲和性失败: {cache_exc}")
logger.debug(f" [{request_id}] 请求成功: Provider={provider_name}, 耗时={elapsed_ms}ms")
# Non-stream requests don't have first-byte telemetry in this path.
# Use elapsed latency as a conservative fallback for pool latency sampling.
ttfb_ms: int | None = None
if not is_stream:
raw_ttfb = getattr(execution_result.response, "first_byte_time_ms", None)
try:
if raw_ttfb is not None:
ttfb_ms = max(int(raw_ttfb), 0)
except (TypeError, ValueError):
ttfb_ms = None
if ttfb_ms is None and elapsed_ms >= 0:
ttfb_ms = int(elapsed_ms)
2025-12-10 20:52:44 +08:00
return (
execution_result.response,
provider_name,
candidate_record_id,
provider_id,
endpoint_id,
key_id,
ttfb_ms,
2025-12-10 20:52:44 +08:00
)