55
66import importlib
77import inspect
8+ import logging
89import math
910import os
1011import subprocess
1112import sys
1213from dataclasses import asdict , is_dataclass
1314from pathlib import Path
14- from typing import Any , Dict , List
15+ from typing import Any , Dict , List , Optional , Tuple
1516
1617from fastapi import APIRouter , Depends , HTTPException , Request
1718from pydantic import BaseModel , Field
2122from src .auth import COOKIE_NAME , is_auth_enabled , verify_session
2223
2324router = APIRouter ()
25+ logger = logging .getLogger (__name__ )
2426
2527ALPHASIFT_DSA_ADAPTER_MODULE = "alphasift.dsa_adapter"
28+ ALPHASIFT_EXPECTED_MISSING_MODULES = frozenset ({"alphasift" , ALPHASIFT_DSA_ADAPTER_MODULE })
2629ALLOWED_ALPHASIFT_INSTALL_SPECS = frozenset ({DEFAULT_ALPHASIFT_INSTALL_SPEC })
2730
2831
@@ -46,23 +49,18 @@ class AlphaSiftStrategyResponse(BaseModel):
4649
4750@router .get ("/status" )
4851def alphasift_status (config : Config = Depends (get_config_dep )) -> Dict [str , Any ]:
49- adapter_status : Dict [str , Any ] = {}
50- available = _is_alphasift_available ()
51- if available :
52- try :
53- adapter_status = _call_alphasift_status ()
54- available = bool (adapter_status .get ("available" , True ))
55- except Exception :
56- available = False
57-
58- return {
52+ adapter_status , available , diagnostics = _get_alphasift_status_snapshot ()
53+ payload = {
5954 "enabled" : bool (config .alphasift_enabled ),
6055 "available" : available ,
6156 "install_spec_is_default" : _is_default_alphasift_install_spec (config .alphasift_install_spec ),
6257 "contract_version" : adapter_status .get ("contract_version" ),
6358 "version" : adapter_status .get ("version" ),
6459 "strategy_count" : adapter_status .get ("strategy_count" ),
6560 }
61+ if diagnostics :
62+ payload ["diagnostics" ] = diagnostics
63+ return payload
6664
6765
6866@router .get ("/strategies" )
@@ -251,10 +249,20 @@ def _ensure_alphasift_enabled(config: Config) -> None:
251249
252250
253251def _is_alphasift_available () -> bool :
252+ _ , available , _ = _get_alphasift_status_snapshot ()
253+ return available
254+
255+
256+ def _get_alphasift_status_snapshot () -> Tuple [Dict [str , Any ], bool , Optional [Dict [str , str ]]]:
254257 try :
255- return _is_adapter_available (_call_alphasift_status ())
256- except Exception :
257- return False
258+ adapter_status = _call_alphasift_status ()
259+ except HTTPException as exc :
260+ return {}, False , _extract_alphasift_diagnostics (exc )
261+ except Exception as exc :
262+ diagnostics = _log_unexpected_alphasift_exception ("status_probe" , exc )
263+ return {}, False , diagnostics
264+
265+ return adapter_status , _is_adapter_available (adapter_status ), None
258266
259267
260268def _is_adapter_available (adapter_status : Any ) -> bool :
@@ -264,16 +272,24 @@ def _is_adapter_available(adapter_status: Any) -> bool:
264272
265273
266274def _import_alphasift () -> Any :
267- _prepare_alphasift_runtime_env ()
268275 try :
276+ _prepare_alphasift_runtime_env ()
269277 return importlib .import_module (ALPHASIFT_DSA_ADAPTER_MODULE )
278+ except ModuleNotFoundError as exc :
279+ if _is_expected_alphasift_missing (exc ):
280+ raise _alphasift_unavailable_exception (
281+ f"AlphaSift 未安装或未挂载到当前 Python 环境,无法导入 { ALPHASIFT_DSA_ADAPTER_MODULE } :{ exc } "
282+ ) from exc
283+ diagnostics = _log_unexpected_alphasift_exception ("import_adapter" , exc )
284+ raise _alphasift_unavailable_exception (
285+ f"AlphaSift 适配层导入失败,请检查依赖完整性和当前 Python 环境:{ exc } " ,
286+ diagnostics = diagnostics ,
287+ ) from exc
270288 except Exception as exc :
271- raise HTTPException (
272- status_code = 424 ,
273- detail = {
274- "error" : "alphasift_unavailable" ,
275- "message" : f"AlphaSift 未安装或未挂载到当前 Python 环境,无法导入 { ALPHASIFT_DSA_ADAPTER_MODULE } :{ exc } " ,
276- },
289+ diagnostics = _log_unexpected_alphasift_exception ("import_adapter" , exc )
290+ raise _alphasift_unavailable_exception (
291+ f"AlphaSift 适配层导入失败,请检查依赖完整性和当前 Python 环境:{ exc } " ,
292+ diagnostics = diagnostics ,
277293 ) from exc
278294
279295
@@ -309,22 +325,64 @@ def _get_adapter_callable(adapter: Any, name: str, missing_error: str) -> Any:
309325
310326def _call_alphasift_status () -> Dict [str , Any ]:
311327 adapter = _import_alphasift ()
312- get_status = _get_adapter_callable (adapter , "get_status" , "get_status() 不可调用。" )
328+ try :
329+ get_status = _get_adapter_callable (adapter , "get_status" , "get_status() 不可调用。" )
330+ except HTTPException as exc :
331+ diagnostics = _log_unexpected_alphasift_exception ("get_status_callable" , exc )
332+ raise _alphasift_unavailable_exception (
333+ "AlphaSift 适配层 get_status 不可调用,请检查适配层版本。" ,
334+ diagnostics = diagnostics ,
335+ ) from exc
313336 try :
314337 result = _to_plain (get_status ())
315338 except Exception as exc :
316- raise HTTPException (
317- status_code = 424 ,
318- detail = {
319- "error" : "alphasift_unavailable" ,
320- "message" : f"AlphaSift 适配层 get_status 调用失败:{ exc } " ,
321- },
339+ diagnostics = _log_unexpected_alphasift_exception ("get_status" , exc )
340+ raise _alphasift_unavailable_exception (
341+ f"AlphaSift 适配层 get_status 调用失败:{ exc } " ,
342+ diagnostics = diagnostics ,
322343 ) from exc
323344 if not isinstance (result , dict ):
324- return {}
345+ exc = TypeError (f"get_status returned { type (result ).__name__ } , expected dict" )
346+ diagnostics = _log_unexpected_alphasift_exception ("get_status_result" , exc )
347+ raise _alphasift_unavailable_exception (
348+ "AlphaSift 适配层 get_status 返回结构非法,请检查适配层版本。" ,
349+ diagnostics = diagnostics ,
350+ ) from exc
325351 return result
326352
327353
354+ def _is_expected_alphasift_missing (exc : ModuleNotFoundError ) -> bool :
355+ return getattr (exc , "name" , None ) in ALPHASIFT_EXPECTED_MISSING_MODULES
356+
357+
358+ def _alphasift_unavailable_exception (
359+ message : str ,
360+ * ,
361+ diagnostics : Optional [Dict [str , str ]] = None ,
362+ ) -> HTTPException :
363+ detail : Dict [str , Any ] = {"error" : "alphasift_unavailable" , "message" : message }
364+ if diagnostics :
365+ detail ["diagnostics" ] = diagnostics
366+ return HTTPException (status_code = 424 , detail = detail )
367+
368+
369+ def _log_unexpected_alphasift_exception (stage : str , exc : BaseException ) -> Dict [str , str ]:
370+ logger .warning ("Unexpected AlphaSift %s failure: %s" , stage , exc , exc_info = exc .__traceback__ is not None )
371+ return {
372+ "reason" : "unexpected_exception" ,
373+ "stage" : stage ,
374+ "error_type" : exc .__class__ .__name__ ,
375+ }
376+
377+
378+ def _extract_alphasift_diagnostics (exc : HTTPException ) -> Optional [Dict [str , str ]]:
379+ detail = exc .detail if isinstance (exc .detail , dict ) else {}
380+ diagnostics = detail .get ("diagnostics" )
381+ if not isinstance (diagnostics , dict ):
382+ return None
383+ return {str (key ): str (value ) for key , value in diagnostics .items ()}
384+
385+
328386def _list_strategies () -> List [Dict [str , Any ]]:
329387 adapter = _get_dsa_adapter ()
330388 list_strategies = _get_adapter_callable (adapter , "list_strategies" , "list_strategies() 不可调用。" )
0 commit comments