Skip to content

Conversation

Flartiny
Copy link
Contributor

@Flartiny Flartiny commented Jun 25, 2025

Motivation

为可能的插件联动做铺垫,方便监控插件生命周期

Modifications

  1. 新增on_star_activated和on_star_deactivated两个事件钩子,帮助了解其他插件的状态,使用上形如:
@filter.on_star_activated("astrbot_plugin_monitered")
async def on_star_activated(self, star: StarMetadata):
        logger.info(f"插件 {star.name} 已启用")
  1. 为使以上改动在初始化时按预期工作,尝试通过引入可选的dependencies字段控制插件的初始化顺序
    举例来说:有A插件,其功能一定程度上依赖B插件
name: astrbot_plugin_A
desc: This is plugin A
version: v1.0.0
author: AstrBot
repo: https://github.com/author/astrbot_plugin_A
dependencies:
    - astrbot_plugin_B

则A先于B初始化,待B初始化(启用)时,就能触发A中的on_star_activated钩子
此外,系统插件优先初始化

Check

目前仅进行了简单测试(一级依赖),复杂用例与可能导致错误的用例待测试

  • 😊 我的 Commit Message 符合良好的规范
  • 👀 我的更改经过良好的测试
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。
  • 😮 我的更改没有引入恶意代码

好的,这是将 pull request 总结翻译成中文的结果:

Sourcery 总结

引入了两个插件生命周期事件钩子用于激活和停用,并支持通过元数据中新的“dependencies”字段实现依赖驱动的插件初始化顺序。

新特性:

  • 添加 on_star_activated 事件钩子,用于在指定插件激活时发出通知
  • 添加 on_star_deactivated 事件钩子,用于在指定插件停用时发出通知
  • 支持插件元数据中的可选 dependencies 字段,用于指定加载依赖项

增强功能:

  • 实现拓扑排序,以根据声明的依赖关系确定插件加载顺序,并优先加载系统插件
  • 在插件激活和停用过程中触发新的生命周期事件
Original summary in English

Summary by Sourcery

Introduce two plugin lifecycle event hooks for activation and deactivation, and support dependency-driven plugin initialization order via a new 'dependencies' field in metadata.

New Features:

  • Add on_star_activated event hook to notify when a specified plugin is activated
  • Add on_star_deactivated event hook to notify when a specified plugin is deactivated
  • Support an optional dependencies field in plugin metadata for specifying load dependencies

Enhancements:

  • Implement topological sorting to determine plugin load order based on declared dependencies and prioritise system plugins
  • Trigger the new lifecycle events during plugin activation and deactivation processes

This comment was marked as outdated.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

@Flartiny - 我已经查看了你的更改 - 这里有一些反馈:

  • 避免在加载元数据时在 _get_load_order 中静默地吞下异常——至少记录堆栈跟踪,这样插件故障就不会被隐藏。
  • _get_load_order 中提取依赖关系图构建和拓扑排序到更小的辅助方法中,以提高可读性和可测试性。
  • 目前,缺少依赖项只会发出警告,但仍然会加载插件——考虑排除或快速失败未解决的依赖项,以避免不可预测的运行时错误。
AI 代理的提示
请解决此代码审查中的评论:
## 总体评论
- 避免在加载元数据时在 `_get_load_order` 中静默地吞下异常——至少记录堆栈跟踪,这样插件故障就不会被隐藏。
-`_get_load_order` 中提取依赖关系图构建和拓扑排序到更小的辅助方法中,以提高可读性和可测试性。
- 目前,缺少依赖项只会发出警告,但仍然会加载插件——考虑排除或快速失败未解决的依赖项,以避免不可预测的运行时错误。

Sourcery 对开源是免费的 - 如果你喜欢我们的评论,请考虑分享它们 ✨
帮助我更有用!请点击每个评论上的 👍 或 👎,我将使用反馈来改进你的评论。
Original comment in English

Hey @Flartiny - I've reviewed your changes - here's some feedback:

  • Avoid silently swallowing exceptions in _get_load_order when loading metadata—at minimum log the stack trace so plugin failures aren’t hidden.
  • Extract the dependency‐graph construction and topological sort from _get_load_order into smaller helper methods to improve readability and testability.
  • Right now missing dependencies only emit warnings but still load the plugin—consider excluding or failing fast on unresolved dependencies to avoid unpredictable runtime errors.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Avoid silently swallowing exceptions in `_get_load_order` when loading metadata—at minimum log the stack trace so plugin failures aren’t hidden.
- Extract the dependency‐graph construction and topological sort from `_get_load_order` into smaller helper methods to improve readability and testability.
- Right now missing dependencies only emit warnings but still load the plugin—consider excluding or failing fast on unresolved dependencies to avoid unpredictable runtime errors.

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 on lines 887 to 889
# 检查这个 handler 是否监听了特定的插件名
target_star_name = handler.extras_configs.get("target_star_name")
if target_star_name:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
# 检查这个 handler 是否监听了特定的插件名
target_star_name = handler.extras_configs.get("target_star_name")
if target_star_name:
if target_star_name := handler.extras_configs.get("target_star_name"):
Original comment in English

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
# 检查这个 handler 是否监听了特定的插件名
target_star_name = handler.extras_configs.get("target_star_name")
if target_star_name:
if target_star_name := handler.extras_configs.get("target_star_name"):

Comment on lines 941 to 942
metadata = self._load_plugin_metadata(plugin_dir_path)
if metadata:
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
metadata = self._load_plugin_metadata(plugin_dir_path)
if metadata:
if metadata := self._load_plugin_metadata(plugin_dir_path):
Original comment in English

suggestion (code-quality): Use named expression to simplify assignment and conditional (use-named-expression)

Suggested change
metadata = self._load_plugin_metadata(plugin_dir_path)
if metadata:
if metadata := self._load_plugin_metadata(plugin_dir_path):

Copy link
Member

@Soulter Soulter left a comment

Choose a reason for hiding this comment

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

这两个钩子和 terminal() 与 initialize() 有区别嘛

@Soulter Soulter self-assigned this Jun 28, 2025
@Flartiny
Copy link
Contributor Author

这两个钩子和 terminal() 与 initialize() 有区别嘛

我认为有一定区别,terminal() 与 initialize()用于控制插件自身在生命周期首尾的活动,而on_star_activated和on_star_deactivated帮助插件在自身生命周期中期了解其他插件的启停情况并作出响应。

接前文,经测试,目前的改动一般情况下运行正常,但存在几处问题:

  1. 存在循环依赖时不加载任何插件,需手动处理造成循环的插件后重启
  2. 假设A依赖B,即A通过on_star_activated("B")监听B,A重载后需手动重载B以达到预期效果

正尝试基于图结构更好地管理插件间的关联,包括以子图为单位自动进行插件重载等

@Flartiny
Copy link
Contributor Author

引入networkx进行相关操作,这个依赖应该未来的记忆部分也会引入(?
图片
以上图为例:
重载A时,实际重载插件的顺序为[A,B,C,D]
重载B时,实际重载插件的顺序为[B,C,D]
重载C时,实际重载插件的顺序为[C,D]
重载D时,实际重载插件的顺序为[D]

关于此pr的说明:
metadata.yaml引入dependencies仅帮助控制加载顺序,它在逻辑上建立插件间的依赖关系
两个事件钩子的作用就如其字面含义,但多数情况下会需要经过控制加载顺序以正常工作


现在存在循环依赖时,仍加载全部插件,目的是不影响无关插件的工作。但导致循环依赖的部分插件可能不按预期工作,这点由用户或开发者自行处理。
现在涉及加载插件的操作(启用、重载、安装)均以子图为单位进行,如上图。


install_plugin_from_file未经测试(我的Astrbot之前“从压缩包安装”就用不了,可能和docker或者其他未知原因有关。但是从代码上看应该没问题)

@Soulter Soulter changed the base branch from master to releases/4.0.0 August 18, 2025 15:36
from .star_handler import star_handlers_registry
from .updator import PluginUpdator
from .star_handler import EventType, StarHandlerMetadata
import networkx as nx
Copy link
Member

Choose a reason for hiding this comment

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

这里如果不用 networkx 的话可以实现嘛?可能目前基于图的长期记忆还暂时不会引入。

tips: 抱歉这么久没处理;这个 PR 打算放到 releases/4.0.0 分支中;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

理论上可行, 即在 2340c5c 基础上修改,之前稍微尝试了一下感觉比较冗长(时间有点久了,只记得至少有一处细节手动处理起来比较复杂,而交由networkx管理相对清晰)并且不易扩展(比如目前想到的是依赖项后跟版本号进行更细致的区分)

@Soulter Soulter requested a review from Raven95676 August 20, 2025 05:02
Copy link
Member

@Raven95676 Raven95676 left a comment

Choose a reason for hiding this comment

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

看起来没什么问题了,虽然实现有点反直觉(

现在这个实现的话,需要在文档中特别注明被依赖的插件一定会在依赖它的插件之后加载,所以说想要获取被依赖的插件实例只能使用这两个钩子,在其他位置无法保证能够正确获取到被依赖插件的实例。

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.

3 participants