Skip to content

Optimize LLM hook cache-friendly context injection#201

Merged
EterUltimate merged 1 commit into
NickCharlie:mainfrom
EterUltimate:codex/self-learning-cache-hit
Jun 11, 2026
Merged

Optimize LLM hook cache-friendly context injection#201
EterUltimate merged 1 commit into
NickCharlie:mainfrom
EterUltimate:codex/self-learning-cache-hit

Conversation

@EterUltimate

@EterUltimate EterUltimate commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • keep LLM hook dynamic context in temporary extra_user_content_parts instead of changing the stable system_prompt prefix
  • default llm_hook_injection_target to the cache-friendly AstrBot API while preserving legacy fallback targets for older AstrBot versions
  • update WebUI schema/docs/log text and add regression coverage for temp TextPart injection and fallback behavior

Validation

  • python -m pytest tests\unit\test_feature_delegation.py tests\unit\test_config.py tests\unit\test_config_service.py --basetemp .tmp\pytest-basetemp -p no:cacheprovider
  • python -m py_compile config.py services\hooks\llm_hook_handler.py webui\services\config_service.py tests\unit\test_feature_delegation.py tests\unit\test_config.py tests\unit\test_config_service.py
  • git diff --cached --check
  • AstrBot source smoke: verified real TextPart.mark_as_temp() exists and hook injection keeps req.system_prompt unchanged while appending a TextPart

Summary by Sourcery

Optimize LLM hook context injection to use cache-friendly temporary user content parts by default while maintaining backward-compatible fallbacks and updating configuration, tests, and documentation accordingly.

Enhancements:

  • Route LLM hook dynamic context through a helper that appends temporary extra_user_content_parts when supported, falling back to legacy system_prompt or prompt injection based on configuration.
  • Introduce centralized configuration for LLM hook injection targets with aliases, normalization, and a cache-friendly default target for both static and runtime settings.
  • Clarify WebUI configuration schema, log messages, and architecture docs to describe temporary context injection behavior and legacy fallback paths.

Documentation:

  • Update learning-flow, architecture, and README diagrams/text to describe temporary extra_user_content_parts injection with legacy fallbacks and caching implications.

Tests:

  • Add regression tests for temporary TextPart injection without mutating system_prompt, legacy prompt fallback behavior, and normalization of LLM hook injection target aliases in config and WebUI schema.

@sourcery-ai

sourcery-ai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Refactors LLM hook context injection to prefer cache-friendly temporary extra_user_content_parts while preserving configurable legacy fallbacks, updates configuration defaults/normalization and WebUI schema accordingly, and adds tests and docs to cover the new behavior.

Sequence diagram for cache-friendly LLM hook context injection with legacy fallback

sequenceDiagram
    participant H as LLMHookHandler
    participant Req as req
    participant TP as TextPart

    H->>H: _inject(req, injections, hook_start)
    H->>H: _append_extra_user_content(req, context_text)
    alt [extra_user_content_parts supported]
        H->>TP: TextPart(text=context_text)
        H->>TP: mark_as_temp()
        H->>Req: extra_user_content_parts.append(part)
    else [not supported]
        H->>H: _legacy_inject(req, injection_text, target)
        alt [target == "prompt"]
            H->>Req: set req.prompt
        else [target == "system_prompt" or default]
            H->>Req: set req.system_prompt
        end
    end
Loading

Flow diagram for LLM hook injection target normalization and usage

flowchart TD
    A[配置文件或WebUI输入
llm_hook_injection_target] --> B[PluginConfig._normalize_llm_hook_injection_target]
    B --> C{在 LLM_HOOK_TARGET_ALIASES 中?}
    C -- 是 --> D[标准化为
extra_user_content_parts
system_prompt
或 prompt]
    C -- 否 --> E[记录 warning
并使用
CACHE_FRIENDLY_LLM_HOOK_TARGET]
    D --> F[PluginConfig.llm_hook_injection_target]
    E --> F
    F --> G[LLMHookHandler._inject
读取 llm_hook_injection_target]
    G --> H{支持 extra_user_content_parts?}
    H -- 是 --> I[使用临时
extra_user_content_parts 注入]
    H -- 否 --> J[按 legacy 目标
回退到 system_prompt
或 prompt]
Loading

File-Level Changes

Change Details Files
Make LLM hook inject dynamic context as temporary extra_user_content_parts when supported, with configurable legacy fallbacks.
  • Refactor _inject to build a ... block and delegate to a new _append_extra_user_content helper before falling back.
  • Introduce _append_extra_user_content to append a TextPart, calling mark_as_temp() when available, and return whether injection succeeded.
  • Introduce _legacy_inject to route fallback injection to prompt or system_prompt based on configured target and LEGACY_LLM_HOOK_TARGETS, with updated logging and warnings.
  • Import CACHE_FRIENDLY_LLM_HOOK_TARGET and LEGACY_LLM_HOOK_TARGETS with a try/except to support both package and flat layouts, and log injection target in debug messages.
services/hooks/llm_hook_handler.py
Change plugin configuration so llm_hook_injection_target defaults to the cache-friendly AstrBot API and normalize aliases.
  • Add CACHE_FRIENDLY_LLM_HOOK_TARGET, LEGACY_LLM_HOOK_TARGETS, and LLM_HOOK_TARGET_ALIASES constants to centralize target names and aliases.
  • Set PluginConfig.llm_hook_injection_target default to CACHE_FRIENDLY_LLM_HOOK_TARGET and adjust create_from_config runtime default accordingly.
  • Add a field_validator to normalize llm_hook_injection_target using LLM_HOOK_TARGET_ALIASES and warn on unknown values while falling back to the cache-friendly default.
config.py
Update WebUI schema and runtime config service to expose the new default target and guidance in the settings UI.
  • Change the llm_hook_injection_target schema default to extra_user_content_parts and expand hint text to explain cache benefits and legacy fallback behavior.
  • Extend options to include extra_user_content_parts as the recommended choice and mark system_prompt/prompt as legacy fallbacks.
  • Ensure the runtime schema returned by get_config_schema reflects the new default value, options order, and hint content and add assertions for this.
webui/services/config_service.py
tests/unit/test_config_service.py
Add and adjust tests for LLM hook injection behavior and config normalization.
  • Add a unit test validating that _inject uses a fake TextPart with mark_as_temp, preserves system_prompt, and appends a single temporary context block to extra_user_content_parts.
  • Add a unit test verifying legacy prompt fallback when extra_user_content_parts is unavailable and the injection target is prompt, confirming system_prompt remains unchanged and prompt is extended.
  • Extend PluginConfig tests to assert the new default llm_hook_injection_target value in default and empty-config cases and to verify alias normalization (e.g., user_message_tail).
tests/unit/test_feature_delegation.py
tests/unit/test_config.py
Align documentation, diagrams, and log messages with the temporary context injection model.
  • Update learning-flow and architecture docs to describe temporary extra_user_content_parts injection with mark_as_temp and clarify that legacy fallback can use system_prompt or prompt, plus explain cache/history benefits.
  • Adjust README and docs/README sequence/flow diagrams to describe temporary extra_user_content_parts injection with legacy fallback wording.
  • Update the LLM_REQUEST_HOOK_SUCCESS log message to say it injects temporary LLM context instead of specifically targeting system_prompt.
docs/learning-flow.md
docs/architecture.md
README.md
docs/README.md
statics/messages.py

Possibly linked issues

  • #[Feature] 自动清理注入到user prompt的提示: PR通过临时 extra_user_content_parts 注入和缓存友好目标,实质解决了Issue提出的提示注入膨胀与缓存问题。

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@EterUltimate EterUltimate force-pushed the codex/self-learning-cache-hit branch from 483e6f8 to caaf3a6 Compare June 11, 2026 11:09

@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, and left some high level feedback:

  • In _append_extra_user_content, failures silently fall back to legacy injection; consider adding a debug log when extra_user_content_parts/TextPart support is missing so it's easier to understand why the cache‑friendly path isn't being used at runtime.
  • The <context>\n{injection_text}\n</context> wrapper string is duplicated between _inject and the tests; extracting this format into a small helper/constant would reduce the risk of future drift between behavior and assertions.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `_append_extra_user_content`, failures silently fall back to legacy injection; consider adding a debug log when `extra_user_content_parts`/`TextPart` support is missing so it's easier to understand why the cache‑friendly path isn't being used at runtime.
- The `<context>\n{injection_text}\n</context>` wrapper string is duplicated between `_inject` and the tests; extracting this format into a small helper/constant would reduce the risk of future drift between behavior and assertions.

## Individual Comments

### Comment 1
<location path="config.py" line_range="344-353" />
<code_context>
     def _normalize_target_blacklist(cls, value) -> List[str]:
         return normalize_identifier_list(value)

+    @field_validator("llm_hook_injection_target", mode="before")
+    @classmethod
+    def _normalize_llm_hook_injection_target(cls, value) -> str:
+        target = str(value or CACHE_FRIENDLY_LLM_HOOK_TARGET).strip()
+        normalized = LLM_HOOK_TARGET_ALIASES.get(target)
+        if normalized:
+            return normalized
+        logger.warning(
+            f"未知 LLM Hook 注入目标 {value!r},"
+            "已回退到 cache-friendly extra_user_content_parts"
+        )
+        return CACHE_FRIENDLY_LLM_HOOK_TARGET
+
     def model_post_init(self, __context) -> None:
</code_context>
<issue_to_address>
**suggestion:** Normalize `llm_hook_injection_target` more robustly (e.g., case-insensitive, alias-friendly).

The validator currently does `str(value).strip()` then `LLM_HOOK_TARGET_ALIASES.get(target)`, so values are whitespace-tolerant but still case-sensitive (e.g., `"System_Prompt"` falls back to `extra_user_content_parts`). Normalizing case for `target` and alias keys (or otherwise canonicalizing the input) would make configuration more resilient to variations from different environments or UIs.

Suggested implementation:

```python
    @field_validator("llm_hook_injection_target", mode="before")
    @classmethod
    def _normalize_llm_hook_injection_target(cls, value) -> str:
        # 允许大小写和空白差异的健壮归一化
        raw_target = str(value or CACHE_FRIENDLY_LLM_HOOK_TARGET).strip()
        normalized_key = raw_target.lower()

        # 优先使用小写键查找,其次回退到原始键,方便兼容旧配置
        normalized = (
            LLM_HOOK_TARGET_ALIASES.get(normalized_key)
            or LLM_HOOK_TARGET_ALIASES.get(raw_target)
        )
        if normalized:
            return normalized

        logger.warning(
            f"未知 LLM Hook 注入目标 {value!r}"
            "已回退到 cache-friendly extra_user_content_parts"
        )
        return CACHE_FRIENDLY_LLM_HOOK_TARGET

```

1. 为了让大小写无关的查找完全生效,建议在定义 `LLM_HOOK_TARGET_ALIASES` 的位置,将所有键统一为小写,例如:
   `LLM_HOOK_TARGET_ALIASES = {k.lower(): v for k, v in _ORIGINAL_ALIASES.items()}` 或者在静态定义时就使用小写键。
2. 如果存在直接使用原来大小写敏感键访问 `LLM_HOOK_TARGET_ALIASES` 的代码,需要一并调整为使用小写键(例如:`LLM_HOOK_TARGET_ALIASES[some_key.lower()]`),以保持行为一致。
</issue_to_address>

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 config.py
Comment on lines +344 to +353
@field_validator("llm_hook_injection_target", mode="before")
@classmethod
def _normalize_llm_hook_injection_target(cls, value) -> str:
target = str(value or CACHE_FRIENDLY_LLM_HOOK_TARGET).strip()
normalized = LLM_HOOK_TARGET_ALIASES.get(target)
if normalized:
return normalized
logger.warning(
f"未知 LLM Hook 注入目标 {value!r},"
"已回退到 cache-friendly extra_user_content_parts"

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.

suggestion: Normalize llm_hook_injection_target more robustly (e.g., case-insensitive, alias-friendly).

The validator currently does str(value).strip() then LLM_HOOK_TARGET_ALIASES.get(target), so values are whitespace-tolerant but still case-sensitive (e.g., "System_Prompt" falls back to extra_user_content_parts). Normalizing case for target and alias keys (or otherwise canonicalizing the input) would make configuration more resilient to variations from different environments or UIs.

Suggested implementation:

    @field_validator("llm_hook_injection_target", mode="before")
    @classmethod
    def _normalize_llm_hook_injection_target(cls, value) -> str:
        # 允许大小写和空白差异的健壮归一化
        raw_target = str(value or CACHE_FRIENDLY_LLM_HOOK_TARGET).strip()
        normalized_key = raw_target.lower()

        # 优先使用小写键查找,其次回退到原始键,方便兼容旧配置
        normalized = (
            LLM_HOOK_TARGET_ALIASES.get(normalized_key)
            or LLM_HOOK_TARGET_ALIASES.get(raw_target)
        )
        if normalized:
            return normalized

        logger.warning(
            f"未知 LLM Hook 注入目标 {value!r},"
            "已回退到 cache-friendly extra_user_content_parts"
        )
        return CACHE_FRIENDLY_LLM_HOOK_TARGET
  1. 为了让大小写无关的查找完全生效,建议在定义 LLM_HOOK_TARGET_ALIASES 的位置,将所有键统一为小写,例如:
    LLM_HOOK_TARGET_ALIASES = {k.lower(): v for k, v in _ORIGINAL_ALIASES.items()} 或者在静态定义时就使用小写键。
  2. 如果存在直接使用原来大小写敏感键访问 LLM_HOOK_TARGET_ALIASES 的代码,需要一并调整为使用小写键(例如:LLM_HOOK_TARGET_ALIASES[some_key.lower()]),以保持行为一致。

@EterUltimate EterUltimate merged commit c18724b into NickCharlie:main Jun 11, 2026
4 checks passed
@EterUltimate EterUltimate deleted the codex/self-learning-cache-hit branch June 18, 2026 05:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant