Plugin Lifecycle
The AnimaPlugin base class, lifecycle hooks, configuration hooks, and the full startup sequence.
AnimaPlugin Base Class
All plugins inherit from AnimaPlugin. Every hook is optional — implement only what your plugin needs.
from core.plugin_loader import AnimaPlugin
class MyPlugin(AnimaPlugin):
# Set by the plugin loader (do not override)
plugin_dir: str # absolute path to plugin directory
data_dir: str # absolute path to plugin data directory
manifest: dict # parsed plugin.toml
config: dict # parsed settings.toml
Lifecycle Hooks
on_start(engine)
Called when ANIMA starts this plugin. Use for initialization, loading state, starting background threads.
def on_start(self, engine) -> None:
"""
Args:
engine: The core Engine instance. Use this for all API calls.
"""
count = engine.get_belief_count()
print(f"Plugin ready with {count} beliefs")
on_stop(engine)
Called when ANIMA stops this plugin. Use for cleanup, saving state, stopping background threads.
def on_stop(self, engine) -> None:
"""Use for: cleanup, saving state, stopping background threads."""
pass
on_cycle_complete(engine, cycle_number, stats)
Called after each ANIMA consolidation cycle completes. Use for post-cycle processing, building, or analysis.
def on_cycle_complete(self, engine, cycle_number: int, stats: dict) -> None:
"""
Args:
engine: The core Engine instance
cycle_number: Which cycle just finished (1-indexed)
stats: Dict with cycle metrics
"""
new_beliefs = stats.get("new_beliefs", 0)
print(f"Cycle {cycle_number}: {new_beliefs} new beliefs")
Configuration Hooks
register_tables(db_conn)
Create plugin-specific database tables. Called once during startup. Core tables (beliefs, belief_links, etc.) already exist.
def register_tables(self, db_conn) -> None:
"""
Args:
db_conn: SQLite connection to the plugin's persistence.db
"""
db_conn.execute("""
CREATE TABLE IF NOT EXISTS my_data (
id TEXT PRIMARY KEY,
value TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
register_endpoints(app, get_engine)
Register plugin-specific HTTP endpoints. Use APIRouter for namespaced routes.
from fastapi import APIRouter
def register_endpoints(self, app, get_engine) -> None:
router = APIRouter()
@router.get("/my-plugin/status")
async def status():
engine = get_engine()
return {"beliefs": engine.get_belief_count()}
app.include_router(router)
get_prompts()
Return custom prompts for this plugin's domain. If not implemented, ANIMA uses default generic prompts.
def get_prompts(self) -> dict:
"""
Returns:
dict with keys:
"extraction_prompt": str - template for belief extraction
Placeholders: {content}, {filename}, {file_type}, {max_beliefs}
"system_prompt": str - system prompt for extraction LLM
"""
return {
"extraction_prompt": "Read this document and extract...",
"system_prompt": "You are a domain expert in...",
}
get_filters()
Return custom belief filters. The domain filter rejects beliefs that don't belong to this plugin's domain.
def get_filters(self) -> dict:
"""
Returns:
dict with keys:
"domain_filter": callable(statement: str) -> bool
Return True to REJECT the belief, False to keep it.
"""
def _reject_off_topic(statement):
# Use LLM inference, not keyword matching
return False
return {"domain_filter": _reject_off_topic}
get_orchestrator_class()
Return a custom orchestrator class that drives the plugin's lifecycle loop (scan, extract, dream, repeat).
def get_orchestrator_class(self) -> type | None:
"""
Returns:
A class implementing the orchestrator interface,
or None to use the default DMSOrchestrator.
"""
return None
Startup Sequence
This is the exact order ANIMA follows when loading your plugin:
| Step | What Happens |
|---|---|
| 1 | ANIMA discovers plugin (scans plugins/ directory) |
| 2 | Reads plugin.toml — validates manifest |
| 3 | Loads plugin.py — instantiates AnimaPlugin subclass |
| 4 | Calls register_tables(db_conn) — plugin creates its tables |
| 5 | Calls register_endpoints(app, get_engine) — plugin adds HTTP routes |
| 6 | Calls on_start(engine) — plugin initializes |
| 7 | ANIMA runs cycles... |
| 8 | Calls on_cycle_complete(engine, N, stats) after each cycle |
| 9 | Calls on_stop(engine) on shutdown |
Events Table
| Event | When | Arguments |
|---|---|---|
on_start | ANIMA starts this plugin | engine |
on_stop | ANIMA stops this plugin | engine |
on_cycle_complete | After each consolidation cycle | engine, cycle_number, stats |
register_tables | Once during startup | db_conn |
register_endpoints | Once during startup | app, get_engine |
get_prompts | When extraction needs prompts | (none) |
get_filters | When triage needs domain filter | (none) |
get_orchestrator_class | Once during startup | (none) |
Cycle Stats Dict
The stats parameter passed to on_cycle_complete:
{
"beliefs": 512,
"edges": 650,
"new_beliefs": 15,
"new_edges": 8,
"integration_pct": 74.4,
"dream_stats": {
"accepted": 7,
"rejected": 2,
},
"triage_stats": {
"accepted": 12,
"rejected": 5,
},
}