Optimize LLM hook cache-friendly context injection#201
Conversation
Reviewer's GuideRefactors 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 fallbacksequenceDiagram
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
Flow diagram for LLM hook injection target normalization and usageflowchart 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]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
483e6f8 to
caaf3a6
Compare
There was a problem hiding this comment.
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 whenextra_user_content_parts/TextPartsupport 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_injectand 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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| @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" |
There was a problem hiding this comment.
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- 为了让大小写无关的查找完全生效,建议在定义
LLM_HOOK_TARGET_ALIASES的位置,将所有键统一为小写,例如:
LLM_HOOK_TARGET_ALIASES = {k.lower(): v for k, v in _ORIGINAL_ALIASES.items()}或者在静态定义时就使用小写键。 - 如果存在直接使用原来大小写敏感键访问
LLM_HOOK_TARGET_ALIASES的代码,需要一并调整为使用小写键(例如:LLM_HOOK_TARGET_ALIASES[some_key.lower()]),以保持行为一致。
Summary
extra_user_content_partsinstead of changing the stablesystem_promptprefixllm_hook_injection_targetto the cache-friendly AstrBot API while preserving legacy fallback targets for older AstrBot versionsTextPartinjection and fallback behaviorValidation
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:cacheproviderpython -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.pygit diff --cached --checkTextPart.mark_as_temp()exists and hook injection keepsreq.system_promptunchanged while appending aTextPartSummary 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:
Documentation:
Tests: