feat(copilot): real OpenRouter cost + cost-based rate limits (percent-only public API) - #12864
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughMigrates CoPilot rate-limiting and usage from token-based metrics to provider-reported cost (microdollars), introduces percent-only public usage views, updates streaming to accumulate per-chunk provider cost, and updates backend APIs, configs, Redis keys, and frontend displays/tests accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant ChatRoute as Chat Route
participant RateLimit as Rate Limit Service
participant Redis
participant Provider as LLM Provider
Client->>ChatRoute: POST /stream_chat
ChatRoute->>RateLimit: check_rate_limit(user_id, daily_cost_limit, weekly_cost_limit)
RateLimit->>Redis: GET copilot:cost:daily:{user_id}
RateLimit->>Redis: GET copilot:cost:weekly:{user_id}
alt Cost Exceeded
RateLimit-->>ChatRoute: RateLimitExceeded
ChatRoute-->>Client: 429 Too Many Requests
else Cost OK
ChatRoute->>Provider: Stream completion (extra_body={"usage":{"include":true}})
Provider-->>ChatRoute: Streamed chunks with usage.cost
ChatRoute->>ChatRoute: Accumulate chunk.usage.cost
ChatRoute->>RateLimit: record_cost_usage(user_id, cost_microdollars)
RateLimit->>Redis: INCR copilot:cost:daily:{user_id} by cost_microdollars
RateLimit->>Redis: INCR copilot:cost:weekly:{user_id} by cost_microdollars
ChatRoute-->>Client: Completion response
ChatRoute->>RateLimit: get_usage_status(user_id, daily_cost_limit, weekly_cost_limit)
RateLimit->>Redis: GET copilot:cost:daily:{user_id}
RateLimit->>Redis: GET copilot:cost:weekly:{user_id}
RateLimit-->>ChatRoute: CoPilotUsagePublic (percent_used only)
ChatRoute-->>Client: Response with usage percentages
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
🔍 PR Overlap DetectionThis check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early. 🔴 Merge Conflicts DetectedThe following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.
🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 7 conflict(s), 0 medium risk, 7 low risk (out of 14 PRs with file overlap) Auto-generated on push. Ignores: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #12864 +/- ##
==========================================
+ Coverage 65.92% 66.10% +0.18%
==========================================
Files 1866 1868 +2
Lines 139884 140583 +699
Branches 14971 15037 +66
==========================================
+ Hits 92216 92934 +718
+ Misses 44822 44783 -39
- Partials 2846 2866 +20
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx (1)
78-89:⚠️ Potential issue | 🟡 MinorWeak assertion: used and limit both render as "$0.01".
dailyUsed=5000µ$ ≈ $0.005 anddailyLimit=10000µ$ = $0.01 both render to"$0.01", sogetByText("$0.01 / $0.01")would still pass if the component accidentally renderedlimit / limitorused / used. Consider using clearly distinct values (e.g.dailyUsed: 5_000_000,dailyLimit: 10_000_000→"$5.00 / $10.00") so the assertion actually distinguishes the two. The test name also still says "percentages" — worth renaming to match the new spend-based output.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx around lines 78 - 89, The assertion in the UsageLimits test is weak because dailyUsed=5000 and dailyLimit=10000 both format to "$0.01", so change the mocked values in the test that calls mockUseGetV2GetCopilotUsage (and uses makeUsage) to clearly distinct, larger µ$ values (e.g. dailyUsed: 5_000_000, dailyLimit: 10_000_000) so the rendered string in UsageLimits is something like "$5.00 / $10.00" and update the test name from "displays daily and weekly usage percentages" to reflect spend (e.g. "displays daily and weekly usage amounts"); keep the rest of the expectations for "Today", "This week", and "Usage limits" unchanged.autogpt_platform/backend/backend/api/features/chat/routes.py (1)
588-594:⚠️ Potential issue | 🟡 MinorStale "token limit" wording in user-facing docstrings.
This docstring (rendered in OpenAPI) still describes the feature in token terms after the switch to cost-based limits. Same applies to the 429 description on line 794 (
"Token rate-limit or call-frequency cap exceeded"). Worth aligning so the generated docs match the new accounting unit.🔧 Proposed fix
"""Reset the daily CoPilot rate limit by spending credits. - Allows users who have hit their daily token limit to spend credits + Allows users who have hit their daily cost limit to spend credits to reset their daily usage counter and continue working.- 429: {"description": "Token rate-limit or call-frequency cap exceeded"}, + 429: {"description": "Cost rate-limit or call-frequency cap exceeded"},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/api/features/chat/routes.py` around lines 588 - 594, Update the user-facing docstring that currently reads "Reset the daily CoPilot rate limit by spending credits" and any occurrences of "token" (including the 429 response text "Token rate-limit or call-frequency cap exceeded") to reflect cost-based limits — e.g., replace "token" with "cost" or "cost-based usage" and change "token limit" to "daily cost limit" in the docstring and the response message used in the chat route functions (look for the reset docstring and the 429 description string in the chat route handlers). Ensure the OpenAPI-rendered text consistently uses the new wording so the generated docs reference cost-based accounting and not tokens.autogpt_platform/frontend/src/app/api/openapi.json (1)
1795-1797:⚠️ Potential issue | 🟡 MinorUpdate remaining token-based descriptions to cost-based wording.
These descriptions still refer to token limits/allowances after the API moved to microdollar cost accounting. This can mislead generated docs and client consumers even though the schema fields were renamed correctly.
Please update the backend source descriptions/docstrings and regenerate this file. Suggested wording:
- Line 1796: “Cost rate-limit or call-frequency cap exceeded”
- Line 1882: “Returns current cost usage vs limits…”
- Line 1904: “daily cost limit”
- Line 14248: “cost allowances”
- Line 15881: “Maximum cost allowed in this window. 0 means unlimited.”
Based on learnings, the OpenAPI spec file is auto-generated; prefer clarifying behaviors in endpoint descriptions/docstrings rather than hand-editing generated output.
Also applies to: 1881-1882, 1903-1904, 14244-14248, 15875-15882
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/api/openapi.json` around lines 1795 - 1797, Update the backend OpenAPI/docstring sources (not the generated openapi.json) to replace "token"-based wording with "cost"-based wording for the affected responses/fields: change the 429 response description to "Cost rate-limit or call-frequency cap exceeded", update the usage endpoint description to "Returns current cost usage vs limits…" (previously token usage), rename/clarify any "daily token limit" docstrings to "daily cost limit", change "token allowances" phrasing to "cost allowances", and update the max-window description to "Maximum cost allowed in this window. 0 means unlimited."; then regenerate the OpenAPI spec so those changes appear in openapi.json (the JSON keys to look for are the 429 response description and the schema/field descriptions referenced around the previously noted positions).
🧹 Nitpick comments (4)
autogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.ts (1)
5-7: Sub-cent values render as$0.00.With daily limits as low as
$10 (10,000,000 µ$ ), individual turn costs and low cumulative usage will often fall below $0.005 and display as$0.00, which is indistinguishable from zero usage. Consider showing more precision for small amounts (e.g., 4 decimals whenmicrodollars < 10_000) or a"<$0.01"fallback.♻️ Proposed refinement
export function formatMicrodollarsAsUsd(microdollars: number): string { - return `$${(microdollars / 1_000_000).toFixed(2)}`; + const dollars = microdollars / 1_000_000; + if (microdollars > 0 && dollars < 0.01) return "<$0.01"; + return `$${dollars.toFixed(2)}`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/components/usageHelpers.ts around lines 5 - 7, The current formatMicrodollarsAsUsd(microdollars) always rounds to two decimals causing sub-cent values to display as "$0.00"; update this function to detect small amounts and render more precision or a clear fallback: if microdollars < 10_000 (i.e. < $0.01) format with four decimal places (or return "<$0.01" if you prefer a fallback) otherwise keep the existing toFixed(2) behavior; ensure you reference and update the formatMicrodollarsAsUsd function so callers get clearer non-zero tiny values.autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx (1)
106-118: Brittle selector on inline style.
[style*="width: 100%"]depends on exact whitespace and formatting of the inlinestylestring emitted by React/the DOM. Tolerable here, but if the component ever switches to Tailwind classes or different style formatting this test will silently pass/fail for the wrong reason. Adata-testidon the progress bar (or a role-based query) would be more robust.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx around lines 106 - 118, The test "caps bar width at 100% when over limit" uses a brittle inline-style selector; update the UsageLimits component to add a stable identifier (e.g., data-testid like "daily-progress-bar" or an accessible role/aria-label) on the progress bar element, then change the test to query that element using getByTestId or a role-based query and assert its clamped width (via element.style.width or getComputedStyle) instead of matching the raw style string; reference the UsageLimits component and the test file UsageLimits.test.tsx (and the mockUseGetV2GetCopilotUsage mock) when making the changes.autogpt_platform/frontend/src/app/(platform)/admin/components/UsageBar.tsx (1)
3-3: Consider hoistingformatMicrodollarsAsUsdto a shared utility.Admin code now reaches into
@/app/(platform)/copilot/components/usageHelpersfor a generic currency formatter, creating a cross-feature dependency for what's really a shared utility. If the helper is used across copilot + admin, moving it to something like@/lib/formatters(or similar) would avoid coupling admin to the copilot module tree.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/admin/components/UsageBar.tsx at line 3, The UsageBar import creates a cross-feature dependency by importing formatMicrodollarsAsUsd from the copilot component tree; move the function into a shared utility (e.g., create a new module like "@/lib/formatters" and export formatMicrodollarsAsUsd from there), update UsageBar.tsx to import formatMicrodollarsAsUsd from the new shared module, and update any other consumers (e.g., copilot components) to import from "@/lib/formatters"; optionally keep a re-export in the old "@/app/(platform)/copilot/components/usageHelpers" to preserve backwards compatibility during the transition.autogpt_platform/backend/backend/copilot/rate_limit.py (1)
340-345: Use a transactional pipeline for the counter update.This pipeline performs multi-step Redis updates (
INCRBY+EXPIREfor each key). Please keep it transactional so partial execution cannot leave counters without TTLs.Proposed fix
- # transaction=False: these are independent INCRBY+EXPIRE pairs on - # separate keys — no cross-key atomicity needed. Skipping - # MULTI/EXEC avoids the overhead. If the connection drops between - # INCRBY and EXPIRE the key survives until the next date-based key - # rotation (daily/weekly), so the memory-leak risk is negligible. - pipe = redis.pipeline(transaction=False) + pipe = redis.pipeline(transaction=True)As per coding guidelines, use
transaction=Truefor Redis pipelines to ensure atomicity on multi-step operations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/rate_limit.py` around lines 340 - 345, The Redis pipeline used for counter updates currently creates a non-transactional pipeline via redis.pipeline(transaction=False), which can leave INCRBY executed without the following EXPIRE; change it to use a transactional pipeline (redis.pipeline(transaction=True)) and ensure the INCRBY + EXPIRE calls on the same pipe are executed inside that MULTI/EXEC scope (where pipe is created and later .execute() is called) so the counter updates and TTLs are atomic; update the pipeline creation and any related error/execute handling around the pipe variable in rate_limit.py.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/copilot/baseline/service.py`:
- Around line 341-365: The OpenRouter cost-handling branch currently only logs
non-numeric parse errors but silently ignores non-finite or negative numeric
costs; update the branch in the code handling chunk.usage.cost (the block around
cost_raw → cost_val and the math.isfinite(cost_val) / cost_val >= 0 checks) to
emit a warning (e.g., logger.warning or logger.error) whenever cost_val is NaN,
infinite, or negative so provider misbehavior is visible to on-call, and only
add to state.cost_usd when the value is finite and non-negative; keep existing
TypeError/ValueError logging as-is.
In `@autogpt_platform/backend/backend/copilot/rate_limit.py`:
- Around line 331-335: The info log in rate_limit.py that calls logger.info with
"Recording copilot spend for %s: %d microdollars" should stop including the user
identifier; update the logger.info call (the one in the copilot spend recording
block) to omit user_id[:8] and only log the cost_microdollars (or a message that
includes only the amount) so no user identifier is emitted in cost-recording
logs.
In `@autogpt_platform/backend/backend/copilot/token_tracking.py`:
- Around line 182-189: The current broad except in the token tracking block
around record_cost_usage(user_id=..., cost_microdollars=...) swallows all
exceptions; narrow it to only catch fail-open transient errors by replacing
"except Exception" with "except (RedisError, ConnectionError, OSError)" (and
import RedisError from the redis library or appropriate client) so only
Redis/network issues are logged and swallowed while other unexpected errors
propagate; keep the existing logger.warning call for those specific exceptions
around the same log_prefix usage.
---
Outside diff comments:
In `@autogpt_platform/backend/backend/api/features/chat/routes.py`:
- Around line 588-594: Update the user-facing docstring that currently reads
"Reset the daily CoPilot rate limit by spending credits" and any occurrences of
"token" (including the 429 response text "Token rate-limit or call-frequency cap
exceeded") to reflect cost-based limits — e.g., replace "token" with "cost" or
"cost-based usage" and change "token limit" to "daily cost limit" in the
docstring and the response message used in the chat route functions (look for
the reset docstring and the 429 description string in the chat route handlers).
Ensure the OpenAPI-rendered text consistently uses the new wording so the
generated docs reference cost-based accounting and not tokens.
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx:
- Around line 78-89: The assertion in the UsageLimits test is weak because
dailyUsed=5000 and dailyLimit=10000 both format to "$0.01", so change the mocked
values in the test that calls mockUseGetV2GetCopilotUsage (and uses makeUsage)
to clearly distinct, larger µ$ values (e.g. dailyUsed: 5_000_000, dailyLimit:
10_000_000) so the rendered string in UsageLimits is something like "$5.00 /
$10.00" and update the test name from "displays daily and weekly usage
percentages" to reflect spend (e.g. "displays daily and weekly usage amounts");
keep the rest of the expectations for "Today", "This week", and "Usage limits"
unchanged.
In `@autogpt_platform/frontend/src/app/api/openapi.json`:
- Around line 1795-1797: Update the backend OpenAPI/docstring sources (not the
generated openapi.json) to replace "token"-based wording with "cost"-based
wording for the affected responses/fields: change the 429 response description
to "Cost rate-limit or call-frequency cap exceeded", update the usage endpoint
description to "Returns current cost usage vs limits…" (previously token usage),
rename/clarify any "daily token limit" docstrings to "daily cost limit", change
"token allowances" phrasing to "cost allowances", and update the max-window
description to "Maximum cost allowed in this window. 0 means unlimited."; then
regenerate the OpenAPI spec so those changes appear in openapi.json (the JSON
keys to look for are the 429 response description and the schema/field
descriptions referenced around the previously noted positions).
---
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/rate_limit.py`:
- Around line 340-345: The Redis pipeline used for counter updates currently
creates a non-transactional pipeline via redis.pipeline(transaction=False),
which can leave INCRBY executed without the following EXPIRE; change it to use a
transactional pipeline (redis.pipeline(transaction=True)) and ensure the INCRBY
+ EXPIRE calls on the same pipe are executed inside that MULTI/EXEC scope (where
pipe is created and later .execute() is called) so the counter updates and TTLs
are atomic; update the pipeline creation and any related error/execute handling
around the pipe variable in rate_limit.py.
In `@autogpt_platform/frontend/src/app/`(platform)/admin/components/UsageBar.tsx:
- Line 3: The UsageBar import creates a cross-feature dependency by importing
formatMicrodollarsAsUsd from the copilot component tree; move the function into
a shared utility (e.g., create a new module like "@/lib/formatters" and export
formatMicrodollarsAsUsd from there), update UsageBar.tsx to import
formatMicrodollarsAsUsd from the new shared module, and update any other
consumers (e.g., copilot components) to import from "@/lib/formatters";
optionally keep a re-export in the old
"@/app/(platform)/copilot/components/usageHelpers" to preserve backwards
compatibility during the transition.
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/usageHelpers.ts:
- Around line 5-7: The current formatMicrodollarsAsUsd(microdollars) always
rounds to two decimals causing sub-cent values to display as "$0.00"; update
this function to detect small amounts and render more precision or a clear
fallback: if microdollars < 10_000 (i.e. < $0.01) format with four decimal
places (or return "<$0.01" if you prefer a fallback) otherwise keep the existing
toFixed(2) behavior; ensure you reference and update the formatMicrodollarsAsUsd
function so callers get clearer non-zero tiny values.
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx:
- Around line 106-118: The test "caps bar width at 100% when over limit" uses a
brittle inline-style selector; update the UsageLimits component to add a stable
identifier (e.g., data-testid like "daily-progress-bar" or an accessible
role/aria-label) on the progress bar element, then change the test to query that
element using getByTestId or a role-based query and assert its clamped width
(via element.style.width or getComputedStyle) instead of matching the raw style
string; reference the UsageLimits component and the test file
UsageLimits.test.tsx (and the mockUseGetV2GetCopilotUsage mock) when making the
changes.
🪄 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: 27dc35b4-fe3d-4cfc-bf42-2c2ce130b379
📒 Files selected for processing (29)
autogpt_platform/backend/backend/api/features/admin/rate_limit_admin_routes.pyautogpt_platform/backend/backend/api/features/admin/rate_limit_admin_routes_test.pyautogpt_platform/backend/backend/api/features/chat/routes.pyautogpt_platform/backend/backend/api/features/chat/routes_test.pyautogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/baseline/service_unit_test.pyautogpt_platform/backend/backend/copilot/config.pyautogpt_platform/backend/backend/copilot/rate_limit.pyautogpt_platform/backend/backend/copilot/rate_limit_test.pyautogpt_platform/backend/backend/copilot/reset_usage_test.pyautogpt_platform/backend/backend/copilot/sdk/response_adapter_test.pyautogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/token_tracking.pyautogpt_platform/backend/backend/copilot/token_tracking_test.pyautogpt_platform/backend/backend/util/feature_flag.pyautogpt_platform/backend/snapshots/get_rate_limitautogpt_platform/backend/snapshots/reset_user_usage_daily_and_weeklyautogpt_platform/backend/snapshots/reset_user_usage_daily_onlyautogpt_platform/frontend/src/app/(platform)/admin/components/UsageBar.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/RateLimitDisplay.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitDisplay.test.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitManager.test.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/useRateLimitManager.test.tsautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsagePanelContentRender.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.tsautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsxautogpt_platform/frontend/src/app/api/openapi.json
💤 Files with no reviewable changes (1)
- autogpt_platform/backend/backend/copilot/sdk/response_adapter_test.py
Why: Since commit d7653ac dropped cost estimation, most baseline turns log with tracking_type="tokens" and no authoritative USD figure. The rate-limit counter in Redis was also token-weighted with ad-hoc cache discounts and a 5x Opus multiplier — an approximation of cost that diverges from real billing, especially with OpenRouter's per-provider pricing variance. What: Request real generation cost from OpenRouter on every baseline turn (usage.include=true in the request body), charge the rate-limit counter in microdollars against that real cost, and rename the LD flags + config fields to reflect the new unit. How: - baseline/service.py now passes extra_body={"usage":{"include":true}} so the final streaming chunk carries cost, and reads chunk.usage.cost directly. The x-total-cost response-header fallback is removed; if the usage chunk lacks cost we log an error and skip the counter update (previously we under-counted via the removed estimator). - rate_limit.py: record_cost_usage(cost_microdollars) replaces record_token_usage with its weighted-token math and Opus multiplier. Redis key prefix bumped from copilot:usage to copilot:cost so stale token-based counters cannot be misinterpreted as microdollars. - config.py: daily/weekly_token_limit -> daily/weekly_cost_limit_microdollars (FREE-tier defaults: \$10/day, \$50/week). - feature_flag.py: COPILOT_DAILY/WEEKLY_TOKEN_LIMIT -> COPILOT_DAILY/ WEEKLY_COST_LIMIT with keys "copilot-daily-cost-limit-microdollars" and "copilot-weekly-cost-limit-microdollars" (unit is in the LD key so values cannot be set as dollars/cents by accident). - Admin UserRateLimitResponse: daily/weekly_token_limit, daily/weekly_ tokens_used -> the *_cost_*_microdollars variants. Frontend reformats microdollars as USD (\$X.XX). - sdk/service.py: removes the dead _OPUS_COST_MULTIPLIER + the rate- limit multiplier, since real cost already reflects model pricing. - Tests + snapshots updated to match. Deploy notes: 1. Create new LD flags copilot-daily-cost-limit-microdollars and copilot-weekly-cost-limit-microdollars (defaults 10_000_000 / 50_000_000) before rolling out — the old copilot-*-token-limit flags can be left in place for rollback. 2. Redis token-based counters under copilot:usage:* are orphaned and TTL out within 7 days; clean manually if desired.
a828895 to
67c78a1
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
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/api/features/chat/routes.py (1)
590-594:⚠️ Potential issue | 🟡 MinorUpdate remaining token-limit wording in API docs.
These descriptions are now stale after the cost-based migration and will surface in OpenAPI/client docs.
Proposed fix
- Allows users who have hit their daily token limit to spend credits + Allows users who have hit their daily cost limit to spend credits to reset their daily usage counter and continue working.- 429: {"description": "Token rate-limit or call-frequency cap exceeded"}, + 429: {"description": "Cost rate-limit or call-frequency cap exceeded"},Also applies to: 794-794
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/api/features/chat/routes.py` around lines 590 - 594, Update the endpoint docstrings that currently read "Allows users who have hit their daily token limit to spend credits to reset their daily usage counter and continue working. Returns 400 if the feature is disabled or the user is not over the limit. Returns 402 if the user has insufficient credits." to reflect the cost-based migration: change "daily token limit" and "remaining token-limit" wording to "daily usage limit" or "daily usage counter" and ensure the return descriptions state "400 if the feature is disabled or the user is not over the daily usage limit" and "402 if the user has insufficient credits"; apply this exact wording change to both locations of the docstring in chat/routes.py (the two occurrences around the earlier and later docstring blocks).
♻️ Duplicate comments (1)
autogpt_platform/backend/backend/copilot/token_tracking.py (1)
182-189:⚠️ Potential issue | 🟠 MajorDon’t swallow unexpected cost-recording failures here.
record_cost_usage()should own Redis/network fail-open handling. CatchingExceptionhere can hide accounting bugs and silently skip rate-limit updates.Proposed fix
- if user_id and cost_microdollars and cost_microdollars > 0: - try: - await record_cost_usage( - user_id=user_id, - cost_microdollars=cost_microdollars, - ) - except Exception as usage_err: - logger.warning("%s Failed to record cost usage: %s", log_prefix, usage_err) + if user_id and cost_microdollars and cost_microdollars > 0: + await record_cost_usage( + user_id=user_id, + cost_microdollars=cost_microdollars, + )Based on learnings, all fail-open rate-limit paths should catch only
(RedisError, ConnectionError, OSError)specifically, so unexpected exceptions propagate normally.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/token_tracking.py` around lines 182 - 189, The catch-all except in the block that calls record_cost_usage(user_id, cost_microdollars) is hiding unexpected errors; change the except Exception to catch only the fail-open types (e.g., RedisError, ConnectionError, OSError) so record_cost_usage() can own its fail-open logic and other exceptions will propagate; update imports to include the specific RedisError (from redis.exceptions or your Redis client), and keep the existing logger.warning(log_prefix, usage_err) behavior for those specific exceptions only.
🧹 Nitpick comments (2)
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx (1)
106-118: Prefer a behavior-oriented query over inline-style selector.Querying
[style*="width: 100%"]couples the test to an implementation detail (inline style string). A small refactor — e.g., giving the progress fill arole="progressbar"witharia-valuenow, or a stabledata-testid— would make the assertion resilient to styling changes (switching to Tailwind width classes, CSS variables, etc.) while still verifying the clamp.As per coding guidelines: "Test behavior, not implementation—query elements by role/text, not class names".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx around lines 106 - 118, The test for UsageLimits currently queries an inline style string; change it to assert behavior by exposing a semantic hook on the progress element and using an attribute check: update the UsageLimits component to add a role="progressbar" with aria-valuenow (or a stable data-testid on the progress fill), then update the test "caps bar width at 100% when over limit" to query that element via getByRole('progressbar') (or getByTestId) and assert the aria-valuenow (or testid-backed value) is "100" (or capped to 100) instead of querying the inline style string.autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py (1)
993-1024: Cover the tools branch of the usage/cost contract.This test only exercises
tools=[], but_baseline_llm_callerhas a separatetoolscall path. Parameterize it with a non-empty tool list so regressions don’t silently dropextra_body={"usage": {"include": True}}for tool-calling turns; it’s also worth assertingstream_options.Proposed test strengthening
+ `@pytest.mark.parametrize`("tools", [[], [_make_tool("search")]]) `@pytest.mark.asyncio` - async def test_baseline_requests_usage_include_extra_body(self): + async def test_baseline_requests_usage_include_extra_body(self, tools): """The baseline call must pass extra_body={'usage': {'include': True}}. This guards the contract with OpenRouter that triggers inclusion of the authoritative cost on the final usage chunk. Without it the rate-limit counter stays at zero. @@ await _baseline_llm_caller( messages=[{"role": "user", "content": "hi"}], - tools=[], + tools=tools, state=state, ) create_mock.assert_awaited_once() await_args = create_mock.await_args assert await_args is not None + assert await_args.kwargs["stream_options"] == {"include_usage": True} assert await_args.kwargs["extra_body"] == {"usage": {"include": True}}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py` around lines 993 - 1024, The test test_baseline_requests_usage_include_extra_body only covers the tools=[] branch; update it to also exercise the tool-calling path by parameterizing or adding a case with a non-empty tools list (e.g., tools=[{"name":"tool","parameters":...}] or similar) and calling _baseline_llm_caller with that tools value, then assert create_mock was awaited and await_args.kwargs["extra_body"] == {"usage": {"include": True}} for the tools case as well; while here, also assert await_args.kwargs["stream_options"] is present/contains the expected keys to ensure stream options aren’t dropped (use the same _BaselineStreamState and mocked client/create_mock used in the test).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py`:
- Around line 636-641: The test file currently re-imports _baseline_llm_caller
and _BaselineStreamState inside test functions; instead add _baseline_llm_caller
to the existing module-level import block where _BaselineStreamState is already
imported and remove the local imports of _baseline_llm_caller (and avoid
re-importing _BaselineStreamState) from individual tests such as
test_cost_usd_extracted_from_usage_chunk and the other occurrences (e.g., around
lines mentioned in the review), ensuring only lazy/optional heavy dependencies
remain as local imports.
In `@autogpt_platform/backend/backend/copilot/token_tracking.py`:
- Around line 167-178: The guard in token_tracking where cost_usd is converted
to float (variables cost_usd -> val -> cost_float) currently silently skips
non-finite (NaN/Inf) and negative values; update the logic in the block that
handles cost_usd to explicitly log these invalid numeric cases before skipping
billing: after casting to float in the try within the same branch, if not
math.isfinite(val) or val < 0 call logger.error with the same log_prefix and
cost_usd/val information (similar format to the existing except handler) and do
not set cost_float; keep the existing except (ValueError, TypeError) logging
unchanged so non-numeric, non-finite, and negative provider costs are all logged
prior to skipping billing.
---
Outside diff comments:
In `@autogpt_platform/backend/backend/api/features/chat/routes.py`:
- Around line 590-594: Update the endpoint docstrings that currently read
"Allows users who have hit their daily token limit to spend credits to reset
their daily usage counter and continue working. Returns 400 if the feature is
disabled or the user is not over the limit. Returns 402 if the user has
insufficient credits." to reflect the cost-based migration: change "daily token
limit" and "remaining token-limit" wording to "daily usage limit" or "daily
usage counter" and ensure the return descriptions state "400 if the feature is
disabled or the user is not over the daily usage limit" and "402 if the user has
insufficient credits"; apply this exact wording change to both locations of the
docstring in chat/routes.py (the two occurrences around the earlier and later
docstring blocks).
---
Duplicate comments:
In `@autogpt_platform/backend/backend/copilot/token_tracking.py`:
- Around line 182-189: The catch-all except in the block that calls
record_cost_usage(user_id, cost_microdollars) is hiding unexpected errors;
change the except Exception to catch only the fail-open types (e.g., RedisError,
ConnectionError, OSError) so record_cost_usage() can own its fail-open logic and
other exceptions will propagate; update imports to include the specific
RedisError (from redis.exceptions or your Redis client), and keep the existing
logger.warning(log_prefix, usage_err) behavior for those specific exceptions
only.
---
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py`:
- Around line 993-1024: The test test_baseline_requests_usage_include_extra_body
only covers the tools=[] branch; update it to also exercise the tool-calling
path by parameterizing or adding a case with a non-empty tools list (e.g.,
tools=[{"name":"tool","parameters":...}] or similar) and calling
_baseline_llm_caller with that tools value, then assert create_mock was awaited
and await_args.kwargs["extra_body"] == {"usage": {"include": True}} for the
tools case as well; while here, also assert await_args.kwargs["stream_options"]
is present/contains the expected keys to ensure stream options aren’t dropped
(use the same _BaselineStreamState and mocked client/create_mock used in the
test).
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx:
- Around line 106-118: The test for UsageLimits currently queries an inline
style string; change it to assert behavior by exposing a semantic hook on the
progress element and using an attribute check: update the UsageLimits component
to add a role="progressbar" with aria-valuenow (or a stable data-testid on the
progress fill), then update the test "caps bar width at 100% when over limit" to
query that element via getByRole('progressbar') (or getByTestId) and assert the
aria-valuenow (or testid-backed value) is "100" (or capped to 100) instead of
querying the inline style string.
🪄 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: f9aeed42-718c-47d2-98fc-bdfa59a498cc
📒 Files selected for processing (29)
autogpt_platform/backend/backend/api/features/admin/rate_limit_admin_routes.pyautogpt_platform/backend/backend/api/features/admin/rate_limit_admin_routes_test.pyautogpt_platform/backend/backend/api/features/chat/routes.pyautogpt_platform/backend/backend/api/features/chat/routes_test.pyautogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/baseline/service_unit_test.pyautogpt_platform/backend/backend/copilot/config.pyautogpt_platform/backend/backend/copilot/rate_limit.pyautogpt_platform/backend/backend/copilot/rate_limit_test.pyautogpt_platform/backend/backend/copilot/reset_usage_test.pyautogpt_platform/backend/backend/copilot/sdk/response_adapter_test.pyautogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/token_tracking.pyautogpt_platform/backend/backend/copilot/token_tracking_test.pyautogpt_platform/backend/backend/util/feature_flag.pyautogpt_platform/backend/snapshots/get_rate_limitautogpt_platform/backend/snapshots/reset_user_usage_daily_and_weeklyautogpt_platform/backend/snapshots/reset_user_usage_daily_onlyautogpt_platform/frontend/src/app/(platform)/admin/components/UsageBar.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/RateLimitDisplay.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitDisplay.test.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitManager.test.tsxautogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/useRateLimitManager.test.tsautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsagePanelContentRender.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.tsautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsxautogpt_platform/frontend/src/app/api/openapi.json
💤 Files with no reviewable changes (1)
- autogpt_platform/backend/backend/copilot/sdk/response_adapter_test.py
✅ Files skipped from review due to trivial changes (5)
- autogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.ts
- autogpt_platform/backend/snapshots/get_rate_limit
- autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/tests/RateLimitDisplay.test.tsx
- autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/tests/RateLimitManager.test.tsx
- autogpt_platform/backend/backend/copilot/reset_usage_test.py
🚧 Files skipped from review as they are similar to previous changes (14)
- autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
- autogpt_platform/backend/backend/api/features/chat/routes_test.py
- autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/tests/UsagePanelContentRender.test.tsx
- autogpt_platform/frontend/src/app/(platform)/admin/components/UsageBar.tsx
- autogpt_platform/backend/snapshots/reset_user_usage_daily_only
- autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/tests/useRateLimitManager.test.ts
- autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsx
- autogpt_platform/backend/snapshots/reset_user_usage_daily_and_weekly
- autogpt_platform/backend/backend/copilot/baseline/service.py
- autogpt_platform/backend/backend/copilot/rate_limit_test.py
- autogpt_platform/backend/backend/copilot/config.py
- autogpt_platform/frontend/src/app/api/openapi.json
- autogpt_platform/backend/backend/util/feature_flag.py
- autogpt_platform/backend/backend/copilot/rate_limit.py
|
/review |
…at/copilot-cost-based-rate-limit
…id cost, drop user_id log, narrow exception, accessible progress bar, sub-cent display - baseline/service: downgrade missing-cost log ERROR→WARNING and dedupe per-stream via _BaselineStreamState.cost_missing_logged so non-OpenRouter providers don't flood monitoring; explicitly log non-finite/negative cost values instead of silently dropping them. - rate_limit: drop user_id[:8] from the per-turn spend log; switch the counter pipeline back to transaction=True so INCRBY+EXPIRE stay atomic. - token_tracking: split the cost-parse try/except so non-finite/negative cost_usd is logged before being skipped; remove the broad except Exception around record_cost_usage so real accounting bugs propagate (RedisError/ConnectionError/OSError are already owned by record_cost_usage). Test updated to assert a RuntimeError propagates. - chat/routes: replace stale 'token limit' / 'Token rate-limit' wording with cost-based wording in the reset docstring and 429 description; regenerated openapi.json. - baseline/service_unit_test: hoist _baseline_llm_caller into the module import block (remove 12 in-function re-imports); parametrize the extra_body contract test over tools=[] and tools=[tool] and assert stream_options as well. - frontend usageHelpers: return '<$0.01' for sub-cent positive values so they aren't indistinguishable from zero. - UsageLimits: give the progress fill role=progressbar + aria-valuenow and assert that in the clamp test instead of the inline-style string; use distinct used/limit values in the spend test so limit/used can't collide at two decimals.
There was a problem hiding this comment.
📋 Automated Review — PR #12864
PR #12864 — feat(copilot): cost-based rate limiting using real OpenRouter cost
Author: majdyz | Files: 29
🎯 Verdict: REQUEST_CHANGES
PR Description Quality
✅ Has Why + What + How — PR description clearly explains the motivation (token-weighted rate limiting drifted from real billing), the approach (use OpenRouter's usage.cost field, convert to microdollars), and the migration strategy (Redis key prefix bump, new LD flags). Deploy notes are thorough.
What This PR Does
Previously, copilot rate limiting used hand-rolled token-weighted counters with hardcoded multipliers (e.g., Opus 5×, cache discounts at 10%/25%) that drifted from actual provider billing. This PR replaces that system with real cost-based rate limiting: it reads the usage.cost field from OpenRouter streaming responses, converts it to microdollars (1 USD = 1,000,000 µ$), and stores those values in Redis under a new copilot:cost key prefix. The frontend, admin API, and LaunchDarkly flags are all updated to use microdollar units, displaying costs as $X.XX instead of raw token counts.
Specialist Findings
🛡️ Security ✅ — No injection, auth, or secrets issues. Redis operations use parameterized keys. Input validation checks cost values for type, finiteness, and non-negativity before recording.
- 🟡 When OpenRouter omits
usage.cost, the rate-limit counter is not incremented — a persistent absence would allow unlimited usage (service.py:346). Acceptable trade-off vs. blocking, but needs monitoring. - 🟡 No per-turn cost ceiling — a provider bug reporting $1000 for a single turn would instantly exhaust a user's quota (
service.py:356).
🏗️ Architecture ✅ — Clean migration. Single responsibility preserved — rate_limit.py only tracks microdollar counters; pricing knowledge removed. Both baseline (OpenRouter) and SDK (Anthropic) paths funnel through the same persist_and_record_usage() → record_cost_usage() pipeline. Redis key prefix bump from copilot:usage → copilot:cost prevents misinterpretation of legacy counters.
- 🟠 The
UserRateLimitResponsefield renames (daily_token_limit→daily_cost_limit_microdollars) are a breaking API contract change with no versioning or deprecation (rate_limit_admin_routes.py:35). (Flagged by: architect, security, product — 3 specialists) - 🟠 When OpenRouter omits
usage.cost, rate limiting is silently bypassed with only a log line — no metric/counter for alerting on persistent failures (service.py:347). This is the single point of failure for the cost-based system. (Flagged by: architect, performance, security, product — 4 specialists)
⚡ Performance ✅ — Rate-limit check and recording are both O(1) Redis operations. Per-user keys bounded by TTL. No algorithmic concerns.
- 🟡 Two sequential
await get_feature_flag_value()calls inget_global_rate_limits()could be parallelized withasyncio.gatherto cut latency ~50% on that step (rate_limit.py:473). - 🔵
asyncio.gatherof two GETs could be replaced with singleredis.mget()call (rate_limit.py:140).
🧪 Testing
- 🟠
formatMicrodollarsAsUsdis used across 4 files but has no unit test covering edge cases (0, negative, large values, sub-cent rounding) (usageHelpers.ts:5). (Flagged by: testing, product — 2 specialists) - 🟡 Zero-cost turn behavior (
cost_usd=0.0skips rate-limit recording) is likely intentional but no test documents it explicitly (token_tracking.py:182).
📖 Quality ✅ — Readability score: A. Token→cost naming migration is consistent throughout. Docstrings updated, dead code removed (multipliers, header extraction).
- 🟠 Negative
cost_usdsilently bypasses rate-limit recording and cost logging with no warning (token_tracking.py:171). (Flagged by: quality, security — 2 specialists) - 🔵 Truthy chain
cost_microdollars and cost_microdollars > 0should becost_microdollars is not None and cost_microdollars > 0for clarity (token_tracking.py:182). (Flagged by: architect, quality — 2 specialists) - 🔵
logger.errorfor missing cost should belogger.warning— the system handles it gracefully (service.py:347). (Flagged by: quality, performance — 2 specialists)
📦 Product ✅ — UX improvement: $X.XX formatting is more intuitive than raw token counts. Tier multipliers, progress bars, and admin panels all updated correctly.
- 🟡
formatMicrodollarsAsUsduses.toFixed(2)so costs under $0.005 display as$0.00while progress bar shows >0% — contradictory (usageHelpers.ts:6). (Flagged by: architect, product — 2 specialists)
📬 Discussion
- 🔴 CI failing:
check API types— OpenAPI schema in frontend is out of sync with backend changes. Must runpoetry run export-api-schema,pnpm generate:api,pnpm types, and commit.
🔎 QA ✅ — All core scenarios verified end-to-end: real cost extraction (0.107274 USD → 107274 µ$), Redis key migration (copilot:cost prefix confirmed, no copilot:usage keys), tier multipliers (PRO 5× verified), cost accumulation across turns, LD flag fallback defaults, API contract changes, and unauthorized access rejection.
🔴 Blockers
- OpenAPI schema out of sync (
frontend/src/app/api/openapi.json) — Thecheck API typesCI check fails because backend model changes (UserRateLimitResponsefield renames,UsageWindow/SubscriptionTierdescription updates) were not re-exported to the frontend OpenAPI spec. Run:poetry run export-api-schema --output ../frontend/src/app/api/openapi.json && cd ../frontend && pnpm prettier --write src/app/api/openapi.json && pnpm generate:api && pnpm types. (Flagged by: discussion)
🟠 Should Fix
- Add unit tests for
formatMicrodollarsAsUsd(usageHelpers.ts:5) — This helper is used across 4 files with no test. AddusageHelpers.test.tscovering:formatMicrodollarsAsUsd(0),formatMicrodollarsAsUsd(1_500_000),formatMicrodollarsAsUsd(999), negative input. (Flagged by: testing, product — 2) - Log warning for negative
cost_usd(token_tracking.py:171) — Whencost_usdis a valid float but negative,cost_floatstaysNonewith no log. A negative cost from the provider silently bypasses both rate-limit recording and cost logging. Addlogger.warning. (Flagged by: quality, security — 2) - Breaking admin API contract needs coordination (
rate_limit_admin_routes.py:35) — TheUserRateLimitResponsefield renames are breaking for any external consumers. If external scripts/dashboards exist, add Pydantic field aliases to support both names during a transition period, or document the breakage in deploy notes. (Flagged by: architect, security, product — 3)
🟡 Nice to Have
- Add observability metric for missing-cost turns (
service.py:347) — When OpenRouter omitsusage.cost, add a counter/metric (StatsD, Prometheus) alongside the log so persistent failures trigger an alert. This is the single point of failure for cost-based rate limiting. (architect, performance, security, product — 4 specialists flagged this) - Parallelize LD flag fetches (
rate_limit.py:473) — Two sequentialawait get_feature_flag_value()calls are independent;asyncio.gatherwould cut ~50% latency on that step. (performance) - Show
<$0.01instead of$0.00for sub-cent costs (usageHelpers.ts:6) — Prevents contradictory display of$0.00with a visible progress bar. (architect, product — 2) - Add
role="progressbar"ARIA attributes (UsageBar.tsx,UsagePanelContent.tsx) — Pre-existing gap, but now displaying dollar amounts adds informational value for screen readers. (product)
🔵 Nits
- Simplify truthiness chain (
token_tracking.py:182) —cost_microdollars and cost_microdollars > 0→cost_microdollars is not None and cost_microdollars > 0for intent clarity. - Downgrade
logger.errortologger.warning(service.py:347) — The system handles missing cost gracefully; error-level logging could flood during rollout or provider changes. - Extract
extra_bodyconstant (service.py:308) —{"usage": {"include": True}}is recreated on every call; a module-level constant avoids repeated allocation. - Clarify empty-stream test intent (
service_unit_test.py) —_make_stream_mock()called with zero args works but a comment like# empty stream — only validating create() kwargswould help readers.
QA Screenshots
| Screenshot | Description |
|---|---|
![]() |
Browser auth redirect blocked UI testing; all scenarios verified via API instead ✅ |
Human Review Needed
YES — This is a 29-file refactor touching rate limiting, billing, Redis keys, admin API contracts, and LaunchDarkly flags. Multiple specialists flagged the silent rate-limit bypass on missing cost as the single point of failure for the new system. The breaking admin API contract and PR #12668 overlap (~285 conflicting lines) also need human coordination.
Risk Assessment
Merge risk: MEDIUM | Rollback: EASY (Redis key prefix change means old and new systems are isolated; LD flags provide kill switch)
CI Status
❌ 4/6 quality checks failed — Frontend lint, typecheck, build, and tests failed (likely cascading from OpenAPI schema desync). Backend lint passes. Backend tests failed (environment/setup issue, not code).
Why: the previous response leaked raw microdollar used/limit values, letting any client reverse-engineer per-turn Anthropic cost and our platform margin. The admin dashboard still needs the raw figures for debugging, but end-user clients should never see them. What: - Add CoPilotUsagePublic and UsageWindowPublic pydantic models that expose only percent_used (0-100) + resets_at per window, plus tier and reset_cost. - /usage and /usage/reset now return CoPilotUsagePublic; the admin endpoint (UserRateLimitResponse) keeps raw microdollars. - When a window has no cap, the window is null in the response so the frontend can render a simple nullability check. - Frontend: UsagePanelContent, UsageLimits, CopilotPage, BriefingTabContent and credits/page.tsx consume the new schema. UsageBar and UsageMeter now render "N% used" instead of "$X / $Y". - Tests updated to assert both the percent shape and the absence of raw used/limit keys in the payload.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (1)
300-321:⚠️ Potential issue | 🟡 MinorAdd progressbar semantics to the usage meter.
The meter visually represents a percentage, but assistive tech only sees nested divs. Add
role, value bounds, and an accessible label, and clamp the numeric value on both sides.Improve meter accessibility
- const percent = Math.min(100, Math.round(percentUsed)); + const percent = Math.min(100, Math.max(0, Math.round(percentUsed))); @@ <div className="h-2 w-full overflow-hidden rounded-full bg-neutral-200"> <div + role="progressbar" + aria-label={`${label} usage`} + aria-valuemin={0} + aria-valuemax={100} + aria-valuenow={percent} className={`h-full rounded-full transition-[width] duration-300 ease-out ${ isHigh ? "bg-orange-500" : "bg-blue-500" }`} style={{ width: `${Math.max(percent > 0 ? 1 : 0, percent)}%` }} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx around lines 300 - 321, The progress meter in BriefingTabContent uses nested divs (percent, percentUsed, percentLabel, isHigh) but lacks ARIA semantics; clamp the numeric percent to 0–100 (use Math.max(0, Math.min(100, ...)) to compute a safe percentUsed/percent value) and use that clamped value for both the visual width and for accessibility. Update the inner progress bar div to include role="progressbar", aria-valuemin="0", aria-valuemax="100", aria-valuenow set to the clamped numeric percent, and an accessible label (aria-label or aria-labelledby) that includes percentLabel so screen readers get context; keep the existing color-switch logic (isHigh) for visuals only. Ensure aria-valuenow always reflects the same number used in style.width.
🧹 Nitpick comments (3)
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsageLimits.tsx (1)
17-32: Use the sameisSuccessguard as the main Copilot page.This avoids treating idle/error states as equivalent to a successful empty usage response and keeps the usage consumers consistent.
Refactor query readiness guard
- const { data: usage, isLoading } = useGetV2GetCopilotUsage({ + const { data: usage, isSuccess: hasUsage } = useGetV2GetCopilotUsage({ query: { select: (res) => res.data as CoPilotUsagePublic, refetchInterval: 30000, staleTime: 10000, }, }); @@ - if (isLoading || !usage) return null; + if (!hasUsage || !usage) return null; if (!usage.daily && !usage.weekly) return null;Based on learnings, when gating React component logic on a React Query result, prefer destructuring and checking
isSuccessinstead of relying on!isLoading.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/UsageLimits.tsx around lines 17 - 32, The component currently guards rendering with isLoading/usage truthiness which can treat error/idle states as success; update the useGetV2GetCopilotUsage destructure to include isSuccess (e.g., const { data: usage, isSuccess } = useGetV2GetCopilotUsage(...)) and replace the early-return check (if (isLoading || !usage) return null) with a readiness guard that returns early unless isSuccess and usage are present (e.g., if (!isSuccess || !usage) return null), matching the main Copilot page's pattern; keep the rest of the logic (resetCost, hasInsufficientCredits, feature flag) unchanged.autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/page.tsx (1)
30-39: PreferisSuccessfor the usage query guard.This already avoids undefined access via
!usage, butisSuccessmakes the successful-data condition explicit and keeps this consumer aligned with the Copilot query pattern.Refactor query readiness guard
- const { data: usage, isLoading } = useGetV2GetCopilotUsage({ + const { data: usage, isSuccess: hasUsage } = useGetV2GetCopilotUsage({ query: { select: (res) => res.data as CoPilotUsagePublic, refetchInterval: 30000, staleTime: 10000, }, }); - if (isLoading || !usage) return null; + if (!hasUsage || !usage) return null; if (!usage.daily && !usage.weekly) return null;Based on learnings, when gating React component logic on a React Query result, prefer destructuring and checking
isSuccessinstead of relying on!isLoading.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/profile/(user)/credits/page.tsx around lines 30 - 39, Replace the current readiness guard that uses isLoading and !usage with an explicit isSuccess check from the useGetV2GetCopilotUsage result: destructure isSuccess from useGetV2GetCopilotUsage alongside data (usage) and use if (!isSuccess) return null; then keep the existing content checks (e.g., if (!usage.daily && !usage.weekly) return null). This aligns the component with the Copilot query pattern and prevents relying on isLoading/undefined checks for readiness in the useGetV2GetCopilotUsage hook.autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (1)
45-59: Gate the usage section on query success.This panel currently treats “no data yet” and query failure the same. Destructuring
isSuccesskeeps the render path limited to successfully populated usage data.Refactor query readiness guard
- const { data: usage } = useGetV2GetCopilotUsage({ + const { data: usage, isSuccess: hasUsage } = useGetV2GetCopilotUsage({ query: { select: (res) => res.data as CoPilotUsagePublic, refetchInterval: 30000, staleTime: 10000, }, }); @@ - if (!usage || (!usage.daily && !usage.weekly)) return null; + if (!hasUsage || !usage || (!usage.daily && !usage.weekly)) return null;Based on learnings, when gating React component logic on a React Query result, prefer destructuring and checking
isSuccessinstead of relying on missing data/loading state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx around lines 45 - 59, Destructure the query readiness flag from useGetV2GetCopilotUsage and gate rendering on it instead of relying on missing data: change the call to include isSuccess (const { data: usage, isSuccess } = useGetV2GetCopilotUsage(...)) and replace the current readiness check (if (!usage || (!usage.daily && !usage.weekly)) return null) with a single guard that returns early when isSuccess is false (e.g., if (!isSuccess) return null); keep subsequent references to usage (resetCost, usage.daily/weekly) as they are after the isSuccess check so they only run on a successful query.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx:
- Around line 104-112: The test "caps bar width at 100% when over limit"
currently passes dailyPercent: 100 so it doesn't exercise over-limit clamping;
update the fixture returned by mockUseGetV2GetCopilotUsage to use a value
greater than 100 (e.g., dailyPercent: 150) when calling makeUsage, keep
rendering UsageLimits and the assertion that the progressbar (role
"progressbar", name /today usage/i) has aria-valuenow equal to "100" to verify
the component clamps values above 100.
---
Outside diff comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx:
- Around line 300-321: The progress meter in BriefingTabContent uses nested divs
(percent, percentUsed, percentLabel, isHigh) but lacks ARIA semantics; clamp the
numeric percent to 0–100 (use Math.max(0, Math.min(100, ...)) to compute a safe
percentUsed/percent value) and use that clamped value for both the visual width
and for accessibility. Update the inner progress bar div to include
role="progressbar", aria-valuemin="0", aria-valuemax="100", aria-valuenow set to
the clamped numeric percent, and an accessible label (aria-label or
aria-labelledby) that includes percentLabel so screen readers get context; keep
the existing color-switch logic (isHigh) for visuals only. Ensure aria-valuenow
always reflects the same number used in style.width.
---
Nitpick comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/copilot/components/UsageLimits/UsageLimits.tsx:
- Around line 17-32: The component currently guards rendering with
isLoading/usage truthiness which can treat error/idle states as success; update
the useGetV2GetCopilotUsage destructure to include isSuccess (e.g., const {
data: usage, isSuccess } = useGetV2GetCopilotUsage(...)) and replace the
early-return check (if (isLoading || !usage) return null) with a readiness guard
that returns early unless isSuccess and usage are present (e.g., if (!isSuccess
|| !usage) return null), matching the main Copilot page's pattern; keep the rest
of the logic (resetCost, hasInsufficientCredits, feature flag) unchanged.
In
`@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx:
- Around line 45-59: Destructure the query readiness flag from
useGetV2GetCopilotUsage and gate rendering on it instead of relying on missing
data: change the call to include isSuccess (const { data: usage, isSuccess } =
useGetV2GetCopilotUsage(...)) and replace the current readiness check (if
(!usage || (!usage.daily && !usage.weekly)) return null) with a single guard
that returns early when isSuccess is false (e.g., if (!isSuccess) return null);
keep subsequent references to usage (resetCost, usage.daily/weekly) as they are
after the isSuccess check so they only run on a successful query.
In
`@autogpt_platform/frontend/src/app/`(platform)/profile/(user)/credits/page.tsx:
- Around line 30-39: Replace the current readiness guard that uses isLoading and
!usage with an explicit isSuccess check from the useGetV2GetCopilotUsage result:
destructure isSuccess from useGetV2GetCopilotUsage alongside data (usage) and
use if (!isSuccess) return null; then keep the existing content checks (e.g., if
(!usage.daily && !usage.weekly) return null). This aligns the component with the
Copilot query pattern and prevents relying on isLoading/undefined checks for
readiness in the useGetV2GetCopilotUsage hook.
🪄 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: 1ac1a071-86d8-4d25-80ea-a862711969d8
📒 Files selected for processing (17)
autogpt_platform/backend/backend/api/features/chat/routes.pyautogpt_platform/backend/backend/api/features/chat/routes_test.pyautogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/baseline/service_unit_test.pyautogpt_platform/backend/backend/copilot/rate_limit.pyautogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/token_tracking.pyautogpt_platform/backend/backend/copilot/token_tracking_test.pyautogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsageLimits.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsagePanelContentRender.test.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.tsautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsxautogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/page.tsxautogpt_platform/frontend/src/app/api/openapi.json
🚧 Files skipped from review as they are similar to previous changes (6)
- autogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.ts
- autogpt_platform/backend/backend/copilot/token_tracking_test.py
- autogpt_platform/backend/backend/api/features/chat/routes_test.py
- autogpt_platform/backend/backend/copilot/token_tracking.py
- autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsx
- autogpt_platform/backend/backend/api/features/chat/routes.py
…iefing meter - Switch useGetV2GetCopilotUsage consumers (UsageLimits, credits/page, BriefingTabContent) from isLoading to isSuccess so idle/error states don't fall through to the empty-data render path. - Add role=progressbar + aria-valuemin/max/now to the usage meter in BriefingTabContent to match the copilot panel (screen-reader parity). - Update UsageLimits.test.tsx mocks to use isSuccess (matches new readiness flag).
|
Addressed remaining review items in
On the autogpt-pr-reviewer list: tests for |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/rate_limit.py (1)
265-310: Document the invariant thatreset_daily_usageis only called after daily limit is fully exhausted.The function correctly decrements the weekly counter by the full
daily_cost_limitbecause the only caller (the daily reset endpoint) enforces a guard: it verifiesdaily.used >= daily_limitbefore proceeding (line 27 of chat/routes.py). Since the caller always ensures the user has exhausted their daily allowance before resetting it, the accounting is consistent.However, if the call surface widens in the future (e.g., admin resets, mid-day reset UX), this coupling should be explicit: either document the precondition in
reset_daily_usage's docstring, or add a defensive clamp:min(daily_cost_limit, actual_daily_used_before_delete).Separately, the post-pipeline clamp (
redis.set(w_key, 0, keepttl=True)) is already flagged in your comment as best-effort and outside the transaction. A concurrentrecord_cost_usageINCRBY between DECRBY and SET would be lost. This is acceptable for now, but worth revisiting if stricter weekly accounting is required later.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/rate_limit.py` around lines 265 - 310, Document and/or enforce the precondition that reset_daily_usage is only invoked after the user's daily allowance is fully exhausted: update reset_daily_usage's docstring to state the caller must ensure daily.used >= daily_limit (the current daily reset endpoint enforces this), or add a defensive check that reads the current daily counter (use _daily_key and GET) and use min(daily_cost_limit, actual_daily_used_before_delete) when calculating the DECRBY amount so you never decrement weekly by more than was actually used; keep the existing best-effort post-transaction clamp (SET keepttl) but leave a comment noting the possible race with concurrent record_cost_usage INCRBY.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@autogpt_platform/backend/backend/copilot/baseline/service.py`:
- Around line 137-162: _extract_usage_cost currently treats raw = None the same
whether the "cost" key is absent or present-with-null; change it to detect
explicit nulls by inspecting usage.model_extra (e.g., extras = usage.model_extra
or {}), and if "cost" in extras and extras["cost"] is None then log an error via
logger.error (include context like the extras or usage) and return None; leave
the existing numeric parsing and finite/negative checks intact so callers still
receive None but now the provider-null case is logged (this ensures the caller's
dedupe check can see the key while we still surface the provider misbehavior).
---
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/rate_limit.py`:
- Around line 265-310: Document and/or enforce the precondition that
reset_daily_usage is only invoked after the user's daily allowance is fully
exhausted: update reset_daily_usage's docstring to state the caller must ensure
daily.used >= daily_limit (the current daily reset endpoint enforces this), or
add a defensive check that reads the current daily counter (use _daily_key and
GET) and use min(daily_cost_limit, actual_daily_used_before_delete) when
calculating the DECRBY amount so you never decrement weekly by more than was
actually used; keep the existing best-effort post-transaction clamp (SET
keepttl) but leave a comment noting the possible race with concurrent
record_cost_usage INCRBY.
🪄 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: b0182d7c-8f36-4fe7-ba0b-e1f9f8d2dec2
📒 Files selected for processing (5)
autogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/baseline/service_unit_test.pyautogpt_platform/backend/backend/copilot/rate_limit.pyautogpt_platform/backend/backend/copilot/token_tracking.pyautogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py
- autogpt_platform/backend/backend/copilot/token_tracking.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: integration_test
- GitHub Check: lint
- GitHub Check: check API types
- GitHub Check: Seer Code Review
- GitHub Check: end-to-end tests
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (typescript)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (12)
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 developmentFormat frontend code using
pnpm format
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.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/__tests__/usageHelpers.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}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Nodark:Tailwind classes — the design system handles dark mode
Use Next.js<Link>for internal navigation — never raw<a>tags
Noanytypes unless the value genuinely can be anything
No linter suppressors (//@ts-ignore``,// eslint-disable) — fix the actual issue
Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this
Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer
Use generated API hooks from `@/app/api/generated/endpoints/` with pattern `use{Method}{Version}{OperationName}` and regenerate with `pnpm generate:api`
Do not use `useCallback` or `useMemo` unless asked to optimise a given function
Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions in the frontend
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.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 patternuse{Method}{Version}{OperationName}, and regenerate withpnpm 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/componentsfolder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not useuseCallbackoruseMemounless asked to optimise a given function
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
autogpt_platform/frontend/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
No barrel files or
index.tsre-exports in the frontendDo not type hook returns, let Typescript infer as much as possible
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
autogpt_platform/frontend/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Do not type hook returns, let Typescript infer as much as possible
Extract component logic into custom hooks grouped by concern, not by component. Each hook should represent a cohesive domain of functionality (e.g., useSearch, useFilters, usePagination) rather than bundling all state into one useComponentState hook. Put each hook in its own
.tsfile.
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.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 withpnpm test:unit(Vitest + RTL + MSW)
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
autogpt_platform/frontend/src/app/(platform)/**/__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
Write integration tests in
__tests__/next topage.tsxusing Vitest + RTL + MSW for new pages/features
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
autogpt_platform/frontend/src/**/__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
Use Orval-generated MSW handlers from
@/app/api/__generated__/endpoints/{tag}/{tag}.msw.tsfor API mocking
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
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: Usepoetry run ...command for executing Python package dependencies
Use top-level imports only — avoid local/inner imports except for lazy imports of heavy optional dependencies likeopenpyxl
Use absolute imports withfrom 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 — avoidhasattr/getattr/isinstancefor 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%sfor deferred interpolation indebuglog statements for efficiency; use f-strings elsewhere for readability (e.g.,logger.debug("Processing %s items", count)vslogger.info(f"Processing {count} items"))
Sanitize error paths by usingos.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
Usetransaction=Truefor Redis pipelines to ensure atomicity on multi-step operations
Usemax(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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.py
🧠 Learnings (50)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-13T15:49:44.961Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, the original per-session token window (with a TTL-based reset) was replaced with fixed daily and weekly windows. `resets_at` is now derived from `_daily_reset_time()` (midnight UTC) and `_weekly_reset_time()` (next Monday 00:00 UTC) — deterministic fixed-boundary calculations that require no Redis TTL introspection.
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/src/app/(platform)/**/__tests__/**/*.test.{ts,tsx} : Write integration tests in `__tests__/` next to `page.tsx` using Vitest + RTL + MSW for new pages/features
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-08T17:27:45.740Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.740Z
Learning: Applies to autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx} : Run frontend integration tests with `pnpm test:unit` (Vitest + RTL + MSW)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/src/playwright/**/*.spec.ts : E2E tests must import `test` and `expect` from `./coverage-fixture` instead of `playwright/test` to auto-collect V8 coverage per test for Codecov reporting
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/**/*.test.{ts,tsx} : Place unit tests co-located with the file being tested: `Component.test.tsx` next to `Component.tsx` or `utils.test.ts` next to `utils.ts`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-08T17:27:45.740Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.740Z
Learning: Applies to 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
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/src/**/__tests__/**/*.test.{ts,tsx} : Use Orval-generated MSW handlers from `@/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts` for API mocking
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T10:17:09.341Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-15T10:17:09.341Z
Learning: In `Significant-Gravitas/AutoGPT` (`autogpt_platform/frontend`), `vitest.config.mts` does NOT set `globals: true`, and `src/tests/integrations/vitest.setup.tsx` does NOT register the `testing-library/react` auto-cleanup hook. Therefore, integration test files (e.g., under `__tests__/`) MUST include an explicit `afterEach(cleanup)` call from `testing-library/react` to reset the DOM between tests — without it, tests fail with "multiple elements found" errors from the prior test's DOM. Do NOT flag `afterEach(cleanup)` as redundant in this codebase.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/**/__tests__/**/*.test.{ts,tsx} : Place integration tests in a `__tests__` folder next to the component being tested
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-08T17:27:57.501Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:57.501Z
Learning: Applies to autogpt_platform/frontend/**/*.spec.{ts,tsx} : Create a failing test first using `.fixme` marker (Playwright) when fixing a bug or adding a feature, then implement the fix and remove the fixme marker
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/src/playwright/**/*.spec.ts : Place E2E test files in a centralized location at `src/playwright/*.spec.ts`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Use unit tests (Vitest + RTL) for pure utility functions, component rendering with various props, component state changes, and shared hooks with standalone business logic
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 Learning: 2026-04-20T13:17:44.423Z
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:44.423Z
Learning: In the AutoGPT frontend codebase, `testing-library/react`'s `cleanup()` is called globally after each test via `src/tests/integrations/vitest.setup.tsx`. Integration test files under `__tests__/` do NOT need an explicit `afterEach(cleanup)` call; only timer teardown (e.g. `afterEach(() => vi.useRealTimers())`) needs to be added locally when fake timers are used.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts
📚 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/__tests__/usageHelpers.test.ts
📚 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/__tests__/usageHelpers.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/__tests__/usageHelpers.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/__tests__/usageHelpers.test.ts
📚 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/__tests__/usageHelpers.test.ts
📚 Learning: 2026-03-15T15:30:09.706Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/tools/helpers.py:149-185
Timestamp: 2026-03-15T15:30:09.706Z
Learning: In autogpt_platform/backend/backend/copilot/tools/helpers.py, within execute_block, when InsufficientBalanceError occurs after post-execution credit charging (concurrent balance drain after pre-check passed), this is treated as a non-fatal billing leak: log at ERROR level with structured JSON fields `{"billing_leak": True, "user_id": ..., "cost": ...}` for monitoring/alerting, then return BlockOutputResponse normally. Discarding the output would worsen UX since the block already executed with potential side effects. Reuse the credit_model obtained during the pre-execution balance check (guarded by `if cost > 0 and credit_model:`) for the post-execution charge; do not perform a second get_user_credit_model call.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-17T10:57:12.953Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12440
File: autogpt_platform/backend/backend/copilot/workflow_import/converter.py:0-0
Timestamp: 2026-03-17T10:57:12.953Z
Learning: In Significant-Gravitas/AutoGPT PR `#12440`, `autogpt_platform/backend/backend/copilot/workflow_import/converter.py` was fully rewritten (commit 732960e2d) to no longer make direct LLM/OpenAI API calls. The converter now builds a structured text prompt for AutoPilot/CoPilot instead. There is no `response.choices` access or any direct LLM client usage in this file. Do not flag `response.choices` access or LLM client initialization patterns as issues in this file.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-02T14:27:41.807Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12651
File: autogpt_platform/frontend/src/app/api/openapi.json:8653-8696
Timestamp: 2026-04-02T14:27:41.807Z
Learning: Repo: Significant-Gravitas/AutoGPT — Platform costs
The PlatformCostLog.duration is stored in DB but intentionally omitted from the CostLogRow API response to keep the raw logs compact. Do not flag this omission; suggest documenting the intent in the route description if needed.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-17T06:48:26.471Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12445
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1071-1072
Timestamp: 2026-03-17T06:48:26.471Z
Learning: In Significant-Gravitas/AutoGPT (autogpt_platform), the AI SDK enforces `z.strictObject({type, errorText})` on SSE `StreamError` responses, so additional fields like `retryable: bool` cannot be added to `StreamError` or serialized via `to_sse()`. Instead, retry signaling for transient Anthropic API errors is done via the `COPILOT_RETRYABLE_ERROR_PREFIX` constant prepended to persisted session messages (in `ChatMessage.content`). The frontend detects retryable errors by checking `markerType === "retryable_error"` from `parseSpecialMarkers()` — no SSE schema changes and no string matching on error text. This pattern was established in PR `#12445`, commit 64d82797b.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-24T21:25:15.983Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12536
File: autogpt_platform/frontend/src/app/api/openapi.json:5770-5790
Timestamp: 2026-03-24T21:25:15.983Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12536`
File: autogpt_platform/frontend/src/app/api/openapi.json
Learning: The OpenAPI spec file is auto-generated; per established convention, endpoints generally declare only 200/201, 401, and 422 responses. Do not suggest adding explicit 403/404 response entries for single operations unless planning a repo-wide spec update. Prefer clarifying such behaviors in endpoint descriptions/docstrings instead of altering response maps.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-09T10:50:43.907Z
Learnt from: Bentlybro
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-03-09T10:50:43.907Z
Learning: Repo: Significant-Gravitas/AutoGPT — File: autogpt_platform/backend/backend/blocks/llm.py
For xAI Grok models accessed via OpenRouter, the API returns `null` for `max_completion_tokens`. The convention in this codebase is to use the model's context window size as the `max_output_tokens` value in ModelMetadata. For example, Grok 3 uses 131072 (128k) and Grok 4 uses 262144 (256k). Do not flag these as incorrect max output token values.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-16T07:34:53.523Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-16T07:34:53.523Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, the `record_token_usage` Redis warning log omits `user_id` entirely. The final log message is `"Redis unavailable for recording token usage (tokens=%d)"` with only the token count — no user identifier (full or truncated) is included.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-04T23:58:18.476Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:18.476Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-02-27T15:59:00.370Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12213
File: autogpt_platform/frontend/src/app/api/openapi.json:9983-9995
Timestamp: 2026-02-27T15:59:00.370Z
Learning: Repo: Significant-Gravitas/AutoGPT PR: 12213 — OpenAPI/codegen
Learning: Ensuring a field is required in generated TS types needs two sides: (1) no default value on the Pydantic field, and (2) the OpenAPI model's "required" array must list it. For MCPToolInfo, making input_schema required in OpenAPI and removing Field(default_factory=dict) in the backend prevents optional typing drift.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-03-10T08:39:22.025Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-15T13:44:34.273Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12797
File: autogpt_platform/backend/backend/copilot/sdk/service.py:1991-2021
Timestamp: 2026-04-15T13:44:34.273Z
Learning: In `autogpt_platform/backend/backend/copilot/sdk/service.py` (`_run_stream_attempt`), the pre-create block (PR `#12797`) intentionally does NOT call `state.transcript_builder.append_assistant(...)` when inserting the empty assistant placeholder into `ctx.session.messages`. The transcript is left ending at the `tool_result` entry (N entries) while `message_count` metadata is N+1. This mismatch is benign and deliberate: on the next `--resume`, the SDK sees the transcript ending at `tool_result` and correctly regenerates the assistant response. Pre-appending the assistant turn to the transcript would suppress regeneration while leaving `session.messages[-1].content = ""` permanently (worse outcome). On the gap-fallback path, `transcript_msg_count (N+1) >= msg_count-1 (N)` means no gap is injected for the empty placeholder, which is correct because injecting an empty assistant message as context would mislead the SDK. Do NOT flag this transcript/message_count discrepancy as a bug.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.py
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.
Applied to files:
autogpt_platform/backend/backend/copilot/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.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/baseline/service.pyautogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-13T15:49:44.961Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-13T15:49:44.961Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, the original per-session token window (with a TTL-based reset) was replaced with fixed daily and weekly windows. `resets_at` is now derived from `_daily_reset_time()` (midnight UTC) and `_weekly_reset_time()` (next Monday 00:00 UTC) — deterministic fixed-boundary calculations that require no Redis TTL introspection.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-15T23:39:39.754Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-15T23:39:39.754Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, `record_token_usage` uses the same helper functions (`_daily_reset_time()` / `_weekly_reset_time()`) to compute both `resets_at` (the reset timestamp returned to callers) and the Redis key `expire` seconds. This single-source-of-truth design guarantees that the reported reset times and the actual Redis TTLs are always in sync — there is no separate TTL constant that could diverge from the calendar-boundary calculation.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-15T15:29:20.889Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-15T15:29:20.889Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, the daily and weekly Redis keys encode the current date/week directly in the key name (e.g., `copilot:usage:daily:{user_id}:{YYYY-MM-DD}` and `copilot:usage:weekly:{user_id}:{year}-W{week}`). This means a new key is naturally created at each window boundary, so `resets_at` (derived from `_daily_reset_time()` / `_weekly_reset_time()`) is always accurate without any Redis TTL introspection — the key rotation and reset-time calculation are inherently synchronized.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-03T13:50:29.037Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12206
File: autogpt_platform/backend/backend/api/external/v2/rate_limit.py:24-56
Timestamp: 2026-04-03T13:50:29.037Z
Learning: In `autogpt_platform/backend/backend/api/external/v2/rate_limit.py`, the `RateLimiter` class uses in-process (per-worker) memory for sliding-window rate limiting. This is intentionally documented as a known limitation via WARNING comments in the module and class docstrings. A full Redis-backed migration (using ZADD/ZREMRANGEBYSCORE/ZCARD with TTL/Lua for atomic multi-replica enforcement) is deferred to a later PR. Do not re-flag the in-memory implementation as a blocking bug — the limitation is documented and accepted for the initial v2 external API release.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-12T14:42:40.552Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:141-170
Timestamp: 2026-03-12T14:42:40.552Z
Learning: In Significant-Gravitas/AutoGPT, `check_rate_limit` in `autogpt_platform/backend/backend/copilot/rate_limit.py` is intentionally a "pre-turn soft check" (not a hard atomic reservation). Because LLM token counts are unknown before generation completes, a strict check-and-reserve is impractical. The TOCTOU race (two concurrent turns both passing the pre-check and both committing via `record_token_usage`) is an accepted trade-off. If stricter enforcement is ever needed, the approach is a Lua script doing GET+INCRBY atomically in Redis.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-17T07:24:34.302Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-17T07:24:34.302Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, all fail-open `except` blocks catch `(RedisError, ConnectionError, OSError)` specifically — not bare `except Exception`. This applies to `_session_reset_from_ttl`, `get_usage_status`, `check_rate_limit`, and `record_token_usage`. The narrowed tuple ensures only genuine Redis/network failures are swallowed; unexpected exceptions propagate normally.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-09T08:47:32.750Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/graphiti/client.py:20-46
Timestamp: 2026-04-09T08:47:32.750Z
Learning: In Significant-Gravitas/AutoGPT, `user_id` values passed to `derive_group_id` in `autogpt_platform/backend/backend/copilot/graphiti/client.py` are always system-generated UUIDv4s (e.g. `883cc9da-fe37-4863-839b-acba022bf3ef`). The character set `[0-9a-f-]` is fully within `[a-zA-Z0-9_-]`, so the sanitization regex never strips any characters and no collision between two different user IDs is possible. Do not flag `derive_group_id` for collision-resistance issues.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-10T11:22:18.867Z
Learnt from: Swiftyos
Repo: Significant-Gravitas/AutoGPT PR: 12347
File: autogpt_platform/backend/backend/data/invited_user.py:193-193
Timestamp: 2026-03-10T11:22:18.867Z
Learning: In Significant-Gravitas/AutoGPT, the admin data-layer functions in `autogpt_platform/backend/backend/data/invited_user.py` (`list_invited_users`, `create_invited_user`, `revoke_invited_user`, `retry_invited_user_tally`, `bulk_create_invited_users_from_file`) intentionally omit an acting-user/admin ID parameter. Authorization for these functions is enforced entirely at the FastAPI router layer via `Security(requires_admin_user)` in `user_admin_routes.py`. Do not flag the absence of a user_id/actor_id parameter in these functions as a missing data-access guardrail violation.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-08T17:27:45.740Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.740Z
Learning: Applies to autogpt_platform/**/data/**/*.py : For changes touching `data/*.py`, validate user ID checks or explain why not needed
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-02-04T16:49:42.490Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-04T16:49:42.490Z
Learning: Applies to autogpt_platform/backend/backend/data/**/*.py : All data access in backend requires user ID checks; verify this for any 'data/*.py' changes
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-15T22:50:02.270Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11235
File: autogpt_platform/backend/backend/data/diagnostics.py:0-0
Timestamp: 2026-04-15T22:50:02.270Z
Learning: In Significant-Gravitas/AutoGPT, the admin diagnostic data-layer functions in `autogpt_platform/backend/backend/data/diagnostics.py` (e.g., `get_execution_diagnostics`, `get_agent_diagnostics`, `get_schedule_health_metrics`, `get_all_schedules_details`, `get_running_executions_details`, `get_orphaned_executions_details`, `get_long_running_executions_details`, `get_stuck_queued_executions_details`, `get_invalid_executions_details`, `get_failed_executions_count`, `get_failed_executions_details`) intentionally omit a `user_id`/`admin_user_id` parameter. These functions require cross-user, system-wide visibility for admin diagnostics. Authorization is enforced entirely at the FastAPI router layer via `Security(requires_admin_user)` in `diagnostics_admin_routes.py`. Do not flag the absence of a user_id/admin_user_id parameter in these read functions as a missing data-access guardrail violation. Note: write/mutating functions like `cleanup_orphaned_execution`, `stop_all_long_running_executio...
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-04-09T09:07:11.551Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12720
File: autogpt_platform/backend/backend/copilot/tools/graphiti_delete.py:63-69
Timestamp: 2026-04-09T09:07:11.551Z
Learning: In Significant-Gravitas/AutoGPT, gating `graphiti_delete_user_data` (and similar Graphiti memory tools) on the `is_enabled_for_user` / `graphiti-memory` LaunchDarkly flag in the delete path is intentional and acceptable. The scenario where a user has existing Graphiti data but the flag is later disabled (preventing deletion) is not a concern for the team. Do not flag this pattern as an issue.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-10T08:38:36.655Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/tools/run_block.py:349-370
Timestamp: 2026-03-10T08:38:36.655Z
Learning: In the AutoGPT CoPilot HITL (Human-In-The-Loop) flow (`autogpt_platform/backend/backend/copilot/tools/run_block.py`), the review card presented to users sets `editable: false`, meaning reviewers cannot modify the input payload. Therefore, credentials resolved before `is_block_exec_need_review()` remain valid and do not need to be recomputed after the review step — the original `input_data` is unchanged through the review lifecycle.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
📚 Learning: 2026-03-11T16:50:45.654Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12367
File: autogpt_platform/backend/generate_views.py:146-150
Timestamp: 2026-03-11T16:50:45.654Z
Learning: In Significant-Gravitas/AutoGPT PR `#12367` (`autogpt_platform/backend/generate_views.py`), the `security_invoker = false` setting on analytics views is intentional. `analytics_readonly` is granted access only to the `analytics` schema. The views execute as their owner (postgres) to read `auth.*` and `platform.*` tables, but expose only curated, scrubbed columns via their SELECT lists. This is the correct PostgreSQL "view as security boundary" pattern — equivalent to a stored-procedure security definer. Do not flag this as a privilege escalation; switching to `security_invoker = true` would require granting `analytics_readonly` direct SELECT on `auth.sessions`, `auth.audit_log_entries`, `platform.AgentGraphExecution`, etc., which is a much wider blast radius than the current design.
Applied to files:
autogpt_platform/backend/backend/copilot/rate_limit.py
🔇 Additional comments (5)
autogpt_platform/frontend/src/app/(platform)/copilot/components/__tests__/usageHelpers.test.ts (1)
1-76: LGTM!Test coverage aligns with the helper implementation: microdollar/cent conversions, the
<$0.01sub-cent branch, and the threeformatResetTimebranches (past →now, sub-hour minutes, same-dayXh Ym, future-day localized) are all exercised. The future-day assertion wisely avoids pinning locale/timezone output by only checking the negative-match against the relative-time form.autogpt_platform/backend/backend/copilot/baseline/service.py (1)
346-404: LGTM — streaming cost accumulation is solid.The per-chunk
state.cost_usdaccumulation pairs correctly withstream_options={"include_usage": True}(OpenAI/OpenRouter emit usage only in the final chunk, so accumulating is equivalent to a single read). Thecost_missing_loggeddedup keeps non-OpenRouter providers from flooding logs while still surfacing persistent misconfiguration, and invalid-value logging is routed through_extract_usage_costso no branch is silent.One thing worth double-checking in follow-up testing: in a multi-round tool-call loop within a single turn,
cost_missing_loggedpersists across rounds (sharedstate). That's fine for noise control, but if a later round starts reporting cost again after an earlier miss, you won't get a second warning once it's fixed mid-turn — acceptable trade-off, just noting it.autogpt_platform/backend/backend/copilot/rate_limit.py (3)
31-155: LGTM — clean key-prefix migration and public/internal separation.Bumping the Redis prefix to
copilot:costis the right call to avoid misreading legacy token counters as microdollars, and pairing it with the LD flag rename gives a coherent cutover story.CoPilotUsagePublic.from_statuscorrectly guards division by zero (w.limit <= 0 → None) and clampspercent_usedat 100.0, so clients can't reverse-engineer raw spend while still getting a monotonically meaningful gauge.Deploy reminder (already in PR notes): create the new LD flags (
copilot-daily-cost-limit-microdollars,copilot-weekly-cost-limit-microdollars) before rollout, otherwiseget_global_rate_limitsfalls back toconfig_daily/config_weeklyfromChatConfig.
370-422: LGTM —record_cost_usageis tight.
max(0, cost_microdollars)+ early return on non-positive matches the upstream validation intoken_tracking.persist_and_record_usage(finite/non-negative float → microdollars), so invalid costs are rejected at the boundary and this function can stay purely a counter.transaction=Trueon the pipeline ensures INCRBY/EXPIRE pairs land atomically, preserving the key-rotation invariant. Log no longer leaksuser_idper prior review.
525-555: LGTM — parallel LD fetch is a nice latency win.
asyncio.gathercuts the serial LD round-trips roughly in half on the pre-turn hot path, and the TypeError/ValueError fallback per flag keeps a single bad flag value from taking out both windows. Tier multiplier stays int so microdollar math remains exact.
CodeRabbit found that `{"cost": null}` slipped through silently:
- `_extract_usage_cost` returned None because `raw is None`, and
- the caller's dedup check `"cost" not in model_extra` was False
(key exists with a null value), so no missing-cost warning fired.
Now `_extract_usage_cost` distinguishes absent from present-but-null;
the null branch logs an error at the helper (same severity as
non-numeric / non-finite / negative). Added a unit test asserting both
the return value and the log record.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (1)
301-326:⚠️ Potential issue | 🟡 MinorAvoid showing
100% usedbefore the limit is actually exhausted.
Math.round(99.5)displays100% usedand setsaria-valuenow={100}, butUsageFooteronly enables reset actions when the raw value is>= 100.🐛 Proposed fix
- const percent = Math.min(100, Math.max(0, Math.round(percentUsed))); - const isHigh = percent >= 80; + const boundedPercent = Math.min(100, Math.max(0, percentUsed)); + const percent = + boundedPercent >= 100 ? 100 : Math.floor(boundedPercent); + const isHigh = boundedPercent >= 80; const percentLabel = - percentUsed > 0 && percent === 0 ? "<1% used" : `${percent}% used`; + boundedPercent > 0 && percent === 0 ? "<1% used" : `${percent}% used`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx around lines 301 - 326, The displayed/ARIA percent should not round 99.5→100 unless the raw percentUsed is actually >=100: change the percent calculation in BriefingTabContent from using Math.round(percentUsed) to a conditional that yields 100 only when percentUsed >= 100, otherwise clamp rounded values to at most 99 (e.g. percent = percentUsed >= 100 ? 100 : Math.min(99, Math.max(0, Math.round(percentUsed)))); ensure percentLabel, aria-valuenow and the progress bar width all use this new percent variable so the UI and ARIA state only show 100% when the raw percentUsed is >= 100.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx:
- Around line 301-326: The displayed/ARIA percent should not round 99.5→100
unless the raw percentUsed is actually >=100: change the percent calculation in
BriefingTabContent from using Math.round(percentUsed) to a conditional that
yields 100 only when percentUsed >= 100, otherwise clamp rounded values to at
most 99 (e.g. percent = percentUsed >= 100 ? 100 : Math.min(99, Math.max(0,
Math.round(percentUsed)))); ensure percentLabel, aria-valuenow and the progress
bar width all use this new percent variable so the UI and ARIA state only show
100% when the raw percentUsed is >= 100.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7ac6e9c4-4519-43fd-8d05-2208286a8dae
📒 Files selected for processing (4)
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsageLimits.tsxautogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsxautogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/page.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/page.tsx
- autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsageLimits.tsx
📜 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). (7)
- GitHub Check: integration_test
- GitHub Check: end-to-end tests
- GitHub Check: test (3.12)
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: Seer Code Review
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (12)
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 developmentFormat frontend code using
pnpm format
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
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}: Fully capitalize acronyms in symbols, e.g.graphID,useBackendAPI
Use function declarations (not arrow functions) for components and handlers
Nodark:Tailwind classes — the design system handles dark mode
Use Next.js<Link>for internal navigation — never raw<a>tags
Noanytypes unless the value genuinely can be anything
No linter suppressors (//@ts-ignore``,// eslint-disable) — fix the actual issue
Keep files under ~200 lines; extract sub-components or hooks into their own files when a file grows beyond this
Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer
Use generated API hooks from `@/app/api/generated/endpoints/` with pattern `use{Method}{Version}{OperationName}` and regenerate with `pnpm generate:api`
Do not use `useCallback` or `useMemo` unless asked to optimise a given function
Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Use ErrorCard for render errors, toast for mutations, and Sentry for exceptions in the frontend
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
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 patternuse{Method}{Version}{OperationName}, and regenerate withpnpm 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/componentsfolder
Use function declarations for components and handlers, use arrow functions only for callbacks
Do not useuseCallbackoruseMemounless asked to optimise a given function
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
autogpt_platform/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never type with
any, if no types available useunknown
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
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 withpnpm test:unit(Vitest + RTL + MSW)
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
autogpt_platform/frontend/src/app/(platform)/**/components/**/*.tsx
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
Put sub-components in local
components/folder
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
autogpt_platform/frontend/**/*.tsx
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
autogpt_platform/frontend/**/*.tsx: Component props should betype Props = { ... }(not exported) unless it needs to be used outside the component
Use design system components fromsrc/components/(atoms, molecules, organisms)
Never usesrc/components/__legacy__/*
Tailwind CSS only for styling, use design tokens, Phosphor Icons only
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
autogpt_platform/frontend/src/app/(platform)/**/__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
Write integration tests in
__tests__/next topage.tsxusing Vitest + RTL + MSW for new pages/features
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
autogpt_platform/frontend/src/**/__tests__/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (autogpt_platform/frontend/AGENTS.md)
Use Orval-generated MSW handlers from
@/app/api/__generated__/endpoints/{tag}/{tag}.msw.tsfor API mocking
Files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
🧠 Learnings (26)
📓 Common learnings
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12385
File: autogpt_platform/backend/backend/copilot/rate_limit.py:0-0
Timestamp: 2026-03-13T15:49:44.961Z
Learning: In `autogpt_platform/backend/backend/copilot/rate_limit.py`, the original per-session token window (with a TTL-based reset) was replaced with fixed daily and weekly windows. `resets_at` is now derived from `_daily_reset_time()` (midnight UTC) and `_weekly_reset_time()` (next Monday 00:00 UTC) — deterministic fixed-boundary calculations that require no Redis TTL introspection.
📚 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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/src/playwright/**/*.spec.ts : E2E tests must import `test` and `expect` from `./coverage-fixture` instead of `playwright/test` to auto-collect V8 coverage per test for Codecov reporting
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/src/**/__tests__/**/*.test.{ts,tsx} : Use Orval-generated MSW handlers from `@/app/api/__generated__/endpoints/{tag}/{tag}.msw.ts` for API mocking
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-08T17:27:45.740Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.740Z
Learning: Applies to autogpt_platform/frontend/**/*.{test,spec}.{ts,tsx} : Run frontend integration tests with `pnpm test:unit` (Vitest + RTL + MSW)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-15T14:10:52.947Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/src/tests/AGENTS.md:0-0
Timestamp: 2026-04-15T14:10:52.947Z
Learning: Applies to autogpt_platform/frontend/src/tests/**/*.test.{ts,tsx} : Place unit tests co-located with the file being tested: `Component.test.tsx` next to `Component.tsx` or `utils.test.ts` next to `utils.ts`
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-08T17:27:45.740Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-08T17:27:45.740Z
Learning: Applies to 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
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-15T10:17:09.341Z
Learnt from: kcze
Repo: Significant-Gravitas/AutoGPT PR: 0
File: :0-0
Timestamp: 2026-04-15T10:17:09.341Z
Learning: In `Significant-Gravitas/AutoGPT` (`autogpt_platform/frontend`), `vitest.config.mts` does NOT set `globals: true`, and `src/tests/integrations/vitest.setup.tsx` does NOT register the `testing-library/react` auto-cleanup hook. Therefore, integration test files (e.g., under `__tests__/`) MUST include an explicit `afterEach(cleanup)` call from `testing-library/react` to reset the DOM between tests — without it, tests fail with "multiple elements found" errors from the prior test's DOM. Do NOT flag `afterEach(cleanup)` as redundant in this codebase.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/src/app/(platform)/**/__tests__/**/*.test.{ts,tsx} : Write integration tests in `__tests__/` next to `page.tsx` using Vitest + RTL + MSW for new pages/features
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-07T18:08:03.548Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12701
File: .claude/skills/orchestrate/scripts/verify-complete.sh:120-121
Timestamp: 2026-04-07T18:08:03.548Z
Learning: In Significant-Gravitas/AutoGPT, verify-complete.sh (`.claude/skills/orchestrate/scripts/verify-complete.sh`) uses `commits[-1].committedDate` (not `updatedAt`) to identify stale CHANGES_REQUESTED reviews. This is intentional: `updatedAt` changes on any PR activity (bot comments, label changes, description edits), which would falsely classify a reviewer's CHANGES_REQUESTED as stale — a silent false negative. The `committedDate` edge case (commit created locally before a review but pushed after) only causes a false positive (unnecessary re-brief), which is the safer failure mode. Do not suggest switching to `updatedAt` for this comparison.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-03-10T08:39:22.025Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12356
File: autogpt_platform/backend/backend/copilot/constants.py:9-12
Timestamp: 2026-03-10T08:39:22.025Z
Learning: In Significant-Gravitas/AutoGPT PR `#12356`, the `COPILOT_SYNTHETIC_ID_PREFIX = "copilot-"` check in `create_auto_approval_record` (human_review.py) is intentional and safe. The `graph_exec_id` passed to this function comes from server-side `PendingHumanReview` DB records (not from user input); the API only accepts `node_exec_id` from users. Synthetic `copilot-*` IDs are only ever created server-side in `run_block.py`. The prefix skip avoids a DB lookup for a `AgentGraphExecution` record that legitimately does not exist for CoPilot sessions, while `user_id` scoping is enforced at the auth layer and on the resulting auto-approval record.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx
📚 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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.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/UsageLimits/__tests__/UsageLimits.test.tsx
📚 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/UsageLimits/__tests__/UsageLimits.test.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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.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/UsageLimits/__tests__/UsageLimits.test.tsxautogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.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/UsageLimits/__tests__/UsageLimits.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/UsageLimits/__tests__/UsageLimits.test.tsx
📚 Learning: 2026-04-15T14:10:18.177Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/backend/copilot/graphiti/CLAUDE.md:0-0
Timestamp: 2026-04-15T14:10:18.177Z
Learning: Applies to autogpt_platform/backend/backend/copilot/graphiti/**/*agent*.{ts,tsx} : Use dependency injection for agent dependencies to improve testability
Applied to files:
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 Learning: 2026-04-15T14:10:18.177Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/backend/backend/copilot/graphiti/CLAUDE.md:0-0
Timestamp: 2026-04-15T14:10:18.177Z
Learning: Applies to autogpt_platform/backend/backend/copilot/graphiti/**/*agent*.{ts,tsx} : All agent interactions must include proper logging and monitoring hooks
Applied to files:
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 Learning: 2026-04-15T22:50:11.883Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 11235
File: autogpt_platform/frontend/src/app/(platform)/admin/diagnostics/components/DiagnosticsContent.tsx:4-11
Timestamp: 2026-04-15T22:50:11.883Z
Learning: In `autogpt_platform/frontend/src/app/(platform)/admin/diagnostics/components/DiagnosticsContent.tsx`, the design system Card at `@/components/atoms/Card/Card.tsx` only exports `Card` (a simple wrapper div with no sub-components). `CardHeader`, `CardTitle`, `CardContent`, and `CardDescription` only exist in `@/components/__legacy__/ui/card`. The correct pattern is to import `Card` from the design system and the sub-components from legacy until the design system Card is extended. Do not flag this split import as a blocking issue.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 Learning: 2026-03-26T00:32:06.673Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12566
File: autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts:968-974
Timestamp: 2026-03-26T00:32:06.673Z
Learning: In Significant-Gravitas/AutoGPT, the admin-facing methods in `autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts` (e.g., `addUserCredits`, `getUsersHistory`, `getUserRateLimit`, `resetUserRateLimit`) intentionally follow the legacy `BackendAPI` pattern with manually defined types in `autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts`. Migrating these admin endpoints to the generated OpenAPI hooks (`@/app/api/__generated__/endpoints/`) is a planned separate effort covering all admin endpoints together, not done piecemeal per PR. Do not flag individual admin type additions in `types.ts` as blocking issues.
Applied to files:
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
📚 Learning: 2026-04-08T17:28:40.841Z
Learnt from: CR
Repo: Significant-Gravitas/AutoGPT PR: 0
File: autogpt_platform/frontend/AGENTS.md:0-0
Timestamp: 2026-04-08T17:28:40.841Z
Learning: Applies to autogpt_platform/frontend/**/*.{ts,tsx} : Separate render logic (`.tsx`) from business logic (`use*.ts` hooks)
Applied to files:
autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx
🔇 Additional comments (6)
autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx (2)
29-50: LGTM — fixture aligns withCoPilotUsagePublicshape.The updated
makeUsagehelper correctly models the new percent-based public API (percent_used+resets_at, withnullfor unconfigured windows) and theisSuccessgating in the test cases matches the preferred React Query pattern.
104-114: Over-limit clamp test now correctly exercises the 150% case.Confirmed addressed from prior review —
dailyPercent: 150properly verifies thearia-valuenowclamp at"100".autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (4)
3-3: LGTM — uses the generated public usage model.This keeps the component aligned with the new percent-only API shape.
44-60: LGTM —isSuccessis the right render gate here.This avoids treating idle/error states as successful empty usage responses. Based on learnings, React Query component logic should prefer checking
isSuccessbecauseisLoadingcan be false whiledatais still undefined.
84-97: LGTM — meter inputs now match the public API contract.Rendering by window presence and passing
percent_usedkeeps raw cost/limit values out of this public-facing panel.
241-253: LGTM — footer logic is now percent-based.The reset/add-credit visibility checks are aligned with the new public usage shape.
…tray followups.md
|
/review |
…values Mirror the UsageMeter fallback from BriefingTabContent so users with tiny non-zero spend see a consistent '<1% used' label across both surfaces, rather than a misleading '0% used' with an empty bar.
There was a problem hiding this comment.
📋 Automated Review — PR #12864
PR #12864 — feat(copilot): real OpenRouter cost + cost-based rate limits (percent-only public API)
Author: majdyz | Files: 35
🎯 Verdict: REQUEST_CHANGES
PR Description Quality
✅ Has Why + What + How — PR clearly explains the migration from token-weighted estimates to real OpenRouter cost, the public API privacy model (percent-only), Redis key migration, and LD flag changes.
What This PR Does
Previously, copilot rate limiting used synthetic token-weighted estimates with hand-tuned multipliers (Opus 3×, cache discounts) that drifted from actual billing. This PR replaces that with real cost reported by OpenRouter via usage.cost in streaming responses, stored as microdollars in Redis. The public API now exposes only percent_used (hiding raw spend), while the admin API shows actual microdollar amounts. Redis keys migrate from copilot:usage:* to copilot:cost:*, and LaunchDarkly flags are renamed with -microdollars suffixes.
Specialist Findings
🛡️ Security
- 🟠 Rate-limit bypass when cost is missing (
token_tracking.py:195-202): When OpenRouter omitsusage.cost, the rate-limit counter is never incremented — users face zero rate limiting. (Flagged by: security, architect, product — 3 specialists) - 🟡 No sanity check on provider-reported cost (
baseline/service.py:137-167): If OpenRouter under-reports cost (bug or compromise), rate limits become ineffective with no independent validation. - 🟡 Fail-open on Redis unavailability (
rate_limit.py:262-264): Combined with cost-only tracking (no token fallback), a Redis outage now means zero rate limiting of any kind.
🏗️ Architecture ✅ — Clean separation between CoPilotUsagePublic and CoPilotUsageStatus. Removed fragile model-cost-multiplier coupling. asyncio.gather for parallel LD flag fetches is a good latency improvement.
- 🟠 Breaking admin API contract (
rate_limit_admin_routes.py:35):UserRateLimitResponsefield renames (daily_token_limit→daily_cost_limit_microdollars) break any existing admin tooling. No aliases or deprecation path. (Flagged by: architect, discussion — 2 specialists) - 🟡
transaction=Trueadds unnecessary MULTI/EXEC overhead (rate_limit.py:407): The two INCRBY+EXPIRE pairs target independent keys; cross-key atomicity provides no benefit. (Flagged by: architect, performance — 2 specialists) - 🔵 Module name
token_tracking.pyis misleading now that it primarily orchestrates cost recording.
⚡ Performance ✅ — All rate-limit operations remain O(1) Redis commands. No hot-path allocations. The asyncio.gather parallelization for LD flag fetches reduces latency. The transaction=True change adds ~1 extra RTT per pipeline but is acceptable at current scale.
🧪 Testing
- 🟠 No unit tests for
CoPilotUsagePublic.from_status()(rate_limit.py:137): This security-relevant projection has three distinct branches (limit≤0→None, used≥limit→100.0, else percentage). Only exercised indirectly with a single happy-path case. (Flagged by: testing — 1 specialist) - 🟠 Near-boundary rounding untested (
rate_limit.py:151):round(100.0 * 9995/10000, 1)= 100.0, making UI show "limit reached" with headroom remaining. No test documents whether this is intentional. (Flagged by: testing — 1 specialist) - 🟠 Pipeline
transaction=Truechange unasserted (rate_limit_test.py:255): Tests mockpipeline()without assertingtransaction=Trueis passed. A regression would silently break the atomicity guarantee. (Flagged by: testing — 1 specialist)
📖 Quality ✅ — Excellent naming consistency (token → cost/microdollars applied uniformly). Thorough docstrings. Clean public/internal schema separation.
- 🔵 Cost validation duplicated between
_extract_usage_costandpersist_and_record_usage— consider extracting a shared_parse_cost_usd()helper. - 🔵
logger.infoatrate_limit.py:399lost theuser_id[:8]prefix, making per-user log correlation harder. - 🔵 Barrel-style re-export in
UsageLimits.tsx:16violates AGENTS.md convention.
📦 Product ✅ — Core UX flow is solid: percentage-based usage bars, credit-funded resets, privacy-preserving public API. Accessibility is good with proper ARIA attributes on progress bars.
- 🟡 Small text contrast concern (
UsagePanelContent.tsx:29):text-[10px] text-neutral-400may not meet WCAG AA 4.5:1 contrast ratio. - 🟡 No explicit "Limit reached" text in standalone usage panel when at 100% — user must infer from full bar.
📬 Discussion ✅ — All 7 bot-raised inline threads resolved by author with commit references. No unresolved human comments. CodeRabbit paused due to rapid commits; the stale CHANGES_REQUESTED from the automated reviewer predates most fixes.
🔎 QA ✅ — 8 scenarios verified end-to-end: real cost flows from OpenRouter → Redis → public percent-only API. Key migration confirmed. Auth enforcement and guard rails validated. Percentage calculation accuracy verified (107263 microdollars / 5M limit = 2.1%).
🟠 Should Fix
- Add unit tests for
CoPilotUsagePublic.from_status()(rate_limit.py:137) — This is security-relevant projection logic with three branches. Add parametrized tests for: limit=0→None, used=limit→100.0, used>limit→100.0, used=0→0.0, normal percentage, near-boundary (used=9995/limit=10000). (Flagged by: testing — 1 specialist) - Test near-100% rounding semantics (
rate_limit.py:151) — Document whetherround(99.95, 1)snapping to 100.0 is intentional. If not, usemath.floor(pct * 10) / 10instead. (Flagged by: testing — 1 specialist) - Assert
transaction=Truein pipeline test (rate_limit_test.py:255) — Addmock_redis.pipeline.assert_called_with(transaction=True)to lock down the atomicity guarantee. (Flagged by: testing — 1 specialist) - Coordinate breaking admin API change (
rate_limit_admin_routes.py:35) — Either add PydanticField(alias='daily_token_limit')for backward compat during transition, or explicitly confirm in deploy notes that no external consumers exist. (Flagged by: architect, discussion — 2 specialists)
🟡 Nice to Have
- Fallback cost estimate when provider omits cost (
token_tracking.py:195) — Consider a configurable conservative charge (e.g., token_count × max_cost_per_token) behind a feature flag, so persistent missing-cost scenarios still charge something. (security, architect, product) - Revert to
transaction=False(rate_limit.py:407) — The two INCRBY+EXPIRE pairs on independent keys don't benefit from MULTI/EXEC. Saves ~1 RTT per recording. (architect, performance) - Observability metric for missing-cost turns (
baseline/service.py:399) — Emit a StatsD/Prometheus counter alongside the WARNING log so ops can alert on persistent cost-tracking failures. (discussion, product) - Improve small-text contrast (
UsagePanelContent.tsx:29) — Bumptext-neutral-400totext-neutral-500and considertext-xs(12px) minimum for WCAG AA compliance. (product)
🔵 Nits
- DRY cost validation (
token_tracking.py:167/baseline/service.py:137) — Extract a shared_parse_cost_usd(raw) -> float | Noneutility. - Restore user context in log (
rate_limit.py:399) — Adduser_id[:8]back for operational debugging. - Remove barrel re-export (
UsageLimits.tsx:16) — Have consumers import directly fromUsagePanelContent.tsx. - Rename module (
token_tracking.py) — Now primarily about cost recording;cost_tracking.pyorusage_tracking.pywould be clearer. (follow-up PR)
QA Screenshots
| Screenshot | Description |
|---|---|
![]() |
Copilot page showing percentage-based usage bars with real cost data ✅ |
Human Review Needed
YES — This PR touches billing/rate-limiting logic, Redis key structure, admin API contracts, and LaunchDarkly flag configuration across 35 files. The breaking admin API change and cost-tracking trust model warrant human sign-off, particularly from someone familiar with admin tooling consumers.
Risk Assessment
Merge risk: MEDIUM | Rollback: MODERATE — Requires Redis key cleanup (old copilot:cost:* keys), LD flag rollback to old names, and admin API consumers to revert field expectations. Deploy notes cover most of this but coordination is needed.
CI Status
❌ 4/6 quality checks failed (frontend lint, frontend typecheck, backend test, frontend test all errored), ✅ 2/6 passed (backend lint). Note: failures may be environment-related (0s execution times suggest setup issues rather than real failures). CI on GitHub shows 30+ checks passing with 5 still pending.
…baseline cache-create metric OpenRouter streams the cache-write count in ``prompt_tokens_details`` as ``cache_write_tokens``. Anthropic's native API uses the different name ``cache_creation_input_tokens``. PR #12864 only checked the Anthropic- native field via ``model_extra``, so every baseline request reported ``turn_cache_creation_tokens=0`` even when caching was active — masking real cost and leaving operators with no signal on cache-miss behaviour. Verified the field name empirically by probing the OpenRouter streaming endpoint; the response shape is: "prompt_tokens_details": { "audio_tokens": 0, "cached_tokens": 0, "cache_write_tokens": 0, "video_tokens": 0 } ``cache_write_tokens`` is a provider-specific extra not declared on ``PromptTokensDetails``, so we read it via ``model_extra`` (same pattern as the Anthropic fallback). Check OpenRouter first because that's the production baseline path; fall through to Anthropic-native for any future direct-API wiring. Three new unit tests pin both field names and the absent case.
Issues attributed to commits in this pull requestThis pull request was merged and Sentry observed the following issues:
|


Why
After d7653ac removed cost estimation, most baseline turns log with
tracking_type="tokens"and no authoritative USD figure (see: dashboard flipped fromcost_usdtotokensafter 4/14/2026). Rate-limit counters were also token-weighted with hand-rolled cache discounts (cache_read @ 10%, cache_create @ 25%) and a 5× Opus multiplier — a proxy for cost that drifts from real OpenRouter billing.This PR wires real generation cost from OpenRouter into both the cost-tracking log and the rate limiter, and hides raw spend figures from the user-facing API so clients can't reverse-engineer per-turn cost or platform margins.
What
extra_body={"usage": {"include": True}}and readschunk.usage.costfrom the final streaming chunk.x-total-costheader path removed. Missing cost logs an error and skips the counter update (vs the old estimator that silently under-counted).record_token_usage(...)→record_cost_usage(cost_microdollars). The weighted-token math, cache discount factors, and_OPUS_COST_MULTIPLIERare gone; real USD already reflects model + cache pricing.copilot:usage:*→copilot:cost:*so stale token counters can't be misinterpreted as microdollars.copilot-daily-cost-limit-microdollars/copilot-weekly-cost-limit-microdollars(unit in the LD key so values can't accidentally be set in dollars or cents)./usagehides raw $$ — newCoPilotUsagePublic/UsageWindowPublicschemas expose onlypercent_used(0-100) +resets_at+tier+reset_cost. Admin endpoint keeps raw microdollars for debugging.UserRateLimitResponsefields renameddaily/weekly_token_limit→daily/weekly_cost_limit_microdollars,daily/weekly_tokens_used→daily/weekly_cost_used_microdollars. Admin UI displays$X.XX.How
baseline/service.py— passextra_body, extract cost fromchunk.usage.cost, drop thex-total-costheader fallback entirely.rate_limit.py— rewritten aroundrecord_cost_usage,check_rate_limit(daily_cost_limit, weekly_cost_limit), new Redis key prefix. AddsCoPilotUsagePublic.from_status()projector for the public API.token_tracking.py— convertscost_usd→ microdollars viausd_to_microdollarsand callsrecord_cost_usageonly when cost is present.sdk/service.py— deletes_OPUS_COST_MULTIPLIERand simplifies_resolve_model_and_multiplierto_resolve_sdk_model_for_request./usageand/usage/resetreturnCoPilotUsagePublic. Internal server-side limit checks still use the raw microdollarCoPilotUsageStatus.UsagePanelContent,UsageLimits,CopilotPage,BriefingTabContent,credits/page.tsxconsume the new public schema and render "N% used" + progress bar. AdminRateLimitDisplay/UsageBarkeep$X.XX. HelperformatMicrodollarsAsUsdretained for admin use.used/limitkeys are absent from the public payload.Deploy notes
copilot-daily-cost-limit-microdollars(default500000) andcopilot-weekly-cost-limit-microdollars(default2500000). Oldcopilot-*-token-limitflags can stay in LD for rollback.copilot:usage:*are orphaned and will TTL out within 7 days. Safe to ignore or delete manually.Test plan
poetry run test— all impacted backend tests pass (182/182 in targeted scope)pnpm test:unit— all 1628 integration tests passpoetry run format/pnpm format/pnpm typesclean/pr-test --fixend-to-end against local native stack