From 4ca21e5e29cdd59a566812ce85c46d1119d3e51d Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Fri, 3 Apr 2026 15:39:54 -0400 Subject: [PATCH] debug: add exc_info=True + unique logger names to expose real traceback Both 'Inference error' log lines use identical logger strings, making it impossible to tell which except block is catching the crash. Also, broad except blocks were swallowing the full traceback so the real crash site and cause were hidden. Changes: - policy_server.py: logger renamed to 'ml.serving.policy_server'; predict() and predict_batch() error handlers add exc_info=True - routes/policy.py: logger renamed to 'routes.policy.decide'; _infer_from_vector() error handler adds exc_info=True; decide() fallback warning adds exc_info=True The next crash will print the full stack trace, pinpointing the exact line and call chain causing the TypeError. --- ml/serving/policy_server.py | 11 ++++++----- routes/policy.py | 16 ++++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ml/serving/policy_server.py b/ml/serving/policy_server.py index d7e2db7..1676ab6 100644 --- a/ml/serving/policy_server.py +++ b/ml/serving/policy_server.py @@ -52,7 +52,8 @@ from ml.config.scope import ( ) from ml.encoder.state_encoder import CardEmbeddingIndex, StateEncoder -logger = logging.getLogger("ml.serving") +# Unique logger name so log lines are attributable to this module specifically +logger = logging.getLogger("ml.serving.policy_server") def _unwrap_logits(raw): @@ -175,7 +176,7 @@ class PolicyInferenceService: except Exception as e: self._load_error = str(e) - logger.error("Failed to load checkpoint: %s", e) + logger.error("Failed to load checkpoint: %s", e, exc_info=True) return False def reload(self, checkpoint_path: str = None) -> bool: @@ -207,7 +208,7 @@ class PolicyInferenceService: except Exception as e: self._load_error = str(e) - logger.error("Hot-reload failed: %s", e) + logger.error("Hot-reload failed: %s", e, exc_info=True) return False def predict( @@ -286,7 +287,7 @@ class PolicyInferenceService: except Exception as e: elapsed_ms = (time.time() - t_start) * 1000 - logger.error("Inference error: %s", e) + logger.error("Inference error: %s", e, exc_info=True) return { "error": str(e), "inference_ms": round(elapsed_ms, 2), @@ -346,7 +347,7 @@ class PolicyInferenceService: ) except Exception as e: - logger.error("Batch inference error: %s", e) + logger.error("Batch inference error: %s", e, exc_info=True) results = [{"error": str(e)}] * len(snapshots) return results diff --git a/routes/policy.py b/routes/policy.py index 95f1f84..32e2d65 100644 --- a/routes/policy.py +++ b/routes/policy.py @@ -1,5 +1,5 @@ """Commander AI Lab — Policy Decision Routes (Phase 2) -════════════════════════════════════════════════════ +════════════════════════════════════════════════════════ Live Forge ↔ policy server IPC endpoints for online training. These endpoints are purpose-built for the real-time decision loop where Forge's Java side calls the Python policy server at each @@ -24,7 +24,8 @@ import numpy as np from fastapi import APIRouter, HTTPException from pydantic import BaseModel, Field -logger = logging.getLogger("routes.policy") +# Unique logger name so log lines are attributable to this module specifically +logger = logging.getLogger("routes.policy.decide") router = APIRouter(prefix="/api/policy", tags=["policy"]) @@ -310,7 +311,10 @@ async def decide(req: DecideRequest): req.snapshot_schema, req.resolved_turn(), ) except Exception as e: - logger.warning("Precomputed vector encode failed (%s) — falling back", e) + logger.warning( + "Precomputed vector encode failed — falling back to full encoder", + exc_info=True, + ) state_vec = None else: state_vec = None @@ -373,7 +377,7 @@ async def collect_tuples(req: CollectRequest): try: _online_store.add_tuples(req.tuples) except Exception as e: - logger.error("Failed to store tuples: %s", e) + logger.error("Failed to store tuples: %s", e, exc_info=True) raise HTTPException(500, detail=str(e)) return { @@ -402,7 +406,7 @@ async def submit_reward(req: RewardRequest): winner_seat=req.winner_seat, ) except Exception as e: - logger.error("Failed to assign reward: %s", e) + logger.error("Failed to assign reward: %s", e, exc_info=True) raise HTTPException(500, detail=str(e)) return { @@ -563,7 +567,7 @@ def _infer_from_vector(state_vec: np.ndarray, policy_service, temperature: float } except Exception as e: elapsed_ms = (time.time() - t_start) * 1000 - logger.error("Direct inference error: %s", e) + logger.error("Direct inference error in _infer_from_vector: %s", e, exc_info=True) return {"error": str(e), "inference_ms": round(elapsed_ms, 2)}