Skip to main content
Plugins add tools, hooks, and guardrails that can rewrite — or now block — what an agent sees and does; drop a Python file in ~/.praisonai/plugins/ and load it in one line.
The user asks a question; plugins add tools and hooks before the agent answers.

Quick Start

1

Simple Usage

Create ~/.praisonai/plugins/my_tools.py:
Load and run:
2

With Configuration

Point the environment (or config file) at plugins and Agent construction wires them for you — no explicit plugins.enable() call needed:
Prefer to turn plugins on in code? Call plugins.enable(...) before creating the agent:
Place plugins in ~/.praisonai/plugins/ (user-wide) or ./.praisonai/plugins/ (project-specific).
plugins.enable() auto-discovers filesystem and entry-point plugins only when PRAISONAI_ALLOW_PLUGIN_DISCOVERY=true (or 1/yes). Without this env var, plugins.enable() still bridges plugins you register manually via PluginManager.register(...), but no directory or entry-point scan runs.

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:

Choosing How to Load Plugins

Pick the loading method that fits your setup.

Plugin Locations


Hook Plugin Example

Create ~/.praisonai/plugins/my_logger.py:
Create ~/.praisonai/plugins/tool_sandbox.py to filter advertised tools:

Lifecycle-Method Plugins

Subclass Plugin 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:
Load and enable:

Session & Error Lifecycle Plugins

Override session_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.
Create ~/.praisonai/plugins/session_logger.py:

Observe errors

on_error observes errors during a run — use it to log without changing behavior.
Create ~/.praisonai/plugins/error_reporter.py:

Rewrite config

on_config returns a dict to rewrite runtime configuration in place.
Create ~/.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.
Create ~/.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 explicit plugins.enable() call needed. Set the env var, then any Agent(...) wires plugins before it runs:
Or turn them on in .praisonai/config.toml:
Want each plugin to receive its own options from one file? See Configure Plugins from .praisonai/config.yaml for per-plugin option blocks.
Under the hood, Agent init calls 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.yaml / 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.
Auto-enable does not imply discovery. Filesystem and entry-point scanning still require PRAISONAI_ALLOW_PLUGIN_DISCOVERY=true (or 1/yes). Without it, only plugins registered manually via PluginManager.register(...) are bridged.
Verify auto-enable at runtime — constructing an Agent triggers the wiring:

Configure Plugins from .praisonai/config.yaml

Turn plugins on and hand each one its own options from a single YAML file — no code. ~/.praisonai/config.yaml:
app.py:
Agent construction auto-loads the config, enables plugins, and hands pii_guardrail its {redact: [email, phone]} dict via on_config. Nothing else to wire up.

Where the config file is found

The first file found wins — TOML and YAML have equal standing; whichever appears first in the search stops the walk.
PyYAML is an optional dependency. Without it, .yaml configs return empty (debug log only) — install with pip install pyyaml to enable the YAML surface.

The four shapes of enabled

The JSON config schema advertises boolean and array for enabled; the loader also accepts a bare string at runtime for TOML users who prefer enabled = "pii_guardrail".

Per-plugin option blocks

Reserved keys (enabled, auto_discover, directories) configure the plugin system; any other key whose value is a mapping is treated as a per-plugin option map delivered to that plugin’s on_config hook.
An explicit top-level enabled: false wins over any per-plugin block. Omit enabled at the top level and per-plugin blocks decide.

Reading options inside a plugin — on_config

apply_plugin_options() invokes plugin.on_config(options) for every enabled plugin that has a configured option map — errors in a single plugin are logged and do not abort delivery to the others. Create ~/.praisonai/plugins/pii_guardrail.py:
on_config fires once per enable — the reserved enabled flag (if present in the plugin block) is preserved so plugins can read it too.

Deliver options from code — options_by_name

Skip the file and hand each plugin its options directly. plugins.enable() takes an options_by_name map that it delivers to each plugin’s on_config hook:
.praisonai/config.yaml calls this for you — maybe_enable_from_config() runs enable(get_enabled_plugins(), options_by_name=get_plugin_options()) under the hood. Call get_plugin_options() yourself to read the per-plugin maps the loader parsed.

Choose a plugin config surface

Precedence

Explicit plugins.enable(...) in code > PRAISONAI_PLUGINS env var > [plugins] in config.yaml / config.toml > disabled by default.

Edge cases

  • PyYAML missing — YAML configs silently return empty (debug log only); install pyyaml to enable.
  • Unknown reserved key — the wrapper resolver’s typo-suggestion validator only checks against {enabled, auto_discover, directories}; per-plugin blocks (dicts) are always accepted.
  • Removed plugin block on reload — the manager stores options with replace=True by default, so a block dropped from a later config no longer delivers stale options.
  • Plugin on_config raises — logged as a warning; delivery to other plugins is not aborted.

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.
The five before_* methods (before_agent, before_llm, before_tool, before_tool_definitions, before_message) also accept a deny/block decision instead of a rewrite — see Blocking Plugins.
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.

Blocking Plugins (Guardrails & Policies)

Guardrail and policy plugins can now stop an action before it happens, not just rewrite it — refuse a dangerous tool call, drop a spam message, or decline an LLM request.

Quick Start

1

Block a tool call

Return PluginDecision.block(reason) from before_tool to skip a forbidden tool.
2

Refuse an LLM request

Return PluginDecision.deny(reason) from before_llm and the agent returns "[LLM request blocked by hook: <reason>]" without calling the model.
3

Drop an inbound message

Return PluginDecision.deny(reason) from before_message to drop a message before the agent sees it.

Three Ways to Block

The same block, written three ways — pick the one that reads cleanest in your code.
PluginDecision.deny(reason) and PluginDecision.block(reason) both stop the action (is_denied() is True for each); allow(reason=None) is an explicit no-op. GuardrailBlocked(reason: str = "Blocked by guardrail plugin") is caught by the bridge and converted to a block.

Where Blocks Fire

Five before_* methods can block; after_* methods are observational and cannot.

How a Block Flows

Which Style Should I Use?


Ship a Plugin as a pip Package

Register your plugin class in the praisonai.plugins entry-point group in pyproject.toml:
Agent construction (or plugins.enable()) auto-discovers and bridges it — no user code changes needed.

CLI Commands


Configuration Options


Best Practices

One file per concern — weather tools, logging, or guardrails, not all three.
Load before creating the agent so tools and hooks register globally.
Pass tools=["get_weather"] — the string must match the @tool function name.
Enable logging and metrics without writing plugin files.
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.
PluginDecision.deny(reason) reads as one line and only needs the plugin import. Use HookResult.deny(reason) only when you already import it for another reason, and raise GuardrailBlocked(reason) only when you’re inside a validator that already raises on failure. Prefer block over deny when the action is categorically forbidden; use deny when it’s contextually refused (this input, this user, this time).

Hooks

Hook events and the HookRegistry API

Toolsets

Create and register custom agent tools

Config File

Turn plugins on from [plugins] in config.toml

Tool Discovery

How Agent resolves tool names at runtime

Guardrails

Validate agent output — automatic retry on failure