Skip to content

feat(backend/copilot): AutoPilot task queue with 5 running + 15 in-flight caps - #13069

Merged
majdyz merged 48 commits into
devfrom
zamilmajdy/secrt-2339-add-autopilot-task-queue-with-5-concurrent-runs-and-15-in
May 11, 2026
Merged

feat(backend/copilot): AutoPilot task queue with 5 running + 15 in-flight caps#13069
majdyz merged 48 commits into
devfrom
zamilmajdy/secrt-2339-add-autopilot-task-queue-with-5-concurrent-runs-and-15-in

Conversation

@majdyz

@majdyz majdyz commented May 9, 2026

Copy link
Copy Markdown
Contributor

Why

We hard-capped concurrent AutoPilot turns at 15 per user as a hotfix, which rejected the 16th request with HTTP 429 — blunt UX, easy to hit by accident. This PR keeps the safeguard but introduces a soft running cap of 5 with a FIFO queue up to 15 in-flight (running + queued). The user can submit beyond 5; the dispatcher auto-promotes queued sessions as running slots free.

What

  • Soft cap: 5 running tasks per user (configurable: max_running_copilot_turns_per_user).
  • Hard cap: 15 in-flight (running + queued) per user (existing setting, semantics shifted from "concurrent" to "in-flight").
  • FIFO queue at the SESSION level. ChatSession.chatStatus is a single text enum: idle (default) | queued | running (open enum). The user's pending message is just a normal ChatMessage row; the session carries the lifecycle.
  • Cancel: the existing POST /sessions/{session_id}/cancel handles both states uniformly. Queued sessions flip back to idle (no executor cancel needed); running sessions publish a RabbitMQ cancel event as before. No dedicated /queued-tasks/* endpoints.
  • Frontend: Queued-state badge on the latest user message of any session whose chat_status === 'queued'. Sidebar shows a green pulsing dot for running and a purple hourglass for queued so the user can see at a glance which of their chats are in flight. Cancel button on the badge calls the same session-cancel endpoint.

How

Layer Storage Purpose
Session lifecycle ChatSession.chatStatus (this PR) Soft cap (count running) + queue (count queued), single source of truth
Dispatcher payload ChatMessage.metadata JSONB (this PR) Submit-time file_ids / mode / model / permissions / context / request_arrival_at stashed on the queued user row so promotion can replay the turn faithfully
Execution RabbitMQ (existing) Worker pool, retries

Queue lifecycle — where it lives, who clears it

Storage. The queue lives entirely in Postgres (no Redis sorted set, no Lua). Per session: a single chatStatus text column (idle | queued | running, open enum), plus the user's pending message persisted as a normal ChatMessage row with the dispatcher's submit-time payload stashed in metadata JSONB.

Submission path (backend/copilot/turn_queue.py:enqueue_turn):

  1. HTTP route tries immediate dispatch via acquire_turn_slot. If user is at the 5-running cap, ConcurrentTurnLimitError is raised.
  2. Route catches the error and falls through to try_enqueue_turn which: checks the 15 in-flight hard cap, persists the user message + metadata, then CAS-flips ChatSession.chatStatus idlequeued. Returns an empty-stream response so the SSE client knows the message landed.

Promotion path (backend/copilot/turn_queue.py:dispatch_next_for_user) — fires per-user, not globally:

  1. Trigger Complete prompt redesign #1: mark_session_completed (in stream_registry.py) — after every turn completes (success / failure / cancel), the session's user_id is read from Redis meta and dispatch_next_for_user(user_id) is invoked. The dispatcher errors are swallowed with a loud log so a queue hiccup never breaks the completion path.
  2. Trigger #2: periodic timer (covers missed dispatch events / GKE rolling restarts).
  3. Picks the user's oldest queued session via list_chat_sessions_by_status(user_id, queued) ordered by updatedAt.
  4. Re-validates paywall + per-window USD cap — paywalled / rate-limited sessions stay queued for the next tick.
  5. CAS-claims queuedrunning. Recovers the user message + submit-time metadata, builds a TurnSlot, calls dispatch_turn. On any error during dispatch, rolls the session back to queued.

Cancellation (POST /sessions/{id}/cancel): single endpoint handles both lifecycle states — queued sessions flip back to idle (no executor cancel needed); running sessions publish a RabbitMQ cancel event as before.

The cap and queue queries are both count / find_many on ChatSession by chatStatus. Both running-turn tracking and queue admission are non-locked CAS-then-count — same TOCTOU tolerance the graph-execution credit rate-limit accepts on its INCRBY path. Going briefly to 16/17 in-flight under burst is acceptable; the cap is a safeguard, not a budget.

The DB-manager surface is 4 generic ChatSession primitives (count_chat_sessions_by_status, list_chat_sessions_by_status, update_chat_session_status with optional expect_status CAS gate, get_chat_session_status) plus the existing add_chat_message extended with optional message_id + metadata. Adding a new lifecycle state is a code-only change at call sites. DB access goes through backend.data.db_accessors.chat_db() so the dispatcher works from both the HTTP server (Prisma directly) and the CoPilotExecutor subprocess (RPC via DatabaseManager).

Route gates (is_turn_in_flight, acquire_turn_slot) treat both queued and running as "in flight", so a resubmit to a queued session lands in the pending buffer or falls through to the cross-session queue rather than racing the dispatcher.

Test plan

  • Backend unit tests — active_turns_test.py, turn_queue_test.py, db_test.py (60+ tests covering admission, refresh, release, queued-collision, cap-rollback, dispatcher branches: paywall/rate-limited stays queued, rate-limit unavailable, happy path, dispatch failure → restore).
  • New integration tests in stream_registry_test.py: pin the per-user slot-free dispatcher invocation on mark_session_completed, plus the error-swallowing behaviour so a queue hiccup never breaks the turn completion path.
  • Migration scoped to additive nullable column + an additional non-partial index (cheap on Postgres, no table rewrite, no backfill needed).
  • Frontend integration tests (Vitest): queued-state badge, cancel button, error paths (404 silent, 5xx destructive toast, network error), sidebar running/queued/idle indicators.
  • Live UI proof inline on the PR (scroll down): Queued badge, pill tooltip, cancel-hover red state, after-cancel hidden — all rendered by the production component against a real backend running this branch. Sidebar running-dot + queued-hourglass also screenshotted.
  • CI: lint, types, integration_test, end-to-end tests.

Stuck-running bugs found and fixed in this PR

Audit during review surfaced multiple "DB says running, executor isn't actually running it" failure modes. Every one fixed in this PR; none deferred.

# Scenario Symptom Fix
1 Executor crashes mid-turn, Redis meta TTLs out, DB chatStatus stays running Sidebar green dot forever; new submits buffer indefinitely cancel_session_task now calls release_turn_slot on the no-active-session branch — the user clicking Cancel always clears the orphan. get_session route also resets when DB says running but Redis is empty, so just opening the stuck chat clears it.
2 acquire_turn_slot flips DB idle → running but the request aborts before dispatch_turn runs Same orphan, no Redis meta written Same fixes as #1 catch this on the next cancel/open. The new periodic sweep below covers the no-interaction case.
3 dispatch_turn succeeds at create_session (Redis meta written) but fails at the RabbitMQ enqueue Redis hash sits at status='running' until TTL; is_turn_in_flight keeps reporting in-flight dispatch_turn now uses try/finally on a committed flag — Redis meta is deleted on ANY non-happy-path exit including CancelledError (which except Exception would miss). Cleanup runs inside dispatch_turn itself so both the HTTP schedule_chat_turn path and the queue dispatcher path are covered.
4 mark_session_completed itself fails mid-completion (Redis blip during the CAS) DB never flips back to idle Same chat-open / cancel reset fixes handle this. No proactive sweep was viable here because we can't distinguish "legit long-running tool call" from "stuck" without a Redis side check.
5 Dispatcher rollback used except Exception, leaking on CancelledError Task cancellation during dispatch could leave DB stuck at running Switched to except BaseException in dispatch_next_for_user so cancellation still rolls back the DB claim. Pairs with the try/finally in dispatch_turn for full Redis+DB symmetry.
6 chatStatus was an open TEXT column with no DB-level validation A typo like "runnin" could persist and break the cap-count Converted to a Postgres enum ChatSessionStatus (`idle

Why no periodic sweep. The reactive cleanups (rows 1 and 2) catch every user-visible path: clicking Cancel and opening the chat both reset the orphan. The existing 6h+5min inline stale-CAS in get_active_session remains the ultimate backstop. Adding a per-pod APScheduler job would chase the narrow "user has stuck sessions but never opens them and never cancels" case at the cost of constant operational surface — not worth it for this rare scenario.

All paths that mutate chatStatus now also clean the matching Redis state (either inline or via the sweep) — the two layers stay in sync.

Cap + queue safety against double-promotion (carry-over from earlier review)

  • mark_session_completed does release_turn_slot (running → idle) BEFORE dispatch_next_for_user. Two concurrent completions release two slots first, then race the CAS on the same head — only one wins per slot, so at most N promotions for N releases. Cap holds.
  • claim_queued_session is an atomic Postgres UPDATE … WHERE chatStatus='queued' AND id=…: keyed to the specific head row, only one CAS matches.
  • All db.py functions exposed on DatabaseManager return primitives or DTOs (ChatSessionInfo, ChatMessage) — no raw Prisma rows cross the RPC boundary, so the executor subprocess can safely route through chat_db().

…caps)

SECRT-2339: when a user submits beyond the soft running cap (5), persist
the turn into a FIFO queue (DB-backed, on the existing ``ChatMessage``
table via a sparse ``queueStatus`` column) instead of returning 429.
Hard cap at 15 in-flight (running + queued) preserves the abuse
safeguard from SECRT-2335.

The queued task IS the user's chat message — when the dispatcher
promotes it back into the running pool the queue columns clear and the
row becomes an ordinary chat message. Cancelled / blocked queued rows
stay visible to the user with a reason instead of silently disappearing.

Backend changes:

- New ``turn_queue`` module: enqueue / cancel / list / claim / dispatch
  ops over ``ChatMessage`` with the new ``queueStatus``,
  ``queueBlockedReason``, ``queueMetadata``, ``queueStartedAt`` columns.
- ``acquire_turn_slot`` now reads the *running* cap (5, configurable via
  ``Settings.config.max_running_copilot_turns_per_user``); the existing
  ``max_concurrent_copilot_turns_per_user`` (default 15) is repurposed
  as the in-flight cap.
- ``stream_chat_post``: on running-cap rejection, falls through to the
  queue if in-flight < 15, else returns 429 with the new in-flight
  message (``running + queued``).
- ``mark_session_completed``: after releasing a running slot, kicks
  ``dispatch_next_for_user`` to promote the user's oldest queued turn
  (with pre-start re-validation: paywall + per-window USD cap).
- New endpoints: ``GET /chat/queued-tasks`` (list queued + blocked +
  caps), ``DELETE /chat/queued-tasks/{message_id}`` (cancel).
- 7 unit tests for the queue module's state-transition logic.

Migration adds 4 nullable columns + a partial index
``WHERE queueStatus IS NOT NULL`` so the dispatcher's FIFO scan stays
tiny on the hot ChatMessage table.

Frontend wiring follows in a follow-up commit on this branch.
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a DB-backed per-user FIFO queue for AutoPilot chat turns, separates running vs in‑flight caps, enqueues requests when inflight capacity allows, exposes list/cancel API endpoints, auto-promotes queued turns on slot release, and surfaces queued/blocked badges and cancel actions in the UI.

Changes

Chat Turn Queueing Feature

Layer / File(s) Summary
Database Schema & Data Model
autogpt_platform/backend/migrations/20260509120000_add_chat_message_queue_status/migration.sql, autogpt_platform/backend/schema.prisma, autogpt_platform/backend/backend/copilot/model.py
Adds queueStatus, queueBlockedReason, queueMetadata, queueStartedAt to ChatMessage with partial index; Pydantic ChatMessage adds queue_status and queue_blocked_reason; minor Prisma formatting edits.
Turn Limits & Capacity Tracking
autogpt_platform/backend/backend/copilot/active_turns.py
Splits concurrent-turn semantics into running (soft) and in‑flight (hard) limits; adds get_running_turn_limit(), get_inflight_turn_limit(), count_running_turns(), and inflight/running user-facing messages with back‑compat aliases.
Queue Core Implementation
autogpt_platform/backend/backend/copilot/turn_queue.py
New module: status constants; queue inspection (count/list); try_enqueue_turn() with post-insert recount and conditional rollback; enqueue_turn() with queueMetadata under a session lock; cancel_queued_turn(), mark_queued_turn_blocked(), claim_next_queued_turn(), and dispatch_next_for_user() with pre-start validation and rollback semantics; metadata helpers.
API Routes & Request Queueing
autogpt_platform/backend/backend/api/features/chat/routes.py
On ConcurrentTurnLimitError, attempts hard-cap admission and enqueues via _try_enqueue_chat_turn() when below inflight cap (returns empty SSE); on inflight overflow raises HTTP 429 using inflight_turn_limit_message(...). Adds GET /queued-tasks and DELETE /queued-tasks/{message_id} plus QueuedTaskItem/QueuedTaskList models.
Stream Completion & Auto-Dispatch
autogpt_platform/backend/backend/copilot/stream_registry.py
After marking a session completed and releasing the running slot, best-effort calls dispatch_next_for_user(user_id); exceptions are logged and do not affect completion.
Executor Scheduling Wiring
autogpt_platform/backend/backend/copilot/executor/utils.py
Scheduler now acquires capacity using get_inflight_turn_limit() when scheduling non-HTTP turns (passes capacity into acquire_turn_slot).
Settings & Documentation
autogpt_platform/backend/backend/util/settings.py
Clarifies max_running_copilot_turns_per_user (soft running cap) vs max_concurrent_copilot_turns_per_user (in‑flight running+queued) in field descriptions and references the scheduling paths that enforce them.
Backend Tests & Cleanup
autogpt_platform/backend/backend/copilot/turn_queue_test.py, autogpt_platform/backend/backend/executor/billing.py
Adds unit tests for enqueue metadata, cancel atomicity, dispatch blocking and empty-queue behavior; pins status constants; removes an unused import from billing.
Frontend Conversion
autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
Converter skips cancelled rows and exposes queueStatus/queueBlockedReason/rawMessageId in TurnStats for UI usage.
Frontend UI & Tests
autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/..., .../QueueBadge.tsx, .../__tests__/*
Adds QueueBadge component and tests, updates ChatMessagesContainer to render queue badges for queued/blocked user messages, and adds tests for badge rendering and cancellation behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChatAPI
  participant DB
  participant Dispatcher

  Client->>ChatAPI: POST /chat
  ChatAPI->>DB: count_running + count_queued (compute inflight)
  alt inflight < limit
    ChatAPI->>DB: try_enqueue_turn (queueStatus=queued, queueMetadata)
    ChatAPI-->>Client: empty SSE (queued)
  else inflight >= limit
    ChatAPI-->>Client: 429
  end

  Note over Dispatcher,DB: On session completion
  Dispatcher->>DB: dispatch_next_for_user (claim head, validate)
  alt validation passes
    Dispatcher->>DB: clear queueStatus, set queueStartedAt, schedule dispatch
  else validation fails
    Dispatcher->>DB: mark_queued_turn_blocked
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

platform/blocks

Suggested reviewers

  • ntindle
  • Pwuts
  • Swiftyos

"I stacked the turns like carrot rows,
A purple badge for patient prose,
When slots free up the next one goes,
Hop, claim, dispatch — the pipeline flows,
Rabbit claps as the queue unglows."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% 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
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.
Title check ✅ Passed The title 'feat(backend/copilot): AutoPilot task queue with 5 running + 15 in-flight caps' clearly and specifically describes the main change: introduction of a task queue system with defined capacity limits.
Description check ✅ Passed The pull request description thoroughly explains the motivation (replacing blunt 429 responses with a queueing UX), implementation details (soft cap of 5 running + hard cap of 15 in-flight), and technical approach (queue stored in ChatMessage.queueStatus with FIFO promotion logic).

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zamilmajdy/secrt-2339-add-autopilot-task-queue-with-5-concurrent-runs-and-15-in

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 added platform/backend AutoGPT Platform - Back end size/xl labels May 9, 2026
@github-actions

github-actions Bot commented May 9, 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.

🟢 Low Risk — File Overlap Only

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

Summary: 4 conflict(s), 0 medium risk, 8 low risk (out of 12 PRs with file overlap)


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

@codecov

codecov Bot commented May 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.02454% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.64%. Comparing base (60ce5f4) to head (b289439).
⚠️ Report is 1 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #13069      +/-   ##
==========================================
+ Coverage   70.57%   70.64%   +0.07%     
==========================================
  Files        2194     2197       +3     
  Lines      164859   165365     +506     
  Branches    16841    16902      +61     
==========================================
+ Hits       116341   116824     +483     
- Misses      45154    45156       +2     
- Partials     3364     3385      +21     
Flag Coverage Δ
platform-backend 79.63% <92.60%> (+0.05%) ⬆️
platform-frontend 31.60% <80.00%> (+0.28%) ⬆️
platform-frontend-e2e 31.00% <0.00%> (-0.54%) ⬇️

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

Components Coverage Δ
Platform Backend 79.63% <92.60%> (+0.05%) ⬆️
Platform Frontend 37.92% <80.00%> (+0.04%) ⬆️
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.

@majdyz
majdyz marked this pull request as ready for review May 9, 2026 09:57
@majdyz
majdyz requested a review from a team as a code owner May 9, 2026 09:57
@majdyz
majdyz requested review from Pwuts and ntindle and removed request for a team May 9, 2026 09:57
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
The dispatcher claimed a queued ChatMessage row and then routed
through ``schedule_chat_turn`` to schedule it. That helper always
runs ``append_and_save_message`` inside the slot context — which
hit a PK collision on the queued row's existing id, returned None,
and silently dropped the dispatch. Net effect: a queued turn could
be claimed (queueStatus cleared, slot acquired) without ever
actually enqueuing the executor task.

Fix: ``dispatch_next_for_user`` now uses ``acquire_turn_slot`` +
``dispatch_turn`` directly, skipping the redundant message-save
since the row is already in the DB. Also invalidate the chat
session cache on enqueue / dispatch so the frontend's 'Queued'
badge appears + clears in step with the row's queueStatus.
@github-actions github-actions Bot added the platform/frontend AutoGPT Platform - Front end label May 9, 2026

@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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
autogpt_platform/backend/backend/util/settings.py (1)

177-202: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate that the running cap never exceeds the in-flight cap.

The new description says max_running_copilot_turns_per_user must be <= max_concurrent_copilot_turns_per_user, but nothing enforces it. If ops sets running=20 and inflight=15, acquire_turn_slot() will still admit 20 running turns and the hard 15-task safeguard disappears.

Suggested fix
-from pydantic import (
+from pydantic import (
     AliasChoices,
     BaseModel,
     Field,
     PrivateAttr,
     ValidationInfo,
     field_validator,
+    model_validator,
 )
 class Config(UpdateTrackingModel["Config"], BaseSettings):
@@
     max_running_copilot_turns_per_user: int = Field(
         default=5,
         ge=1,
         le=1000,
         description=(
@@
         ),
     )
+
+    `@model_validator`(mode="after")
+    def validate_copilot_turn_caps(self) -> "Config":
+        if (
+            self.max_running_copilot_turns_per_user
+            > self.max_concurrent_copilot_turns_per_user
+        ):
+            raise ValueError(
+                "max_running_copilot_turns_per_user must be <= "
+                "max_concurrent_copilot_turns_per_user"
+            )
+        return self
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@autogpt_platform/backend/backend/util/settings.py` around lines 177 - 202,
Add a Pydantic root validator to enforce that max_running_copilot_turns_per_user
<= max_concurrent_copilot_turns_per_user at model validation time: in the
Settings class (where max_concurrent_copilot_turns_per_user and
max_running_copilot_turns_per_user are defined) implement a
`@root_validator`(pre=False) that reads both fields and raises a ValueError with a
clear message if max_running_copilot_turns_per_user is greater than
max_concurrent_copilot_turns_per_user so misconfigured ops settings are rejected
during startup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/api/features/chat/routes.py`:
- Around line 1216-1242: The route does a TOCTOU by calling
turn_queue.count_inflight_turns() then _enqueue_chat_turn(); instead, implement
an atomic enqueue operation in the queue layer (e.g.,
turn_queue.enqueue_with_hard_cap or similar) that performs the inflight count
check, sequence generation (get_next_sequence), and the insert inside one DB
transaction/lock so the hard-cap admission and per-session sequence assignment
cannot race; update this route to call that new atomic method and remove the
separate count/check logic (keep using get_inflight_turn_limit() only inside the
queue implementation) and ensure the new method returns an explicit
error/exception you translate to HTTP 429 when the hard cap is exceeded.

In `@autogpt_platform/backend/backend/copilot/stream_registry.py`:
- Around line 877-893: The dispatch is happening while the session's
executor/SDK stream locks may still be held; change the call site so
dispatch_next_for_user(user_id) runs only after those session locks are fully
cleared — e.g., ensure release_turn_slot(session_id) actually releases executor
and SDK stream locks or, more robustly, defer the dispatch by scheduling it
(asyncio.create_task) or invoking it from a callback that runs after the locks
are released. Update stream_registry so that release_turn_slot completes lock
teardown before calling dispatch_next_for_user, referencing the functions
release_turn_slot and dispatch_next_for_user to locate and modify the code path.

In `@autogpt_platform/backend/backend/copilot/turn_queue_test.py`:
- Around line 9-13: The test file has import sorting/spacing issues; run the
project's formatter (poetry run format) on
autogpt_platform/backend/backend/copilot/turn_queue_test.py to fix import order
and spacing, then re-check imports (from unittest.mock import AsyncMock,
MagicMock, patch and import pytest / from backend.copilot import turn_queue) are
in the correct sorted groups and commit the formatted file; ensure the commit
passes CI formatting checks before merging.

In `@autogpt_platform/backend/backend/copilot/turn_queue.py`:
- Around line 108-157: The public enqueue_turn signature accepts user_id but
never uses it; fix by either removing the user_id parameter from enqueue_turn
and all callers, or enforce ownership by querying ChatSession (e.g.,
ChatSession.find_first) for a session with id == session_id and userId ==
user_id before creating the ChatMessage; if no session is found, raise an
appropriate error (e.g., ValueError or custom exception) and return early so the
message insert cannot proceed without confirmed ownership. Ensure the check
happens at the start of enqueue_turn (before ChatMessage.prisma().create) and
keep the rest of the function logic unchanged if ownership is confirmed.
- Around line 74-76: The current count_inflight_turns calls count_running_turns
then count_queued_turns which creates a TOCTOU window where a queued→running
promotion can be missed; change the order to call count_queued_turns first and
then count_running_turns so a concurrent promotion is at worst double-counted
(never under-counted), and update the count_inflight_turns docstring to state
the count may briefly be conservatively high but will never read lower than the
true in-flight total (so the hard cap is enforced). Ensure you only modify the
body and docstring of count_inflight_turns and keep calling the existing
count_queued_turns and count_running_turns helpers.
- Around line 350-362: The two helper functions _generate_id and _utcnow
currently perform stdlib imports inside the function body; hoist the imports for
uuid and datetime/timezone to module top-level instead and remove the inner
imports so _generate_id simply returns str(uuid.uuid4()) and _utcnow returns
datetime.now(timezone.utc); keep the local imports in dispatch_next_for_user
(executor/rate-limit pipeline) untouched as they are intentionally guarded.
- Around line 178-191: mark_queued_turn_blocked currently unconditionally
updates the row and can overwrite a user-set "cancelled" status; change the
update to an atomic guarded update by using
ChatMessage.prisma().update_many(where={"id": message_id, "queueStatus":
STATUS_QUEUED}, data={"queueStatus": STATUS_BLOCKED, "queueBlockedReason":
reason}) so the transition only occurs if the row is still queued, remove the
except RecordNotFoundError branch (it becomes unnecessary), and delete the
now-unused RecordNotFoundError import; this preserves user cancellations (see
mark_queued_turn_blocked and related cancel_queued_turn/dispatch_next_for_user
flows).
- Around line 311-333: The rollback after schedule_chat_turn can re-queue a
ChatMessage even when enqueue_copilot_turn already published a RabbitMQ task,
causing duplicate execution; to fix, add a message_id field to
CoPilotExecutionEntry and propagate the ChatMessage.id when calling
enqueue_copilot_turn/dispatch_turn/schedule_chat_turn so the worker can use that
message_id as an idempotency key (store it in CoPilotExecutionEntry and check it
against active/completed entries before starting work), and tighten the rollback
only to cases before publish by ensuring enqueue_copilot_turn returns
success/failure deterministically (or throw a clearly documented
PrePublishError) so the exception handler only retries on true pre-publish
failures; update ChatMessage.prisma().update_many usage only for genuine
pre-publish exceptions.

In
`@autogpt_platform/backend/migrations/20260509120000_add_chat_message_queue_status/migration.sql`:
- Around line 1-13: The migration fails on fresh DB because it alters
"platform"."ChatMessage" before ensuring the platform schema exists; update
migration.sql to first create the schema if it doesn't exist (e.g., run a CREATE
SCHEMA IF NOT EXISTS "platform" or equivalent) before the ALTER TABLE
"platform"."ChatMessage" statement and before creating the
"ChatMessage_queue_dispatch_idx" index so both the table alteration and index
creation succeed on an empty DB.

---

Outside diff comments:
In `@autogpt_platform/backend/backend/util/settings.py`:
- Around line 177-202: Add a Pydantic root validator to enforce that
max_running_copilot_turns_per_user <= max_concurrent_copilot_turns_per_user at
model validation time: in the Settings class (where
max_concurrent_copilot_turns_per_user and max_running_copilot_turns_per_user are
defined) implement a `@root_validator`(pre=False) that reads both fields and
raises a ValueError with a clear message if max_running_copilot_turns_per_user
is greater than max_concurrent_copilot_turns_per_user so misconfigured ops
settings are rejected during startup.
🪄 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: 0f457917-b25c-4b61-9ba5-76b4995a1bd3

📥 Commits

Reviewing files that changed from the base of the PR and between 2624b6f and 5421cf4.

📒 Files selected for processing (10)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/executor/billing.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/migrations/20260509120000_add_chat_message_queue_status/migration.sql
  • autogpt_platform/backend/schema.prisma
📜 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). (1)
  • GitHub Check: Seer Code Review
🧰 Additional context used
📓 Path-based instructions (6)
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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
autogpt_platform/{backend,autogpt_libs}/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/turn_queue_test.py
autogpt_platform/backend/schema.prisma

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

Run database migrations with 'poetry run prisma migrate dev' and 'poetry run prisma generate' after schema changes in backend

Files:

  • autogpt_platform/backend/schema.prisma
autogpt_platform/backend/backend/api/features/**/*.py

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

Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
autogpt_platform/backend/**/api/**/*.py

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

autogpt_platform/backend/**/api/**/*.py: Use Security() instead of Depends() for authentication dependencies to get proper OpenAPI security specification
Follow SSE (Server-Sent Events) protocol: use data: lines for frontend-parsed events (must match Zod schema) and : comment lines for heartbeats/status

Files:

  • autogpt_platform/backend/backend/api/features/chat/routes.py
🧠 Learnings (10)
📚 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/migrations/20260509120000_add_chat_message_queue_status/migration.sql
  • autogpt_platform/backend/backend/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/schema.prisma
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.

Applied to files:

  • autogpt_platform/backend/backend/executor/billing.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/util/settings.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/stream_registry.py
  • autogpt_platform/backend/backend/copilot/model.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
🪛 GitHub Actions: AutoGPT Platform - Backend CI / 3_test (3.11).txt
autogpt_platform/backend/schema.prisma

[error] 1-1: Prisma migrate deploy failed with error P3018. Migration 20260509120000_add_chat_message_queue_status could not be applied because the PostgreSQL schema "platform" does not exist (Database error code: 3F000; ERROR: schema "platform" does not exist; detail from Postgres namespace.c line 3096).

🪛 GitHub Actions: AutoGPT Platform - Backend CI / 6_lint.txt
autogpt_platform/backend/backend/copilot/turn_queue_test.py

[error] 11-12: Imports are incorrectly sorted and/or formatted (linting failure). Lint tool reported a formatting/sorting diff near a removed blank line after 'from backend.copilot import turn_queue'.

🪛 GitHub Actions: AutoGPT Platform - Backend CI / lint
autogpt_platform/backend/backend/copilot/turn_queue_test.py

[error] 11-11: Lint failed: file has incorrectly sorted and/or formatted imports (isort/ruff formatting).

🔇 Additional comments (1)
autogpt_platform/backend/backend/executor/billing.py (1)

27-27: Import cleanup looks correct.

discord_send_alert removal is safe here, and keeping DiscordChannel is required by its usages in the alert paths.

Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py
Comment thread autogpt_platform/backend/backend/copilot/stream_registry.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/turn_queue_test.py
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
Two concurrent submits to the same chat session could race on
get_next_sequence (a SELECT MAX + 1) and PK-collide on
(sessionId, sequence) because enqueue_turn was bypassing the
Redis NX session lock that append_and_save_message uses for
exactly this reason.

Take the lock + re-fetch the sequence inside it, matching the
existing ordering guarantee. Drop the sequence parameter on
enqueue_turn since the caller's pre-fetch is now redundant
(authoritative value is whatever the lock holder sees).
Comment thread autogpt_platform/backend/backend/copilot/active_turns.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/active_turns.py Outdated

@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

🧹 Nitpick comments (1)
autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx (1)

40-54: 💤 Low value

Consider deduplicating session query invalidation logic.

Both the 204 and 404 branches conditionally invalidate the session query with identical logic (lines 40-44 and 50-54). Extract into a helper or unconditionally invalidate after the status checks to reduce duplication.

♻️ Optional refactor
 onSuccess: (response) => {
+  const shouldInvalidateSession = response.status === 204 || response.status === 404;
+  
   if (response.status === 204) {
-    if (sessionID) {
-      queryClient.invalidateQueries({
-        queryKey: getGetV2GetSessionQueryKey(sessionID),
-      });
-    }
     queryClient.invalidateQueries({
       queryKey: ["/api/chat/queued-tasks"],
     });
-  } else if (response.status === 404) {
-    // Already promoted / not owned — refetch to sync UI with reality.
-    if (sessionID) {
-      queryClient.invalidateQueries({
-        queryKey: getGetV2GetSessionQueryKey(sessionID),
-      });
-    }
   }
+  
+  if (shouldInvalidateSession && sessionID) {
+    queryClient.invalidateQueries({
+      queryKey: getGetV2GetSessionQueryKey(sessionID),
+    });
+  }
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
around lines 40 - 54, Duplicate conditional invalidation of the session query
appears in the 204 and 404 response branches in QueueBadge.tsx; consolidate by
extracting the logic that calls queryClient.invalidateQueries({ queryKey:
getGetV2GetSessionQueryKey(sessionID) }) into a small helper (e.g.,
invalidateSessionIfPresent(sessionID)) or by moving a single conditional
invalidate after the response.status checks so you only call
queryClient.invalidateQueries once; update uses in both branches to call the
helper (or remove the branch-specific calls) and keep the call to
queryClient.invalidateQueries({ queryKey: ["/api/chat/queued-tasks"] }) where
appropriate.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx:
- Around line 32-33: Remove the redundant cleanup() call from the test file's
afterEach block (the afterEach wrapper calling cleanup); testing-library/react
cleanup is already run globally so simply delete that afterEach(...) or its
cleanup() invocation and only add a local afterEach teardown when you need to
restore resources not handled globally (e.g., clear/fake timers with
vi.useFakeTimers()/vi.restoreAllMocks() or vi.clearAllTimers()).

In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx:
- Line 14: In QueueBadge (QueueBadge.tsx) replace the deprecated bare Phosphor
imports with the -Icon suffixed aliases: change imports of Hourglass,
WarningCircle, and XCircle from "@phosphor-icons/react" to HourglassIcon,
WarningCircleIcon, and XCircleIcon respectively, and update all JSX usages of
<Hourglass>, <WarningCircle>, and <XCircle> to <HourglassIcon>,
<WarningCircleIcon>, and <XCircleIcon> (retain the same props/attributes).

---

Nitpick comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx:
- Around line 40-54: Duplicate conditional invalidation of the session query
appears in the 204 and 404 response branches in QueueBadge.tsx; consolidate by
extracting the logic that calls queryClient.invalidateQueries({ queryKey:
getGetV2GetSessionQueryKey(sessionID) }) into a small helper (e.g.,
invalidateSessionIfPresent(sessionID)) or by moving a single conditional
invalidate after the response.status checks so you only call
queryClient.invalidateQueries once; update uses in both branches to call the
helper (or remove the branch-specific calls) and keep the call to
queryClient.invalidateQueries({ queryKey: ["/api/chat/queued-tasks"] }) where
appropriate.
🪄 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: 1a45a47b-fcf0-4bde-a2f7-3acd405ff02d

📥 Commits

Reviewing files that changed from the base of the PR and between 5421cf4 and f0cfaf3.

📒 Files selected for processing (9)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
✅ Files skipped from review due to trivial changes (1)
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/turn_queue.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: integration_test
  • GitHub Check: end-to-end tests
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (16)
autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}

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

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Use Node.js 21+ with pnpm package manager for frontend development
Always run 'pnpm format' for formatting and linting code in frontend development

Format frontend code using pnpm format

autogpt_platform/frontend/**/*.{ts,tsx,js,jsx}: Fully capitalize acronyms in symbols, e.g. graphID, useBackendAPI
No linter suppressors (// @ts-ignore``, // eslint-disable) — fix the actual issue

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.{tsx,ts}

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

autogpt_platform/frontend/**/*.{tsx,ts}: Use function declarations for components and handlers (not arrow functions) in React components
Only use arrow functions for small inline lambdas (map, filter, etc.) in React components
Use PascalCase for component names and camelCase with 'use' prefix for hook names in React
Use Tailwind CSS utilities only for styling in frontend components
Use design system components from 'src/components/' (atoms, molecules, organisms) in frontend development
Never use 'src/components/legacy/' in frontend code
Only use Phosphor Icons (@phosphor-icons/react) for icons in frontend components
Use generated API hooks from '@/app/api/generated/endpoints/' instead of deprecated 'BackendAPI' or 'src/lib/autogpt-server-api/
'
Use React Query for server state (via generated hooks) in frontend development
Default to client components ('use client') in Next.js; only use server components for SEO or extreme TTFB needs
Use '' component for rendering errors in frontend UI; use toast notifications for mutation errors; use 'Sentry.captureException()' for manual exceptions
Separate render logic from data/behavior in React components; keep comments minimal (code should be self-documenting)

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.{ts,tsx}

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

autogpt_platform/frontend/**/*.{ts,tsx}: No barrel files or 'index.ts' re-exports in frontend code
Regenerate API hooks with 'pnpm generate:api' after backend OpenAPI spec changes in frontend development

autogpt_platform/frontend/**/*.{ts,tsx}: Use function declarations (not arrow functions) for components/handlers
No any types unless the value genuinely can be anything
Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/src/**/*.{ts,tsx}: Use generated API hooks from @/app/api/__generated__/endpoints/ following the pattern use{Method}{Version}{OperationName}, and regenerate with pnpm generate:api
Separate render logic from business logic using component.tsx + useComponent.ts + helpers.ts pattern, colocate state when possible and avoid creating large components, use sub-components in local /components folder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not use useCallback or useMemo unless asked to optimise a given function

autogpt_platform/frontend/src/**/*.{ts,tsx}: Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this
Use generated API hooks from @/app/api/__generated__/endpoints/ with pattern use{Method}{Version}{OperationName}
Always import the -Icon-suffixed alias from @phosphor-icons/react (e.g. TrashIcon, PlusIcon, SquareIcon) — bare exports are deprecated
Do not use useCallback or useMemo unless asked to optimize a given function
Never use src/components/__legacy__/* — use design system components from src/components/

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Tailwind CSS only for styling, use design tokens, and use Phosphor Icons only

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
autogpt_platform/frontend/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Component props should use interface Props { ... } (not exported) unless the interface needs to be used outside the component

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
autogpt_platform/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never type with any, if no types available use unknown

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx}: Use Vitest + RTL + MSW for integration tests as the primary testing approach (~90%, page-level), use Playwright for E2E critical flows, and use Storybook for design system components
Run frontend integration tests with pnpm test:unit (Vitest + RTL + MSW)

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.{tsx,jsx}

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

autogpt_platform/frontend/**/*.{tsx,jsx}: No dark: Tailwind classes — the design system handles dark mode
Use Next.js <Link> for internal navigation — never raw <a> tags
Use Tailwind CSS only for styling with design tokens and Phosphor Icons only

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
autogpt_platform/frontend/src/**/components/**/*.{tsx,jsx}

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

Put sub-components in local components/ folder; component props should be type Props = { ... } (not exported) unless used outside the component

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
autogpt_platform/frontend/src/**/components/**/*.{ts,tsx}

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

Structure components as ComponentName/ComponentName.tsx + useComponentName.ts + helpers.ts

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
autogpt_platform/frontend/src/app/**/__tests__/**/*.{test,spec}.{ts,tsx}

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

Write integration tests in __tests__/ next to page.tsx using Vitest + RTL + MSW for new pages/features

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/src/**/__tests__/**/*.{test,spec}.{ts,tsx}

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

Use Orval-generated MSW handlers from @/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts for API mocking

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/src/**/*.{ts,tsx,js,jsx}

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

Avoid index and barrel files

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

No barrel files or index.ts re-exports in the frontend

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
autogpt_platform/frontend/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Do not type hook returns, let Typescript infer as much as possible

autogpt_platform/frontend/src/**/*.ts: Extract component logic into custom hooks grouped by concern, not by component, with each hook in its own .ts file
Do not type hook returns; let TypeScript infer as much as possible

Files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
🧠 Learnings (11)
📚 Learning: 2026-02-27T10:45:49.499Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx:23-24
Timestamp: 2026-02-27T10:45:49.499Z
Learning: Prefer using generated OpenAPI types from '@/app/api/__generated__/' for payloads defined in openapi.json (e.g., MCPToolsDiscoveredResponse, MCPToolOutputResponse). Use inline TypeScript interfaces only for payloads that are SSE-stream-only and not exposed via OpenAPI. Apply this pattern to frontend tool components (e.g., RunMCPTool) and related areas where similar SSE/openapi-discrepancies occur; avoid re-implementing types when a generated type is available.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
📚 Learning: 2026-03-24T02:05:04.672Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12526
File: autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx:0-0
Timestamp: 2026-03-24T02:05:04.672Z
Learning: When gating React component logic on a React Query result (e.g., hooks like `useQuery` / `useGetV2GetCopilotUsage`), prefer destructuring and checking `isSuccess` (or aliasing it to a meaningful boolean like `isSuccess: hasUsage`) instead of relying on `!isLoading`. Reason: `isLoading` can be `false` in error/idle states where `data` may still be `undefined`, while `isSuccess` indicates the query completed successfully and `data` is populated.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
📚 Learning: 2026-03-24T02:23:31.305Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12526
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/RateLimitResetDialog/RateLimitResetDialog.tsx:0-0
Timestamp: 2026-03-24T02:23:31.305Z
Learning: In the Copilot platform UI code, follow the established Orval hook `onError` error-handling convention: first explicitly detect/handle `ApiError`, then read `error.response?.detail` (if present) as the primary message; if not available, fall back to `error.message`; and finally fall back to a generic string message. This convention should be used for generated Orval hooks even if the custom Orval mutator already maps details into `ApiError.message`, to keep consistency across hooks/components (e.g., `useCronSchedulerDialog.ts`, `useRunGraph.ts`, and rate-limit/reset flows).

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
📚 Learning: 2026-03-31T14:04:42.444Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12623
File: autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/ChatInput.tsx:172-177
Timestamp: 2026-03-31T14:04:42.444Z
Learning: In the Copilot frontend components under autogpt_platform/frontend/src/app/(platform)/copilot/, Tailwind dark mode variants (e.g., `dark:*`) are intentional and should be allowed. Do not flag `dark:` utilities in these Copilot UI components as incorrect; they are used to ensure proper contrast and correct behavior in both light and dark themes.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
📚 Learning: 2026-04-01T18:54:16.035Z
Learnt from: Bentlybro
Repo: Significant-Gravitas/AutoGPT PR: 12633
File: autogpt_platform/frontend/src/app/(platform)/library/components/AgentFilterMenu/AgentFilterMenu.tsx:3-10
Timestamp: 2026-04-01T18:54:16.035Z
Learning: In the frontend, the legacy Select component at `@/components/__legacy__/ui/select` is an intentional, codebase-wide visual-consistency pattern. During code reviews, do not flag or block PRs merely for continuing to use this legacy Select. If a migration to the newer design-system Select is desired, bundle it into a single dedicated cleanup/migration PR that updates all Select usages together (e.g., avoid piecemeal replacements).

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
📚 Learning: 2026-04-07T09:24:16.582Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12686
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/__tests__/PainPointsStep.test.tsx:1-19
Timestamp: 2026-04-07T09:24:16.582Z
Learning: In Significant-Gravitas/AutoGPT’s `autogpt_platform/frontend` (Vite + `vitejs/plugin-react` with the automatic JSX transform), do not flag usages of React types/components (e.g., `React.ReactNode`) in `.ts`/`.tsx` files as missing `React` imports. Since the React namespace is made available by the project’s TS/Vite setup, an explicit `import React from 'react'` or `import type { ReactNode } ...` is not required; only treat it as missing if typechecking (e.g., `pnpm types`) would actually fail.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
📚 Learning: 2026-04-02T05:43:49.128Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12640
File: autogpt_platform/frontend/src/app/(no-navbar)/onboarding/steps/WelcomeStep.tsx:13-13
Timestamp: 2026-04-02T05:43:49.128Z
Learning: Do not flag `import { Question } from "phosphor-icons/react"` as an invalid import. `Question` is a valid named export from `phosphor-icons/react` (as reflected in the package’s generated `.d.ts` files and re-exports via `dist/index.d.ts`), so it should be treated as a supported named export during code reviews.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
📚 Learning: 2026-04-13T13:11:07.445Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12764
File: autogpt_platform/frontend/src/app/(platform)/library/components/SitrepItem/SitrepItem.tsx:143-145
Timestamp: 2026-04-13T13:11:07.445Z
Learning: In `autogpt_platform/frontend`, do not flag direct interpolation of `executionID` UUID strings into URL query parameters (e.g., `activeItem=${executionID}` in JSX/Next links). If the value is a UUID string matching `[0-9a-f-]`, it contains no reserved URL characters, so additional `encodeURIComponent` or Next.js object-based `href` encoding is unnecessary. Only treat it as an encoding issue if the query-param value is not guaranteed to be UUID-formatted (i.e., may include characters outside `[0-9a-f-]`).

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
📚 Learning: 2026-04-15T22:49:06.896Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11235
File: autogpt_platform/frontend/src/app/(platform)/admin/diagnostics/components/ExecutionsTable.tsx:0-0
Timestamp: 2026-04-15T22:49:06.896Z
Learning: In the AutoGPT frontend (React Query + toast/ErrorCard patterns), do not require `Sentry.captureException` in React Query mutation `catch` blocks. React Query handles error propagation for mutation paths, so follow the established pattern: show toast notifications for mutation errors and use `ErrorCard` for render/fetch errors. Only add `Sentry.captureException` for truly manual/unexpected exception paths that are outside React Query’s control (e.g., standalone async utilities or event handlers not wired through React Query).

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
📚 Learning: 2026-04-20T13:17:39.951Z
Learnt from: 0ubbe
Repo: Significant-Gravitas/AutoGPT PR: 12854
File: autogpt_platform/frontend/src/app/(platform)/library/__tests__/briefing.test.tsx:84-84
Timestamp: 2026-04-20T13:17:39.951Z
Learning: In the AutoGPT frontend, `testing-library/react` cleanup is already handled globally after each test via `src/tests/integrations/vitest.setup.tsx`. Therefore, for integration test files under `__tests__/`, do NOT add redundant `afterEach(() => cleanup())`. Only add local `afterEach` teardown for resources that are not covered globally—specifically, when using fake timers, add `afterEach(() => vi.useRealTimers())` (or equivalent) to restore real timers and prevent cross-test interference.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
📚 Learning: 2026-04-20T20:07:22.981Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11235
File: autogpt_platform/frontend/src/app/(platform)/admin/diagnostics/__tests__/ExecutionsTable.test.tsx:27-76
Timestamp: 2026-04-20T20:07:22.981Z
Learning: In this codebase, Orval-generated API modules under `src/app/api/__generated__/` are not committed to git and must be generated via `pnpm generate:api` (requires a running backend). In integration tests, it’s acceptable—and expected—to stub generated hooks/modules by mocking them with `vi.mock("@/app/api/__generated__/endpoints/{tag}/{tag}")`. Do not treat `vi.mock` of these generated hook modules as a violation of the MSW handler guideline, since the corresponding MSW handlers cannot be imported at test time when generated files are absent.

Applied to files:

  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts
🔇 Additional comments (7)
autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts (3)

7-17: LGTM! Queue lifecycle fields are well-typed and documented.

The new TurnStats fields (queueStatus, queueBlockedReason, rawMessageId) are properly nullable and include clear JSDoc explaining their purpose for rendering queue badges in the UI.


279-284: LGTM! Cancelled message filtering preserves audit trail while cleaning up UI.

Filtering queue_status: "cancelled" messages from the conversation view is the correct behavior—they remain in the DB for audit/logging, but don't clutter the chat after the user clicks cancel.


424-431: LGTM! Queue stats correctly scoped to user messages.

Queue lifecycle fields (queueStatus, queueBlockedReason, rawMessageId) are only populated for user-role messages, which matches the design—only user messages can be queued.

autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts (1)

388-466: LGTM! Comprehensive test coverage for queue lifecycle.

The new test suite validates all queue states:

  • Queued messages populate queueStatus and rawMessageId
  • Blocked messages include queueBlockedReason
  • Cancelled messages are filtered from the conversation view
  • Normal messages leave queue fields null
autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (1)

565-585: LGTM! Clean QueueBadge integration for user messages.

The conditional rendering correctly:

  • Checks queueStatus from turnStats using an IIFE
  • Only renders for "queued" or "blocked" states
  • Passes all required props (queueStatus, queueBlockedReason, rawMessageId, sessionID)
  • Wraps in MessageActions with data-testid for test coverage
autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx (1)

541-642: LGTM! Comprehensive test coverage for queue badge integration.

The new test suite validates:

  • Queued badge renders with correct queueStatus and rawMessageId attributes
  • Blocked badge includes queueBlockedReason
  • No badge renders for normal (non-queued) user messages

The mock QueueBadge exposes props as data attributes for easy assertion.

autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx (1)

38-55: Endpoint not found in OpenAPI spec.

The useDeleteV2CancelQueuedTask hook references an endpoint that does not appear in the OpenAPI specification (autogpt_platform/frontend/src/app/api/openapi.json). No DELETE endpoint exists for canceling queued tasks. The test file mocks the response with { status: number }, but without the actual endpoint definition or generated hook, the actual response shape cannot be verified. Add the endpoint to the OpenAPI spec and regenerate hooks with pnpm generate:api to ensure the response type is correctly typed.

OnboardingProvider in the integration test wrapper calls useToast(),
so the partial mock that only exported `toast` was failing the suite
with "No 'useToast' export is defined". Add the missing export.
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
- atomic try_enqueue_turn with optimistic post-insert recount + rollback
  closes the inflight-cap TOCTOU window from the route check + insert
- mark_queued_turn_blocked guards on queueStatus='queued' so a parallel
  user cancel isn't silently overwritten with 'blocked'
- count_inflight_turns counts queued first then running, biasing toward
  conservative over-count under burst load (cap never reads low)
- enqueue_turn: drop unused user_id; route validates session ownership
  upstream
- stream_registry: dispatch_next_for_user fires AFTER cluster + SDK
  stream lock cleanup so the promoted turn doesn't race stale locks
- hoist uuid + datetime imports to module top
- minor: clean up the awkward two-string concat in the paywall reason

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/turn_queue.py`:
- Around line 244-277: Both cancel_queued_turn and mark_queued_turn_blocked
update queueStatus but do not call invalidate_session_cache, causing stale UI
state; fix cancel_queued_turn to obtain the affected sessionId (either accept
session_id as a parameter from the route or perform an update-then-read inside a
transaction that updates via ChatMessage.prisma().update_many / update and
returns the Session.id) and then call invalidate_session_cache(session_id) when
the update actually affected rows, and fix mark_queued_turn_blocked to read the
row after the update (or use an update that returns the Session.id) to get
sessionId and call invalidate_session_cache(session_id) whenever the update
changes queueStatus from STATUS_QUEUED to STATUS_BLOCKED; keep existing guard on
STATUS_QUEUED and only invalidate when updated_count > 0.
🪄 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: 82017b5f-b498-4227-911e-811a50e425ae

📥 Commits

Reviewing files that changed from the base of the PR and between 5f10890 and fc07db5.

📒 Files selected for processing (4)
  • autogpt_platform/backend/backend/api/features/chat/routes.py
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
✅ Files skipped from review due to trivial changes (1)
  • autogpt_platform/backend/backend/copilot/turn_queue_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • autogpt_platform/backend/backend/copilot/stream_registry.py
  • autogpt_platform/backend/backend/api/features/chat/routes.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). (14)
  • GitHub Check: check API types
  • GitHub Check: lint
  • GitHub Check: integration_test
  • GitHub Check: Seer Code Review
  • GitHub Check: test (3.12)
  • GitHub Check: type-check (3.13)
  • GitHub Check: type-check (3.11)
  • GitHub Check: test (3.11)
  • GitHub Check: type-check (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: Analyze (python)
  • GitHub Check: end-to-end tests
  • GitHub Check: Analyze (typescript)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (2)
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/turn_queue.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/turn_queue.py
🧠 Learnings (10)
📚 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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.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/turn_queue.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/turn_queue.py

Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
majdyz added 2 commits May 9, 2026 17:55
… session-busy check

- acquire_turn_slot accepts a `capacity` parameter; HTTP keeps the
  running cap (5) so it falls through to the queue, non-HTTP callers
  (schedule_turn / run_sub_session / AutoPilotBlock) pass the inflight
  cap (15) to preserve the prior #13064 behaviour
- ConcurrentTurnLimitError default message now describes the running
  cap; the back-compat shim points at running_turn_limit_message so
  uncaught raises on non-HTTP paths surface the right number
- dispatch_next_for_user skips queued heads whose session already has a
  running turn — otherwise acquire_turn_slot returns REFRESHED, two
  turns share a slot, and the first turn's release frees both
- frontend QueueBadge: switch to `*Icon`-suffixed Phosphor imports;
  drop redundant testing-library cleanup() (already global)
…ueue migration

Other ChatMessage migrations (20260115081736_add_chat_tables,
20260326120000_add_chat_message_duration_ms) target unqualified
"ChatMessage", and 97c6516 explicitly removed the multiSchema /
@@Schema("platform") pattern from this codebase. Keeping the prefix
caused P3018 / "schema platform does not exist" on every test run.

@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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/copilot/active_turns.py`:
- Around line 159-199: The current Redis-exception handling in
get_running_session_ids and count_running_turns collapses "unknown" into empty
results; change their error-path behavior to surface failure explicitly by
returning None instead of set() / 0 and update their type hints to
Optional[set[str]] and Optional[int] (and update the docstrings) so callers can
detect an unknown state; in the except block for
RedisError/RedisClusterException/ConnectionError/OSError, keep the
logger.warning with the exception details but return None, and then update the
dispatcher/queue code that calls get_running_session_ids and count_running_turns
to treat None as "unknown — skip promoting/adjust enforcing" rather than
treating it as zero.

In `@autogpt_platform/backend/backend/copilot/executor/utils.py`:
- Around line 330-332: The current use of acquire_turn_slot(user_id, session_id,
capacity=get_inflight_turn_limit()) only expands the running-slot budget and
ignores queued turns, so replace this admission logic with the same
running+queued check used by the chat route (or add an explicit check against
the user's running set + queued set in Redis) before entering acquire_turn_slot;
specifically, call the chat-route admission function (or replicate its Redis
queries) to compute current_running + current_queued and deny/schedule
accordingly, and only call acquire_turn_slot when that combined count is below
get_inflight_turn_limit(); update schedule_turn and any non-HTTP callers to use
this same admission check to enforce the global per-user hard cap.
🪄 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: 0a96ce93-0e0a-407e-ae1b-8a8b8d72bbed

📥 Commits

Reviewing files that changed from the base of the PR and between fc07db5 and f925093.

📒 Files selected for processing (5)
  • autogpt_platform/backend/backend/copilot/active_turns.py
  • autogpt_platform/backend/backend/copilot/executor/utils.py
  • autogpt_platform/backend/backend/copilot/turn_queue.py
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/QueueBadge.test.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/tests/QueueBadge.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/QueueBadge.tsx
  • autogpt_platform/backend/backend/copilot/turn_queue.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). (14)
  • GitHub Check: integration_test
  • GitHub Check: lint
  • GitHub Check: check API types
  • GitHub Check: Seer Code Review
  • GitHub Check: end-to-end tests
  • GitHub Check: Analyze (typescript)
  • GitHub Check: type-check (3.13)
  • GitHub Check: Analyze (python)
  • GitHub Check: test (3.11)
  • GitHub Check: type-check (3.12)
  • GitHub Check: test (3.12)
  • GitHub Check: type-check (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (2)
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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
🧠 Learnings (10)
📚 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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.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/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.py
📚 Learning: 2026-05-07T18:48:14.242Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 13040
File: autogpt_platform/backend/backend/blocks/llm.py:0-0
Timestamp: 2026-05-07T18:48:14.242Z
Learning: In this repository, isort may split imports from the same module into separate blocks when some imports are aliased (e.g., `from module import X as Y`) and others are not. Preserve the two-block layout when it results from isort (such as keeping `from openai.types.chat import ChatCompletion as OpenAIChatCompletion` separate from non-aliased imports from `openai.types.chat`). Do not treat that split as a style issue during review; merging them into a single block can fail CI with `Imports are incorrectly sorted and/or formatted`.

Applied to files:

  • autogpt_platform/backend/backend/copilot/executor/utils.py
  • autogpt_platform/backend/backend/copilot/active_turns.py

Comment thread autogpt_platform/backend/backend/copilot/active_turns.py Outdated
Comment thread autogpt_platform/backend/backend/copilot/executor/utils.py
…heck on schedule_turn

- cancel_queued_turn / mark_queued_turn_blocked now invalidate the
  session cache after a successful status transition so the frontend
  drops the 'Queued' badge on its next refetch
- schedule_turn pre-checks running + queued against the inflight cap
  so non-HTTP callers (run_sub_session / AutoPilotBlock) honour the
  per-user 15-task ceiling — passing capacity to acquire_turn_slot
  alone only widens the running-slot budget, queued turns from the
  HTTP route would still be invisible to the slot pool
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py Outdated
…dundant index

Round 4 review caught a real Prisma leak: the slot-free hook in
``mark_session_completed`` is invoked from
``backend/copilot/executor/processor.py:91, 578`` — the CoPilotExecutor
subprocess, which has NO direct Prisma connection. The dispatcher
chain ``mark_session_completed → dispatch_next_for_user →
list_queued_sessions → copilot_db.list_chat_sessions_by_status`` hit a
raw ``PrismaChatSession.prisma()`` call in that subprocess and threw,
so queued sessions never promoted when the completion fired from the
executor side (i.e. most turns).

Fix: ``list_chat_sessions_by_status`` now returns
``list[ChatSessionInfo]`` (``from_db``-converted) so the response
serializer can pass it across the DatabaseManager RPC boundary. The
function is re-exposed on ``DatabaseManager`` /
``DatabaseManagerAsyncClient``. ``turn_queue.list_queued_sessions``
and ``active_turns.get_running_session_ids`` route through ``chat_db()``
again and read ``.session_id`` (the Pydantic field) instead of ``.id``.

Index consolidation (per review): drop the redundant
``[userId, updatedAt]`` 2-col index and let
``[userId, chatStatus, updatedAt]`` cover all three ChatSession query
shapes — cap-count, queue-list with ORDER BY, and the sidebar list
``WHERE userId ORDER BY updatedAt``. The sidebar path sorts in memory
across chatStatus sub-buckets per user; at typical per-user N (≤100s
of sessions) the in-memory sort is negligible compared to maintaining
a parallel index. Migration drops the existing index in the same
migration that adds the 3-col one.

Tests: ``turn_queue_test``'s ``_patch_queued_list`` now patches
``list_queued_sessions`` directly (independent of how chat_db()
resolves); ``_mock_session`` uses ChatSessionInfo's ``.session_id`` /
``.updated_at`` field names. ``db_test`` constructs real
``PrismaChatSession`` rows and asserts on the converted app-model
fields. 180 tests pass; pyright clean.
Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py
@majdyz

majdyz commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

/pr-test --fix Round 2 — Post-Polish Verification

Commit tested: 6273f5332f (latest after /pr-polish convergence)
Stack: native (poetry run app + pnpm dev), MAX_RUNNING_COPILOT_TURNS_PER_USER=1, CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true

Scenarios

# Scenario Result
1 RPC: list_chat_sessions_by_status returns ChatSessionInfo (not Prisma) PASS
2 Real queue + dispatch live flow (cap=1, S1 running → S2 queues → S1 done → S2 promoted → S2 done) PASS
3 No Prisma object found errors in any log during dispatcher path PASS

Scenario 1 — RPC type safety

Invoked DatabaseManagerAsyncClient().list_chat_sessions_by_status(...) directly from a Python REPL outside the API process. Returned list[ChatSessionInfo] (Pydantic app model), accessed .session_id and .chat_status cleanly. Previously this was the Round-4 blocker: returning raw PrismaChatSession would throw "Service methods must return application models, not Prisma objects" from the RPC serializer.

Scenario 2 — Real queue + dispatcher live

Sent two HTTP /stream requests racing against the soft cap (set to 1 for fast verification):

  • t=0: POST S1 with mode=extended_thinking, model=advanced — long-running prompt. Acquires the cap; chatStatus → running.
  • t=0.3s: POST S2 with mode=fast, model=standard. Cap full → ConcurrentTurnLimitError → falls through to try_enqueue_turn → message persisted + chatStatus → queued.
  • S1 completes → mark_session_completedrelease_turn_slotdispatch_next_for_user → CAS-claims S2 → dispatches S2 turn.
  • S2 completes → settles to idle.

Final DB state: all 3 sessions back to idle. Both S1 and S2 produced real assistant messages.

Scenario 3 — Dispatcher path stays RPC-safe

The dispatcher chain (mark_session_completed → dispatch_next_for_user → list_queued_sessions) runs from the CoPilotExecutor subprocess on every completion. With the Round-4 fix, every DB read returns either a primitive (bool/int/str) or a Pydantic DTO (ChatSessionInfo, ChatMessage). Scenario 2 exercised this path end-to-end without producing any Prisma object found log entries or completion-hook failure messages.

Summary

  • 3/3 scenarios PASS against 6273f5332f.
  • The Round-4 Prisma-leak fix is verified live: the executor-process completion → dispatcher → queue-list path returns app-model DTOs across the RPC boundary, and a real queued session was promoted + completed end-to-end.
  • No new bugs found. CI: 41 pass / 3 skip / 0 fail. PR is merge-ready.

Replace the manual reverse for-loop with ``messages.findLastIndex``
(ES2023, already in use in ``useCopilotPage`` and
``ChatMessagesContainer/helpers``). Same behaviour, one line.

Also widen the root .gitignore: ``.ign/*`` only caught contents of an
``.ign/`` directory; the test convention also uses tee'd files like
``.ign.application.logs`` and lock files like ``.ign.testing.lock``.
Replaced with ``.ign*`` + ``**/.ign*`` so any path component starting
with ``.ign`` is ignored.
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py
majdyz added 2 commits May 11, 2026 14:26
The lazy-import comment in ``mark_session_completed`` claimed the
chain ``turn_queue → executor.utils → stream_registry`` would be
circular at module load.  It's not: ``turn_queue``'s only path back
into ``stream_registry``-adjacent code is via its own lazy import of
``executor.utils.dispatch_turn`` (line 247) — that's call-time, not
import-time.

Top-leveling the import simplifies the slot-free hook and matches the
style of the other queue-related imports in the file.  Tests rebind
the patch target from
``backend.copilot.turn_queue.dispatch_next_for_user`` to
``backend.copilot.stream_registry.dispatch_next_for_user`` so the
mock catches the new binding.
…lure

When ``dispatch_turn`` succeeds at ``create_session`` (Redis meta
written, status='running') but then the RabbitMQ ``enqueue_copilot_turn``
fails, the dispatcher's exception handler rolled back the DB
``chatStatus`` (running → queued) but left the Redis session meta in
place.  ``is_turn_in_flight`` reads both Redis status and DB
chatStatus, so the half-failed session kept reporting in-flight even
though no executor would ever pick the turn up — new submits got
pending-buffered indefinitely until the meta key's TTL expired.

Add ``stream_registry.delete_session_meta`` (a tiny ``redis.delete``
on the meta key) and call it in the dispatcher's rollback path
alongside the DB restore.  Best-effort: a Redis error here only
delays cleanup to TTL expiry, which is the prior failure mode.

Test extension: ``test_dispatch_rolls_claim_back_on_dispatch_failure``
now asserts ``delete_session_meta`` is awaited once with the failed
session id.

Resolves r3216996658.
@majdyz

majdyz commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Design recap — what lives where after this PR

The queue feature touches two independent layers that I want to be explicit about, since the recent dispatcher-rollback fix sits across both.

Layer 1 — the queue + cap (this PR)

Storage: 100% Postgres. No Redis involved.

  • ChatSession.chatStatus is a single open-enum text column: "idle" (DEFAULT) | "queued" | "running".
  • The user's pending message is a normal ChatMessage row. The dispatcher's submit-time payload (file_ids, mode, model, permissions, context, request_arrival_at) is stashed in that row's metadata JSONB so a later promotion can replay the turn faithfully.
  • All state transitions are atomic CAS via UPDATE … WHERE chatStatus = expected_status (Postgres update_many returning row count):
    • idle → queued (HTTP route falls into queue) — enqueue_turn
    • queued → running (dispatcher claims) — claim_queued_session
    • queued → idle (user cancel) — cancel_queued_turn
    • running → idle (turn complete) — release_turn_slot
    • running → queued (dispatcher rollback) — exception handler in dispatch_next_for_user

Single compound index (userId, chatStatus, updatedAt) covers all three query shapes on this layer: cap-count, queue-list with ordering, and the sidebar list.

Layer 2 — the stream registry (existing, predates this PR)

Storage: Redis (hash per active session at chat:session_meta:{session_id}).

This is the streaming layer, not the queue layer. It exists because SSE streams need:

  • Resume after client reconnect: the meta key carries turn_id so a returning client can subscribe back to the right Redis stream.
  • Cancel propagation: a RabbitMQ FANOUT cancel event matches by session_id and uses Redis to coordinate "is this session still streaming?".
  • Status visibility: is_turn_in_flight reads the Redis hash to know whether a turn is currently producing output.

This layer was already in place. The queue PR doesn't change its storage.

Where the two layers couple — and what the dispatcher rollback fixes

dispatch_turn writes to BOTH layers in sequence:

  1. stream_registry.create_session — writes Redis meta (status='running')
  2. enqueue_copilot_turn — publishes the turn to RabbitMQ

If step 2 fails (RabbitMQ blip, network glitch), step 1's Redis meta is orphaned. The dispatcher already rolled back the Postgres chatStatus (running → queued), but is_turn_in_flight reads Redis.status == "running" OR DB.chatStatus IN ("queued", "running"). So the Redis side kept the session "in-flight" until the meta key's TTL expired (~1h), even though no executor would ever pick the turn up.

The fix in 09c00a62b73b042a550c5d26ff60ab5fcb52af19 adds stream_registry.delete_session_meta and calls it alongside the DB restore so both layers get reverted atomically. Best-effort: a Redis error here only delays cleanup to TTL, which was the pre-fix failure mode.

Dispatcher triggers

Only ONE: mark_session_completed (in stream_registry, runs after every turn ends, including the executor-subprocess path). The earlier draft of this PR also had a periodic backfill timer, but I removed it (cef50af) because the only realistic scenario it caught was "user has N queued + 0 running + never submits again", and that resolves the next time the user submits.

CAS safety against double-promotion

  • Two concurrent mark_session_completed runs for the same user both call dispatch_next_for_user(user_id). Both call list_queued_sessions and pick the same head. Both call claim_queued_session which is UPDATE … WHERE chatStatus='queued' AND id=… — only one matches. The other gets count=0 and returns False.
  • release_turn_slot runs before dispatch_next_for_user in the completion path, so N completions release N slots before claiming, never exceeding cap.

Cross-process RPC safety

Two paths reach the DB:

  • HTTP route → in-process Prisma (direct module import via chat_db() accessor)
  • CoPilotExecutor subprocess → DatabaseManagerAsyncClient RPC (response serializer rejects raw Prisma rows)

Every PR-added db.py function returns either a primitive (int/bool/str) or a Pydantic DTO (ChatSessionInfo, ChatMessage) — no PrismaChatSession/PrismaChatMessage ever crosses the RPC boundary.

Audit pass after PR-test surfaced multiple "DB chatStatus=running but
no executor actually running it" failure modes. Every one fixed here;
nothing deferred.  Full table in the PR description.

**1. Cancel route no longer leaves orphan DB state.** When
``get_active_session`` returns None on the running branch, also call
``release_turn_slot`` so the sidebar's green dot doesn't persist after
the executor crashed mid-turn and the Redis meta TTL'd out.

**2. ``get_session`` route resets DB-only orphans on chat-open.** If
the user opens a chat whose DB says ``running`` but Redis has no live
stream, force-release the slot — opening the chat is a strong "show
me the current state" signal.

**3. ``dispatch_turn`` cleans Redis on EVERY non-happy-path exit.**
Switched from ``except Exception`` (which misses ``CancelledError``)
to ``try/finally`` on a ``committed`` flag.  Cleanup lives inside
``dispatch_turn`` so the HTTP ``schedule_chat_turn`` path and the
queue dispatcher path are both covered; removed the duplicate
delete-meta call from ``dispatch_next_for_user``.

**4. Dispatcher rollback uses ``except BaseException``.** A task
cancellation mid-dispatch now still rolls back the DB claim from
``running`` back to ``queued`` — the previous ``except Exception``
let cancellations leak.

**5. New periodic sweep ``cleanup_stuck_copilot_sessions``.** Default:
every 5 min, threshold 30 min.  Finds sessions stuck ``running``
beyond the threshold, checks Redis for a live stream, force-releases
when there isn't one.  Catches the no-user-interaction case (#1/#2
only fire on cancel/chat-open).

**6. ``chatStatus`` is now a Postgres enum, not open TEXT.** Added
``ChatSessionStatus`` Prisma enum (``idle | queued | running``); the
DB rejects typos / invalid values at the column-type layer.  Future
states need a tiny ``ALTER TYPE ADD VALUE`` migration (cheap on
PG 12+).

Settings: ``copilot_stuck_session_max_age_secs`` (default 30 min, 1m–24h)
and ``copilot_stuck_session_sweep_interval_secs`` (default 5 min,
30s–1h) drive the new sweep.

Tests: ``test_cancel_session_no_active_task`` pins the new
``release_turn_slot`` call; the dispatcher-rollback test no longer
asserts on Redis cleanup (now lives in ``dispatch_turn``).
Comment thread autogpt_platform/backend/backend/copilot/pending_message_helpers.py
majdyz added 2 commits May 11, 2026 15:02
The reactive cleanups in the previous commit catch every user-visible
path: clicking Cancel and opening the chat both reset the orphan.  The
inline stale-CAS in ``get_active_session`` (6h+5min) remains the
ultimate backstop.  An extra per-pod APScheduler sweep only ever helped
the "user has stuck sessions but never opens them, never cancels, and
keeps submitting new chats until the cap fills" case — too narrow to
justify the constant operational surface.

Drops:
- ``cleanup_stuck_copilot_sessions`` job in executor scheduler
- ``list_stuck_running_sessions`` in db.py + its DB-manager exposures
- ``copilot_stuck_session_max_age_secs`` / ``..._sweep_interval_secs``
  config knobs
…ilable

Sentry r3217196828 caught `is_turn_in_flight` letting Prisma /
DB-down exceptions bubble as a raw 500 from the HTTP layer.  Redis
errors in the same function are already mapped to typed
`StreamRegistryUnavailable` so the chat-route pre-flight chain
returns 503 + Retry-After — DB errors now follow the same fail-closed
path for symmetry.

Test: `test_is_turn_in_flight_raises_when_chat_status_lookup_fails`
pins the new exception mapping.
Comment thread autogpt_platform/backend/backend/copilot/turn_queue.py
Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py Outdated
majdyz added 2 commits May 11, 2026 15:13
Locks the contract for the new chat_status='running' + empty-Redis
fixup added in 2de3469.  Mirrors test_cancel_session_no_active_task
on the sibling cancel-route fixup so a future change that drops the
cleanup fails both tests instead of silently regressing the sidebar UX.

Resolves r3217265328.
… paths

Sentry r3217251053: when dispatch_next_for_user rolls back the
session status (either the pending is None corrupted-state branch
or the dispatch_turn failure branch), the DB chatStatus flip
isn't paired with invalidate_session_cache.  Cached reads (sidebar,
chat-page) keep showing the stale running indicator until the
session cache TTLs out or another write touches it.

Both rollback paths now call invalidate_session_cache immediately
after the status flip, matching what the happy path does at the end
of dispatch_next_for_user.
Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py Outdated
…atch race

Sentry r3217310172: the previous orphan-reset (``get_session`` and
``cancel_session_task``) fired whenever DB ``chatStatus='running'`` +
no Redis meta — but those two states ALSO match the sub-millisecond
window between ``acquire_turn_slot`` (DB flip ``idle → running``) and
``dispatch_turn.create_session`` (Redis meta write).  A get/cancel
landing in that gap would force-release the slot while ``dispatch_turn``
keeps going, leaving DB ``idle`` while RabbitMQ-published work
executes — and worse, lets the user start a second concurrent turn
past the cap.

Fix: gate the reset on session age — ``_try_release_orphan_running``
only fires if the row's ``updatedAt`` is older than
``_ORPHAN_RUNNING_RESET_THRESHOLD_SECONDS`` (30s).  Anything newer is
treated as an in-flight admit, not an orphan.  Threshold is a
generous safety margin: the acquire→create_session window is a few
ms in practice.

Cancel route now distinguishes the two outcomes in its response:
``reason="orphan_released"`` when the age-gated release fires,
``"no_active_session"`` when it doesn't (already idle, or fresh
admit racing the read).

Tests:
- ``test_cancel_session_releases_orphan_running`` — pins the orphan
  branch with a stale ``updatedAt``.
- ``test_cancel_session_skips_orphan_release_within_race_window`` —
  pins the race-window skip with a 1s-old admit.
- ``test_get_session_releases_orphan_when_redis_empty_and_db_running`` —
  updated to use stale ``updatedAt`` and to mock the new
  ``get_chat_session_metadata`` lookup.
Comment thread autogpt_platform/backend/backend/api/features/chat/routes.py
isort wanted datetime to sort before typing — fixed.

Sentry r3217397817 flagged a TypeError risk if meta.updated_at
came back timezone-naive (Prisma returns tz-aware UTC today, but a
schema tweak could change that).  Normalise to UTC defensively before
subtracting so the orphan-reset path can't ever blow up on the
arithmetic.
@majdyz

majdyz commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

/pr-test --fix Round 3 — Stuck-running fixes verified live

Commit tested: b2894399ec (post /pr-polish convergence)
Stack: native (poetry run app + pnpm dev), MAX_RUNNING_COPILOT_TURNS_PER_USER=1

Scenarios

# Scenario Expected Actual Result
1 Stale orphan (DB running, updatedAt = 1h ago, Redis empty) + cancel reason="orphan_released", DB → idle ✅ same PASS
2 Fresh race window (DB running, updatedAt = now, Redis empty) + cancel reason="no_active_session", DB stays running ✅ same PASS
3 Stale orphan + chat-open (GET /sessions/{id}) Response chat_status="idle", DB → idle ✅ same PASS
4 Live queue: cap=1, S1 running → S2 queues → S1 done → dispatcher promotes S2 → both idle All transitions correct, no orphans ✅ same PASS

Evidence

Scenario 1 — stale orphan cancel returns orphan_released

Before cancel: chatStatus=running, updatedAt=1h ago
HTTP 200 — {"cancelled":true,"reason":"orphan_released"}
After cancel: chatStatus=idle ✓

Scenario 2 — fresh race-window cancel preserves the in-flight admit

Before cancel: chatStatus=running, updatedAt=now
HTTP 200 — {"cancelled":true,"reason":"no_active_session"}
After cancel: chatStatus=running ✓ (the in-flight admit isn't stomped)

Scenario 3 — chat-open auto-reset

Before open: chatStatus=running, updatedAt=1h ago
GET /sessions/{id} → response.chat_status="idle"
After open: chatStatus=idle ✓

Scenario 4 — live queue + dispatcher path

S1 submitted → chatStatus=running (cap=1 taken)
S2 submitted 0.3s later → chatStatus=queued (cap full → falls into queue)
S1 completes → mark_session_completed → dispatch_next_for_user → CAS-claims S2
S2 dispatched, runs, completes → both sessions idle

Summary

  • 4/4 scenarios PASS against b2894399ec.
  • Stuck-running fixes from this PR are verified live:
    • Cancel returns orphan_released only when the age gate is satisfied (≥30s stuck).
    • Cancel returns no_active_session and skips the release when DB running is sub-30s (fresh admit racing the read).
    • GET /sessions/{id} performs the same age-gated reset, so opening a stuck chat auto-clears the orphan.
    • The existing queue lifecycle (cap, promotion, completion) still works end-to-end — no regression.
  • No new bugs found. Ready to merge.

@majdyz
majdyz merged commit e4cc0b8 into dev May 11, 2026
45 checks passed
@majdyz
majdyz deleted the zamilmajdy/secrt-2339-add-autopilot-task-queue-with-5-concurrent-runs-and-15-in branch May 11, 2026 09:26
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to ✅ Done in AutoGPT development kanban May 11, 2026
@github-project-automation github-project-automation Bot moved this to Done in Frontend May 11, 2026
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 platform/blocks platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant