feat(copilot): render context compaction as tool-call UI events - #12250
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:
WalkthroughSurfaces context-compaction events across CoPilot: query builder returns a compaction flag, security hooks accept an Changes
Sequence DiagramsequenceDiagram
participant Client
participant Service as CoPilot Service (streaming)
participant SDK as SDK/_build_query_message
participant Hooks as Security Hooks
participant Events as Stream Event Emitter
Client->>Service: request chat completion
Service->>SDK: build query (may compact)
SDK-->>Service: (query_message, was_compacted)
alt was_compacted == true
Service->>Hooks: call on_compact()
Hooks-->>Service: ack
Service->>Events: StreamStartStep
Service->>Events: StreamTextStart(compaction_text_id)
Service->>Events: StreamTextDelta("[COPILOT_SYSTEM] compaction notice")
Service->>Events: StreamTextEnd(compaction_text_id)
Service->>Events: StreamFinishStep
Events-->>Client: compaction notification events
end
Service->>Events: continue normal streaming of tool/response events
Events-->>Client: remaining stream events
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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.
🟡 Medium Risk — Some Line OverlapThese PRs have some overlapping changes:
🟢 Low Risk — File Overlap OnlyThese PRs touch the same files but different sections (click to expand)
Summary: 1 conflict(s), 4 medium risk, 3 low risk (out of 8 PRs with file overlap) Auto-generated on push. Ignores: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/service.py`:
- Around line 970-986: The compaction marker is emitted as normal StreamText*
events (StreamStartStep, StreamTextStart/Delta/End, StreamFinishStep using
compaction_text_id) which causes stream_chat_completion to treat it as assistant
text (flipping has_received_text/text_streaming_ended and appending into
assistant_response.content); change the compaction emission to be out-of-band so
the outer state machine ignores it: either emit dedicated out-of-band events
(e.g., OutOfBandStart/OutOfBandDelta/OutOfBandEnd or StreamMeta* events) or set
a clear out_of_band flag/metadata on the StreamText* events for
compaction_text_id, and update stream_chat_completion to detect that flag or
those event types and skip toggling has_received_text/text_streaming_ended and
skipping appending to assistant_response.content when handling compaction chunks
(reference compaction_text_id, StreamTextStart/StreamTextDelta/StreamTextEnd,
StreamStartStep/StreamFinishStep, and stream_chat_completion).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
autogpt_platform/backend/backend/copilot/response_model.pyautogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: types
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
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
Files:
autogpt_platform/backend/backend/copilot/response_model.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/copilot/response_model.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/copilot/response_model.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/response_model.pyautogpt_platform/backend/backend/copilot/service.py
🧠 Learnings (2)
📚 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: As of PR `#12213`, MCP tool response types (MCPToolsDiscoveredResponse, MCPToolOutputResponse) are defined in openapi.json and frontend code in autogpt_platform/frontend/src/app/(platform)/copilot/tools/RunMCPTool/helpers.tsx uses the generated types from `@/app/api/__generated__/`. Other tools like RunBlock still use inline TypeScript interfaces (e.g., BlockDetailsResponse) for SSE stream payloads that are not included in openapi.json schemas. The pattern is tool-specific: use generated types when available in openapi.json, use inline types only when the payload schema is truly SSE-stream-only and not exposed via OpenAPI.
Applied to files:
autogpt_platform/backend/backend/copilot/response_model.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/response_model.pyautogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/response_model.py (1)
autogpt_platform/backend/backend/copilot/tools/models.py (1)
ResponseType(12-50)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
StreamStartStep(87-94)StreamTextStart(110-114)StreamTextDelta(117-122)StreamTextEnd(125-129)StreamFinishStep(97-104)
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/response_model.py (1)
45-45: Compaction response modeling is consistent with the existing stream schema.Line 45 and Lines 230-240 cleanly extend the response model pattern without introducing protocol-shape inconsistencies in this file.
Also applies to: 230-240
There was a problem hiding this comment.
🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
758-773: Remove redundant import —uuidis already imported at module level.Line 760 imports
uuid as uuid_moduleinside the conditional block, butuuidis already imported at line 9. Use the existing module-level import instead.♻️ Proposed fix
# Notify the user if context was compacted before this query if was_compacted: - import uuid as uuid_module - - compaction_text_id = str(uuid_module.uuid4()) + compaction_text_id = str(uuid.uuid4()) yield StreamStartStep() yield StreamTextStart(id=compaction_text_id)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 758 - 773, The code redundantly imports uuid inside the was_compacted block; remove the local import statement and use the module-level uuid instead (replace uuid_module.uuid4() with uuid.uuid4()). Update the compaction_text_id assignment in the was_compacted branch where StreamStartStep, StreamTextStart, StreamTextDelta, StreamTextEnd, and StreamFinishStep are yielded to use uuid.uuid4() and delete the inner "import uuid as uuid_module" line.
910-926: Remove redundant import and consolidate duplication.Same issue as above:
uuidis already imported at module level. Additionally, the notification emission logic (lines 763-773 and 916-926) is duplicated.♻️ Proposed fix for import
# Check if the SDK triggered internal compaction if sdk_compacted_event.is_set(): sdk_compacted_event.clear() - import uuid as uuid_module - - cid = str(uuid_module.uuid4()) + cid = str(uuid.uuid4()) yield StreamStartStep() yield StreamTextStart(id=cid)Consider extracting a helper generator function to emit compaction notifications, reducing duplication between lines 763-773 and 916-926. This could be deferred to a follow-up.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 910 - 926, Remove the redundant local import "import uuid as uuid_module" and use the module-level uuid already imported; then replace the duplicated compaction-notification block inside the sdk_compacted_event branch (the code that yields StreamStartStep, StreamTextStart, StreamTextDelta with the summarization message, StreamTextEnd, StreamFinishStep) by calling a single helper generator function (e.g., emit_compaction_notification or _compaction_notification_generator) that accepts the event id (generate id with the module-level uuid.uuid4()) and yields the same StreamStartStep/StreamTextStart/StreamTextDelta/StreamTextEnd/StreamFinishStep sequence; update both locations (the block guarded by sdk_compacted_event and the other duplicate at lines ~763-773) to call this helper to eliminate duplication while preserving behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 758-773: The code redundantly imports uuid inside the
was_compacted block; remove the local import statement and use the module-level
uuid instead (replace uuid_module.uuid4() with uuid.uuid4()). Update the
compaction_text_id assignment in the was_compacted branch where StreamStartStep,
StreamTextStart, StreamTextDelta, StreamTextEnd, and StreamFinishStep are
yielded to use uuid.uuid4() and delete the inner "import uuid as uuid_module"
line.
- Around line 910-926: Remove the redundant local import "import uuid as
uuid_module" and use the module-level uuid already imported; then replace the
duplicated compaction-notification block inside the sdk_compacted_event branch
(the code that yields StreamStartStep, StreamTextStart, StreamTextDelta with the
summarization message, StreamTextEnd, StreamFinishStep) by calling a single
helper generator function (e.g., emit_compaction_notification or
_compaction_notification_generator) that accepts the event id (generate id with
the module-level uuid.uuid4()) and yields the same
StreamStartStep/StreamTextStart/StreamTextDelta/StreamTextEnd/StreamFinishStep
sequence; update both locations (the block guarded by sdk_compacted_event and
the other duplicate at lines ~763-773) to call this helper to eliminate
duplication while preserving behavior.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: types
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: Seer Code Review
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (6)
autogpt_platform/backend/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development
Files:
autogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*_test.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
autogpt_platform/backend/**/*_test.py: Always review snapshot changes withgit diffbefore committing when updating snapshots withpoetry run pytest --snapshot-update
Use pytest with snapshot testing for API responses in test files
Colocate test files with source files using the*_test.pynaming convention
Files:
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
autogpt_platform/backend/**/*test*.py
📄 CodeRabbit inference engine (AGENTS.md)
Run
poetry run testfor backend testing (runs pytest with docker based postgres + prisma)
Files:
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py
🧠 Learnings (1)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.
Applied to files:
autogpt_platform/backend/backend/copilot/sdk/security_hooks.pyautogpt_platform/backend/backend/copilot/sdk/query_builder_test.pyautogpt_platform/backend/backend/copilot/sdk/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)
_build_query_message(445-489)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
StreamFinishStep(97-104)StreamHeartbeat(213-227)StreamStart(62-78)StreamStartStep(87-94)StreamTextDelta(117-122)StreamTextEnd(125-129)StreamTextStart(110-114)autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
create_security_hooks(145-376)
🔇 Additional comments (11)
autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (2)
150-151: LGTM!The
on_compactcallback parameter is cleanly integrated with proper type hints, documentation, and conditional invocation. The callback is appropriately optional and invoked only when provided.Also applies to: 167-169
332-334: LGTM!The conditional invocation is correct. Since the current caller passes
asyncio.Event.setwhich is guaranteed not to raise, the lack of try/except is acceptable.autogpt_platform/backend/backend/copilot/sdk/service.py (4)
34-34: LGTM!The imports for stream event types are correctly added to support the new compaction notification functionality.
Also applies to: 37-40
445-489: LGTM!The return type change to
tuple[str, bool]is well-documented and correctly implemented. Thewas_compactedflag appropriately indicates when conversation history was compressed into the query context.
654-663: LGTM!The
asyncio.Eventis correctly scoped to the stream function and safely wired to the security hooks callback. Usingsdk_compacted_event.setas the callback is appropriate sinceEvent.set()is synchronous and thread-safe.
743-756: LGTM!The unpacking of the tuple return value and subsequent logging are correctly implemented.
autogpt_platform/backend/backend/copilot/sdk/query_builder_test.py (5)
121-130: LGTM!Test correctly updated to unpack tuple and assert
was_compacted=Falsewhen transcript is up-to-date.
145-156: LGTM!Test correctly asserts
was_compacted=Truewhen stale transcript requires gap context compression.
169-177: LGTM!Test correctly asserts
was_compacted=Falsewhen transcript_msg_count is zero.
184-192: LGTM!Test correctly asserts
was_compacted=Falsefor single-message sessions.
215-226: LGTM!Test correctly asserts
was_compacted=Truewhen compression fallback is used for multi-message sessions without resume.
autogpt-reviewer
left a comment
There was a problem hiding this comment.
📋 PR #12250 — feat(copilot): surface context compaction events to the UI
Author: Otto-AGPT | Requested by: 0ubbe, majdyz | Files: response_model.py (+14), sdk/service.py (+59/−5), service.py (+17), query_builder_test.py (+10/−5), security_hooks.py (+5)
🎯 Verdict: APPROVE WITH CONDITIONS
What This PR Does
When CoPilot's context window fills up and the SDK compacts (summarizes/truncates) conversation history, users currently have no visibility. This PR adds a backend-only system notification — emitted as [COPILOT_SYSTEM] stream events — so the frontend's existing parseSpecialMarkers renders a subtle gray info bar: "Earlier messages were summarized to fit within context limits." Three compaction paths are covered: pre-query history compression, SDK-internal compaction (via asyncio.Event bridge), and the legacy OpenAI streaming path.
Specialist Findings
🛡️ Security ✅ — No security concerns. All notification text is hardcoded (no injection vector), no new endpoints or auth changes, no secrets exposure. The on_compact callback is appropriately minimal (Callable[[], None]). asyncio.Event.set() from the hook is sync-safe in CPython's single-threaded event loop.
🏗️ Architecture StreamCompaction model is defined but never used (dead code). CodeRabbit's concern about compaction events contaminating stream_chat_completion's state machine is valid for the sdk/service.py mid-stream path (events emitted inside the message processing loop could flip has_received_text/text_streaming_ended). The service.py path is safe (emitted before the loop).
⚡ Performance ✅ — No performance concerns. All new code runs in cold/rare paths (compaction is infrequent). The asyncio.Event per session, 5 extra stream events per compaction (~500 bytes), and tuple return are all negligible. Lazy import uuid is technically unnecessary (already imported at module level) but ~200ns overhead is irrelevant.
🧪 Testing _build_query_message tuple return is properly tested (all 5 existing tests updated, was_compacted asserted). However, the core feature — the 3-site notification emission — has zero test coverage. The on_compact callback and sdk_compacted_event flow are also untested. This is the primary gap.
📖 Quality compaction_text_id vs cid). Magic string repeated 3× with inconsistent line-breaking. Redundant import uuid as uuid_module inside function bodies when uuid is already imported at module level. StreamCompaction model is unused dead code.
📦 Product
📬 Discussion sdk/service.py. CI tests still pending at review time.
🔎 QA ✅ — Frontend loads correctly, copilot chat interface functional, zero console errors. parseSpecialMarkers correctly parses [COPILOT_SYSTEM] prefix and renders as subtle gray italic info bar. Cannot trigger actual compaction in QA (requires exceeding context window), but code path verified end-to-end.
QA Screenshots:
Should Fix (before merge)
-
Extract notification helper to eliminate 3× duplication —
sdk/service.py:760-773,sdk/service.py:913-926,service.py:973-986. Create a sharedemit_compaction_notification()function (e.g., inresponse_model.py) that returns the 5 stream events. This also eliminates 3× redundantimport uuid, 3× inconsistent magic strings, and thecompaction_text_idvscidnaming inconsistency. TheCOPILOT_SYSTEM_PREFIXconstant already exists atsdk/service.py:143— use it. -
Remove or use
StreamCompactionmodel —response_model.py:230-241definesStreamCompactionwithResponseType.COMPACTIONbut it's never instantiated. Either delete it (since notifications use raw stream events) or make it the canonical factory for compaction events. -
Verify mid-stream compaction doesn't corrupt SDK stream state —
sdk/service.py:913-926: compaction events emitted inside the message-processing loop could interact with the stream adapter's state tracking (has_received_text,text_streaming_ended,assistant_response.content). Verify this path doesn't append the system marker text to the assistant response or confuse the state machine. Theservice.pypath (pre-loop) is safe. -
Add deduplication for compaction notifications — Both
was_compacted(pre-query) andsdk_compacted_event(mid-stream) can fire during the same query turn, causing the user to see two identical gray bars. Track whether a notification was already emitted and skip duplicates.
Nice to Have (follow-up OK)
-
Add unit tests for notification emission — At minimum: test that
_stream_chat_chunksyields the 5-event sequence when compaction occurs, and thatstream_chat_completion_sdkdoes the same for both trigger paths. Teston_compactcallback invocation. -
Improve notification copy — Consider "Earlier messages were condensed. The assistant may not recall specific details." to set clearer expectations about potential data loss.
-
Move
import uuidto module top-level — Already imported in both files; the inline re-imports are redundant. -
Coordinate with PR #12212 — Medium overlap risk on
sdk/service.pyat exactly the lines this PR modifies (L765-777, L916-921).
Risk Assessment
Merge risk: LOW | Rollback: EASY
The feature is purely additive — it emits extra SSE events that the frontend already knows how to render. No database changes, no new endpoints, no auth changes. If the notification causes issues, the 3 if was_compacted / if sdk_compacted_event.is_set() / if context_result.was_compacted guards can be trivially reverted. The duplication and missing tests are quality concerns, not correctness risks for the happy path.
Automated review by PR Review Squad — 8/8 specialists reported • [QA screenshots above]
There was a problem hiding this comment.
🧹 Nitpick comments (2)
autogpt_platform/backend/backend/copilot/service.py (2)
69-71: Consider extractingCOPILOT_SYSTEM_PREFIXto a shared module.The constant is duplicated from
sdk/service.pywith a comment noting the duplication. To avoid drift, consider moving it to a shared location (e.g.,response_model.pyor a newconstants.py).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/service.py` around lines 69 - 71, COPILOT_SYSTEM_PREFIX is duplicated here and in sdk/service.py; extract it into a shared module (e.g., create or use response_model.py or a new constants.py) and import it from there in autogpt_platform/backend/backend/copilot/service.py and sdk/service.py to avoid drift; update both files to remove the local definition of COPILOT_SYSTEM_PREFIX and replace with a single import of the constant (refer to COPILOT_SYSTEM_PREFIX, service.py, and sdk/service.py to locate usages), run tests/lint to ensure imports resolve.
4-4: Remove duplicateuuidimport.The module imports
uuid as uuid_moduleat Line 4 (top-level), but Line 497 imports it again inside the function. The function-level import is redundant and should be removed.♻️ Proposed fix
@@ -494,9 +494,6 @@ tool_response_messages: list[ChatMessage] = [] should_retry = False - # Generate unique IDs for AI SDK protocol - import uuid as uuid_module - is_continuation = _continuation_message_id is not None message_id = _continuation_message_id or str(uuid_module.uuid4()) text_block_id = str(uuid_module.uuid4())Also applies to: 497-497
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/service.py` at line 4, Remove the redundant inner import of uuid inside the function in autogpt_platform/backend/backend/copilot/service.py and use the module-level alias uuid_module (imported as "import uuid as uuid_module" at top) instead; locate the function that contains a local "import uuid" at the end of the file, delete that inner import statement, and update any local uses to call uuid_module.uuid4()/other members as needed so all uuid references use the single top-level uuid_module import.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/service.py`:
- Around line 69-71: COPILOT_SYSTEM_PREFIX is duplicated here and in
sdk/service.py; extract it into a shared module (e.g., create or use
response_model.py or a new constants.py) and import it from there in
autogpt_platform/backend/backend/copilot/service.py and sdk/service.py to avoid
drift; update both files to remove the local definition of COPILOT_SYSTEM_PREFIX
and replace with a single import of the constant (refer to
COPILOT_SYSTEM_PREFIX, service.py, and sdk/service.py to locate usages), run
tests/lint to ensure imports resolve.
- Line 4: Remove the redundant inner import of uuid inside the function in
autogpt_platform/backend/backend/copilot/service.py and use the module-level
alias uuid_module (imported as "import uuid as uuid_module" at top) instead;
locate the function that contains a local "import uuid" at the end of the file,
delete that inner import statement, and update any local uses to call
uuid_module.uuid4()/other members as needed so all uuid references use the
single top-level uuid_module import.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: types
- GitHub Check: Seer Code Review
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
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
Files:
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
🧠 Learnings (1)
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.
Applied to files:
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
StreamFinishStep(96-103)StreamHeartbeat(212-226)StreamStart(61-77)StreamStartStep(86-93)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamTextStart(109-113)autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
create_security_hooks(145-376)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
StreamTextStart(109-113)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamStartStep(86-93)StreamFinishStep(96-103)
🔇 Additional comments (7)
autogpt_platform/backend/backend/copilot/sdk/service.py (5)
34-43: LGTM! Clean import additions for stream event types.The new imports for
StreamFinishStep,StreamStartStep,StreamTextDelta,StreamTextEnd, andStreamTextStartare correctly organized and necessary for the compaction notification feature.
451-489: LGTM! Well-documented signature change for_build_query_message.The function now returns a
tuple[str, bool]where the boolean indicates whether compaction occurred. The docstring is properly updated to document the return value, and all return paths correctly provide both values.
654-663: LGTM! Clean asyncio.Event integration for SDK compaction signaling.Using
asyncio.Eventto signal compaction from the SDK's pre-compact hook is a good pattern. The event is passed as a callback viaon_compact=sdk_compacted_event.set, which aligns with the security hooks signature documented in the relevant snippets.
757-771: LGTM! Pre-query compaction notification emits a self-contained step.The compaction notice correctly uses a distinct
compaction_text_id(separate from the main text stream) and emits a complete step sequence:StreamStartStep→StreamTextStart→StreamTextDelta→StreamTextEnd→StreamFinishStep. This ensures the frontend can render it as an out-of-band system message without contaminating the assistant response state.
908-923: LGTM! Mid-stream compaction notification with proper event clearing.The
sdk_compacted_event.is_set()check followed byclear()ensures each compaction event is surfaced exactly once. The notification sequence mirrors the pre-query emission, maintaining consistency.autogpt_platform/backend/backend/copilot/service.py (2)
526-537: LGTM! Out-of-band event passthrough correctly addresses stream state contamination.The logic properly detects events with a different
idthantext_block_idand yields them without updatinghas_received_text,text_streaming_ended, or appending toassistant_response.content. TheStreamStartStepandStreamFinishStepevents are also passed through directly. This prevents compaction notices from polluting the primary text stream state.
988-1002: LGTM! Compaction notification emits a self-contained step with distinct ID.The implementation correctly:
- Uses a unique
compaction_text_idseparate from the maintext_block_id- Emits a complete step sequence that the outer
stream_chat_completionwill pass through as out-of-band- Uses the
COPILOT_SYSTEM_PREFIXmarker for frontend parsingThis aligns with the SDK path implementation in
sdk/service.py.
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/backend/backend/copilot/sdk/service.py (1)
460-486:⚠️ Potential issue | 🟠 Major
was_compactedis setTruefor non-compaction paths.This currently triggers the compaction notice even when history was only reformatted or when compression did not compact anything, so users can get a misleading system message.
💡 Proposed fix
async def _build_query_message( @@ - if gap_context: + if gap_context: logger.info( f"[SDK] Transcript stale: covers {transcript_msg_count} " f"of {msg_count} messages, compressing {len(gap)} missed" ) return ( f"{gap_context}\n\nNow, the user says:\n{current_message}", - True, + False, ) @@ - compressed = await _compress_conversation_history(session) + compressed, was_compacted = await _compress_conversation_history(session) history_context = _format_conversation_context(compressed) if history_context: return ( f"{history_context}\n\nNow, the user says:\n{current_message}", - True, + was_compacted, ) return current_message, False-async def _compress_conversation_history( - session: ChatSession, -) -> list[ChatMessage]: +async def _compress_conversation_history( + session: ChatSession, +) -> tuple[list[ChatMessage], bool]: @@ - if len(messages) < 2: - return messages + if len(messages) < 2: + return messages, False @@ - return [ + return ([ ChatMessage( role=m["role"], content=m.get("content"), tool_calls=m.get("tool_calls"), tool_call_id=m.get("tool_call_id"), ) for m in result.messages - ] + ], True) - return messages + return messages, False🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 460 - 486, The boolean "was_compacted" is being set True even when no compaction occurred; update the returns in the branches inside the function (the block that calls _compress_conversation_history(session) and the gap-handling block that builds gap_context via _format_conversation_context) so that True is only returned when actual compaction happened: for the compression fallback, call _compress_conversation_history(session), then set was_compacted = True only if len(compressed) < msg_count (or if _compress_conversation_history can return a flag, use that); for the transcript gap path do not set was_compacted=True unless you perform a real compaction (e.g., if you later compress gap and its length is reduced). Ensure the final return signature remains (context_string, was_compacted) and update any callers if the boolean semantics change.
🤖 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/backend/backend/copilot/sdk/service.py`:
- Around line 460-486: The boolean "was_compacted" is being set True even when
no compaction occurred; update the returns in the branches inside the function
(the block that calls _compress_conversation_history(session) and the
gap-handling block that builds gap_context via _format_conversation_context) so
that True is only returned when actual compaction happened: for the compression
fallback, call _compress_conversation_history(session), then set was_compacted =
True only if len(compressed) < msg_count (or if _compress_conversation_history
can return a flag, use that); for the transcript gap path do not set
was_compacted=True unless you perform a real compaction (e.g., if you later
compress gap and its length is reduced). Ensure the final return signature
remains (context_string, was_compacted) and update any callers if the boolean
semantics change.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
autogpt_platform/backend/backend/copilot/constants.pyautogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: types
- GitHub Check: Seer Code Review
- GitHub Check: test (3.11)
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
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
Files:
autogpt_platform/backend/backend/copilot/constants.pyautogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/copilot/constants.pyautogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/copilot/constants.pyautogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/constants.pyautogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
🧠 Learnings (2)
📚 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/constants.pyautogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
📚 Learning: 2026-02-20T03:28:06.619Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12173
File: autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py:153-179
Timestamp: 2026-02-20T03:28:06.619Z
Learning: The copilot executor (`backend.copilot.executor`) runs in its own Docker container with Python 3.13, so Python 3.11+ features like `asyncio.timeout()` are available in copilot code even though the general backend pyproject.toml specifies `python = ">=3.10,<3.14"`.
Applied to files:
autogpt_platform/backend/backend/copilot/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
StreamTextStart(109-113)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamStartStep(86-93)StreamFinishStep(96-103)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (7)
StreamFinishStep(96-103)StreamHeartbeat(212-226)StreamStart(61-77)StreamStartStep(86-93)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamTextStart(109-113)autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
create_security_hooks(145-376)
🔇 Additional comments (3)
autogpt_platform/backend/backend/copilot/constants.py (1)
1-7: Good centralization of frontend marker constants.This keeps marker definitions single-sourced and makes cross-module behavior more consistent.
autogpt_platform/backend/backend/copilot/service.py (1)
524-535: Out-of-band compaction handling looks correct.The non-primary
StreamText*pass-through plus self-contained compaction step prevents primary text-state contamination while still surfacing the UI notice.Also applies to: 986-1000
autogpt_platform/backend/backend/copilot/sdk/service.py (1)
651-661: Nice wiring for SDK compaction hook → stream notification.Using an
asyncio.Eventbridge from hooks to stream emission is a clean, low-coupling approach.Also applies to: 905-921
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/backend/backend/copilot/sdk/service.py (1)
740-790:⚠️ Potential issue | 🟠 MajorAvoid preemptive “Summarizing…” events before compaction is confirmed.
Line 747 emits a compaction-in-progress notice based on
may_compactheuristic, but Lines 776-787 only emit completion whenwas_compactedis true. On false positives, users still see “Summarizing earlier messages...” even though no compaction happened.💡 Proposed fix
- may_compact = (not use_resume and len(session.messages) > 1) or ( - use_resume - and transcript_msg_count > 0 - and transcript_msg_count < len(session.messages) - 1 - ) - if may_compact: - start_id = str(uuid.uuid4()) - yield StreamStartStep() - yield StreamTextStart(id=start_id) - yield StreamTextDelta( - id=start_id, - delta=( - f"{COPILOT_SYSTEM_PREFIX} Summarizing earlier " - "messages..." - ), - ) - yield StreamTextEnd(id=start_id) - query_message, was_compacted = await _build_query_message( current_message, session, use_resume, transcript_msg_count, session_id, ) @@ - if was_compacted: + if was_compacted: done_id = str(uuid.uuid4()) + yield StreamStartStep() yield StreamTextStart(id=done_id) yield StreamTextDelta( id=done_id, delta=( f"{COPILOT_SYSTEM_PREFIX} Earlier messages were " "summarized to fit within context limits." ), ) yield StreamTextEnd(id=done_id) - - if may_compact: yield StreamFinishStep()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 740 - 790, The code currently emits a "Summarizing earlier messages..." stream when may_compact is true before calling _build_query_message, which can lead to a false positive if _build_query_message returns was_compacted=False; change the logic to only emit the summarizing start/delta/end (using start_id, StreamTextStart, StreamTextDelta, StreamTextEnd) and the corresponding StreamFinishStep after _build_query_message and only when was_compacted is true; keep may_compact only as a pre-check if needed for behavior gating but do not send any stream events until was_compacted is confirmed by _build_query_message.
🧹 Nitpick comments (1)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)
740-759: Extract a helper for compaction notice emission.These blocks duplicate near-identical step/text emission logic; a small helper would reduce drift risk between “start/done” variants and make future copy changes safer.
Also applies to: 775-787, 826-843, 945-966
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@autogpt_platform/backend/backend/copilot/sdk/service.py` around lines 740 - 759, The compaction-notice emission logic is duplicated across blocks (see uses of may_compact and the sequence of StreamStartStep, StreamTextStart, StreamTextDelta, StreamTextEnd); extract a small helper function (e.g., emit_compaction_notice or _emit_compaction_steps) that takes the generated id and the notice text and yields the four steps so callers like the block computing may_compact can replace their inlined yield sequence with a single call to that helper; update all duplicate sites (including the other occurrences around the file) to call the helper to keep start/done variants consistent and reduce drift risk.
🤖 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/backend/backend/copilot/sdk/service.py`:
- Around line 740-790: The code currently emits a "Summarizing earlier
messages..." stream when may_compact is true before calling
_build_query_message, which can lead to a false positive if _build_query_message
returns was_compacted=False; change the logic to only emit the summarizing
start/delta/end (using start_id, StreamTextStart, StreamTextDelta,
StreamTextEnd) and the corresponding StreamFinishStep after _build_query_message
and only when was_compacted is true; keep may_compact only as a pre-check if
needed for behavior gating but do not send any stream events until was_compacted
is confirmed by _build_query_message.
---
Nitpick comments:
In `@autogpt_platform/backend/backend/copilot/sdk/service.py`:
- Around line 740-759: The compaction-notice emission logic is duplicated across
blocks (see uses of may_compact and the sequence of StreamStartStep,
StreamTextStart, StreamTextDelta, StreamTextEnd); extract a small helper
function (e.g., emit_compaction_notice or _emit_compaction_steps) that takes the
generated id and the notice text and yields the four steps so callers like the
block computing may_compact can replace their inlined yield sequence with a
single call to that helper; update all duplicate sites (including the other
occurrences around the file) to call the helper to keep start/done variants
consistent and reduce drift risk.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
autogpt_platform/backend/backend/copilot/sdk/service.pyautogpt_platform/backend/backend/copilot/service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: types
- GitHub Check: Seer Code Review
- GitHub Check: test (3.11)
- GitHub Check: test (3.13)
- GitHub Check: test (3.12)
- GitHub Check: Check PR Status
🧰 Additional context used
📓 Path-based instructions (4)
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
Files:
autogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/copilot/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
🧠 Learnings (2)
📚 Learning: 2026-02-20T03:28:06.619Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12173
File: autogpt_platform/backend/backend/copilot/sdk/tool_adapter.py:153-179
Timestamp: 2026-02-20T03:28:06.619Z
Learning: The copilot executor (`backend.copilot.executor`) runs in its own Docker container with Python 3.13, so Python 3.11+ features like `asyncio.timeout()` are available in copilot code even though the general backend pyproject.toml specifies `python = ">=3.10,<3.14"`.
Applied to files:
autogpt_platform/backend/backend/copilot/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/service.pyautogpt_platform/backend/backend/copilot/sdk/service.py
🧬 Code graph analysis (2)
autogpt_platform/backend/backend/copilot/service.py (1)
autogpt_platform/backend/backend/copilot/response_model.py (5)
StreamTextStart(109-113)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamStartStep(86-93)StreamFinishStep(96-103)
autogpt_platform/backend/backend/copilot/sdk/service.py (2)
autogpt_platform/backend/backend/copilot/response_model.py (5)
StreamStart(61-77)StreamStartStep(86-93)StreamTextDelta(116-121)StreamTextEnd(124-128)StreamTextStart(109-113)autogpt_platform/backend/backend/copilot/sdk/security_hooks.py (1)
create_security_hooks(145-376)
🔇 Additional comments (3)
autogpt_platform/backend/backend/copilot/sdk/service.py (1)
651-661: Good hook wiring for SDK-side compaction signaling.Using
sdk_compacted_eventand passingon_compact=sdk_compacted_event.setcleanly bridges SDK hooks to the stream layer without coupling hook internals to SSE emission.autogpt_platform/backend/backend/copilot/service.py (2)
524-535: Out-of-band stream chunks are now correctly isolated from primary text state.The ID mismatch gate plus direct pass-through for
StreamStartStep/StreamFinishStepprevents compaction/system chunks from pollutingassistant_response.contentand text boundary flags.
987-1000: Compaction event shape is well-formed and UI-friendly.The
StartStep → TextStart → TextDelta → TextEnd → FinishStepsequence with a dedicated text ID is consistent and keeps compaction messaging self-contained.
autogpt-reviewer
left a comment
There was a problem hiding this comment.
📋 PR #12250 — feat(copilot): surface context compaction events to the UI (Re-review #2)
Author: Otto-AGPT | Requested by: 0ubbe, majdyz | HEAD: 0bcdbcef
Files: constants.py (+7), sdk/service.py (+102/−9), service.py (+32/−1), query_builder_test.py (+10/−5), security_hooks.py (+5)
New commits since last review (4184b22 → 0bcdbce):
8bc2b983b— refactor: extract prefix constants toconstants.py0bcdbcefb— feat: bracket compaction like tool calls (start/done)
🎯 Verdict: APPROVE WITH CONDITIONS
What This PR Does
Adds user-visible notifications when CoPilot compacts (summarizes) conversation history to fit within context limits. The backend emits [COPILOT_SYSTEM]-prefixed stream events that the frontend's existing parseSpecialMarkers renders as subtle gray info bars. New in this iteration: a "Summarizing earlier messages..." in-progress notice appears before compaction, followed by "Earlier messages were summarized to fit within context limits." when done — mirroring the tool-call UX pattern.
Previous Should-Fix Status
| # | Item | Status |
|---|---|---|
| 1 | Extract constant duplication (COPILOT_SYSTEM_PREFIX) |
✅ FIXED — constants.py created, both services import from it |
| 2 | Remove/use StreamCompaction dead code |
✅ FIXED — removed from codebase |
| 3 | Verify mid-stream safety (stream state contamination) | ✅ FIXED — out-of-band passthrough at service.py:524-535 isolates compaction events from primary text state |
| 4 | Add notification dedup + tests | was_compacted return, but emission paths untested; dedup not addressed |
Specialist Findings
🛡️ Security ✅ — No security issues. All notification text is hardcoded (no injection vector). on_compact callback is minimal (Callable[[], None]), invoked with no arguments — no data exfiltration possible. asyncio.Event bridge is safe (single-threaded event loop). No new endpoints, auth changes, or secrets. Pre-existing minor concern: LLM could output [COPILOT_SYSTEM] to spoof UI, but not introduced by this PR.
🏗️ Architecture _build_query_message → tuple[str, bool] is clean. may_compact heuristic is correct but can false-positive (emits "Summarizing..." when no compaction occurs). Out-of-band passthrough in service.py works but is implicit (ID-based, not flagged).
⚡ Performance ✅ — No performance concerns. may_compact is O(1) integer comparison. SSE bracketing adds <1KB on infrequent compaction events. asyncio.Event.is_set() is a boolean check. Pass-through filter adds negligible isinstance + getattr per chunk.
🧪 Testing _build_query_message tuple return is properly tested (all 5 tests updated, was_compacted asserted for each path). However, the core feature — notification emission from 5 sites — has zero test coverage. on_compact callback, sdk_compacted_event flow, may_compact heuristic, and out-of-band passthrough are all untested.
📖 Quality start_id, done_id, s_id, compaction_text_id). Redundant inline import uuid as uuid_module remains in service.py:495. A simple _emit_system_notice(msg) helper would eliminate all 5 duplication sites.
📦 Product
📬 Discussion was_compacted returns True for non-compaction paths (gap-context, compression fallback) — potentially over-triggering notifications. CodeRabbit paused auto-reviews noting "branch under active development." Medium merge-conflict risk with PR #12212 on exact lines modified.
🔎 QA ✅ — Frontend loads correctly. Signup works. Copilot chat functional. Both compaction messages render correctly as gray italic info bars. "Summarizing..." appeared during processing, "Summarized..." appeared on completion. All 13 backend tests pass. No regressions. No console errors.
QA Screenshots:
Should Fix (before merge)
-
Extract notification helper to eliminate 5× duplication —
sdk/service.py(4 sites) +service.py(1 site). Create a shared generator/function like_emit_system_notice(msg: str)that yields the 5 stream events (StreamStartStep → StreamTextStart → StreamTextDelta → StreamTextEnd → StreamFinishStep). This was the primary should-fix from the previous review and duplication has increased from 3× to 5×. -
Fix
may_compactfalse-positive —sdk/service.py:742-746: whenmay_compact=Truebutwas_compacted=False, the user sees "Summarizing earlier messages..." followed by the step closing with no confirmation. Either: (a) don't emit the "Summarizing..." start until after_build_query_messageconfirms compaction (emit start+done together), or (b) emit a "No summarization needed" close message. CodeRabbit also flaggedwas_compactedreturningTruefor non-compaction paths. -
Add deduplication for compaction notifications — Pre-query (
was_compacted) and SDK-internal (sdk_compacted_event) can both fire in the same turn, showing two identical notices. Track whether notification was already emitted per turn.
Nice to Have (follow-up OK)
-
Add unit tests for notification emission — Test
_stream_chat_chunksyields compaction events whencontext_result.was_compacted=True. Teston_compactcallback invocation. Testsdk_compacted_eventproduces bracket events. -
Consistent variable naming — Pick one name for the text-block ID (
text_idornotice_id) instead of 5 different names. -
Remove redundant inline
import uuid—service.py:495has an inlineimport uuid as uuid_modulewhen it's already imported at line 4. -
Coordinate with PR #12212 — Medium merge-conflict risk on
sdk/service.pyat the exact lines this PR modifies.
Risk Assessment
Merge risk: LOW | Rollback: EASY
The feature is purely additive — extra SSE events the frontend already renders. No database changes, no new endpoints, no auth changes. The may_compact false-positive is cosmetic (user sees a briefly misleading "Summarizing..." that closes without confirmation). The dedup issue is also cosmetic (two notices instead of one). Both are UX polish, not correctness risks.
The constants extraction and stream state isolation from the previous review cycle are solid improvements. The bracket pattern (start/done) is architecturally sound. The main remaining concern is code hygiene (5× duplication) and the minor UX rough edges.
Automated re-review by PR Review Squad — 8/8 specialists reported • [QA screenshots above]
0e92ddf to
3f33617
Compare
ab8903a to
859129b
Compare
e21cd2c to
e3822b0
Compare
…pact When pre-query compression fired and the compressed context was still large enough for the SDK to trigger its own compaction, the user saw "Earlier messages were summarized" twice in the same turn. - emit_pre_query now sets _done=True to suppress SDK-internal notification - reset_for_query is called before emit_pre_query so the pre-query flag takes effect for the current turn
The size-based upload guard was skipping both content and metadata updates when the transcript size was unchanged. This caused message_count to stagnate, making the gap-fill logic re-compress the same messages every turn.
compaction_events() generated a new random ID when building persistence events, causing a mismatch with the ID already streamed to the frontend. After page refresh the frontend couldn't match the tool call to its output.
Move COMPACTION_TOOL_NAME, compaction event builders (compaction_start_events, compaction_end_events, compaction_events), filter_compaction_messages, and is_compaction_tool_call into the single compaction module. - response_model.py: removed all compaction code (was ~55 lines) - sdk/service.py: removed _filter_compaction_messages, replaced inline COMPACTION_TOOL_NAME filtering in _format_conversation_context with filter_compaction_messages() call - service.py (legacy): imports compaction_events from sdk/compaction - sdk/compaction.py: single source of truth for all compaction logic
… API - Remove noqa: ANN201 by adding proper Callable type annotation - Make event builders private (_start_events, _end_events, _new_tool_call_id) - Remove is_compaction_tool_call (inlined into filter_compaction_messages) - Simplify persist_compaction to _persist(session, tool_call_id, message) instead of parsing events to extract IDs - Add emit_compaction() convenience for the legacy path - Remove COMPACTION_DONE_MSG import from service.py (no longer needed) - Net -23 lines
- Add passthrough filter for compaction StreamToolInput*/StreamToolOutput* events in service.py to prevent contaminating accumulated_tool_calls and spurious LLM continuation calls (blocker from reviews #4-#6) - Add comprehensive unit tests for sdk/compaction.py (22 tests covering CompactionTracker state machine, filter_compaction_messages, emit_compaction, event builders, and tool_call_id consistency) - Assert was_compacted in all query_builder_test.py tests, add test for True path - Simplify emit_pre_query to delegate to emit_compaction (DRY) - Make compact_start attribute private (_compact_start)
…g COMPACTION_TOOL_NAME service.py importing from sdk.compaction triggers circular import: service.py → sdk.compaction → sdk/__init__.py → sdk/service.py → service.py Use 'context_compaction' string directly in the passthrough filter.
Avoids circular import while keeping a single source of truth. constants.py has no internal deps so it's safe to import from anywhere.
c2fd871 to
823069a
Compare
|
Conflicts have been resolved! 🎉 A maintainer will review the pull request shortly. |
autogpt-reviewer
left a comment
There was a problem hiding this comment.
PR #12250 — feat(copilot): render context compaction as tool-call UI events
Author: Otto-AGPT | Files: 17 changed (+909/−621) | HEAD: 089d0875
🎯 Verdict: APPROVE
What This PR Does
Surfaces context compaction (conversation summarization to fit within token limits) as a visible tool-call UI element. Users see a spinning ArrowsClockwise icon with "Summarizing earlier messages…" during compaction, then "Earlier messages were summarized" when done. Reuses the existing GenericTool infrastructure. The latest commit adds hex-suffixed marker prefixes ([__COPILOT_ERROR_f7a1__], [__COPILOT_SYSTEM_e3b0__]) to prevent LLM false-positive marker detection.
Previous Condition Status (from re-reviews #1–#7)
| # | Condition | Status |
|---|---|---|
| 1 | Non-SDK passthrough filter for tool-call events | ✅ FIXED (re-review #7) |
| 2 | sdk/compaction.py needs unit tests |
✅ FIXED — 291 lines covering all public APIs |
| 3 | Assert was_compacted in query builder tests |
✅ FIXED — all tests assert + new True path test |
| 4 | isExplicitStopRef stop-button behavior |
✅ DEFERRED to PR #12254 |
All previous blockers and conditions remain resolved.
Specialist Findings
🛡️ Security ✅ — No issues. Hex-suffixed markers are a security improvement — previous simple [COPILOT_ERROR] strings could plausibly appear in LLM output enabling fake error card injection. New _f7a1/_e3b0 suffixes make this statistically negligible. All notification text hardcoded. filter_compaction_messages uses safe dict .get() chains. asyncio.Event bridge safe within single-threaded model. No auth/endpoint changes.
🏗️ Architecture ✅ — constants.py is a proper leaf module with zero imports. Dependency graph clean: constants.py → sdk/compaction.py → sdk/service.py. CompactionTracker 3-state machine (idle → start_emitted → done) is sound with clean reset semantics. Minor note: lazy import of emit_compaction in legacy path crosses copilot → copilot.sdk boundary but is functionally clean. Heartbeat interval changed 3s → 10s (2s margin vs 12s frontend timeout — tight but valid).
⚡ Performance ✅ — No concerns. filter_compaction_messages is optimal O(n) with set-based lookups. Double-call in compress+format paths is harmless at realistic conversation sizes. Heartbeat increase 3s→10s is a net positive (fewer SSE events). Compaction emits exactly 5 events per occurrence, at most once per query. Transcript metadata always-update avoids redundant re-compression.
🧪 Testing ✅ — Excellent coverage. compaction_test.py (291 lines, 22 tests): CompactionTracker state machine, event builders, filtering, persistence. query_builder_test.py (14 tests): all assert was_compacted, new True path test. CI fully green: 3.11/3.12/3.13, lint, types, e2e, integration. Minor gap: no frontend component tests for compaction category, consistent with project patterns.
📖 Quality ✅ — Clean organization. Constants centralized with inline comments. Excellent module docstring in compaction.py. DRY throughout — compaction_events() / _start_events() / _end_events() factoring avoids repetition. Minor nits: _new_tool_call_id missing docstring, inline import in legacy path could use explaining comment. Nothing blocking.
📦 Product ✅ — UX well-considered. ArrowsClockwise icon intuitive for summarization. Copy is clear and non-technical ("Summarizing earlier messages…" / "Earlier messages were summarized"). Accordion correctly suppressed for compaction (no meaningful output to expand). Both pre-query and SDK compaction paths emit consistent UI events. Hex-suffix change invisible to users.
📬 Discussion emit_start_if_ready() but before message arrives — finally block doesn't close spinner. Merge conflicts resolved. Overlap with PR #12259 (0ubbe) on sdk/service.py.
🔎 QA ✅ — Live testing passed. Frontend loads, signup works, copilot chat functional, library navigates correctly, no console errors. All 36 backend tests pass. ArrowsClockwise icon import verified. Tool category mapping context_compaction → "compaction" correct. Cannot trigger actual compaction end-to-end (requires token overflow), but code paths well-tested. Screenshots captured.
QA Screenshots:
Should Fix (Follow-up OK)
- Stuck spinner on abnormal SDK stream termination — Sentry flagged: if SDK stream terminates after heartbeat triggers
emit_start_if_ready()but before any message arrives,finallyblock doesn't close the compaction spinner. Edge case but worth a follow-up. - Heartbeat margin —
_HEARTBEAT_INTERVALat 10s with 12s frontend timeout leaves only 2s margin. Consider 7-8s for safety. - Frontend tests for compaction category — No component tests exist for GenericTool categories (pre-existing gap).
- Coordinate with PR #12259 — Confirmed merge conflict overlap on
sdk/service.pyand frontend files.
Risk Assessment
Merge risk: LOW | Rollback: EASY
Feature is purely additive — extra SSE events the frontend already renders via established GenericTool patterns. No database changes, no new endpoints, no auth changes. Hex-suffixed markers are a security hardening improvement. 291+ lines of new tests provide solid coverage. All previous blockers from 7 iterations are resolved. CI fully green.
Iteration History
| # | Date | HEAD | Verdict |
|---|---|---|---|
| 1–6 | Mar 2–3 | various | APPROVE WITH CONDITIONS |
| 7 | Mar 3 | c2fd871 | ✅ APPROVE |
| 8 | Mar 3 | 089d087 | ✅ APPROVE |
Automated re-review #8 by PR Review Squad — 8/8 specialists reported
@ntindle All previous blockers resolved. Hex-suffixed markers improve security posture. CI green, QA passed. Zero human reviews after 8 iterations — please engage.
Requested by @majdyz
When CoPilot compacts (summarizes/truncates) conversation history to fit within context limits, the user now sees it rendered like a tool call — a spinner while compaction runs, then a completion notice.
Backend:
compaction_start_events(),compaction_end_events(),compaction_events()inresponse_model.pyusing the existing tool-call SSE protocol (tool-input-start→tool-input-available→tool-output-available)service.py, SDK pre-query, SDK mid-stream) use the same patternFrontend:
compactiontool category toGenericToolwithArrowsClockwiseiconCleanup:
system_notice_start/end_events,COMPACTION_STARTED_MSGsystem_notice_events,system_error_events,_system_text_eventsCloses SECRT-2053