Skip to content

feat: 功能融合页接入 LivingMemory 官方面板入口(4.3.0) - #252

Merged
EterUltimate merged 3 commits into
mainfrom
feat/lm-27-official-page-link
Sep 12, 2026
Merged

EterUltimate merged 3 commits into
mainfrom
feat/lm-27-official-page-link

Conversation

@EterUltimate

@EterUltimate EterUltimate commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

概述

功能融合页接入 LivingMemory 官方面板入口,并完成对伴随插件与 AstrBot 最新版本的兼容性核对。版本 4.2.0 → 4.3.0。

背景

LivingMemory 2.6.0 起移除了独立 WebUI(config_manager.webui_settings 已删除),改为自带 AstrBot 官方插件页(pages/dashboard/index.html)。self_learning 的功能融合页因此一直显示「面板不可用:该插件面板未开启或尚未检测到可用入口」——实际上 LM 有了新的入口,只是本插件没有对接。

改动

  • 自动探测 LM 官方插件页:集成服务从 star 的 module_path 定位插件 pages/<页名>/index.html,结合 AstrBot 主配置 dashboard.host/port 生成 dashboard.official_page_url(AstrBot Dashboard 前端 hash 路由 /#/plugin-page/<插件名>/<页名>,该路由在 v4.27.4–v4.28 保持稳定)。
  • 功能融合页 LivingMemory 卡片新增「官方面板」按钮(新窗口打开,复用浏览器内 AstrBot 会话的 JWT)。
  • 嵌入壳降级修正:AstrBot 官方插件页固定返回 X-Frame-Options: SAMEORIGIN 且资产需 JWT(Authorization 头)换取,iframe 带不了凭据——embeddable 相应置为 false,嵌入壳由「面板不可用」变为「面板禁止内嵌 + 新窗口打开」。
  • 测试、文档(docs/integrations.md)、CHANGELOG、7 处版本号同步。

伴随插件核对(逐项契约,无需破坏性改动)

对照本地拉取最新后逐项核对:

契约 livingmemory 2.7.0-beta.1(9e1c2d7) 结论
注册名 metadata astrbot_plugin_livingmemory 不变
initializer.memory_engine main.py 引用不变 不变
memory_engine.graph_store memory_engine.py:135/208 不变
get_graph_snapshot(session_id, persona_id, limit_*) 返回 nodes/edges/entries/memories storage/graph_store_snapshot.py:320 签名一致 不变;2.6.x 新增的防御性 LIMIT 只作用于本插件未调用的 get_full_graph_snapshot
memory_engine.get_statistics() memory_engine_batch.py 自基线零 diff 不变

group_chat_plus HEAD 仍为 89ae2e1(V1.2.3.hotfix.2,与上次核对基线一致),无漂移。

AstrBot 兼容性核对(v4.28,e606a3037)

  • 本插件使用的全部 API 符号逐一验证存在:astrbot.api.{logger, AstrBotConfig}、astrbot.api.event.{AstrMessageEvent, filter}(filter 为子包)、astrbot.api.star.{Context, StarTools}、astrbot.api.web.request(web.py:322)、astrbot.core.{agent.message.TextPart, db.po.Personality, message.components.Plain, platform.message_type.MessageType, provider.entities.*, provider.provider.*, utils.astrbot_path.get_astrbot_data_path}。
  • astrbot/api/event 与 astrbot/api/web 自 v4.27.5 以来零提交;context.register_web_api 在 v4.28 仍存在(context.py:705)。

本地验证

  • pytest tests/ -q:786 passed(含本 PR 新增 2 个 official_page_url 用例)
  • web_src: pnpm build 产物已提交;pnpm test 50 passed
  • ruff check 通过;已安装至本地 AstrBot/data/plugins 并做导入级验证(4.3.0,真实 LM 安装探测到 dashboard 页)

上游动态扫描(过去 24h)

版本

4.2.0 → 4.3.0(metadata.yaml、__init__.py、web_src/package.json、README 徽章 ×2、docs/README.md)

Summary by Sourcery

Integrate LivingMemory’s official AstrBot panel into the feature integration dashboard while preserving compatibility with existing memory and companion-plugin integrations.

New Features:

  • Add automatic discovery of LivingMemory’s AstrBot official plugin pages and expose the generated dashboard entry URL.
  • Add an Official Panel action to the integrations page for opening LivingMemory’s official panel in a new window.

Bug Fixes:

  • Handle official AstrBot plugin pages as non-embeddable and provide a clear new-window fallback instead of reporting the panel as unavailable.

Enhancements:

  • Verify compatibility with LivingMemory 2.7.0-beta.1 and AstrBot v4.28 without changing existing integration contracts.

Build:

  • Update the dashboard build artifacts and synchronize the project version to 4.3.0.

Documentation:

  • Document the official LivingMemory panel URL, embedding behavior, and troubleshooting guidance.

Tests:

  • Add coverage for official page discovery, dotted module paths, missing page origins, and non-embeddable panel behavior.

Chores:

  • Update the changelog and version badges for the 4.3.0 release.

LivingMemory 2.6.0+ removed its standalone WebUI and now ships an
AstrBot official plugin page (pages/dashboard/index.html). The
integration service now detects that page from the star's module_path
and the astrbot_config dashboard host/port, and exposes it as
dashboard.official_page_url (AstrBot Dashboard hash route
/#/plugin-page/<name>/<page>).

Because official plugin pages are served with X-Frame-Options:
SAMEORIGIN and fetch page assets with a short-lived JWT token, they
cannot be embedded in an iframe; embeddable is therefore reported as
false and the embed shell degrades from 'panel unavailable' to
'open in a new window'. The integrations page adds an official-panel
button on the LivingMemory card.

Companion contract re-check against livingmemory 2.7.0-beta.1
(9e1c2d7) and group_chat_plus V1.2.3.hotfix.2 (89ae2e1): registration
name, initializer.memory_engine, memory_engine.graph_store,
get_graph_snapshot signature/return keys and get_statistics are all
unchanged, so the local graph adapter needs no further changes. The
defensive full-graph snapshot caps added in LM 2.6.0 only affect
get_full_graph_snapshot, which this plugin never calls.

AstrBot API surface re-check against v4.28 (e606a3037): every symbol
this plugin imports still exists and astrbot/api/event + astrbot/api/web
saw no commits since v4.27.5.
@sourcery-ai

sourcery-ai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

本 PR 将 LivingMemory 2.6+ 的 AstrBot 官方插件页接入功能融合页:服务端通过插件文件结构和 AstrBot Dashboard 配置自动生成官方入口,前端以新窗口按钮打开并明确禁止 iframe 嵌入,同时补充测试、文档、构建产物和 4.3.0 版本同步。

Sequence diagram for LivingMemory official panel discovery and opening

sequenceDiagram
    participant Dashboard as AstrBot Dashboard
    participant Service as IntegrationService
    participant Star as LivingMemory Star
    participant Config as AstrBotConfig
    participant UI as IntegrationsPage
    actor User

    Service->>Star: module_path
    Service->>Service: _discover_plugin_pages(module_path)
    Service->>Config: dashboard.host and dashboard.port
    Service->>Service: _astrbot_dashboard_origin(astrbot_config)
    Service->>Service: _livingmemory_official_page_url(star)
    Service-->>UI: dashboard.official_page_url
    User->>UI: Click official panel
    UI->>UI: openOfficialPage(item)
    UI->>Dashboard: window.open(official_page_url)
    Dashboard-->>User: Plugin page with existing JWT session
Loading

Flow diagram for LivingMemory panel fallback behavior

flowchart TD
    A[LivingMemory integration status] --> B{official_page_url detected?}
    B -- No --> C[Show existing panel or local graph entry]
    B -- Yes --> D{Existing external dashboard URL?}
    D -- Yes --> E[Open external panel]
    D -- No --> F[Set embeddable to false]
    F --> G[Show official panel button]
    G --> H[Open AstrBot official plugin page in a new window]
Loading

File-Level Changes

Change Details Files
自动发现 LivingMemory 的 AstrBot 官方插件页并生成可复用的 Dashboard 入口 URL。
  • 从插件 module_path 的 pages 目录发现包含 index.html 的页面。
  • 读取 AstrBot dashboard host/port,构造带插件名和页面名的 hash 路由 URL。
  • 未检测到页面、配置或插件路径时安全返回空入口。
webui/services/integration_service.py
tests/unit/test_integration_service.py
调整 LivingMemory 面板状态与打开方式,适配官方页面不能嵌入的限制。
  • 将官方页面标记为不可 iframe 嵌入,并保留新窗口打开目标。
  • 在功能融合卡片增加“官方面板”按钮,使用新窗口复用 AstrBot 会话。
  • 补充官方入口缺失、可用性和降级提示的测试覆盖。
webui/services/integration_service.py
web_src/src/components/business/IntegrationCard.tsx
web_src/src/components/business/IntegrationCard.module.scss
web_src/src/pages/integrations/IntegrationsPage.tsx
tests/unit/test_integration_service.py
同步发布文档、变更记录和前端构建产物至 4.3.0。
  • 更新插件、Python 包、前端包及文档中的版本号。
  • 记录 LivingMemory 2.6/2.7、AstrBot v4.28 兼容性核对结果。
  • 补充官方插件页字段、内嵌限制和故障排查说明。
  • 更新已提交的 Dashboard 静态构建资源引用。
CHANGELOG.md
README.md
README_EN.md
__init__.py
metadata.yaml
docs/README.md
docs/integrations.md
web_src/package.json
web_res/static/dashboard/index.html
web_res/static/dashboard/assets/index-B-FWb1Zi.css
web_res/static/dashboard/assets/index-DfjsDeQS.js
web_res/static/dashboard/assets/index-BUfCwZyG.js

Possibly linked issues

  • #未提供: PR 将 LivingMemory 官方插件页接入功能融合页面,直接通过 AstrBot Dashboard 新窗口访问。

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

@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

Fixed security issues:

  • Cross-site scripting (XSS) via untrusted HTML/JS injection in web rendering sinks (link) · Dashboard
Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="webui/services/integration_service.py" line_range="123-124" />
<code_context>
+    """
+    if not module_path:
+        return []
+    base = Path(str(module_path))
+    for pages_root in (base / "pages", base.parent / "pages"):
+        if not pages_root.is_dir():
+            continue
</code_context>
<issue_to_address>
**issue (bug_risk):** When AstrBot exposes `star.module_path` as the dotted import path used elsewhere in this repository (for example `data.plugins.astrbot_plugin_livingmemory.main`), `Path(module_path)` does not point to the installed plugin directory, so the `pages/` scan finds nothing and `official_page_url` is always `None`.

**Triggers:** When the runtime Star object uses a dotted module path rather than an entry-file filesystem path.

**Suggested fix:** Resolve dotted module paths through the loaded module or derive the plugin directory from `root_dir_name` before scanning `pages/`.
</issue_to_address>

### Comment 2
<location path="webui/services/integration_service.py" line_range="124-137" />
<code_context>
+    if not module_path:
+        return []
+    base = Path(str(module_path))
+    for pages_root in (base / "pages", base.parent / "pages"):
+        if not pages_root.is_dir():
+            continue
+        try:
</code_context>
<issue_to_address>
**issue (bug_risk):** A permission or filesystem error from `pages_root.is_dir()` is outside the `OSError` handler, so status generation raises instead of returning no official page when the plugin directory cannot be inspected.

**Triggers:** When the installed plugin path or its `pages/` directory is inaccessible or disappears during status generation.

**Suggested fix:** Include the `is_dir()` checks inside the existing `OSError` handling and return an empty page list on filesystem errors.

```suggestion
    for pages_root in (base / "pages", base.parent / "pages"):
        try:
            if not pages_root.is_dir():
                continue
            names = sorted(
                item.name
                for item in pages_root.iterdir()
                if item.is_dir() and (item / "index.html").is_file()
            )
        except OSError:
            return []
        if names:
            return names
    return []
```
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: webui/services/integration_service.py:124, webui/services/integration_service.py:137


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

Comment thread webui/services/integration_service.py Outdated
Comment thread webui/services/integration_service.py Outdated
AstrBot's star/base.py overwrites StarMetadata.module_path with
cls.__module__ (e.g. data.plugins.<name>.main), so scanning
Path(module_path) alone never finds pages/ and official_page_url
stayed None in production. Resolve dotted import paths through
sys.modules[...].__file__ and keep scanning filesystem paths
directly. Also guard the pages/ directory inspection against
OSError so an unreadable plugin directory degrades to 'no official
page' instead of failing status generation.

Addresses the Sourcery review findings on PR #252.

@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.

Sourcery assessment

Approved.

@EterUltimate
EterUltimate merged commit 60b38d7 into main Sep 12, 2026
19 checks passed
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