diff --git a/rdagent/oai/backend/base.py b/rdagent/oai/backend/base.py index f7703ae2..20853cad 100644 --- a/rdagent/oai/backend/base.py +++ b/rdagent/oai/backend/base.py @@ -720,7 +720,13 @@ class APIBackend(ABC): if finish_reason is None or finish_reason != "length": break # we get a full response now. - new_messages.append({"role": "assistant", "content": response}) + # Merge into the previous assistant message if there already is one at the end. + # Appending a second consecutive assistant message causes llama-server to return 400 + # ("Cannot have 2 or more assistant messages at the end of the list"). + if new_messages and new_messages[-1]["role"] == "assistant": + new_messages[-1]["content"] += response + else: + new_messages.append({"role": "assistant", "content": response}) else: raise RuntimeError(f"Failed to continue the conversation after {try_n} retries.") diff --git a/rdagent/utils/workflow/loop.py b/rdagent/utils/workflow/loop.py index fe54d986..d4bbc25c 100644 --- a/rdagent/utils/workflow/loop.py +++ b/rdagent/utils/workflow/loop.py @@ -270,6 +270,11 @@ class LoopBase: msg = "We have reset the loop instance, stop all the routines and resume." raise self.LoopResumeError(msg) from e else: + # Do NOT advance step_idx for unhandled exceptions (e.g. LoopResumeError + # propagating from _propose). Keeping step_idx at the current step lets + # kickoff_loop retry step 0 on the next resume instead of permanently + # corrupting the loop with a missing direct_exp_gen result. + step_forward = False raise # re-raise unhandled exceptions finally: # No matter the execution succeed or not, we have to finish the following steps