~/.praisonai/plugins/ and load it in one line.
Quick Start
Place plugins in
~/.praisonai/plugins/ (user-wide) or ./.praisonai/plugins/ (project-specific).How It Works
plugins.enable() auto-calls wire_into_hook_registry(), which registers each enabled plugin’s lifecycle methods on the default hook registry the agent consults at runtime — and Agent init calls this for you when the env var or config file requests it (see Auto-Enable from Env or Config).
The hooks fire in this order around each agent run:
| Plugin type | What it adds |
|---|---|
| Tool | Functions registered via @tool |
| Hook | Lifecycle callbacks (before_tool, before_tool_definitions, after_llm, …) |
| Guardrail | Output validation on after_agent |
Choosing How to Load Plugins
Pick the loading method that fits your setup.Plugin Locations
| Location | Scope |
|---|---|
~/.praisonai/plugins/ | User-wide |
./.praisonai/plugins/ | Project-specific |
Hook Plugin Example
Create~/.praisonai/plugins/my_logger.py:
~/.praisonai/plugins/tool_sandbox.py to filter advertised tools:
Lifecycle-Method Plugins
SubclassPlugin and override a lifecycle method to transform prompts, messages, or responses. Call plugins.enable() — or set PRAISONAI_PLUGINS=true / [plugins] enabled = true and let Agent construction wire it — to activate the method at runtime.
Create ~/.praisonai/plugins/pii_redactor.py:
Session & Error Lifecycle Plugins
Overridesession_start, session_end, on_error, on_config, or on_auth to react to session boundaries, errors, config, and credential resolution.
Choosing a lifecycle event
Observe sessions
session_start and session_end observe when a session opens and closes.
~/.praisonai/plugins/session_logger.py:
Observe errors
on_error observes errors during a run — use it to log without changing behavior.
~/.praisonai/plugins/error_reporter.py:
Rewrite config
on_config returns a dict to rewrite runtime configuration in place.
~/.praisonai/plugins/config_defaults.py:
Inject credentials
on_auth returns a dict of credentials — the bridge writes them back even when credentials starts as None, so first-time injection works.
~/.praisonai/plugins/token_injector.py:
Auto-Enable from Env or Config
Agent construction auto-enables plugins when the environment or config file requests it — no explicitplugins.enable() call needed.
Set the env var, then any Agent(...) wires plugins before it runs:
.praisonai/config.toml:
plugins.maybe_enable_from_config(), which reads the env var and config file, then runs enable(get_enabled_plugins()) exactly once per process.
Precedence: explicit plugins.enable(...) > PRAISONAI_PLUGINS env var > [plugins] in config.toml > disabled.
maybe_enable_from_config() is idempotent and runs at most once per process, so instantiating multiple agents is safe — plugins are wired a single time.How the Bridge Works
plugins.enable() auto-calls wire_into_hook_registry() — no manual step. Only lifecycle methods a plugin actually overrides (or declares in PluginInfo.hooks) are bridged, so a plugin with one guardrail never fires on every event. Return a new value and the bridge writes it back onto the payload in place. Errors in a lifecycle method are non-fatal, and plugins.disable([...]) calls unwire_from_hook_registry(name) so the plugin truly stops firing.
| Plugin method | Hook event | In-place mutation |
|---|---|---|
before_agent(prompt, ctx) | BEFORE_AGENT | Return a str → rewrites data.prompt |
after_agent(response, ctx) | AFTER_AGENT | Return a str → rewrites data.response |
before_llm(messages, params) | BEFORE_LLM | Return (new_messages, ...) → replaces data.messages |
after_llm(response, usage) | AFTER_LLM | Return a str → rewrites data.response |
before_tool(name, args) | BEFORE_TOOL | Return a dict → replaces data.tool_input |
after_tool(name, result) | AFTER_TOOL | Observational (no mutation) |
before_tool_definitions(defs) | BEFORE_TOOL_DEFINITIONS | Return a list → replaces data.tool_definitions |
before_message(msg) | MESSAGE_RECEIVED | Return {"content": "..."} → rewrites data.content |
after_message(msg) | MESSAGE_SENDING | Return {"content": "..."} → rewrites data.content |
on_permission_ask(target, reason) | ON_PERMISSION_ASK | True/False/None → allow/deny/allow |
on_config(config) | ON_CONFIG | Return a dict → rewrites data.config |
on_auth(auth_type, credentials) | ON_AUTH | Return a dict → rewrites data.credentials (works when starting as None) |
session_start(context) | SESSION_START | Observational (session source, session_name, session_id) |
session_end(context) | SESSION_END | Observational (reason, total_turns, total_tokens, session_id) |
on_error(error_type, error_message, context) | ON_ERROR | Observational (stack_trace, nested context, session_id) |
on_config and on_auth write their returned dict back onto the payload even when the target attribute starts as None — so a plugin can inject credentials the first time they’re requested, not only edit an existing dict.
Ship a Plugin as a pip Package
Register your plugin class in thepraisonai.plugins entry-point group in pyproject.toml:
plugins.enable()) auto-discovers and bridges it — no user code changes needed.
CLI Commands
Configuration Options
| Field | Required | Description |
|---|---|---|
Plugin Name | Yes | Display name in plugin header |
Description | Yes | What the plugin does |
Version | Yes | Semantic version |
Hooks | No | Hook events this plugin registers |
Best Practices
Keep plugins single-purpose
Keep plugins single-purpose
One file per concern — weather tools, logging, or guardrails, not all three.
Call discover_and_load_plugins() once
Call discover_and_load_plugins() once
Load before creating the agent so tools and hooks register globally.
Reference tools by name
Reference tools by name
Pass
tools=["get_weather"] — the string must match the @tool function name.Use plugins.enable for built-ins
Use plugins.enable for built-ins
Enable
logging and metrics without writing plugin files.Call plugins.enable() to activate lifecycle methods
Call plugins.enable() to activate lifecycle methods
Tools and guardrails work without
plugins.enable(). But lifecycle-method plugins (subclasses of Plugin that override before_llm, after_llm, and so on) only fire after they are wired into the runtime hook registry. Call plugins.enable() explicitly, or set PRAISONAI_PLUGINS=true / [plugins] enabled = true and Agent construction wires them automatically.Related
Hooks
Hook events and the HookRegistry API
Toolsets
Create and register custom agent tools
Config File
Turn plugins on from
[plugins] in config.tomlTool Discovery
How Agent resolves tool names at runtime

