Preserve diagnostics in live-mode failure payloads

The previous hardening pass still dropped source diagnostics and data-quality context once live-mode serialized a dual-lane failure. Keep those fields when a structured CombinedSignalFailure reaches the websocket layer so consumers can distinguish provider mismatch, stale data, and other degraded cases even when no final signal exists.

Constraint: Follow-on fix after 63858bf should stay minimal and not reopen unrelated executor/calendar work
Rejected: Fold this into a larger amend of the prior commit | history is already shared and the delta is a single behavioral correction
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: When failure exceptions carry structured diagnostics, live serializers must preserve them instead of flattening to a generic message
Tested: python -m pytest web_dashboard/backend/tests/test_executors.py web_dashboard/backend/tests/test_services_migration.py web_dashboard/backend/tests/test_api_smoke.py orchestrator/tests/test_market_calendar.py orchestrator/tests/test_live_mode.py orchestrator/tests/test_application_service.py orchestrator/tests/test_quant_runner.py orchestrator/tests/test_llm_runner.py -q
Tested: python -m compileall orchestrator web_dashboard/backend
Tested: npm run build (web_dashboard/frontend)
Not-tested: real websocket consumers against provider-backed failure paths
This commit is contained in:
陈少杰 2026-04-14 02:10:31 +08:00
parent a4def7aff9
commit eb2ab0afcf
1 changed files with 6 additions and 4 deletions

View File

@ -59,9 +59,11 @@ class LiveMode:
@staticmethod
def _serialize_error(*, ticker: str, date: str, exc: Exception) -> dict:
reason_codes = []
if isinstance(exc, ValueError) and "both quant and llm signals are None" in str(exc):
reason_codes = list(getattr(exc, "reason_codes", ()) or ())
if not reason_codes and isinstance(exc, ValueError) and "both quant and llm signals are None" in str(exc):
reason_codes.append(ReasonCode.BOTH_SIGNALS_UNAVAILABLE.value)
source_diagnostics = dict(getattr(exc, "source_diagnostics", {}) or {})
data_quality = getattr(exc, "data_quality", None)
return {
"contract_version": CONTRACT_VERSION,
"ticker": ticker,
@ -76,9 +78,9 @@ class LiveMode:
"degradation": {
"degraded": bool(reason_codes),
"reason_codes": reason_codes,
"source_diagnostics": {},
"source_diagnostics": source_diagnostics,
},
"data_quality": None,
"data_quality": data_quality,
}
async def run_once(self, tickers: List[str], date: Optional[str] = None) -> List[dict]: