Skip to content

fix(backend/copilot): normalize model name based on actual transport, not config shape - #12932

Merged
majdyz merged 4 commits into
devfrom
fix/copilot-sdk-transport-aware-model-normalize
Apr 28, 2026
Merged

fix(backend/copilot): normalize model name based on actual transport, not config shape#12932
majdyz merged 4 commits into
devfrom
fix/copilot-sdk-transport-aware-model-normalize

Conversation

@majdyz

@majdyz majdyz commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Summary

When CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true is paired with a populated CHAT_BASE_URL=https://openrouter.ai/api/v1 (e.g. left over from an earlier OpenRouter setup), the SDK was passing the OpenRouter slug anthropic/claude-opus-4.7 straight through to the Claude Code CLI subprocess. The CLI uses OAuth and ignores CHAT_BASE_URL/CHAT_API_KEY, so it rejects the slug:

There's an issue with the selected model (anthropic/claude-opus-4.7). It may not exist or you may not have access to it.

The bug was in _normalize_model_name, which gated on config.openrouter_active (config-shape check) instead of the transport the CLI actually uses for the turn.

Changes

  • Add ChatConfig.effective_transport property returning subscription | openrouter | direct_anthropic, detected in that priority order. Subscription wins over OpenRouter config because the CLI subprocess uses OAuth and ignores the OpenRouter env vars (see build_sdk_env mode 1).
  • Switch _normalize_model_name to gate on effective_transport. Subscription and direct-Anthropic transports both produce the CLI-friendly hyphenated form (claude-opus-4-7) and reject non-Anthropic vendors loudly.
  • _resolve_sdk_model_for_request already routes any LD-served override through _normalize_model_name, so a per-user advanced-tier override under subscription now correctly becomes claude-opus-4-7 instead of the OpenRouter slug. The standard-tier "no LD override → return None" behaviour is preserved.
  • Update two existing service tests to assert the corrected behaviour (Kimi LD override under subscription falls back to tier default normalised for the CLI; Opus advanced override returns hyphenated form).

Test plan

  • poetry run pytest backend/copilot/sdk/service_helpers_test.py backend/copilot/sdk/service_test.py backend/copilot/config_test.py -v — 165 passed.
  • poetry run pytest backend/copilot/sdk/env_test.py backend/copilot/sdk/p0_guardrails_test.py — 136 passed (other call sites of openrouter_active unchanged).
  • poetry run ruff format + ruff check clean on touched files.

New tests added (service_helpers_test.py)

  • Subscription transport with OpenRouter base URL set + advanced-tier LD override → returns claude-opus-4-7 (not the OpenRouter slug, not None).
  • Subscription transport with OpenRouter base URL set + standard-tier no override → returns None (existing behaviour preserved).
  • Subscription transport rejects non-Anthropic vendor (moonshotai/...) → ValueError.
  • effective_transport returns subscription when subscription is on regardless of OpenRouter config; returns openrouter when subscription is off and OpenRouter is fully configured; returns direct_anthropic otherwise.

… not config shape

When CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true is paired with a populated
CHAT_BASE_URL=https://openrouter.ai/api/v1 (e.g. left over from an earlier
OpenRouter setup), `_normalize_model_name` used to gate on
`config.openrouter_active` and pass the OpenRouter slug
`anthropic/claude-opus-4.7` straight through to the Claude Code CLI
subprocess. The CLI uses OAuth and ignores CHAT_BASE_URL/CHAT_API_KEY,
so it rejected the slug with:

  There's an issue with the selected model (anthropic/claude-opus-4.7).
  It may not exist or you may not have access to it.

Add `ChatConfig.effective_transport` returning the transport the CLI
actually uses (`subscription` | `openrouter` | `direct_anthropic`),
detected in that order. Subscription wins over OpenRouter config.

Switch `_normalize_model_name` to gate on `effective_transport` so the
subscription and direct-Anthropic transports both produce the
CLI-friendly hyphenated form (`claude-opus-4-7`) and reject non-Anthropic
vendors loudly.

`_resolve_sdk_model_for_request` already routes any LD-served override
through `_normalize_model_name`, so a per-user advanced-tier override
under subscription now correctly becomes `claude-opus-4-7` instead of
the OpenRouter slug. The standard-tier "no override → return None"
behaviour is preserved.
@majdyz
majdyz requested a review from a team as a code owner April 28, 2026 02:34
@majdyz
majdyz requested review from Pwuts and Swiftyos and removed request for a team April 28, 2026 02:35
@github-actions github-actions Bot added the platform/backend AutoGPT Platform - Back end label Apr 28, 2026
@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 30978166-3436-41d7-9125-ea0c859de9ad

📥 Commits

Reviewing files that changed from the base of the PR and between a99c142 and 12d974e.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: check API types
  • GitHub Check: Seer Code Review
  • GitHub Check: end-to-end tests
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (python)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🧠 Learnings (36)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:28.595Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:41.614Z
Learning: In Significant-Gravitas/AutoGPT, the Anthropic API model ID for Claude Opus 4.6 is `claude-opus-4-6` (hyphen-separated), matching `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`. The OpenRouter routing variant uses a dot separator: `anthropic/claude-opus-4.6`. Do not flag `claude-opus-4-6` as an incorrect model ID when used in Anthropic API contexts; only the OpenRouter path uses the dot form.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `anthropic/claude-sonnet-4-6` (hyphen-separated) is the project-wide convention for the Claude Sonnet 4.6 model ID, used consistently in llm.py, blocks/test, reasoning.py, _is_anthropic_model tests, and config defaults. OpenRouter accepts both `anthropic/claude-sonnet-4-6` (hyphen) and `anthropic/claude-sonnet-4.6` (dot). Do NOT flag the hyphen form as incorrect — it has been in production since the Sonnet 4.6 upgrade and is intentional.
📚 Learning: 2026-04-08T17:27:26.657Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: classic/original_autogpt/CLAUDE.md:0-0
Timestamp: 2026-04-08T17:27:26.657Z
Learning: Applies to classic/original_autogpt/**/tests/**/*.py : In tests, use fixtures from `tests/conftest.py` (app_data_dir, config, storage, llm_provider, agent) for initializing test components. Run tests from the `classic/` directory using pytest commands like `poetry run pytest original_autogpt/tests/`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-08T17:26:41.549Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: classic/CLAUDE.md:0-0
Timestamp: 2026-04-08T17:26:41.549Z
Learning: Applies to classic/**/tests/**/conftest.py : Define test fixtures in conftest.py files providing: tmp_project_root, storage, config, llm_provider, agent

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
📚 Learning: 2026-04-25T02:53:53.938Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/conftest.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T12:26:42.571Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-13T14:19:19.341Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T05:58:28.595Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:28.595Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T23:58:18.476Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T04:36:19.755Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit_subscription_test.py:1119-1122
Timestamp: 2026-04-21T04:36:19.755Z
Learning: In `autogpt_platform/backend/backend/data/credit_subscription_test.py` (and related subscription test files), test mocks for the user object returned by `get_user_by_id` should use snake_case `subscription_tier` (not camelCase `subscriptionTier`). This is because `get_user_by_id` (defined in `backend/data/user.py`) returns `backend.data.model.User` — a Pydantic application model with `subscription_tier: SubscriptionTier` — not a raw Prisma model. Production code in `backend/data/credit.py` reads `user.subscription_tier` from that Pydantic model. Do NOT flag `mock_user.subscription_tier = ...` as incorrect in these tests.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T13:44:34.273Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12797
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1991-2021
Timestamp: 2026-04-15T13:44:34.273Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (`_run_stream_attempt`), the pre-create block (PR `#12797`) intentionally does NOT call `state.transcript_builder.append_assistant(...)` when inserting the empty assistant placeholder into `ctx.session.messages`. The transcript is left ending at the `tool_result` entry (N entries) while `message_count` metadata is N+1. This mismatch is benign and deliberate: on the next `--resume`, the SDK sees the transcript ending at `tool_result` and correctly regenerates the assistant response. Pre-appending the assistant turn to the transcript would suppress regeneration while leaving `session.messages[-1].content = ""` permanently (worse outcome). On the gap-fallback path, `transcript_msg_count (N+1) >= msg_count-1 (N)` means no gap is injected for the empty placeholder, which is correct because injecting an empty assistant message as context would mislead the SDK. Do NOT flag this transcript/message_count discrepancy as a bug.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-16T12:33:44.990Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12796
File: autogpt_platform/backend/backend/api/features/chat/routes.py:504-527
Timestamp: 2026-04-16T12:33:44.990Z
Learning: In `autogpt_platform/backend/backend/api/features/chat/routes.py`, `get_session` (PR `#12796`, commit 3771bfad9c1) closes the TOCTOU race between the initial `stream_registry.get_active_session()` pre-check and `get_chat_messages_paginated()` with a post-check re-verification: after the DB fetch, if `is_initial_load and active_session is not None`, it calls `get_active_session` a second time; if `post_active is None` (stream completed during the window), it resets `from_start=True`, `forward_paginated=True`, and re-fetches messages from sequence 0. Do NOT flag the double `get_active_session` call pattern as redundant — it is the intentional TOCTOU mitigation for pagination direction selection.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-14T14:36:25.545Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-18T14:03:32.534Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12473
File: autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py:86-195
Timestamp: 2026-03-18T14:03:32.534Z
Learning: In Significant-Gravitas/AutoGPT, the integration tests in `autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py` intentionally use real external URLs (example.com, httpbin.org). They are gated with `pytest.mark.skipif(shutil.which("agent-browser") is None, ...)`, so they are automatically skipped in standard CI where agent-browser is not installed. They are designed to be run explicitly inside the Docker environment to verify that system Chromium (AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium) actually launches and can fetch pages end-to-end. Do not flag the use of real network calls in these tests as a flakiness concern.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T17:31:23.683Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12873
File: autogpt_platform/backend/backend/copilot/baseline/reasoning.py:0-0
Timestamp: 2026-04-21T17:31:23.683Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/reasoning.py` (`BaselineReasoningEmitter`), when `render_in_ui=False`, BOTH the `StreamReasoning*` wire events AND the `ChatMessage(role="reasoning")` persistence append must be suppressed together. `convertChatSessionToUiMessages.ts` unconditionally re-renders all persisted `role="reasoning"` rows as `{type:"reasoning"}` UI parts on reload, so persisting rows while silencing live wire events would resurrect the reasoning collapse on page refresh. The audit trail is preserved through the provider transcript and `_format_sdk_content_blocks` (SDK path) instead. The baseline and SDK paths mirror each other: flag off → no live wire event, no persisted row, no hydrated collapse. This was established in PR `#12873`, commit 7ef10b26c.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T01:26:38.257Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T05:57:34.861Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/backend/backend/copilot/baseline/service.py:0-0
Timestamp: 2026-04-22T05:57:34.861Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/service.py`, the approved pattern for `_run_task_subagent` (PR `#12879`, commit 187f0a5) uses a nested `try/except Exception` inside an outer `try/finally`. The outer `finally` block resets `_TASK_DEPTH_VAR` (via `_TASK_DEPTH_VAR.reset(token)`) AND calls `_absorb_inner_usage(parent_state, inner_state)` unconditionally, so both the depth ContextVar and usage roll-up are guaranteed on all exit paths including `CancelledError`/`KeyboardInterrupt`/`SystemExit`. The inner `except Exception` catches and converts failures into a `TaskResponse` error payload that is returned as `StreamToolOutputAvailable`. Do NOT flag missing ContextVar reset or usage roll-up on BaseException paths in this function.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-15T16:52:15.463Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12426
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-03-15T16:52:15.463Z
Learning: In Significant-Gravitas/AutoGPT (copilot backend), GitHub tokens (GH_TOKEN / GITHUB_TOKEN) for the `gh` CLI are injected lazily per-command in `autogpt_platform/backend/backend/copilot/tools/bash_exec._execute_on_e2b()` by calling `integration_creds.get_integration_env_vars(user_id)`, not on the global SDK subprocess environment in `sdk/service.py`. This scopes credentials to individual E2B sandbox command invocations and prevents token leakage into tool output streams or uploaded transcripts.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-17T10:57:12.953Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T10:17:09.341Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-15T10:17:09.341Z
Learning: In `Significant-Gravitas/AutoGPT` (`autogpt_platform/frontend`), `vitest.config.mts` does NOT set `globals: true`, and `src/tests/integrations/vitest.setup.tsx` does NOT register the `testing-library/react` auto-cleanup hook. Therefore, integration test files (e.g., under `__tests__/`) MUST include an explicit `afterEach(cleanup)` call from `testing-library/react` to reset the DOM between tests — without it, tests fail with "multiple elements found" errors from the prior test's DOM. Do NOT flag `afterEach(cleanup)` as redundant in this codebase.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-10T08:39:22.025Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-07T18:08:03.548Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12701
File: .claude/skills/orchestrate/scripts/verify-complete.sh:120-121
Timestamp: 2026-04-07T18:08:03.548Z
Learning: In Significant-Gravitas/AutoGPT, verify-complete.sh (`.claude/skills/orchestrate/scripts/verify-complete.sh`) uses `commits[-1].committedDate` (not `updatedAt`) to identify stale CHANGES_REQUESTED reviews. This is intentional: `updatedAt` changes on any PR activity (bot comments, label changes, description edits), which would falsely classify a reviewer's CHANGES_REQUESTED as stale — a silent false negative. The `committedDate` edge case (commit created locally before a review but pushed after) only causes a false positive (unnecessary re-brief), which is the safer failure mode. Do not suggest switching to `updatedAt` for this comparison.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T14:54:01.937Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12636
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-01T14:54:01.937Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `claude_agent_max_transient_retries` (default=3) in `ChatConfig` counts **total attempts including the initial one**, not the number of extra retries. With the pre-incremented `transient_retries >= max_transient` guard in `service.py`, a value of 3 yields 3 total stream attempts (initial + 2 retries with exponential backoff: 1s, 2s). Do NOT flag this as an off-by-one — the `>=` check is intentional.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-27T08:39:45.696Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12592
File: autogpt_platform/frontend/AGENTS.md:1-3
Timestamp: 2026-03-27T08:39:45.696Z
Learning: In Significant-Gravitas/AutoGPT, Claude is the primary coding agent. AGENTS.md files intentionally retain Claude-specific wording (e.g., "CLAUDE.md - Frontend", "This file provides guidance to Claude Code") even though AGENTS.md is the canonical cross-agent instruction source. Do not flag Claude-specific titles or phrasing in AGENTS.md files as issues.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-18T09:59:25.999Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12466
File: autogpt_platform/backend/Dockerfile:124-133
Timestamp: 2026-03-18T09:59:25.999Z
Learning: In Significant-Gravitas/AutoGPT, `AGENT_BROWSER_EXECUTABLE_PATH` is an environment variable consumed internally by the `agent-browser` npm package's daemon startup code (`daemon.js`) via `process.env.AGENT_BROWSER_EXECUTABLE_PATH`. The Python integration in `backend/copilot/tools/agent_browser.py` invokes the `agent-browser` CLI as a subprocess, which inherits the container environment, so the daemon automatically picks up the env var. No Python-level code is needed to read or forward `AGENT_BROWSER_EXECUTABLE_PATH`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-27T09:36:59.358Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12591
File: .claude/skills/setup-repo/SKILL.md:32-33
Timestamp: 2026-03-27T09:36:59.358Z
Learning: In the Significant-Gravitas/AutoGPT repository, bash code blocks inside `.claude/skills/*/SKILL.md` files are illustrative guidance patterns for AI agents to adapt, not directly executable scripts. Code correctness standards (e.g., regex safety, error handling) for these snippets should be evaluated against their role as intent-communicating documentation rather than production code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T13:53:40.315Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12894
File: autogpt_platform/backend/backend/data/block_cost_config.py:271-277
Timestamp: 2026-04-23T13:53:40.315Z
Learning: In `autogpt_platform/backend/backend/data/block_cost_config.py`, `compute_token_credits()` intentionally returns `MODEL_COST[model]` (the flat tier) on pre-flight (when `stats is None`) for the `TOKENS` billing path. Returning 0 pre-flight would allow a zero-balance user to bypass the credit gate and trigger an LLM call, with the insufficient-balance error only surfacing post-flight (a billing leak). The overcharge concern (actual token cost < MODEL_COST estimate) is handled by `_charge_reconciled_usage_sync` in `autogpt_platform/backend/backend/executor/billing.py`, which issues a negative-delta refund via `spend_credits(cost=negative)` when real usage falls below the pre-flight estimate. Do NOT flag the MODEL_COST pre-flight floor in this function as an overcharge bug; the refund path covers it.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🔇 Additional comments (4)
autogpt_platform/backend/backend/copilot/sdk/conftest.py (1)

14-40: Env cleanup fixture looks solid and deterministic.

Good centralization of env-backed ChatConfig inputs into _CONFIG_ENV_VARS plus _clean_config_env; this should keep transport/model-normalization tests stable across CI and developer shells.

autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py (3)

380-410: Great regression coverage for env-isolated transport normalization.

Wiring _clean_config_env into these fixtures and asserting subscription behavior even with OpenRouter fields present is exactly the right guard against the original config-shape regression.

Also applies to: 452-489


497-547: effective_transport precedence tests are clear and on-point.

These cases lock in the intended priority (subscription > openrouter > direct_anthropic) and prevent future drift back to openrouter_active shape checks.


554-617: Nice coverage of transport-aware LD override resolution.

The advanced override normalization assertion plus standard-tier None behavior under subscription directly validates the intended resolver behavior for CLI-compatible model selection.


Walkthrough

Adds ChatConfig.effective_transport and shifts model-normalization and runtime vendor checks to consult this computed precedence (subscription > openrouter > direct_anthropic); updates docs and validation behavior and extends tests to cover subscription precedence, env cleanup, and LD-driven normalization outcomes.

Changes

Cohort / File(s) Summary
Config Transport Abstraction
autogpt_platform/backend/backend/copilot/config.py
Adds ChatConfig.effective_transport: Literal["subscription","openrouter","direct_anthropic"]. Re-documents openrouter_active as a config-shape validator and updates _validate_sdk_model_vendor_compatibility docs to reflect subscription-mode resolution and runtime fallback behavior.
Service Model Normalization
autogpt_platform/backend/backend/copilot/sdk/service.py
_normalize_model_name now branches on config.effective_transport (subscription > openrouter > direct). Error messages and docstrings updated to reference effective transport and subscription vs OpenRouter normalization rules and misconfiguration scenarios.
Test Fixtures & Coverage
autogpt_platform/backend/backend/copilot/sdk/conftest.py, autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py, autogpt_platform/backend/backend/copilot/sdk/service_test.py
Adds centralized _clean_config_env fixture to clear CHAT_/OpenRouter env vars; expands tests for effective_transport precedence, subscription-mode hyphenation, vendor rejection, and LD-driven model-resolution behaviors (including cases that return None for tier defaults).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ChatConfig
    participant Normalizer as _normalize_model_name
    participant Resolver as _resolve_sdk_model_for_request
    participant CLI

    Client->>ChatConfig: construct / request transport intent
    ChatConfig->>ChatConfig: compute effective_transport
    ChatConfig->>Normalizer: provide effective_transport + model slug
    Normalizer->>Resolver: return normalized CLI model or raise ValueError
    Resolver->>CLI: pass normalized model (or None) for per-turn routing
    CLI->>Client: execute request via subscription / openrouter / direct_anthropic
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Swiftyos
  • Pwuts
  • kcze
  • ntindle

Poem

🐰 I hop through configs, sniff each flag and line,
Subscription first — then routers fall in line,
Hyphens I tuck where slashes used to dwell,
Tests clap their paws; the CLI knows well,
A cheerful rabbit — routing all is fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing model name normalization to use actual transport instead of config shape, which directly addresses the bug described in the PR.
Description check ✅ Passed The description is detailed and relevant, clearly explaining the bug scenario, the root cause, the fixes implemented, and the test coverage—all directly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/copilot-sdk-transport-aware-model-normalize

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

  • fix(copilot): prevent 524 timeout on chat deletion by deferring cleanup #12668 (Otto-AGPT · updated 11d ago)
    • autogpt_platform/backend/backend/api/features/library/db.py (5 conflicts, ~67 lines)
    • autogpt_platform/backend/backend/api/features/library/model.py (1 conflict, ~4 lines)
    • autogpt_platform/backend/backend/api/features/subscription_routes_test.py (22 conflicts, ~1047 lines)
    • autogpt_platform/backend/backend/api/features/v1.py (10 conflicts, ~233 lines)
    • autogpt_platform/backend/backend/copilot/baseline/service.py (2 conflicts, ~15 lines)
    • autogpt_platform/backend/backend/copilot/model_test.py (1 conflict, ~5 lines)
    • autogpt_platform/backend/backend/copilot/prompting.py (1 conflict, ~5 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service.py (3 conflicts, ~53 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py (2 conflicts, ~133 lines)
    • autogpt_platform/backend/backend/copilot/transcript.py (1 conflict, ~11 lines)
    • autogpt_platform/backend/backend/data/credit.py (12 conflicts, ~816 lines)
    • autogpt_platform/backend/backend/data/credit_subscription_test.py (26 conflicts, ~1894 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/PulseChips/usePulseChips.ts (1 conflict, ~13 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.ts (1 conflict, ~9 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (9 conflicts, ~147 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/StatsGrid.tsx (2 conflicts, ~9 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/ContextualActionButton/ContextualActionButton.tsx (2 conflicts, ~12 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/SitrepItem/SitrepItem.tsx (2 conflicts, ~15 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/SitrepItem/useSitrepItems.ts (4 conflicts, ~97 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/hooks/useAgentStatus.ts (2 conflicts, ~10 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/hooks/useLibraryFleetSummary.ts (7 conflicts, ~57 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/types.ts (1 conflict, ~4 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/SubscriptionTierSection.tsx (11 conflicts, ~185 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/__tests__/SubscriptionTierSection.test.tsx (21 conflicts, ~486 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/useSubscriptionTierSection.ts (4 conflicts, ~60 lines)
    • autogpt_platform/frontend/src/app/api/openapi.json (2 conflicts, ~40 lines)
    • docs/integrations/block-integrations/llm.md (7 conflicts, ~35 lines)
    • docs/integrations/block-integrations/misc.md (1 conflict, ~5 lines)

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 1 conflict(s), 0 medium risk, 6 low risk (out of 7 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py (1)

459-467: Hoist the new imports to module scope.

These added inner imports are ordinary repo-local imports, so they should live at the top of the file rather than inside fixtures/tests.

As per coding guidelines "Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl".

Also applies to: 501-548, 573-605

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py` around
lines 459 - 467, The test currently does local imports inside fixtures/tests
(e.g., "from backend.copilot import config as cfg_mod" and creation of cfg with
cfg_mod.ChatConfig plus
monkeypatch.setattr("backend.copilot.sdk.service.config", cfg)); move these
import statements to the module top-level (module scope) and keep only
test-specific setup in the tests/fixtures, ensuring the same names (cfg_mod,
ChatConfig) are available to the test functions; also apply the same change for
the other occurrences where local imports are used in the file (the blocks that
create ChatConfig and monkeypatch.setattr around service.config).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/config.py`:
- Around line 404-407: Update the docstring for openrouter_active to remove the
contradiction by making it clear that openrouter_active reflects only the config
shape while effective_transport determines actual SDK routing; specifically,
edit the line that currently calls openrouter_active "the single source of truth
for actual SDK routing" so it instead indicates it is a config/shaping flag
(e.g., "indicates whether OpenRouter settings are present in config") and add a
clarifying sentence that effective_transport (and subscription mode) govern
runtime routing behavior; reference the openrouter_active and
effective_transport names so the change is applied to the correct docstring.

In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py`:
- Around line 448-467: The fixture _subscription_with_openrouter_config (and the
other affected fixtures at the indicated ranges) must clear any
environment-backed CHAT_* settings before constructing
backend.copilot.config.ChatConfig so CI/local env doesn't change
effective_transport: use monkeypatch.delenv for relevant vars (e.g.
CHAT_BASE_URL, CHAT_API_KEY, CHAT_TRANSPORT or any CHAT_* used by ChatConfig) or
call the shared env-clearing fixture used in service_test.py prior to
instantiating ChatConfig, then proceed to set
monkeypatch.setattr("backend.copilot.sdk.service.config", cfg) as before.

In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 926-948: Update the docstring examples that currently show
dot-form Anthropic OpenRouter IDs ("anthropic/claude-opus-4.6" and
"anthropic/claude-opus-4.7") to the repo's hyphenated convention
("anthropic/claude-opus-4-6" and "anthropic/claude-opus-4-7") and adjust the
explanatory text to state that hyphenated IDs are canonical; also ensure any
nearby validation or error messaging referenced in this docstring (e.g., the
note about stripping "anthropic/" and converting dots to hyphens) consistently
describes and expects the hyphenated form so readers and operators are not
misled by dot-form examples.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py`:
- Around line 459-467: The test currently does local imports inside
fixtures/tests (e.g., "from backend.copilot import config as cfg_mod" and
creation of cfg with cfg_mod.ChatConfig plus
monkeypatch.setattr("backend.copilot.sdk.service.config", cfg)); move these
import statements to the module top-level (module scope) and keep only
test-specific setup in the tests/fixtures, ensuring the same names (cfg_mod,
ChatConfig) are available to the test functions; also apply the same change for
the other occurrences where local imports are used in the file (the blocks that
create ChatConfig and monkeypatch.setattr around service.config).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bc124c50-7c0a-4adb-8a97-2988f85e6cdf

📥 Commits

Reviewing files that changed from the base of the PR and between 4a567a5 and d8ccff3.

📒 Files selected for processing (4)
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: check API types
  • GitHub Check: end-to-end tests
  • GitHub Check: test (3.11)
  • GitHub Check: type-check (3.11)
  • GitHub Check: type-check (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: type-check (3.13)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🧠 Learnings (16)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 Learning: 2026-04-22T12:26:42.571Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T04:36:19.755Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit_subscription_test.py:1119-1122
Timestamp: 2026-04-21T04:36:19.755Z
Learning: In `autogpt_platform/backend/backend/data/credit_subscription_test.py` (and related subscription test files), test mocks for the user object returned by `get_user_by_id` should use snake_case `subscription_tier` (not camelCase `subscriptionTier`). This is because `get_user_by_id` (defined in `backend/data/user.py`) returns `backend.data.model.User` — a Pydantic application model with `subscription_tier: SubscriptionTier` — not a raw Prisma model. Production code in `backend/data/credit.py` reads `user.subscription_tier` from that Pydantic model. Do NOT flag `mock_user.subscription_tier = ...` as incorrect in these tests.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T23:58:18.476Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
📚 Learning: 2026-04-25T02:53:53.938Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-13T14:19:19.341Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_test.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T13:53:40.315Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12894
File: autogpt_platform/backend/backend/data/block_cost_config.py:271-277
Timestamp: 2026-04-23T13:53:40.315Z
Learning: In `autogpt_platform/backend/backend/data/block_cost_config.py`, `compute_token_credits()` intentionally returns `MODEL_COST[model]` (the flat tier) on pre-flight (when `stats is None`) for the `TOKENS` billing path. Returning 0 pre-flight would allow a zero-balance user to bypass the credit gate and trigger an LLM call, with the insufficient-balance error only surfacing post-flight (a billing leak). The overcharge concern (actual token cost < MODEL_COST estimate) is handled by `_charge_reconciled_usage_sync` in `autogpt_platform/backend/backend/executor/billing.py`, which issues a negative-delta refund via `spend_credits(cost=negative)` when real usage falls below the pre-flight estimate. Do NOT flag the MODEL_COST pre-flight floor in this function as an overcharge bug; the refund path covers it.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🔇 Additional comments (3)
autogpt_platform/backend/backend/copilot/sdk/service_test.py (1)

615-648: Good regression coverage for subscription transport precedence.

These assertions pin the two branches that were easy to regress here: invalid standard-tier LD overrides now fail soft to the tier default, and advanced Anthropic overrides stay normalized for the subscription CLI even when stale OpenRouter config is still present.

Based on learnings _resolve_sdk_model_for_request fallback must be tier-specific — config.thinking_advanced_model for advanced tier and config.thinking_standard_model for standard tier.

Also applies to: 709-737

autogpt_platform/backend/backend/copilot/sdk/service.py (1)

950-963: Transport-aware normalization gate looks correct.

Using config.effective_transport at Line 950 fixes the subscription+stale-OpenRouter config-shape bug and keeps non-OpenRouter paths on CLI-compatible model names.

autogpt_platform/backend/backend/copilot/config.py (1)

416-442: effective_transport precedence is clear and correctly ordered.

The subscription-first check cleanly prevents stale OpenRouter config from being treated as active transport for SDK model-shape decisions.

Comment thread autogpt_platform/backend/backend/copilot/config.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/sdk/service.py
@codecov

codecov Bot commented Apr 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.57%. Comparing base (4a567a5) to head (12d974e).
⚠️ Report is 1 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #12932      +/-   ##
==========================================
+ Coverage   68.50%   68.57%   +0.07%     
==========================================
  Files        2028     2028              
  Lines      151580   151625      +45     
  Branches    15795    15797       +2     
==========================================
+ Hits       103836   103975     +139     
+ Misses      44646    44552      -94     
  Partials     3098     3098              
Flag Coverage Δ
platform-backend 77.98% <100.00%> (+<0.01%) ⬆️
platform-frontend-e2e 31.23% <ø> (+1.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 77.98% <100.00%> (+<0.01%) ⬆️
Platform Frontend 34.90% <ø> (+0.36%) ⬆️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Address CodeRabbit review:
- openrouter_active docstring no longer claims to be the SDK-routing source
  of truth; effective_transport owns that (config-shape vs runtime split).
- _normalize_model_name examples switch to hyphenated Anthropic OpenRouter
  IDs to match repo convention.
- Add _clean_config_env fixture to service_helpers_test and wire it into
  the OpenRouter / direct-Anthropic / subscription fixtures plus the new
  TestEffectiveTransport / TestResolveSdkModelForRequestTransportAware
  classes so env-backed CHAT_* settings don't flip ChatConfig under the
  assertions.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/config.py`:
- Around line 569-575: The docstring overstates subscription fallback behavior:
update the text around the subscription/tier fallback so it clarifies that when
a per-user LaunchDarkly value fails in _normalize_model_name the code will try
the tier-specific default, but if that tier default also fails normalization
then _resolve_sdk_model_for_request will re-raise the original LD error (so it
can still surface a 500); edit the docstring to replace the phrase implying it
"without 500-ing the turn" with wording that explicitly notes the tier fallback
is attempted and that a re-raise occurs if normalization fails for both values,
referencing _normalize_model_name and _resolve_sdk_model_for_request.

In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py`:
- Around line 359-381: The env cleanup tuple _CONFIG_ENV_VARS is missing the CLI
override variables used by ChatConfig; update _CONFIG_ENV_VARS to include
"CHAT_CLAUDE_AGENT_CLI_PATH" and "CLAUDE_AGENT_CLI_PATH" so the pytest fixture
_clean_config_env clears those environment variables too, ensuring ChatConfig
constructed in tests isn't affected by exported CLI paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 448c2b21-0a67-4bf8-aec6-488b6636ee89

📥 Commits

Reviewing files that changed from the base of the PR and between d8ccff3 and f1141bb.

📒 Files selected for processing (3)
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • autogpt_platform/backend/backend/copilot/sdk/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
  • GitHub Check: check API types
  • GitHub Check: Seer Code Review
  • GitHub Check: end-to-end tests
  • GitHub Check: type-check (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: type-check (3.11)
  • GitHub Check: type-check (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🧠 Learnings (25)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:41.614Z
Learning: In Significant-Gravitas/AutoGPT, the Anthropic API model ID for Claude Opus 4.6 is `claude-opus-4-6` (hyphen-separated), matching `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`. The OpenRouter routing variant uses a dot separator: `anthropic/claude-opus-4.6`. Do not flag `claude-opus-4-6` as an incorrect model ID when used in Anthropic API contexts; only the OpenRouter path uses the dot form.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `anthropic/claude-sonnet-4-6` (hyphen-separated) is the project-wide convention for the Claude Sonnet 4.6 model ID, used consistently in llm.py, blocks/test, reasoning.py, _is_anthropic_model tests, and config defaults. OpenRouter accepts both `anthropic/claude-sonnet-4-6` (hyphen) and `anthropic/claude-sonnet-4.6` (dot). Do NOT flag the hyphen form as incorrect — it has been in production since the Sonnet 4.6 upgrade and is intentional.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:28.595Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.
📚 Learning: 2026-04-23T00:07:27.117Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T00:07:27.117Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/openrouter_cost.py`, the `openrouter-cost-reconcile` Langfuse event carries `cost_source` ("openrouter" for authoritative OpenRouter-resolved cost, "fallback" for rate-card fallback) and `resolved_generation_id_count` alongside the reconciled cost and token/model/provider metadata. This lets operators distinguish authoritative reconciliations from fallbacks in Langfuse. Established in PR `#12889` commit 5ce3d0388.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
📚 Learning: 2026-04-01T04:17:38.279Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py:530-535
Timestamp: 2026-04-01T04:17:38.279Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py`, the `ToolAnnotations(readOnlyHint=True)` annotation (stored as `_PARALLEL_ANNOTATION`) is intentionally applied to ALL registered MCP tools — including E2B write/edit tools (e.g., `write_file`, `edit_file`). This is a parallel-dispatch hint to the Claude Agent SDK CLI, not a semantic read-only contract. The `_READ_ONLY_E2B_TOOLS` set was dead code and was removed in commit `12ae03c`; the constant was renamed from `_READONLY_ANNOTATION` to `_PARALLEL_ANNOTATION` in commit `c88ca88` to avoid confusion. Do not flag this as a correctness issue.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
📚 Learning: 2026-03-24T21:27:22.326Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12536
File: autogpt_platform/frontend/src/app/api/openapi.json:5732-5752
Timestamp: 2026-03-24T21:27:22.326Z
Learning: Repo: Significant-Gravitas/AutoGPT — Preference: Do not add explicit 403/404 entries to FastAPI route decorators for admin endpoints just to influence OpenAPI. Keep openapi.json autogenerated and use route docstrings to document admin-only (403) and not-found (404) behavior; rely on tests for enforcement. File context: autogpt_platform/backend/backend/api/features/admin/store_admin_routes.py. PR `#12536`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
📚 Learning: 2026-04-23T01:26:38.257Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
📚 Learning: 2026-03-04T23:57:59.510Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:5593-5593
Timestamp: 2026-03-04T23:57:59.510Z
Learning: In Significant-Gravitas/AutoGPT backend (FastAPI), openapi.json is autogenerated: descriptions come from route docstrings and schemas from response_model/type annotations. To prevent drift when models are renamed (e.g., AdminView variants), avoid embedding specific schema class names in route docstrings; instead describe behavior, or keep names synced via backend edits—never hand-edit frontend/src/app/api/openapi.json.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
📚 Learning: 2026-04-22T12:26:42.571Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-25T02:53:53.938Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-13T14:19:19.341Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T04:36:19.755Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit_subscription_test.py:1119-1122
Timestamp: 2026-04-21T04:36:19.755Z
Learning: In `autogpt_platform/backend/backend/data/credit_subscription_test.py` (and related subscription test files), test mocks for the user object returned by `get_user_by_id` should use snake_case `subscription_tier` (not camelCase `subscriptionTier`). This is because `get_user_by_id` (defined in `backend/data/user.py`) returns `backend.data.model.User` — a Pydantic application model with `subscription_tier: SubscriptionTier` — not a raw Prisma model. Production code in `backend/data/credit.py` reads `user.subscription_tier` from that Pydantic model. Do NOT flag `mock_user.subscription_tier = ...` as incorrect in these tests.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T13:44:34.273Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12797
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1991-2021
Timestamp: 2026-04-15T13:44:34.273Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (`_run_stream_attempt`), the pre-create block (PR `#12797`) intentionally does NOT call `state.transcript_builder.append_assistant(...)` when inserting the empty assistant placeholder into `ctx.session.messages`. The transcript is left ending at the `tool_result` entry (N entries) while `message_count` metadata is N+1. This mismatch is benign and deliberate: on the next `--resume`, the SDK sees the transcript ending at `tool_result` and correctly regenerates the assistant response. Pre-appending the assistant turn to the transcript would suppress regeneration while leaving `session.messages[-1].content = ""` permanently (worse outcome). On the gap-fallback path, `transcript_msg_count (N+1) >= msg_count-1 (N)` means no gap is injected for the empty placeholder, which is correct because injecting an empty assistant message as context would mislead the SDK. Do NOT flag this transcript/message_count discrepancy as a bug.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-14T14:36:25.545Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-16T12:33:44.990Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12796
File: autogpt_platform/backend/backend/api/features/chat/routes.py:504-527
Timestamp: 2026-04-16T12:33:44.990Z
Learning: In `autogpt_platform/backend/backend/api/features/chat/routes.py`, `get_session` (PR `#12796`, commit 3771bfad9c1) closes the TOCTOU race between the initial `stream_registry.get_active_session()` pre-check and `get_chat_messages_paginated()` with a post-check re-verification: after the DB fetch, if `is_initial_load and active_session is not None`, it calls `get_active_session` a second time; if `post_active is None` (stream completed during the window), it resets `from_start=True`, `forward_paginated=True`, and re-fetches messages from sequence 0. Do NOT flag the double `get_active_session` call pattern as redundant — it is the intentional TOCTOU mitigation for pagination direction selection.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-18T14:03:32.534Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12473
File: autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py:86-195
Timestamp: 2026-03-18T14:03:32.534Z
Learning: In Significant-Gravitas/AutoGPT, the integration tests in `autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py` intentionally use real external URLs (example.com, httpbin.org). They are gated with `pytest.mark.skipif(shutil.which("agent-browser") is None, ...)`, so they are automatically skipped in standard CI where agent-browser is not installed. They are designed to be run explicitly inside the Docker environment to verify that system Chromium (AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium) actually launches and can fetch pages end-to-end. Do not flag the use of real network calls in these tests as a flakiness concern.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T17:31:23.683Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12873
File: autogpt_platform/backend/backend/copilot/baseline/reasoning.py:0-0
Timestamp: 2026-04-21T17:31:23.683Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/reasoning.py` (`BaselineReasoningEmitter`), when `render_in_ui=False`, BOTH the `StreamReasoning*` wire events AND the `ChatMessage(role="reasoning")` persistence append must be suppressed together. `convertChatSessionToUiMessages.ts` unconditionally re-renders all persisted `role="reasoning"` rows as `{type:"reasoning"}` UI parts on reload, so persisting rows while silencing live wire events would resurrect the reasoning collapse on page refresh. The audit trail is preserved through the provider transcript and `_format_sdk_content_blocks` (SDK path) instead. The baseline and SDK paths mirror each other: flag off → no live wire event, no persisted row, no hydrated collapse. This was established in PR `#12873`, commit 7ef10b26c.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T13:53:40.315Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12894
File: autogpt_platform/backend/backend/data/block_cost_config.py:271-277
Timestamp: 2026-04-23T13:53:40.315Z
Learning: In `autogpt_platform/backend/backend/data/block_cost_config.py`, `compute_token_credits()` intentionally returns `MODEL_COST[model]` (the flat tier) on pre-flight (when `stats is None`) for the `TOKENS` billing path. Returning 0 pre-flight would allow a zero-balance user to bypass the credit gate and trigger an LLM call, with the insufficient-balance error only surfacing post-flight (a billing leak). The overcharge concern (actual token cost < MODEL_COST estimate) is handled by `_charge_reconciled_usage_sync` in `autogpt_platform/backend/backend/executor/billing.py`, which issues a negative-delta refund via `spend_credits(cost=negative)` when real usage falls below the pre-flight estimate. Do NOT flag the MODEL_COST pre-flight floor in this function as an overcharge bug; the refund path covers it.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🔇 Additional comments (2)
autogpt_platform/backend/backend/copilot/config.py (1)

398-443: Nice fix: runtime transport is now explicit.

Separating openrouter_active (config shape) from effective_transport (actual SDK routing) makes the normalization decision much easier to reason about and matches the reported subscription + leftover OpenRouter regression.

autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py (1)

477-657: Good regression coverage for the transport-aware normalization path.

These cases pin the exact failure mode from the PR summary: subscription winning over leftover OpenRouter config, vendor rejection on the CLI path, and LD overrides being normalized against the actual transport instead of config shape.

Comment thread autogpt_platform/backend/backend/copilot/config.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py Outdated
Address CodeRabbit follow-ups on the previous fix:
- _validate_sdk_model_vendor_compatibility docstring no longer claims the
  subscription path always degrades without 500-ing. Clarified that the
  tier-default fallback only avoids a request error when the default is
  itself valid; otherwise the original LD ValueError is re-raised by
  _resolve_sdk_model_for_request.
- Extend _CONFIG_ENV_VARS with CHAT_CLAUDE_AGENT_CLI_PATH and the
  unprefixed CLAUDE_AGENT_CLI_PATH so a dev shell exporting either
  doesn't bleed into ChatConfig under the new test fixtures.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py (2)

359-383: Extract the env-cleanup allowlist into a shared test helper.

This tuple/fixture is now a second copy of the ChatConfig env scrub logic from service_test.py. The missed CLI-path vars were exactly the kind of drift this duplication causes, so I'd centralize _CONFIG_ENV_VARS / _clean_config_env in one shared test helper before the next env-backed field gets added.

As per coding guidelines, "Duplicate code (copy/paste, similar logic, abstractions)" should be refactored.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py` around
lines 359 - 383, Extract the duplicated env-cleanup allowlist and fixture into a
single shared test helper module and update callers to import it: move the
_CONFIG_ENV_VARS tuple and the _clean_config_env fixture into a new or existing
shared test helper (e.g., tests.helpers or a common conftest) and delete the
duplicate definitions in service_helpers_test.py and service_test.py; ensure the
fixture name remains _clean_config_env and that tests import/use that shared
fixture so the duplicated CLI-path vars (and any future env-backed settings) are
maintained in one place.

403-427: Move the new backend.copilot.* imports to module scope.

These are regular project imports, not lazy optional dependencies, so repeating them inside fixtures/tests just adds noise. The file already imports .service at module load anyway, so top-level imports would be simpler and align with the backend import rule.

Suggested cleanup
+from backend.copilot import config as cfg_mod
+from backend.copilot.config import ChatConfig
+from backend.copilot.sdk.service import _resolve_sdk_model_for_request
...
-        from backend.copilot import config as cfg_mod
...
-        from backend.copilot.config import ChatConfig
...
-        from backend.copilot.sdk.service import _resolve_sdk_model_for_request

As per coding guidelines, "Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl."

Also applies to: 479-500, 534-583, 604-640

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py` around
lines 403 - 427, Move module imports for backend.copilot.* out of test/fixture
bodies into the module top-level: replace local imports like "from
backend.copilot import config as cfg_mod" inside fixtures such as
_direct_anthropic_config and _openrouter_config and the other fixtures
referenced (lines ~479-500, ~534-583, ~604-640) with equivalent top-level
imports, then update fixture code to use those module-level names and keep
monkeypatch.setattr targets (e.g. "backend.copilot.sdk.service.config")
unchanged; ensure no remaining backend.copilot.* imports are performed inside
functions except for genuine lazy/optional dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py`:
- Around line 359-383: Extract the duplicated env-cleanup allowlist and fixture
into a single shared test helper module and update callers to import it: move
the _CONFIG_ENV_VARS tuple and the _clean_config_env fixture into a new or
existing shared test helper (e.g., tests.helpers or a common conftest) and
delete the duplicate definitions in service_helpers_test.py and service_test.py;
ensure the fixture name remains _clean_config_env and that tests import/use that
shared fixture so the duplicated CLI-path vars (and any future env-backed
settings) are maintained in one place.
- Around line 403-427: Move module imports for backend.copilot.* out of
test/fixture bodies into the module top-level: replace local imports like "from
backend.copilot import config as cfg_mod" inside fixtures such as
_direct_anthropic_config and _openrouter_config and the other fixtures
referenced (lines ~479-500, ~534-583, ~604-640) with equivalent top-level
imports, then update fixture code to use those module-level names and keep
monkeypatch.setattr targets (e.g. "backend.copilot.sdk.service.config")
unchanged; ensure no remaining backend.copilot.* imports are performed inside
functions except for genuine lazy/optional dependencies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 80aafb04-5f31-4a5d-8769-ce06c1b370bd

📥 Commits

Reviewing files that changed from the base of the PR and between f1141bb and a99c142.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/copilot/config.py
  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • autogpt_platform/backend/backend/copilot/config.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: check API types
  • GitHub Check: test (3.12)
  • GitHub Check: type-check (3.11)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: end-to-end tests
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (3)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

autogpt_platform/backend/**/*.py: Use poetry run ... command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies like openpyxl
Use absolute imports with from backend.module import ... for cross-package imports; single-dot relative imports are acceptable for sibling modules within the same package; avoid double-dot relative imports
Do not use duck typing — avoid hasattr/getattr/isinstance for type dispatch; use typed interfaces/unions/protocols instead
Use Pydantic models over dataclass/namedtuple/dict for structured data
Do not use linter suppressors — no # type: ignore, # noqa, # pyright: ignore; fix the type/code instead
Prefer list comprehensions over manual loop-and-append patterns
Use early return with guard clauses first to avoid deep nesting
Use %s for deferred interpolation in debug log statements for efficiency; use f-strings elsewhere for readability (e.g., logger.debug("Processing %s items", count) vs logger.info(f"Processing {count} items"))
Sanitize error paths by using os.path.basename() in error messages to avoid leaking directory structure
Be aware of TOCTOU (Time-Of-Check-Time-Of-Use) issues — avoid check-then-act patterns for file access and credit charging
Use transaction=True for Redis pipelines to ensure atomicity on multi-step operations
Use max(0, value) guards for computed values that should never be negative
Keep files under ~300 lines; if a file grows beyond this, split by responsibility (extract helpers, models, or a sub-module into a new file)
Keep functions under ~40 lines; extract named helpers when a function grows longer
...

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
autogpt_platform/backend/**/*_test.py

📄 CodeRabbit inference engine (autogpt_platform/backend/AGENTS.md)

autogpt_platform/backend/**/*_test.py: Use pytest with snapshot testing for API responses
Colocate test files with source files using *_test.py naming convention
Mock at boundaries — mock where the symbol is used, not where it's defined; after refactoring, update mock targets to match new module paths
Use AsyncMock from unittest.mock for async functions in tests
When writing tests, use Test-Driven Development (TDD): write failing tests marked with @pytest.mark.xfail before implementation, then remove the marker once the implementation is complete
When creating snapshots in tests, use poetry run pytest path/to/test.py --snapshot-update; always review snapshot changes with git diff before committing

Files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
🧠 Learnings (32)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:41.614Z
Learning: In Significant-Gravitas/AutoGPT, the Anthropic API model ID for Claude Opus 4.6 is `claude-opus-4-6` (hyphen-separated), matching `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`. The OpenRouter routing variant uses a dot separator: `anthropic/claude-opus-4.6`. Do not flag `claude-opus-4-6` as an incorrect model ID when used in Anthropic API contexts; only the OpenRouter path uses the dot form.
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:28.595Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `anthropic/claude-sonnet-4-6` (hyphen-separated) is the project-wide convention for the Claude Sonnet 4.6 model ID, used consistently in llm.py, blocks/test, reasoning.py, _is_anthropic_model tests, and config defaults. OpenRouter accepts both `anthropic/claude-sonnet-4-6` (hyphen) and `anthropic/claude-sonnet-4.6` (dot). Do NOT flag the hyphen form as incorrect — it has been in production since the Sonnet 4.6 upgrade and is intentional.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
📚 Learning: 2026-04-22T12:26:42.571Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-22T12:26:42.571Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `_resolve_sdk_model_for_request`: when a per-user LaunchDarkly model value fails `_normalize_model_name` (e.g. a `moonshotai/kimi-*` slug in direct-Anthropic mode), the fallback must be tier-specific — `config.thinking_advanced_model` for advanced tier, `config.thinking_standard_model` for standard tier — NOT the generic `_resolve_sdk_model()` (which is standard-only and returns None under subscription mode). If the tier-specific config default also fails `_normalize_model_name`, re-raise the original LD error; this is a deployment-level misconfiguration that `model_validator` should have caught at startup. Established in PR `#12881` commit 637d2fef5.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-25T02:53:53.938Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-25T02:53:53.938Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (PR `#12918`, commit 6576bf561):
- `_flush_unresolved_tool_calls` was renamed to `flush_unresolved_tool_calls` (public); all call sites updated, `# noqa: SLF001` suppressor removed.
- `_flush_orphan_tool_uses_to_session` and `_InterruptedAttempt.finalize` both return `list[StreamBaseResponse]`; the post-loop caller yields those events directly to avoid double-flush and skipped UI cleanup events.
- The three former post-loop blocks (partial restore + redundant re-flush + two separate `yield StreamError` sites) are collapsed into a single block driven by `_classify_final_failure` returning a `_FinalFailure(display_msg, code, retryable)` dataclass, so history marker and SSE yield share one source of truth.
Do NOT flag double-flush risk or mismatched history/SSE marker as issues in the post-loop section of `stream_chat_completion_sdk`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-13T14:19:19.341Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12740
File: autogpt_platform/frontend/src/app/api/openapi.json:0-0
Timestamp: 2026-04-13T14:19:19.341Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
When adding new CoPilot tool response models (e.g., ScheduleListResponse, ScheduleDeletedResponse), update backend/api/features/chat/routes.py to include them in the ToolResponseUnion so the frontend’s autogenerated openapi.json dummy export (/api/chat/schema/tool-responses) exposes them for codegen. Do not hand-edit frontend/src/app/api/openapi.json.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T04:36:19.755Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12865
File: autogpt_platform/backend/backend/data/credit_subscription_test.py:1119-1122
Timestamp: 2026-04-21T04:36:19.755Z
Learning: In `autogpt_platform/backend/backend/data/credit_subscription_test.py` (and related subscription test files), test mocks for the user object returned by `get_user_by_id` should use snake_case `subscription_tier` (not camelCase `subscriptionTier`). This is because `get_user_by_id` (defined in `backend/data/user.py`) returns `backend.data.model.User` — a Pydantic application model with `subscription_tier: SubscriptionTier` — not a raw Prisma model. Production code in `backend/data/credit.py` reads `user.subscription_tier` from that Pydantic model. Do NOT flag `mock_user.subscription_tier = ...` as incorrect in these tests.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T23:58:18.476Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T05:58:28.595Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/frontend/src/app/api/openapi.json:14576-14577
Timestamp: 2026-04-22T05:58:28.595Z
Learning: Repo: Significant-Gravitas/AutoGPT — autogpt_platform
Process convention: When adding new CoPilot tool response models and updating ToolResponseUnion in backend/api/features/chat/routes.py, regenerate the frontend OpenAPI schema via `poetry run export-api-schema` (do not hand-edit autogpt_platform/frontend/src/app/api/openapi.json).

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-17T10:57:12.953Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T13:44:34.273Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12797
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1991-2021
Timestamp: 2026-04-15T13:44:34.273Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (`_run_stream_attempt`), the pre-create block (PR `#12797`) intentionally does NOT call `state.transcript_builder.append_assistant(...)` when inserting the empty assistant placeholder into `ctx.session.messages`. The transcript is left ending at the `tool_result` entry (N entries) while `message_count` metadata is N+1. This mismatch is benign and deliberate: on the next `--resume`, the SDK sees the transcript ending at `tool_result` and correctly regenerates the assistant response. Pre-appending the assistant turn to the transcript would suppress regeneration while leaving `session.messages[-1].content = ""` permanently (worse outcome). On the gap-fallback path, `transcript_msg_count (N+1) >= msg_count-1 (N)` means no gap is injected for the empty placeholder, which is correct because injecting an empty assistant message as context would mislead the SDK. Do NOT flag this transcript/message_count discrepancy as a bug.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-16T12:33:44.990Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12796
File: autogpt_platform/backend/backend/api/features/chat/routes.py:504-527
Timestamp: 2026-04-16T12:33:44.990Z
Learning: In `autogpt_platform/backend/backend/api/features/chat/routes.py`, `get_session` (PR `#12796`, commit 3771bfad9c1) closes the TOCTOU race between the initial `stream_registry.get_active_session()` pre-check and `get_chat_messages_paginated()` with a post-check re-verification: after the DB fetch, if `is_initial_load and active_session is not None`, it calls `get_active_session` a second time; if `post_active is None` (stream completed during the window), it resets `from_start=True`, `forward_paginated=True`, and re-fetches messages from sequence 0. Do NOT flag the double `get_active_session` call pattern as redundant — it is the intentional TOCTOU mitigation for pagination direction selection.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-14T14:36:25.545Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12773
File: autogpt_platform/backend/backend/copilot/pending_messages.py:52-64
Timestamp: 2026-04-14T14:36:25.545Z
Learning: In `autogpt_platform/backend/backend/copilot` (PR `#12773`, commit d7bced0c6): when draining pending messages into `session.messages`, each message's text is sanitized via `strip_user_context_tags` before persistence to prevent user-controlled `<user_context>` injection from bypassing the trusted server-side context prefix. Additionally, if `upsert_chat_session` fails after draining, the drained `PendingMessage` objects are requeued back to Redis to avoid silent message loss. Do NOT flag the drain-then-requeue pattern as redundant — it is the intentional failure-resilience strategy for the pending buffer.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-18T14:03:32.534Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12473
File: autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py:86-195
Timestamp: 2026-03-18T14:03:32.534Z
Learning: In Significant-Gravitas/AutoGPT, the integration tests in `autogpt_platform/backend/backend/copilot/tools/agent_browser_integration_test.py` intentionally use real external URLs (example.com, httpbin.org). They are gated with `pytest.mark.skipif(shutil.which("agent-browser") is None, ...)`, so they are automatically skipped in standard CI where agent-browser is not installed. They are designed to be run explicitly inside the Docker environment to verify that system Chromium (AGENT_BROWSER_EXECUTABLE_PATH=/usr/bin/chromium) actually launches and can fetch pages end-to-end. Do not flag the use of real network calls in these tests as a flakiness concern.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-21T17:31:23.683Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12873
File: autogpt_platform/backend/backend/copilot/baseline/reasoning.py:0-0
Timestamp: 2026-04-21T17:31:23.683Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/reasoning.py` (`BaselineReasoningEmitter`), when `render_in_ui=False`, BOTH the `StreamReasoning*` wire events AND the `ChatMessage(role="reasoning")` persistence append must be suppressed together. `convertChatSessionToUiMessages.ts` unconditionally re-renders all persisted `role="reasoning"` rows as `{type:"reasoning"}` UI parts on reload, so persisting rows while silencing live wire events would resurrect the reasoning collapse on page refresh. The audit trail is preserved through the provider transcript and `_format_sdk_content_blocks` (SDK path) instead. The baseline and SDK paths mirror each other: flag off → no live wire event, no persisted row, no hydrated collapse. This was established in PR `#12873`, commit 7ef10b26c.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T01:26:38.257Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-23T01:26:38.257Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py`, `langfuse_trace_id = get_client().get_current_trace_id()` must be captured under the `if _lf_span is not None:` guard (before `_lf_span` is torn down), NOT under `if _otel_ctx is not None:`. The `_otel_ctx` guard is too narrow: if `propagate_attributes().__enter__()` raises, `_otel_ctx` is never assigned, and placing the trace-id capture there would silently orphan the `openrouter-cost-reconcile` event from its parent span. Established in PR `#12889` commit d243bf6c9.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T05:57:34.861Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12879
File: autogpt_platform/backend/backend/copilot/baseline/service.py:0-0
Timestamp: 2026-04-22T05:57:34.861Z
Learning: In `autogpt_platform/backend/backend/copilot/baseline/service.py`, the approved pattern for `_run_task_subagent` (PR `#12879`, commit 187f0a5) uses a nested `try/except Exception` inside an outer `try/finally`. The outer `finally` block resets `_TASK_DEPTH_VAR` (via `_TASK_DEPTH_VAR.reset(token)`) AND calls `_absorb_inner_usage(parent_state, inner_state)` unconditionally, so both the depth ContextVar and usage roll-up are guaranteed on all exit paths including `CancelledError`/`KeyboardInterrupt`/`SystemExit`. The inner `except Exception` catches and converts failures into a `TaskResponse` error payload that is returned as `StreamToolOutputAvailable`. Do NOT flag missing ContextVar reset or usage roll-up on BaseException paths in this function.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-08T17:27:26.657Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: classic/original_autogpt/CLAUDE.md:0-0
Timestamp: 2026-04-08T17:27:26.657Z
Learning: Applies to classic/original_autogpt/**/tests/**/*.py : In tests, use fixtures from `tests/conftest.py` (app_data_dir, config, storage, llm_provider, agent) for initializing test components. Run tests from the `classic/` directory using pytest commands like `poetry run pytest original_autogpt/tests/`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-15T16:52:15.463Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12426
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-03-15T16:52:15.463Z
Learning: In Significant-Gravitas/AutoGPT (copilot backend), GitHub tokens (GH_TOKEN / GITHUB_TOKEN) for the `gh` CLI are injected lazily per-command in `autogpt_platform/backend/backend/copilot/tools/bash_exec._execute_on_e2b()` by calling `integration_creds.get_integration_env_vars(user_id)`, not on the global SDK subprocess environment in `sdk/service.py`. This scopes credentials to individual E2B sandbox command invocations and prevents token leakage into tool output streams or uploaded transcripts.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T10:17:09.341Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-15T10:17:09.341Z
Learning: In `Significant-Gravitas/AutoGPT` (`autogpt_platform/frontend`), `vitest.config.mts` does NOT set `globals: true`, and `src/tests/integrations/vitest.setup.tsx` does NOT register the `testing-library/react` auto-cleanup hook. Therefore, integration test files (e.g., under `__tests__/`) MUST include an explicit `afterEach(cleanup)` call from `testing-library/react` to reset the DOM between tests — without it, tests fail with "multiple elements found" errors from the prior test's DOM. Do NOT flag `afterEach(cleanup)` as redundant in this codebase.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-10T08:39:22.025Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-07T18:08:03.548Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12701
File: .claude/skills/orchestrate/scripts/verify-complete.sh:120-121
Timestamp: 2026-04-07T18:08:03.548Z
Learning: In Significant-Gravitas/AutoGPT, verify-complete.sh (`.claude/skills/orchestrate/scripts/verify-complete.sh`) uses `commits[-1].committedDate` (not `updatedAt`) to identify stale CHANGES_REQUESTED reviews. This is intentional: `updatedAt` changes on any PR activity (bot comments, label changes, description edits), which would falsely classify a reviewer's CHANGES_REQUESTED as stale — a silent false negative. The `committedDate` edge case (commit created locally before a review but pushed after) only causes a false positive (unnecessary re-brief), which is the safer failure mode. Do not suggest switching to `updatedAt` for this comparison.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T14:54:01.937Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12636
File: autogpt_platform/backend/backend/copilot/sdk/service.py:0-0
Timestamp: 2026-04-01T14:54:01.937Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), `claude_agent_max_transient_retries` (default=3) in `ChatConfig` counts **total attempts including the initial one**, not the number of extra retries. With the pre-incremented `transient_retries >= max_transient` guard in `service.py`, a value of 3 yields 3 total stream attempts (initial + 2 retries with exponential backoff: 1s, 2s). Do NOT flag this as an off-by-one — the `>=` check is intentional.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-23T13:53:40.315Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12894
File: autogpt_platform/backend/backend/data/block_cost_config.py:271-277
Timestamp: 2026-04-23T13:53:40.315Z
Learning: In `autogpt_platform/backend/backend/data/block_cost_config.py`, `compute_token_credits()` intentionally returns `MODEL_COST[model]` (the flat tier) on pre-flight (when `stats is None`) for the `TOKENS` billing path. Returning 0 pre-flight would allow a zero-balance user to bypass the credit gate and trigger an LLM call, with the insufficient-balance error only surfacing post-flight (a billing leak). The overcharge concern (actual token cost < MODEL_COST estimate) is handled by `_charge_reconciled_usage_sync` in `autogpt_platform/backend/backend/executor/billing.py`, which issues a negative-delta refund via `spend_credits(cost=negative)` when real usage falls below the pre-flight estimate. Do NOT flag the MODEL_COST pre-flight floor in this function as an overcharge bug; the refund path covers it.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-01T04:17:41.600Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12632
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-01T04:17:41.600Z
Learning: When reviewing AutoGPT Copilot tool implementations, accept that `readOnlyHint=True` (provided via `ToolAnnotations`) may be applied unconditionally to *all* tools—even tools that have side effects (e.g., `bash_exec`, `write_workspace_file`, or other write/save operations). Do **not** flag these tools for having `readOnlyHint=True`; this is intentional to enable fully-parallel dispatch by the Anthropic SDK/CLI and has been E2E validated. Only flag `readOnlyHint` issues if they conflict with the established `ToolAnnotations` behavior (e.g., missing/incorrect propagation relative to the intended annotation mechanism).

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-16T16:35:40.236Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/api/features/workflow_import.py:54-63
Timestamp: 2026-03-16T16:35:40.236Z
Learning: Avoid using the word 'competitor' in public-facing identifiers and text. Use neutral naming for API paths, model names, function names, and UI text. Examples: rename 'CompetitorFormat' to 'SourcePlatform', 'convert_competitor_workflow' to 'convert_workflow', '/competitor-workflow' to '/workflow'. Apply this guideline to files under autogpt_platform/backend and autogpt_platform/frontend.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-03-31T15:37:38.626Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/backend/backend/copilot/tools/agent_generator/fixer.py:37-47
Timestamp: 2026-03-31T15:37:38.626Z
Learning: When validating/constructing Anthropic API model IDs in Significant-Gravitas/AutoGPT, allow the hyphen-separated Claude Opus 4.6 model ID `claude-opus-4-6` (it corresponds to `LlmModel.CLAUDE_4_6_OPUS` in `autogpt_platform/backend/backend/blocks/llm.py`). Do NOT require the dot-separated form in Anthropic contexts. Only OpenRouter routing variants should use the dot separator (e.g., `anthropic/claude-opus-4.6`); `claude-opus-4-6` should be treated as correct when passed to Anthropic, and flagged only if it’s used in the OpenRouter path where the dot form is expected.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-15T02:43:36.890Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12780
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:0-0
Timestamp: 2026-04-15T02:43:36.890Z
Learning: When reviewing Python exception handlers, do not flag `isinstance(e, X)` checks as dead/unreachable if the caught exception `X` is a subclass of the exception type being handled. For example, if `X` (e.g., `VirusScanError`) inherits from `ValueError` (directly or via an intermediate class) and it can be raised within an `except ValueError:` block, then `isinstance(e, X)` inside that handler is reachable and should not be treated as dead code.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:04.431Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/config.py:0-0
Timestamp: 2026-04-22T11:46:04.431Z
Learning: Do not flag the Claude Sonnet 4.6 model ID as incorrect when it uses the project’s established hyphenated convention: `anthropic/claude-sonnet-4-6`. This hyphen form is the intentional, production convention and should be treated as valid (including in files like llm.py, blocks tests, reasoning.py, `_is_anthropic_model` tests, and config defaults). Note that OpenRouter also accepts the dot variant `anthropic/claude-sonnet-4.6`, so either form may be tolerated, but `anthropic/claude-sonnet-4-6` should be considered the standard to match project usage.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py
📚 Learning: 2026-04-22T11:46:12.892Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12881
File: autogpt_platform/backend/backend/copilot/baseline/service.py:322-332
Timestamp: 2026-04-22T11:46:12.892Z
Learning: In this codebase (Significant-Gravitas/AutoGPT), OpenRouter-routed Anthropic model IDs should use the hyphen-separated convention (e.g., `anthropic/claude-sonnet-4-6`, `anthropic/claude-opus-4-6`). Although OpenRouter may accept both hyphen and dot variants, treat the hyphen-separated form as the intended, correct codebase-wide convention and do not flag it as an error. Only flag the dot-separated variant (e.g., `anthropic/claude-sonnet-4.6`) as incorrect when reviewing/validating model ID strings for OpenRouter-routed Anthropic models.

Applied to files:

  • autogpt_platform/backend/backend/copilot/sdk/service_helpers_test.py

Address CodeRabbit nitpicks:
- Move _CONFIG_ENV_VARS + _clean_config_env to copilot/sdk/conftest.py so
  both service_test and service_helpers_test pull from one source. Adding
  a future env-backed ChatConfig field is now a one-line change instead
  of two parallel updates that drift (CHAT_CLAUDE_AGENT_CLI_PATH gap was
  exactly this drift surfacing).
- Hoist the PR-introduced backend.copilot.* imports to module scope per
  the backend "top-level imports only" guideline; pre-existing local
  imports in unrelated tests are out of scope.
@majdyz

majdyz commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Addressed CodeRabbit review 4185574375 in 12d974e56:

  • Centralised _CONFIG_ENV_VARS and _clean_config_env in copilot/sdk/conftest.py so the two test files share one source — adding a future env-backed ChatConfig field becomes a one-line change.
  • Hoisted the PR-introduced backend.copilot.* imports to module scope per the backend top-level-imports guideline; pre-existing inline imports in unrelated tests are out of scope.

165/165 tests still pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end size/l

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

1 participant