Skip to content

feat(plugin): progressive plugin loading — activate plugin components on demand - #92

Merged
Luhaozhu merged 1 commit into
mainfrom
feat/progressive-plugin-loading
Aug 14, 2026
Merged

feat(plugin): progressive plugin loading — activate plugin components on demand#92
Luhaozhu merged 1 commit into
mainfrom
feat/progressive-plugin-loading

Conversation

@Luhaozhu

Copy link
Copy Markdown
Contributor

Summary

Installed plugins used to inject all of their bundled capabilities into every request — one skill-list line per skill in the system prompt, plus the full JSON schema of every MCP tool in the tools parameter, re-sent on every ReAct round. TTFT therefore grew linearly with the number of installed plugins.

This PR introduces progressive plugin loading: the plugin becomes a runtime aggregation unit again.

  • Assembly time: plugin components are subtracted from the run's enabled sets. The system prompt carries only a one-line directory entry per plugin (slug + description) and a single load_plugin tool is registered.
  • Activation time: when the model decides a request matches a plugin's description, it calls load_plugin. Only then are the plugin's MCP servers connected (stateless HTTP clients appended to the live toolkit) and its skills registered. Schemas and skill instructions are recomputed each round, so everything is usable from the next step.
  • Stickiness: activation is persisted per chat (chat_sessions.metadata.activated_plugins), so subsequent turns assemble the plugin eagerly with no repeated activation. Explicitly invoking a plugin (slash / + menu) counts as activation.
  • Sub-agents: plugins bound to a sub-agent are deferred the same way, with run-scoped activation only (no persistence, to avoid leaking into the parent chat's assembly).

The plugin directory is sorted by slug and byte-stable regardless of activation state, so provider-side prefix caching pays for an activation only on the activation turn and the one after it.

Boundaries

  • Plugins pinned by a restricted chat mode stay eagerly loaded (deliberate admin narrowing).
  • Plugins with stdio-transport MCP components are not deferred.
  • PLUGIN_PROGRESSIVE_LOADING=false reverts to eager assembly.

Also included

  • Hide AgentScope's builtin Skill viewer schema: skills load exclusively through view_text_file in this stack, so the schema was pure per-round prefill waste and a wrong entry point bypassing path mapping / runtime hints / observability.
  • Chat UI polish for the load_plugin tool call: localized label, inline plugin slug, and a formatted activation-summary card instead of a raw JSON dump.

Testing

  • tests/test_plugin_progressive_loading.py (9 cases): deferral resolution (visibility / enabled intersection / stdio exclusion / activation & invocation exemption with persistence), directory rendering stability, load_plugin runtime side effects (toolkit mutation, allow rules, close-list, chat persistence, sub-agent no-persist), env kill switch.
  • Existing plugin / chat-mode / citation regression suites pass.

… on demand

Installed plugins used to inject every bundled skill's frontmatter line and
every MCP tool's full JSON schema into each request, so TTFT grew linearly
with the number of installed plugins. This change brings the plugin back as
a runtime aggregation unit:

- At assembly time, plugin components are removed from the enabled sets;
  the system prompt keeps a one-line directory entry per plugin (slug +
  description, sorted by slug, byte-stable across activation states) and a
  single load_plugin tool is registered.
- When the model activates a plugin, its MCP servers are connected and its
  skills registered in place; schemas and skill instructions are recomputed
  every ReAct round, so they take effect on the next step.
- Activation is sticky per chat (chat_sessions.metadata.activated_plugins);
  explicitly invoking a plugin counts as activation. Sub-agent bound plugins
  are deferred the same way with run-scoped activation only.
- Plugins with stdio-transport MCP components stay eagerly loaded; set
  PLUGIN_PROGRESSIVE_LOADING=false to revert to eager assembly.

Also hides AgentScope's builtin Skill viewer schema (skills load exclusively
through view_text_file here, so the schema was pure per-round prefill waste)
and adds a localized display for the load_plugin tool call in the chat UI.
@Luhaozhu
Luhaozhu merged commit 1e9538d into main Aug 14, 2026
5 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