From 2740b2be3ad213ce3ea9156228626d8a18d11fe9 Mon Sep 17 00:00:00 2001 From: Zamil Majdy Date: Thu, 16 Apr 2026 01:22:20 +0700 Subject: [PATCH 1/2] fix(backend/copilot): disable fallback model to fix prod CLI rejection (#12802) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Why / What / How **Why:** `fffbe0aad8` changed both `ChatConfig.model` and `ChatConfig.claude_agent_fallback_model` to `claude-sonnet-4-6`. The Claude Code CLI rejects this with `Error: Fallback model cannot be the same as the main model`, causing every standard-mode copilot turn to fail with exit code 1 — the session "completes" in ~30s but produces no response and drops the transcript. **What:** Set `claude_agent_fallback_model` default to `""`. `_resolve_fallback_model()` already returns `None` on empty string, which means the `--fallback-model` flag is simply not passed to the CLI. On 529 overload errors the turn will surface normally instead of silently retrying with a fallback. **How:** One-line config change + test update. ### Changes 🏗️ - `ChatConfig.claude_agent_fallback_model` default: `"claude-sonnet-4-6"` → `""` - Update `test_fallback_model_default` to assert the empty default ### Checklist 📋 #### For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] `poetry run pytest backend/copilot/sdk/p0_guardrails_test.py` #### For configuration changes: - [x] `.env.default` is updated or already compatible with my changes - [x] `docker-compose.yml` is updated or already compatible with my changes --- autogpt_platform/backend/backend/copilot/config.py | 5 +++-- .../backend/backend/copilot/sdk/p0_guardrails_test.py | 8 +++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/autogpt_platform/backend/backend/copilot/config.py b/autogpt_platform/backend/backend/copilot/config.py index 8792717cad..36644de680 100644 --- a/autogpt_platform/backend/backend/copilot/config.py +++ b/autogpt_platform/backend/backend/copilot/config.py @@ -156,9 +156,10 @@ class ChatConfig(BaseSettings): "history compression. Falls back to compression when unavailable.", ) claude_agent_fallback_model: str = Field( - default="claude-sonnet-4-6", + default="", description="Fallback model when the primary model is unavailable (e.g. 529 " - "overloaded). The SDK automatically retries with this cheaper model.", + "overloaded). The SDK automatically retries with this cheaper model. " + "Empty string disables the fallback (no --fallback-model flag passed to CLI).", ) claude_agent_max_turns: int = Field( default=50, diff --git a/autogpt_platform/backend/backend/copilot/sdk/p0_guardrails_test.py b/autogpt_platform/backend/backend/copilot/sdk/p0_guardrails_test.py index 9305320fea..17b54797b8 100644 --- a/autogpt_platform/backend/backend/copilot/sdk/p0_guardrails_test.py +++ b/autogpt_platform/backend/backend/copilot/sdk/p0_guardrails_test.py @@ -86,15 +86,14 @@ class TestResolveFallbackModel: assert result == "claude-sonnet-4.5-20250514" def test_default_value(self): - """Default fallback model resolves to a valid string.""" + """Default fallback model resolves to None (disabled by default).""" cfg = _make_config() with patch(f"{_SVC}.config", cfg): from backend.copilot.sdk.service import _resolve_fallback_model result = _resolve_fallback_model() - assert result is not None - assert "sonnet" in result.lower() or "claude" in result.lower() + assert result is None # --------------------------------------------------------------------------- @@ -198,8 +197,7 @@ class TestConfigDefaults: def test_fallback_model_default(self): cfg = _make_config() - assert cfg.claude_agent_fallback_model - assert "sonnet" in cfg.claude_agent_fallback_model.lower() + assert cfg.claude_agent_fallback_model == "" def test_max_turns_default(self): cfg = _make_config() From 559438fd649d761d462ef5557aee29adec8a7108 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:16:28 +0000 Subject: [PATCH 2/2] fix(frontend): run pnpm format on DecomposeGoal test files Agent-Logs-Url: https://github.com/Significant-Gravitas/AutoGPT/sessions/40326921-8824-4f62-bb6b-9f7dc5f89657 Co-authored-by: ntindle <8845353+ntindle@users.noreply.github.com> --- .../__tests__/helpers.test.ts | 12 +++------ .../__tests__/DecomposeGoal.test.tsx | 27 +++++++++++-------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/helpers.test.ts b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/helpers.test.ts index ebae176525..77b2529f4d 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/helpers.test.ts +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/helpers.test.ts @@ -181,9 +181,7 @@ describe("buildRenderSegments", () => { }); it("does not collapse a single generic tool part", () => { - const parts = [ - toolPart("generic_a", "output-available"), - ]; + const parts = [toolPart("generic_a", "output-available")]; const segments = buildRenderSegments(parts); expect(segments).toHaveLength(1); expect(segments[0].kind).toBe("part"); @@ -233,11 +231,9 @@ describe("splitReasoningAndResponse", () => { }); it("pins interactive tool parts to response section", () => { - const interactiveTool = toolPart( - "decompose_goal", - "output-available", - { type: "task_decomposition" }, - ); + const interactiveTool = toolPart("decompose_goal", "output-available", { + type: "task_decomposition", + }); const parts = [ textPart("Thinking..."), interactiveTool, diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/DecomposeGoal/__tests__/DecomposeGoal.test.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/DecomposeGoal/__tests__/DecomposeGoal.test.tsx index 2661df66d7..e0fbd852d0 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/tools/DecomposeGoal/__tests__/DecomposeGoal.test.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/tools/DecomposeGoal/__tests__/DecomposeGoal.test.tsx @@ -60,7 +60,14 @@ const DECOMPOSITION: TaskDecompositionOutput = { function makePart( state: string, output?: unknown, -): { type: string; toolCallId: string; toolName: string; state: string; input?: unknown; output?: unknown } { +): { + type: string; + toolCallId: string; + toolName: string; + state: string; + input?: unknown; + output?: unknown; +} { return { type: "tool-decompose_goal", toolCallId: "call_1", @@ -93,9 +100,7 @@ describe("DecomposeGoalTool", () => { isLastMessage />, ); - expect( - screen.getByText(/Failed to analyze the goal/i), - ).toBeDefined(); + expect(screen.getByText(/Failed to analyze the goal/i)).toBeDefined(); expect(screen.getByText("Try again")).toBeDefined(); }); @@ -124,9 +129,7 @@ describe("DecomposeGoalTool", () => { isLastMessage />, ); - expect( - screen.getByText("Please provide at least one step."), - ).toBeDefined(); + expect(screen.getByText("Please provide at least one step.")).toBeDefined(); }); it("renders the build plan accordion with steps", () => { @@ -173,9 +176,7 @@ describe("DecomposeGoalTool", () => { />, ); expect(screen.queryByText("Modify")).toBeNull(); - expect( - screen.getByText(/Review the plan above and approve/), - ).toBeDefined(); + expect(screen.getByText(/Review the plan above and approve/)).toBeDefined(); }); it("hides action buttons when requires_approval is false", () => { @@ -294,7 +295,11 @@ describe("DecomposeGoalTool", () => { }); expect( - (screen.getAllByPlaceholderText("Step description")[0] as HTMLTextAreaElement).value, + ( + screen.getAllByPlaceholderText( + "Step description", + )[0] as HTMLTextAreaElement + ).value, ).toBe("Fetch RSS feed"); });