"""投顾 Agent 的统一超时与降级执行器。""" from __future__ import annotations import asyncio import inspect import logging from dataclasses import dataclass from typing import Awaitable, Callable, TypeVar from utils.exceptions import ApiError T = TypeVar("T") logger = logging.getLogger("advisor_agent.fallback") @dataclass(frozen=True) class FallbackResult: value: object degraded: bool code: int | None = None async def call_with_fallback( primary: Callable[[], Awaitable[T]], secondary: Callable[[], Awaitable[T]] | None, *, timeout: float, degraded_code: int, on_degraded: Callable[[Exception], None] | None = None, ) -> FallbackResult: try: return FallbackResult( value=await asyncio.wait_for(primary(), timeout=timeout), degraded=False, ) except Exception as primary_error: logger.warning("advisor dependency degraded; using fallback", exc_info=primary_error) if on_degraded is not None: try: callback_result = on_degraded(primary_error) if inspect.isawaitable(callback_result): await callback_result except Exception: logger.warning("advisor degradation audit callback failed", exc_info=True) if secondary is None: raise ApiError(degraded_code, "Agent核心服务调用失败") from primary_error try: value = await asyncio.wait_for(secondary(), timeout=timeout) except Exception as secondary_error: raise ApiError(degraded_code, "Agent降级服务调用失败") from secondary_error return FallbackResult(value=value, degraded=True, code=degraded_code)