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:
host committed 2026-10-26 06:07:07 +08:00
1 parent edc187bb70
commit 5827716a3a
3 files changed
+66 -18

No files matched your search

+38
View File
@@ -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()