* refactor(ltm): redesign long-term memory with context compaction
- Add raw_records / contexts / summaries data model per group
- Add LLM summary compaction strategy alongside truncation
- Add turn-based (_split_into_rounds) granularity
- Add image caption integration into LTM history
- Add tool_call / tool_result persistence into raw_records
- Add active reply support driven by LTM state
- Improve summary injection prefix with system note and delimiters
- Add info-level logging for summary compaction lifecycle
- Clarify default summary prompt with explicit preserve/drop rules
- Add context_guard for history overflow protection in agent runner
- Add internal agent history compaction in agent_sub_stages
- Add comprehensive LTM unit tests and compaction test suites
* fix(ltm): handle malformed JSON in tool args and clean up lock on session removal
* fix(ltm): guard against duplicate system prompt note injection
* fix(ltm): fall back to user message when internal marker parsing fails
- Treat lines starting with <T:CALL>, <T:RES, or <BOT/ as regular user
messages when their respective parsers return None, instead of silently
dropping them. Defensive guard against malformed internal markers.
* fix(ltm): release session lock during LLM summary generation
* fix(ltm): trim raw_records in handle_message to prevent unbounded growth
* perf(ltm): use len(s) instead of len(s.encode()) in trim loop
Avoid allocating a new bytes object for every string when calculating
buffer size in _trim_raw_records. Character count is sufficient for
the approximate memory cap.
* feat(ltm): make user segment truncation limits configurable
* feat(ltm): pre-fill default LTM summary prompt in config and i18n
* refactor(ltm): hardcode internal segment/trim constants
* refactor(ltm): unify compaction strategy with main agent runner
* feat(ltm): add @mention weight marker for group chat messages
* test: fix test failures from LTM compaction unification
* chore(dashboard): remove obsolete LTM compaction i18n metadata
* chore: shrink codebase
* feat(group-chat): implement group chat context management and related functionality
---------
Co-authored-by: Tsukumi <112180165+Tsukumi233@users.noreply.github.com>
Co-authored-by: zenfun <zenfun510@gmail.com>
Co-authored-by: Soulter <905617992@qq.com>
* fix: plugin name in the marketplace does not match the local plugin
* chore: update test
* Update astrbot/dashboard/routes/plugin.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* Update astrbot/dashboard/routes/plugin.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* Update astrbot/dashboard/routes/plugin.py
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* chore: reformat code
* Clean up unused variables in useExtensionPage.js
Removed unused sets for installed repositories and names.
* fix: 统一repo匹配逻辑,移除fallback
---------
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
* feat(plugin): add pages field to plugin list API response
Include discovered page names for each plugin in the /api/plugin/get
response, so the frontend can determine whether a plugin has a WebUI
without an extra detail request.
* feat(webui): add direct WebUI access button on plugin cards
- Add "open-webui" button on plugin cards when plugin has pages
- Navigate to plugin's first page directly from the card
- Adjust PluginPagePage iframe height for better UX
- Add i18n labels (zh-CN/en-US/ru-RU)
* perf(plugin): use asyncio.gather for concurrent page discovery
Replace sequential await loop with concurrent processing to avoid
blocking on disk I/O when discovering plugin pages.
* fix(webui): disable open plugin UI button when plugin is deactivated
Prevent navigation to a disabled plugin's WebUI page which would
result in an error.
* test: update plugin API tests to match pages field in list response
- test_plugin_get_excludes_scanned_pages: expect pages field now present
- test_plugins: use name-based lookup instead of exact count assertion
---------
Co-authored-by: lxfight <lxfight@192.168.5.50>
Only overwrite tool-call-related fields from the re-query response, preserving the original completion_text and reasoning_content that were already sent to the user.
* fix: handle delta=None chunks in streaming to prevent SDK to_dict() error
When certain OpenAI-compatible providers (Gemini, DeepSeek, some proxies)
return chunks with choice.delta=None (e.g. ContentBlockDeltaEvent),
ChatCompletionStreamState._convert_initial_chunk_into_snapshot internally
calls choice.delta.to_dict() at line 747, causing:
'NoneType' object has no attribute 'to_dict'
Fix:
1. Skip handle_chunk when delta is None (delta=None chunks have no
content contribution anyway)
2. Wrap get_final_completion in try/except to gracefully fall back to
empty ChatCompletion if SDK state is corrupted
Refs: openai-python#5069, openai-python#5047
* fix: resolve bugs found by Sourcery and gemini-code-assist review
- Remove orphan logger.error that caused NameError on every chunk
- Replace broken empty ChatCompletion fallback with clean return;
streamed content already yielded, no data loss
Co-authored-by: sourcery-ai[bot] <sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <gemini-code-assist[bot]@users.noreply.github.com>
* fix: properly replace get_final_completion fallback in _query_stream
Previous fix_pr_v3 wrongly injected code into terminate() instead.
Now correctly:
1. Replace empty ChatCompletion fallback with clean return in _query_stream
2. Revert terminate() to original (await self.client.close() only)
* fix: revert corrupted terminate() to original
Previous fix_pr_v3 injected wrong-indentation code into terminate().
---------
Co-authored-by: sourcery-ai[bot] <sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <gemini-code-assist[bot]@users.noreply.github.com>
PR #8015 added 'Noto Sans' to the Google Fonts link and CJK fallback list,
but the font was placed at the end of $cjk-sans-fallback where browsers
never reach it for Cyrillic text. The global $body-font-family also lacked
'Outfit' entirely, causing Vuetify to use CJK fonts as the primary face.
Changes:
- Remove 'Noto Sans' from the end of $cjk-sans-fallback (it is not a CJK font)
- Add 'Outfit' and 'Noto Sans' to $body-font-family before CJK fallbacks
- Update .Outfit class in _container.scss to match the new stack
This ensures:
- Latin text → Outfit
- Cyrillic text → Noto Sans (loaded by vite-plugin-webfont-dl)
- CJK text → Noto Sans SC / PingFang SC etc.
Fixes follow-up to #8015.