This is an automated email from the ASF dual-hosted git repository.
shuke987 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 537049c6f45 [fix](ci) Fall back when the review model is unavailable
(#68535)
537049c6f45 is described below
commit 537049c6f454dda1994464c8ff0aa6f13d5f431b
Author: shuke <[email protected]>
AuthorDate: Mon Sep 28 11:13:25 2026 +0800
[fix](ci) Fall back when the review model is unavailable (#68535)
- Start automated reviews with `gpt-6-sol`, the current Sol model for
Codex with ChatGPT sign-in.
- If the initial model request is explicitly rejected before any review
item runs, retry once with `gpt-5.6-sol`. Preserve the existing capacity
recovery session and shared budget, and stop if both models are
unavailable.
---
.github/scripts/run_review_with_resume.py | 59 +++++++++--
.github/scripts/test_run_review_with_resume.py | 135 +++++++++++++++++++++++++
.github/workflows/code-review-runner.yml | 5 +-
3 files changed, 186 insertions(+), 13 deletions(-)
diff --git a/.github/scripts/run_review_with_resume.py
b/.github/scripts/run_review_with_resume.py
index c27a65c9fdf..eb6edb767fa 100755
--- a/.github/scripts/run_review_with_resume.py
+++ b/.github/scripts/run_review_with_resume.py
@@ -34,6 +34,7 @@ from pathlib import Path
RETRY_DELAYS = (30, 60, 120)
CAPACITY_MESSAGE = "Selected model is at capacity. Please try a different
model."
+UNSUPPORTED_CHATGPT_MODEL = "model is not supported when using Codex with a
ChatGPT account."
PROCESS_EXIT_GRACE_SECONDS = 5
@@ -145,6 +146,19 @@ def attempt_result(events, status, stderr_path):
)
+def can_fallback_model(events, message, model):
+ """Only a rejected first model request may start a new review session."""
+ return (
+ message is not None
+ and f"The '{model}' {UNSUPPORTED_CHATGPT_MODEL}" in message
+ and not any(
+ event.get("type") == "turn.completed"
+ or event.get("type", "").startswith("item.")
+ for event in events
+ )
+ )
+
+
def session_id(events):
ids = [
event.get("thread_id")
@@ -339,6 +353,10 @@ def run_review(args, reaper=None):
deadline = time.monotonic() + args.budget_seconds
started_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
thread_id = None
+ model = args.model
+ fallback_model = getattr(args, "fallback_model", None)
+ capacity_retry = 0
+ attempt_number = 0
def remaining():
seconds = deadline - time.monotonic()
@@ -362,10 +380,12 @@ def run_review(args, reaper=None):
return 1
try:
- for attempt in range(len(RETRY_DELAYS) + 1):
- events_path = attempts / f"{attempt + 1}.jsonl"
- stderr_path = attempts / f"{attempt + 1}.stderr.log"
- output_path = attempts / f"{attempt + 1}.final.txt"
+ while capacity_retry <= len(RETRY_DELAYS):
+ attempt_number += 1
+ events_path = attempts / f"{attempt_number}.jsonl"
+ stderr_path = attempts / f"{attempt_number}.stderr.log"
+ output_path = attempts / f"{attempt_number}.final.txt"
+ (context / "codex-review-model.txt").write_text(model + "\n")
final_message.write_text("")
command = [
"codex",
@@ -374,7 +394,7 @@ def run_review(args, reaper=None):
"--cd",
str(args.cwd),
"--model",
- args.model,
+ model,
"--config",
f"model_reasoning_effort={args.effort}",
"--sandbox",
@@ -390,8 +410,8 @@ def run_review(args, reaper=None):
else:
command += [goal_prompt]
print(
- f"Starting Codex review attempt {attempt + 1}/4 "
- f"(session={thread_id or 'new'},
remaining={remaining():.0f}s)",
+ f"Starting Codex review attempt {attempt_number} "
+ f"(model={model}, session={thread_id or 'new'},
remaining={remaining():.0f}s)",
file=sys.stderr,
flush=True,
)
@@ -417,13 +437,28 @@ def run_review(args, reaper=None):
)
terminal, message = attempt_result(events, status, stderr_path)
print(
- f"Finished Codex review attempt {attempt + 1}/4 "
+ f"Finished Codex review attempt {attempt_number} "
f"(exit_status={status}, terminal_event={terminal},
events={len(events)})",
file=sys.stderr,
flush=True,
)
+ if (
+ fallback_model
+ and fallback_model != model
+ and thread_id is None
+ and can_fallback_model(events, message, model)
+ ):
+ check_resume_target(args, started_at, remaining)
+ print(
+ f"Model {model} is unavailable for this ChatGPT account; "
+ f"retrying with {fallback_model} before review work began",
+ file=sys.stderr,
+ flush=True,
+ )
+ model = fallback_model
+ continue
if message is not None and (
- message != CAPACITY_MESSAGE or attempt == len(RETRY_DELAYS)
+ message != CAPACITY_MESSAGE or capacity_retry ==
len(RETRY_DELAYS)
):
return fail(message)
current_id = session_id(events)
@@ -442,7 +477,7 @@ def run_review(args, reaper=None):
capture_output=True,
timeout=min(10, remaining()),
)
- delay = RETRY_DELAYS[attempt]
+ delay = RETRY_DELAYS[capacity_retry]
if remaining() <= delay:
return fail("Insufficient shared review budget for capacity
backoff")
print(
@@ -456,7 +491,7 @@ def run_review(args, reaper=None):
{
"type": "review.capacity_retry",
"thread_id": thread_id,
- "next_attempt": attempt + 2,
+ "next_attempt": attempt_number + 1,
"delay_seconds": delay,
}
)
@@ -464,6 +499,7 @@ def run_review(args, reaper=None):
)
time.sleep(delay)
check_resume_target(args, started_at, remaining)
+ capacity_retry += 1
except KeyboardInterrupt:
fail("Review cancelled; not resuming")
return 130
@@ -484,6 +520,7 @@ def main():
parser.add_argument("--head-sha", required=True)
parser.add_argument("--base-sha", required=True)
parser.add_argument("--model", required=True)
+ parser.add_argument("--fallback-model")
parser.add_argument("--effort", required=True)
# The workflow owns the total timeout and deducts setup/finalization time.
parser.add_argument("--budget-seconds", type=int, required=True)
diff --git a/.github/scripts/test_run_review_with_resume.py
b/.github/scripts/test_run_review_with_resume.py
index 54a69fb7916..01c49eaf4e8 100755
--- a/.github/scripts/test_run_review_with_resume.py
+++ b/.github/scripts/test_run_review_with_resume.py
@@ -71,6 +71,7 @@ class ResumeReviewTest(unittest.TestCase):
head_sha="a" * 40,
base_sha="b" * 40,
model="gpt-5.6-sol",
+ fallback_model=None,
effort="xhigh",
budget_seconds=1000,
)
@@ -154,6 +155,140 @@ class ResumeReviewTest(unittest.TestCase):
self.target_check.assert_not_called()
self.assertEqual([], self.sleeps)
+ def test_unsupported_model_falls_back_before_review_work(self):
+ self.args.model = "gpt-6-sol"
+ self.args.fallback_model = "gpt-5.6-sol"
+ rejection = (
+ "The 'gpt-6-sol' model is not supported when using Codex "
+ "with a ChatGPT account."
+ )
+ server_error = json.dumps(
+ {
+ "type": "error",
+ "status": 400,
+ "error": {"type": "invalid_request_error", "message":
rejection},
+ }
+ )
+ self.assertEqual(
+ 0,
+ self.execute(
+ [
+ {
+ "events": [
+ thread_event(),
+ {"type": "turn.started"},
+ failed(server_error),
+ ]
+ },
+ {"events": [thread_event(OTHER), completed()], "status":
0},
+ ]
+ ),
+ )
+ self.assertEqual(
+ ["gpt-6-sol", "gpt-5.6-sol"],
+ [command[command.index("--model") + 1] for command in
self.commands],
+ )
+ self.assertNotIn("resume", self.commands[1])
+ self.assertEqual(
+ "gpt-5.6-sol\n", (self.context /
"codex-review-model.txt").read_text()
+ )
+ self.assertEqual([], self.sleeps)
+ self.target_check.assert_called_once()
+
+ def test_unsupported_model_does_not_restart_after_item(self):
+ self.args.model = "gpt-6-sol"
+ self.args.fallback_model = "gpt-5.6-sol"
+ rejection = (
+ "The 'gpt-6-sol' model is not supported when using Codex "
+ "with a ChatGPT account."
+ )
+ self.assertEqual(
+ 1,
+ self.execute(
+ [
+ {
+ "events": [
+ thread_event(),
+ {"type": "item.started"},
+ failed(rejection),
+ ]
+ }
+ ]
+ ),
+ )
+ self.assertEqual(1, len(self.commands))
+ self.target_check.assert_not_called()
+
+ def test_both_models_rejected_stops_after_one_fallback(self):
+ self.args.model = "gpt-6-sol"
+ self.args.fallback_model = "gpt-5.6-sol"
+
+ def rejection(model):
+ return (
+ f"The '{model}' model is not supported when using Codex "
+ "with a ChatGPT account."
+ )
+
+ self.assertEqual(
+ 1,
+ self.execute(
+ [
+ {"events": [thread_event(),
failed(rejection("gpt-6-sol"))]},
+ {"events": [thread_event(OTHER),
failed(rejection("gpt-5.6-sol"))]},
+ ]
+ ),
+ )
+ self.assertEqual(2, len(self.commands))
+ self.assertEqual(rejection("gpt-5.6-sol"), self.last_error())
+
+ def test_capacity_resume_uses_the_fallback_model(self):
+ self.args.model = "gpt-6-sol"
+ self.args.fallback_model = "gpt-5.6-sol"
+ self.write_rollout(thread_id=OTHER)
+ rejection = (
+ "The 'gpt-6-sol' model is not supported when using Codex "
+ "with a ChatGPT account."
+ )
+ self.assertEqual(
+ 0,
+ self.execute(
+ [
+ {"events": [thread_event(), failed(rejection)]},
+ {"events": [thread_event(OTHER), failed()]},
+ {"events": [thread_event(OTHER), completed()], "status":
0},
+ ]
+ ),
+ )
+ self.assertEqual([30], self.sleeps)
+ self.assertEqual(
+ ["gpt-6-sol", "gpt-5.6-sol", "gpt-5.6-sol"],
+ [command[command.index("--model") + 1] for command in
self.commands],
+ )
+ self.assertEqual(["resume", OTHER], self.commands[2][-3:-1])
+ self.assertEqual(
+ "gpt-5.6-sol\n", (self.context /
"codex-review-model.txt").read_text()
+ )
+
+ def test_unsupported_model_during_resume_never_starts_another_review(self):
+ self.args.model = "gpt-6-sol"
+ self.args.fallback_model = "gpt-5.6-sol"
+ rejection = (
+ "The 'gpt-6-sol' model is not supported when using Codex "
+ "with a ChatGPT account."
+ )
+ self.assertEqual(
+ 1,
+ self.execute(
+ [
+ {"events": [thread_event(), failed()]},
+ {"events": [thread_event(), failed(rejection)]},
+ ]
+ ),
+ )
+ self.assertEqual(2, len(self.commands))
+ self.assertEqual([30], self.sleeps)
+ self.assertEqual(rejection, self.last_error())
+
def
test_capacity_resumes_exact_session_with_same_settings_and_ledger(self):
before = self.ledger.read_text()
self.assertEqual(
diff --git a/.github/workflows/code-review-runner.yml
b/.github/workflows/code-review-runner.yml
index dcb97cf206d..75461551e89 100644
--- a/.github/workflows/code-review-runner.yml
+++ b/.github/workflows/code-review-runner.yml
@@ -728,7 +728,8 @@ jobs:
--pr-number "$PR_NUMBER" \
--head-sha "$HEAD_SHA" \
--base-sha "$BASE_SHA" \
- --model "gpt-5.6-sol" \
+ --model "gpt-6-sol" \
+ --fallback-model "gpt-5.6-sol" \
--effort xhigh \
--budget-seconds "$budget_seconds"
status=$?
@@ -1010,7 +1011,7 @@ jobs:
--pr-number "$PR_NUMBER" \
--head-sha "$HEAD_SHA" \
--base-sha "$BASE_SHA" \
- --model "gpt-5.6-sol" \
+ --model "$(cat "$REVIEW_CONTEXT_DIR/codex-review-model.txt")" \
--reasoning-effort "xhigh" \
--environment "github-actions" \
--max-payload-bytes 4000000 \
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]