Fallback: extract HTTP code from failed-turn JSON; Docker codex_home points at real relay home
1. Worker _fallback_token correction: real host.py never returns status='404'; a failed turn is status='failed' with the real error (e.g. '404 model not found') in text/error. The old code returned host_status verbatim, so 'failed' blocked every real fallback and the old event-only branch did too. Now: if host_status itself is a fallbackable code it is used; otherwise the code is extracted from turn JSON text/error, stdout, stderr or runner error, and 'failed' alone never counts as a signal. Tests: failed-turn JSON with 404 in text falls back and succeeds; with 401 it never falls back. 2. Docker config codex_home: /var/lib/codex-app-server/codex-home, mounted at the same path into the container, is the only home that holds the real relay config (xcmapi provider, base_url, env_key). Pointing codex_home at a fresh empty dir made codex POST to api.openai.com with no auth header and loop on connect timeouts until host.py's 600s turn timeout. The /app/work codex-home from PR #17 was necessary to get past initialize but insufficient for a real turn; this replaces it.
This commit is contained in:
1 parent
edc187bb70
commit
5827716a3a
3 files changed
+66
-18
No files matched your search
@@ -346,6 +346,44 @@ class FallbackRetryTestCase(unittest.TestCase):
|
||||
self.assertIn("缺少 --repo", row[0])
|
||||
self.assertEqual(result["events"], [])
|
||||
|
||||
def test_failed_turn_json_with_404_in_text_fallback_succeeds(self):
|
||||
# 真实回合失败形态:host.py JSON 的 status 只是 "failed",
|
||||
# 真正的 404 藏在 text 里;必须从 text 里提取出来 fallback。
|
||||
def fake(cmd):
|
||||
if _model_of(cmd) == "gpt-6-sol":
|
||||
return _host_json("A", "completed", "done by fallback")
|
||||
return _host_json(
|
||||
"A", "failed",
|
||||
"turn failed: 404 model not found: gpt-9.9-nonexistent")
|
||||
|
||||
_insert_task(self.db_path, "A")
|
||||
worker = self._worker(
|
||||
runner=self._recording(fake),
|
||||
model="gpt-6.1-sol",
|
||||
fallback_models=["gpt-6-sol"],
|
||||
max_retries=0)
|
||||
result = worker.run_one()
|
||||
self.assertEqual(result["status"], "completed")
|
||||
self.assertEqual(result["actual_model"], "gpt-6-sol")
|
||||
self.assertEqual(len(self.calls), 2)
|
||||
self.assertEqual(result["events"][0]["reason"], "404")
|
||||
|
||||
def test_failed_turn_json_with_401_in_text_never_fallbacks(self):
|
||||
# 回合 JSON 里是 401 时绝不 fallback,只调一次。
|
||||
def fake(cmd):
|
||||
return _host_json("A", "failed", "turn failed: 401 unauthorized")
|
||||
|
||||
_insert_task(self.db_path, "A")
|
||||
worker = self._worker(
|
||||
runner=self._recording(fake),
|
||||
model="gpt-6.1-sol",
|
||||
fallback_models=["gpt-6-sol"],
|
||||
max_retries=0)
|
||||
result = worker.run_one()
|
||||
self.assertEqual(result["status"], "failed")
|
||||
self.assertEqual(len(self.calls), 1)
|
||||
self.assertEqual(result["events"], [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user