Skip to content

feat: manual context compression - #9795

Open
C10H14N2O5 wants to merge 18 commits into
AstrBotDevs:masterfrom
C10H14N2O5:feat/manual-context-compression
Open

C10H14N2O5 wants to merge 18 commits into
AstrBotDevs:masterfrom
C10H14N2O5:feat/manual-context-compression

Conversation

@C10H14N2O5

@C10H14N2O5 C10H14N2O5 commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

  • Add a /compact command for the local Agent Runner so users can explicitly request LLM-based context compression before the automatic threshold is reached.
  • Add an opt-in Manual Context Compression (Experimental) setting, disabled by default and shown only for local + llm_compress configurations.
  • Reuse the existing context compressor, token estimator, provider resolution, and persisted history format without adding a ChatUI button, backend API, database migration, or remote-runner compatibility layer.
  • Keep the original history when manual compression fails, returns an empty or unchanged result, or does not reduce the estimated token count; manual compression never invokes the automatic half-truncation fallback.
  • Acquire the same per-session lock as normal local Agent requests and revalidate the conversation ID, history, and stop state before persistence.
  • Keep WebChat progress transient while updating the context ring immediately after a successful compression: connected clients see concise progress and completion states, durable history stores only the terminal result, and the generated summary is never exposed.
  • Add focused regression coverage for forced compression, command gates, permissions, checkpoints, concurrency conflicts, database verification, log privacy, and provider resolution, with manual E2E testing also passing on the rebased implementation at 11c668b2.

Background

Fixes #9281

Related to #8348 and #9252, but intentionally does not implement configurable automatic compression thresholds.

AstrBot already supports automatic context management through maximum-turn enforcement and a token-threshold safeguard. When a request approaches the model context-window limit, ContextManager can summarize the history with an LLM or truncate it by conversation turns. However, token-triggered compression does not run until its threshold is reached. With models offering 512K, 1M, or larger context windows, users may encounter practical degradation far below the current 82% threshold:

  • Agent tool calls can accumulate large intermediate outputs with little long-term value.
  • Important task goals can become diluted by details in a long-running conversation.
  • A completed project phase may be worth summarizing before the next phase begins.
  • Re-sending a large history on every request increases latency, token consumption, and API cost.
  • Users should not need to misrepresent a model's context-window size merely to trigger compression earlier.

This PR adds the manual /compact workflow available in other agent harnesses. It lets users request compression at an appropriate point without changing AstrBot's automatic threshold or existing automatic behavior.

The first version uses a command instead of a dedicated ChatUI button. AstrBot's command system already provides registration, autocomplete, enable/disable controls, renaming, and permission management. Reusing it keeps the patch reviewable while supporting WebChat and other messaging platforms without a new API or UI component.

Because an LLM-generated summary may omit role state, narrative facts, or task details, this capability is marked experimental and remains disabled by default. Users must explicitly opt in before the command can run.


Modifications / 改动点

1. /compact user command

Register /compact in the built-in command plugin and reuse the existing command-management behavior:

  • The command can be disabled, renamed, or permission-restricted under Plugins → Manage Behaviors.
  • Regular members may use it in direct messages and group chats with per-member sessions enabled.
  • Shared group conversations require administrator permission, preventing regular members from changing context shared by the entire group.
  • The command rejects execution when AI features are disabled.
  • The command rejects execution when manual context compression is disabled.
  • The command rejects non-local Agent Runners.
  • The command requires the llm_compress context strategy.
  • Missing conversations or unavailable compression providers produce explicit errors.

The live user-visible flow remains intentionally concise:

⏳ Compressing context...
✅ Context compressed.

The generated summary is never printed into the chat. WebChat additionally receives an agent_stats event so the context ring immediately reflects the estimated size of the compressed history.

WebChat marks the progress status as ephemeral. The live client replaces it with the terminal result, while persisted history after a successful run contains only:

user: /compact
astrbot: ✅ Context compressed.

2. Opt-in experimental setting

Add the following configuration value:

{
  "agent_runner": {
    "runner_type": "local",
    "config": {
      "compression": {
        "enable_manual_context_compression": false
      }
    }
  }
}

Configuration behavior:

  • The default is false, so upgrading does not silently enable the feature for existing users.
  • The setting is part of the normalized local Agent Runner compression config and survives save, reload, and profile round trips.
  • The setting is visible only when agent_runner.runner_type=local and agent_runner.config.compression.overflow_strategy=llm_compress.
  • Selecting another compression strategy hides the setting without changing its value.
  • Switching to another runner replaces the local runner config; switching back loads local defaults and resets the setting to false, matching the embedded-runner semantics introduced by refactor: embed agent runner configuration in profiles #9821.
  • Legacy provider_settings.enable_manual_context_compression values migrate into the embedded local compression config through the existing migration flow. Upstream config version 4, its one-time step-limit upgrade, and future config versions are preserved.
  • Legacy default local-runner roots remain recognizable when the manual-compression field is absent and the historical step limit is 30. Only the comparison copy is normalized; the existing migration determines the persisted settings.
  • Chinese, English, Russian, and Japanese configuration metadata include localized labels and risk guidance.
  • No database field, configuration-version bump, frontend component, or generated API client change is introduced.

3. Reuse the existing compression flow

Add an optional force_compress argument to ContextManager.process(). Its default remains false, preserving all existing automatic callers.

With force_compress=True, the manual command:

  • bypasses the 82% automatic token threshold;
  • bypasses the maximum-turn gate;
  • does not depend on model-metadata context-window values;
  • ignores provider-reported token usage that is relevant only to the automatic request path;
  • reuses _run_compression(), the existing TokenCounter, and the LLM summary compressor; and
  • explicitly disables the post-summary half-truncation fallback.

Automatic compression retains its existing half-truncation protection. Only the manual path disables that fallback so a failed /compact operation cannot destructively truncate the original history.

4. Preserve the latest complete turn

Add an optional preserve_latest_round behavior to the LLM summary compressor. It defaults to disabled and therefore does not alter automatic compression output.

When enabled by manual compression:

  • the latest complete user-assistant turn is always preserved verbatim;
  • any incomplete user request or other content following that turn is preserved;
  • a zero-token estimate cannot move the protected latest turn into the summary input;
  • a conversation with only one complete user-assistant turn does not call the provider and reports insufficient history; and
  • additional recent complete turns are retained according to agent_runner.config.compression.keep_recent_ratio, without splitting logical turns.

This reduces the risk of changing the user's most recent requirements, tool state, or editable response.

5. Checkpoint-aware history persistence

The manual command reads the persisted database history and uses the existing checkpoint utilities during conversion:

  • bind_checkpoint_messages() associates persisted checkpoints with their messages.
  • dump_messages_with_checkpoints() serializes the compressed result.
  • Checkpoints belonging to summarized historical turns are removed with those messages.
  • Checkpoints on the preserved latest turn remain available, keeping WebChat edit and regenerate behavior functional.
  • Runtime-only persona, tool, and safety prompts are not persisted and are injected again through the normal request path.

6. Token-benefit validation and non-destructive failure

Estimate tokens both before and after compression:

  • Persistence occurs only when the estimated token count is strictly lower after compression.
  • Provider failures preserve the original history.
  • Empty summaries preserve the original history.
  • Unchanged compressor output preserves the original history.
  • Compressed histories that do not reduce the total estimated token count preserve the original history.
  • Manual compression never invokes half-truncation merely because the result still exceeds the automatic threshold.

On success, the compressed history and token_usage=0 are saved in the same database update. The next normal model response refreshes token state using the provider's actual usage information.

7. Session locking, concurrency revalidation, and stop handling

/compact acquires the same unified-message-origin (UMO) session lock used by normal local Agent requests:

  • It does not stop a running Agent request.
  • If the same session already has an active Agent request, compression waits until that request has persisted its complete result.
  • Different sessions can compact concurrently because their locks are independent.
  • After acquiring the lock, the command reloads the current conversation ID and database history.
  • After the summary request, it reloads the active conversation ID and persisted history again.
  • If the user switches conversations, runs /new, or modifies the same history through the Dashboard/API, the result is discarded.
  • The stop state is checked again immediately before persistence.
  • A WebChat /stop request prevents the compressed result from being written and explicitly reports that the original history was preserved.

Dashboard and API history updates do not acquire this session lock. The additional conversation-ID and history comparisons therefore protect against external changes that occur while the LLM summary is being generated.

8. Safe verification after database exceptions

If update_conversation() raises an exception, the command does not immediately assume that persistence failed. It safely reloads the database state once:

  • If the stored history already equals the target history, the write actually completed and the command continues as successful.
  • If the stored history remains equal to the original history, the command reports failure and confirms preservation.
  • If the stored history is a third state, the command reports an unknown context state and asks the user to inspect it before retrying.
  • If verification itself fails, the command reports the same unknown state.

This covers cases where a database update commits successfully but the client still receives an exception.

9. WebChat statistics, transient progress, and log privacy

  • WebChat progress uses the internal webchat_ephemeral chain type and remains visible to connected clients.
  • Dashboard SSE keeps the progress state in its active-run display snapshot but excludes it from the persistence accumulator.
  • Dashboard WebSocket and OpenAPI WebSocket forward the progress state without adding it to their persistence accumulators.
  • Success, failure, cancellation, and unknown-state terminal messages remain durable; page refreshes and service restarts retain the user command and one terminal bot message.
  • The agent_stats event is sent on a best-effort basis after persistence and after leaving the session lock.
  • A statistics-delivery failure does not roll back history or misreport the completed compression as a failure.
  • Platforms other than WebChat do not receive this event.
  • New exception logs contain only fixed messages and exception types.
  • Exception text, tracebacks, conversation history, and UMO identifiers are not logged, preventing provider or database errors from exposing sensitive context.

10. Reuse provider resolution

Refactor the existing context-compression provider resolver so both normal Agent construction and /compact can call it without changing its selection semantics:

  1. Prefer the explicitly configured compression provider.
  2. If that provider is unavailable, retain the existing fallback to the current session's chat provider.
  3. If no compression provider ID is configured, use the current session's chat provider.
  4. Reject compression when no provider is available.

Existing automatic compression provider selection remains unchanged.


Scope and Compatibility

This PR intentionally keeps the following boundaries:

  • The implementation is based on upstream master at b06550567b25d7ef4404bf526e448c869cc5d4ec and retains the embedded Agent Runner configuration introduced by refactor: embed agent runner configuration in profiles #9821.

  • Upstream attachment-upload limits, resumable chunked uploads, Local computer permission validation, and config-version 4 migration behavior are preserved.

  • Only AstrBot's built-in local Agent Runner is supported.

  • Dify, Coze, Alibaba Cloud Bailian, DeerFlow, and other remote runners own their context remotely; this PR does not attempt to rewrite remote history.

  • No dedicated Compress now ChatUI button is added.

  • No backend API or OpenAPI schema is added.

  • The automatic 82% compression threshold is unchanged.

  • The configurable automatic token thresholds proposed in [Feature]Token-Threshold Context Compression #8348 and [Feature] 上下文压缩阈值可配置化 #9252 are out of scope.

  • The generated summary is not exposed in chat.

  • No dependency or lockfile is changed.

  • No database schema is changed.

  • Existing legacy manual-compression values are migrated into the embedded local runner config; missing values remain disabled.

  • Default behavior for all existing automatic compression callers remains unchanged.

  • This is NOT a breaking change. / 这不是一个破坏性变更。


Changed Files and Size

This PR changes 26 tracked files relative to b06550567b25d7ef4404bf526e448c869cc5d4ec:

Area File Purpose
Command astrbot/builtin_stars/builtin_commands/commands/conversation.py /compact gates, locking, compression, persistence, and feedback
Registration astrbot/builtin_stars/builtin_commands/main.py Register /compact
Compression core astrbot/core/agent/context/compressor.py Preserve the latest complete turn and sanitize error logging
Context configuration astrbot/core/agent/context/config.py Add the optional latest-turn preservation setting
Context manager astrbot/core/agent/context/manager.py Forced compression and manual-mode half-truncation control
Provider resolution astrbot/core/astr_main_agent.py Share the existing compression-provider resolver
Agent Runner configuration astrbot/core/config/agent_runner.py Normalize the opt-in flag in local compression config
Legacy migration astrbot/core/utils/migra_helper.py Preserve the legacy manual-compression value and recognize historical default roots while retaining upstream version/step-limit migration
Configuration metadata astrbot/core/config/default.py Conditional metadata for the embedded compression config
WebChat transport Three Dashboard service files Forward transient progress without persisting it across SSE and WebSocket consumers
Dashboard i18n Four config-metadata.json files Chinese, English, Russian, and Japanese labels and risk guidance
Tests Ten test files Compression, token counting, command, provider, config migration/profile, SSE, and WebSocket regressions

Patch size:

26 files changed, 1679 insertions(+), 63 deletions(-)

Production Python (12 files): +409 / -39, net +370 lines
Dashboard locale metadata (4 files): +16 / -0, net +16 lines
Tests (10 files): +1254 / -24, net +1230 lines

Most added lines are regression tests. The implementation adds no remote-runner compatibility layer, UI component, or dependency.


Screenshots and Test Results

Current Tested State

  • Platform: Windows
  • Python: 3.12.13
  • pytest: 9.1.1
  • Ruff: 0.15.22
  • Upstream baseline: b06550567b25d7ef4404bf526e448c869cc5d4ec
  • Latest tested head: ed8101b492a9b83fa1101c04d5e5533099ed92d2
  • Earlier rebased verification head: 11c668b2287c0267437950ef52ec5d301f8df103

Feature Regression passed on ed8101b49. The Focused Conflict and Upload Regression and Embedded Agent Runner Configuration Regression results below were recorded on 11c668b2. Test runs used isolated ASTRBOT_ROOT directories under the system temporary directory and disabled pytest's cache provider. The suites overlap; their counts are not additive.

Focused Conflict and Upload Regression

.venv/Scripts/python.exe -m pytest -p no:cacheprovider -q tests/unit/test_agent_runner_config.py tests/unit/test_config.py tests/unit/test_config_profile_service.py tests/test_conversation_commands.py tests/test_chat_route.py tests/unit/test_live_chat_service.py tests/unit/test_open_api_service_ws.py tests/unit/test_chat_chunked_upload.py tests/unit/test_upload_filename_sanitization.py tests/unit/test_webchat_upload_image_format.py tests/test_upload_utils.py --tb=short

Result: 200 passed, 3 warnings in 18.82s.

Coverage includes transient progress and durable terminal messages over SSE and WebSocket, profile runtime/permission validation, config metadata, legacy migration, manual command behavior, resumable uploads, upload limits, filenames, and image formats.

The warnings concern deprecated audioop, the deprecated DashScope Assistants API, and an unawaited cleanup coroutine in the unchanged upstream chunked-upload code.

Feature Regression

.venv/Scripts/python.exe -m pytest -p no:cacheprovider -q tests/agent/test_context_manager.py tests/agent/test_token_counter.py tests/test_conversation_commands.py tests/test_tool_loop_agent_runner.py tests/unit/test_astr_main_agent.py tests/unit/test_session_lock.py tests/test_conversation_checkpoint.py tests/test_chat_route.py tests/unit/test_live_chat_service.py tests/unit/test_open_api_service_ws.py --tb=short

Result on ed8101b49: 364 passed, 1 warning in 17.31s.

This includes automatic compression behavior, forced compression without half truncation, latest-round and checkpoint preservation, command gates, provider failures, token-benefit checks, stop handling, session locks, concurrent history changes, database verification states, log privacy, transient progress, durable terminal results, context statistics, and streaming-disconnect persistence.

Compression metrics use independent estimates from the actual messages; the forced manual path uses local estimates throughout.

Embedded Agent Runner Configuration Regression

.venv/Scripts/python.exe -m pytest -p no:cacheprovider -q tests/unit/test_agent_runner_config.py tests/unit/test_config_profile_service.py tests/unit/test_config.py tests/unit/test_astr_agent_tool_exec.py tests/unit/test_core_lifecycle.py tests/unit/test_cron_manager.py tests/unit/test_third_party_agent_sub_stage.py --tb=short

Result: 228 passed, 2 warnings in 16.34s.

Coverage includes defaults, normalization, save/load and profile round trips, runner switching, and metadata keys for all four locales. Existing migration tests now also verify:

  • historical default roots with step limits of 30 or 128 and a missing, disabled, or enabled legacy manual-compression setting;
  • preservation of the manual setting during upstream step-limit migration;
  • config versions 2 and 3 upgrading to 4, versions 4 and 5 retaining their version, and repeated loads remaining idempotent; and
  • user-edited step limits remaining authoritative after the one-time upgrade.

Formatting and Static Checks

.venv/Scripts/python.exe -m ruff format --check --config 'extend-exclude = [".pytest_cache"]' .
513 files already formatted

.venv/Scripts/python.exe -m ruff check --config 'extend-exclude = [".pytest_cache"]' .
All checks passed!

git diff --check upstream/master...HEAD
No whitespace errors

The inaccessible local .pytest_cache directory was excluded from Ruff's traversal. Ruff lint also passed with every Python file in the PR explicitly supplied, including the tests. All four locale JSON files parsed successfully and contained the manual-compression description and hint.

Repository-wide Ruff format and lint checks, explicit lint of the affected Python files, and git diff --check passed.

GitHub CI

See the GitHub checks attached to the latest PR commit for current CI results.

Reviewer Verification Steps

  1. Select the local Agent Runner, choose llm_compress, and enable Manual Context Compression (Experimental).
  2. Create a conversation with multiple complete turns and run /compact below the automatic 82% threshold.
  3. Confirm transient progress, one durable terminal result, a reduced context ring after success, and continued recall on the next turn.
  4. Exercise failure, concurrency, stop, permission, migration, and runner-switch cases; unsuccessful compression must preserve the original history.

Dashboard Build and Manual E2E Verification

  • The Dashboard production build passed on the rebased verification head, 11c668b2287c0267437950ef52ec5d301f8df103: 5653 modules transformed; exit code 0. Dependencies were installed with pnpm@10.28.2 install --frozen-lockfile; npm run build ran the font-subset script, vue-tsc --noEmit, and vite build.
  • Manual E2E testing was rerun on the rebased implementation at 11c668b2287c0267437950ef52ec5d301f8df103 using the freshly built Dashboard, and the PR author confirmed that everything worked normally.
  • The configuration screenshots below now include Japanese (ja-JP) alongside Chinese, English, and Russian.

Earlier Full Repository and CI Results

The following records are from the previous PR head, 6e2bf2b559f75afa665e5d71346b2062c84bca85, based on fe3d77568b88ea3be83b2190d515da0a039da399. They are separate from the current-head regression, build, and manual E2E results above.

  • Full repository tests on Windows: 2240 passed, 1 skipped, 32 failed, 27 warnings. The recorded failures concerned Windows path separators, CRLF, symlinks, local shell, file URI, and sandbox skills; the affected test files were outside the then-current 26-file PR diff. The full repository suite was not rerun after this rebase.
  • Passing CI on the previous head, including CodeQL, unit tests, Dashboard build, formatting, and the cross-platform Python smoke-test matrix.

Manual Behavior and Screenshots

WebChat compression and Context Ring change

压缩示例-1 压缩ring估算

QQ Official Bot

手机运行 00_00_00-00_00_30
Failure preserves the original history

The compression prompt was temporarily changed to request expansion, verifying that output without a token reduction is never persisted:

Compress completed. 11454 -> 11473 tokens.
❌ Context compression failed; the original context was preserved.

The following failure paths were also verified:

  • invalid API key or unreachable provider;
  • both the current chat provider and dedicated compression provider unavailable;
  • only one complete user-assistant turn;
  • manual compression disabled;
  • global AI setting disabled; and
  • strategy changed to turn-based truncation.

None of these tested paths reduced the context ring, half-truncated history, or damaged subsequent recall.

Original history and Context Ring remain unchanged after failure

image
Session isolation and concurrency
  • Compacting session A left session B's context ring and history unchanged.
  • Sessions A and B could compact concurrently without blocking each other.
  • Repeating /compact twice in the same session did not restore stale history or drop the latest turn.
  • When the same session had a slow Agent response, /compact waited for it and then compressed history containing the completed response.
  • Running /new or modifying history through another entry point during compression prevented persistence:
⚠️ Context changed during compression; no changes were saved.
Stop behavior

A WebChat stop request cannot immediately cancel every in-flight provider request, but it prevents the result from being persisted. After the provider returned, the command reported:

⚠️ Compression cancelled; original context was preserved.

History and the context ring remained unchanged. This protects persisted state, although a slow provider request may still run to completion and incur cost, as noted under Known Limitations.

Configuration and i18n
  • The setting is disabled by default.
  • Its value persists after saving and refreshing.
  • Switching to turn-based truncation hides the setting; switching back restores the saved value.
  • Switching to a remote runner replaces the local runner config; switching back to local restores local defaults with manual compression disabled.
  • Disabling the global AI setting hides the section.
  • The saved configuration JSON contains agent_runner.config.compression.enable_manual_context_compression.
  • The configuration screenshots cover Chinese, English, Russian, and Japanese labels and risk guidance.
  • The localized titles are 手动上下文压缩(实验性), Manual Context Compression (Experimental), Ручное сжатие контекста (экспериментальная функция), and 手動コンテキスト圧縮(実験的).

Observed configuration:

Simplified Chinese (zh-CN)

配置1

English (en-US)

配置2

Russian (ru-RU)

配置3

Japanese (ja-JP)

image

Known Limitations

  • Only the local Agent Runner is supported; remote runners manage their context externally.
  • A WebChat stop request prevents persistence but cannot guarantee immediate cancellation of every provider HTTP request.
  • The context ring uses an estimate from the compressed history until the next normal model response updates it with actual provider usage.
  • Like other callers of ConversationManager.update_conversation(), the final update is not a database-level compare-and-swap. This path mitigates concurrent changes by re-reading and comparing the active conversation and persisted history immediately before the update.

SQLModel compatibility note

After syncing with upstream, CI picked up SQLModel 0.0.46 and hit the breaking datetime changes introduced in 0.0.45, causing 11 existing tests to fail. This also reproduces on upstream master and is tracked in #10205. As a temporary compatibility measure, both dependency manifests now constrain SQLModel to >=0.0.24,<0.0.45; the 11 affected tests pass locally with 0.0.44.


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txt 和 pyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。


Summary by Sourcery

Enable users to manually request safe LLM-based context compression for local Agent Runner conversations.

New Features:

  • Add an opt-in /compact command for manually compressing local Agent Runner conversation context with LLM summaries.
  • Expose conditional localized configuration for experimental manual context compression.

Bug Fixes:

  • Prevent failed, ineffective, cancelled, or concurrent manual compression from replacing the original conversation history.
  • Preserve the latest complete conversation turn and its checkpoints during manual compression.
  • Keep compression progress transient while persisting only the terminal result and protect sensitive error details from logs.

Enhancements:

  • Extend context processing and provider resolution to support forced, non-destructive compression while preserving existing automatic behavior.
  • Add session locking, conversation revalidation, database-write verification, and WebChat context statistics for the manual workflow.

Build:

  • Constrain the SQLModel dependency to versions below 0.0.45.

Tests:

  • Add regression coverage for command permissions and configuration gates, compression outcomes, checkpoints, concurrency, stop handling, provider selection, migration, logging privacy, and WebSocket/SSE persistence.

Comment on lines +127 to +129
f"Compress completed."
f" {prev_tokens} -> {tokens_after_summary} tokens,"
f" compression rate: {compress_rate:.2f}%.",
)
else:
logger.info(
f"Compress completed. {prev_tokens} -> {tokens_after_summary} tokens."
@C10H14N2O5
C10H14N2O5 marked this pull request as ready for review August 24, 2026 11:35
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. labels Aug 24, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/builtin_stars/builtin_commands/commands/conversation.py" line_range="311" />
<code_context>
+                "❌ Context compression requires admin permission in a shared "
+                "group conversation."
+            )
+            return
+
+        if not provider_settings.get("enable", True):
</code_context>
<issue_to_address>
**issue (bug_risk):** When the compact event is stopped before or after the provider call, the command returns immediately after sending the transient progress message and never sets a terminal result. WebChat therefore retains or clears the progress state without receiving the documented cancellation message, leaving the user without an explicit outcome.

**Triggers:** When `/stop` marks the compact event itself as stopped.

**Suggested fix:** Call `reply(cancelled)` before returning from both `message.is_stopped()` branches, unless the surrounding transport explicitly guarantees a terminal response for stopped events.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and /compact replaces persisted conversation history with an LLM-generated summary, permanently discarding older exact messages; reverting the code would not restore histories already compressed. The impact is limited to opted-in conversations and requires an explicit command, but an incorrect summary or authorization check could still cause unrecoverable context loss.

Blocking findings: astrbot/builtin_stars/builtin_commands/commands/conversation.py:311


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/builtin_stars/builtin_commands/commands/conversation.py
@C10H14N2O5

Copy link
Copy Markdown
Contributor Author

Hi @Soulter, when you have time, could you please take a quick look at whether the feature direction of this PR fits AstrBot’s current roadmap? I’m mainly looking for direction-level feedback before further iteration; no rush on the detailed review. Thanks!

@C10H14N2O5
C10H14N2O5 marked this pull request as draft August 30, 2026 08:02
@C10H14N2O5

Copy link
Copy Markdown
Contributor Author

I'll resolve the existing merge conflicts and adapt this PR to the latest Agent Runner configuration changes in #9821, then mark it ready for review again.

@C10H14N2O5
C10H14N2O5 force-pushed the feat/manual-context-compression branch from 78915fc to 77cdaa1 Compare August 30, 2026 12:06
@C10H14N2O5
C10H14N2O5 marked this pull request as ready for review August 30, 2026 13:56

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. The command permanently overwrites a conversation's persisted history with an LLM summary, so omitted details cannot be recovered by reverting the code and may require manual reconstruction. The impact is bounded to conversations where an administrator or user explicitly enables and invokes the feature, rather than affecting all conversations by default.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@C10H14N2O5

Copy link
Copy Markdown
Contributor Author

I'll resolve the current merge conflicts and update this PR for the latest v4.28.1 changes, then mark it ready for review again.

buyun14 pushed a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
buyun14 pushed a commit to buyun14/AstrBot that referenced this pull request Sep 17, 2026
@C10H14N2O5
C10H14N2O5 force-pushed the feat/manual-context-compression branch from 6e2bf2b to 11c668b Compare September 19, 2026 16:28
@C10H14N2O5
C10H14N2O5 marked this pull request as ready for review September 19, 2026 16:50

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/agent/context/token_counter.py" line_range="15" />
<code_context>
             )

     async def process(
-        self, messages: list[Message], trusted_token_usage: int = 0
+        self,
+        messages: list[Message],
</code_context>
<issue_to_address>
**issue (bug_risk):** Renaming the public keyword from `trusted_token_usage` to `reported_token_usage` causes existing custom `TokenCounter` implementations and callers that pass `trusted_token_usage=` to raise `TypeError`, despite the PR claiming to preserve the trusted-token-usage interface.

**Triggers:** When a plugin or downstream caller invokes `TokenCounter.count_tokens(..., trusted_token_usage=...)` by keyword.

**Suggested fix:** Accept both keyword names during the compatibility window, or retain `trusted_token_usage` in the public protocol and use `reported_token_usage` only for the internal call path.
</issue_to_address>

### Comment 2
<location path="astrbot/core/agent/context/manager.py" line_range="127-139" />
<code_context>
-        # calculate compress rate
-        compress_rate = (tokens_after_summary / self.config.max_context_tokens) * 100
-        logger.info(
-            f"Compress completed."
-            f" {prev_tokens} -> {tokens_after_summary} tokens,"
-            f" compression rate: {compress_rate:.2f}%.",
-        )
+        if self.config.max_context_tokens > 0:
+            compress_rate = (
</code_context>
<issue_to_address>
**🚨 nitpick (security):** Automatic compression still logs the exact estimated token transition (`prev_tokens -> tokens_after_summary`), contradicting the stated privacy change to avoid logging context token counts and exposing conversation-size metadata in application logs.

**Triggers:** When automatic or forced compression completes successfully and INFO logging is enabled.

**Suggested fix:** Log only a fixed completion message, or move token metrics to a deliberately privacy-reviewed telemetry channel rather than the general application log.

```suggestion
        logger.info("Compress completed.")
```
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and when /compact is invoked, the conversation record is replaced with an LLM-generated summary and older messages are discarded, so a wrong or incomplete summary can permanently lose conversation details. Reverting the code stops future compression but does not restore histories already rewritten; the impact is bounded to conversations where the feature is enabled and invoked.

Blocking findings: astrbot/core/agent/context/token_counter.py:15


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread astrbot/core/agent/context/token_counter.py
Comment thread astrbot/core/agent/context/manager.py
@C10H14N2O5

Copy link
Copy Markdown
Contributor Author

I investigated the two CodeQL clear-text logging alerts. They appear to be false positives involving trusted_token_usage: this parameter represents an integer token count, not an authentication token or secret. CodeQL’s sensitive-name heuristic treats names containing trusted as potentially secret, which may explain these findings.
The flagged statements log locally estimated token counts and a percentage. They do not log credentials, conversation text, or generated summaries. Provider-reported usage is used for the automatic compression threshold; the logged before/after counts are estimated separately without passing that usage value.
The regression test verifies this separation for both the built-in and a custom counter: reported usage of 83 triggers compression, while the log contains the independently estimated 1 -> 3 counts.
The alerts remain visible for review; no CodeQL rules have been disabled or suppressed.

@C10H14N2O5

Copy link
Copy Markdown
Contributor Author

After syncing with upstream, CI picked up SQLModel 0.0.46 and hit the breaking datetime changes introduced in 0.0.45, causing 11 existing tests to fail. This also reproduces on upstream master and is tracked in #10205. As a temporary compatibility measure, both dependency manifests now constrain SQLModel to >=0.0.24,<0.0.45; the 11 affected tests pass locally with 0.0.44.

This branch has not been deployed

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

Labels

area:core The bug / feature is about astrbot's core, backend area:provider The bug / feature is about AI Provider, Models, LLM Agent, LLM Agent Runner. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 增加手动触发上下文压缩的 /compact 指令或 ChatUI 按钮

2 participants