From f3bd188a8be8eb7638360957d516f97c4455cab0 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Mon, 23 Mar 2026 14:29:22 -0400 Subject: [PATCH] fix(#95): implement retry loop and bump max_tokens/max_retries defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit max_retries was configured but never used — choose_action() made exactly one attempt. Now wraps _call_llm() in a retry loop. Bumped max_retries default to 2 and max_tokens to 4096 (R1 needs room for blocks + JSON). Fixes issue #95 item 3. --- src/commander_ai_lab/sim/deepseek_brain.py | 40 +++++++++++++--------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/commander_ai_lab/sim/deepseek_brain.py b/src/commander_ai_lab/sim/deepseek_brain.py index 743540c..9e1dd4b 100644 --- a/src/commander_ai_lab/sim/deepseek_brain.py +++ b/src/commander_ai_lab/sim/deepseek_brain.py @@ -75,7 +75,7 @@ class DeepSeekConfig: # Generation parameters # GPT-OSS 20B does not emit blocks so 1024 tokens is ample temperature: float = 0.3 - max_tokens: int = 2048 + max_tokens: int = 4096 top_p: float = 0.9 # Timeouts @@ -91,7 +91,7 @@ class DeepSeekConfig: log_dir: str = "" # Retry - max_retries: int = 1 + max_retries: int = 2 # ══════════════════════════════════════════════════════════════ @@ -590,25 +590,31 @@ class DeepSeekBrain: if not self._connected: return self._fallback_action(snapshot, t_start, "not_connected") - try: - result = self._call_llm(snapshot) - result["source"] = "llm" - result["latency_ms"] = round((time.time() - t_start) * 1000, 1) +last_err = None + for attempt in range(1, self.config.max_retries + 1): + try: + result = self._call_llm(snapshot) + result["source"] = "llm" + result["latency_ms"] = round((time.time() - t_start) * 1000, 1) - if self.config.cache_enabled: - self._put_cache(cache_key, result) + if self.config.cache_enabled: + self._put_cache(cache_key, result) - if self.config.log_decisions: - self._log_decision(snapshot, result, player_index) + if self.config.log_decisions: + self._log_decision(snapshot, result, player_index) - self._total_latency_ms += result["latency_ms"] - return result + self._total_latency_ms += result["latency_ms"] + return result - except Exception as e: - logger.warning("LLM call failed (turn %d): %s", turn + 1, e) - if self.config.fallback_on_timeout: - return self._fallback_action(snapshot, t_start, str(e)) - raise + except Exception as e: + last_err = e + logger.warning("LLM call failed (turn %d, attempt %d/%d): %s", turn + 1, attempt, self.config.max_retries, e) + if attempt < self.config.max_retries: + continue + + if self.config.fallback_on_timeout: + return self._fallback_action(snapshot, t_start, str(last_err)) + raise last_err def _call_llm(self, snapshot: dict) -> dict: """Send game state to LLM and parse response."""