🪝 Hooks
During execution the Cat emits a few events, for example when a user message arrives, or when a new document is uploaded to memory.
You can hook
these events to execute your own code and change behavior, memory and anything the Cat does.
The Framework → Technical Diagrams
section illustrates with a diagram where the hooks are called during the Cat's execution flow.
Not all the hooks have been documented yet. ( help needed! 😸 ).
Available Hooks
Here is a list of availble hooks. Each has a dedicated page with examples (see Hooks API Reference
on the left menu).
flowchart TD
C(before_cat_reads_message)
D(cat_recall_query)
E(before_cat_recalls_memories)
F(before_cat_recalls_episodic_memories)
G(before_cat_recalls_declarative_memories)
H(before_cat_recalls_procedural_memories)
I(after_cat_recalls_memories)
J(before_cat_stores_episodic_memory)
K(before_cat_sends_message)
C-->D
D-->E
E-->F
E-->G
E-->H
F-->I
G-->I
H-->I
I-- Main Agent execution -->J
J-->K
click C "/docs/plugins/hooks-reference/flow/before_cat_reads_message"
click D "/docs/plugins/hooks-reference/flow/cat_recall_query"
click E "/docs/plugins/hooks-reference/flow/before_cat_recalls_memories"
click F "/docs/plugins/hooks-reference/flow/before_cat_recalls_episodic_memories"
click G "/docs/plugins/hooks-reference/flow/before_cat_recalls_declarative_memories"
click H "/docs/plugins/hooks-reference/flow/before_cat_recalls_procedural_memories"
click I "/docs/plugins/hooks-reference/flow/after_cat_recalls_memories"
click J "/docs/plugins/hooks-reference/flow/before_cat_stores_episodic_memory"
click K "/docs/plugins/hooks-reference/flow/before_cat_sends_message"
flowchart TD
A(before_agent_starts)
B(agent_fast_reply)
C(agent_allowed_tools)
D(agent_prompt_prefix)
E(agent_prompt_suffix)
A --> B
B --> C
C --> D
D --> E
click A "/docs/plugins/hooks-reference/agent/before_agent_starts"
click B "/docs/plugins/hooks-reference/agent/agent_fast_reply"
click C "/docs/plugins/hooks-reference/agent/agent_allowed_tools"
click D "/docs/plugins/hooks-reference/agent/agent_prompt_prefix"
click E "/docs/plugins/hooks-reference/agent/agent_prompt_suffix"
flowchart TD
subgraph Ingestion
A(before_rabbithole_insert_memory)
B(before_rabbithole_splits_text)
C(after_rabbithole_splitted_text)
D(before_rabbithole_stores_documents)
E(after_rabbithole_stored_documents)
A --> B
B --> C
C --> D
D --> E
end
subgraph Instantiation
F(rabbithole_instantiates_parsers)
G(rabbithole_instantiates_splitter)
F --> G
end
click A "/docs/plugins/hooks-reference/rabbit-hole/before_rabbithole_insert_memory"
click B "/docs/plugins/hooks-reference/rabbit-hole/before_rabbithole_splits_text"
click C "/docs/plugins/hooks-reference/rabbit-hole/after_rabbithole_splitted_text"
click D "/docs/plugins/hooks-reference/rabbit-hole/before_rabbithole_stores_documents"
click E "/docs/plugins/hooks-reference/rabbit-hole/after_rabbithole_stored_documents"
click F "/docs/plugins/hooks-reference/rabbit-hole/rabbithole_instantiates_parsers"
click G "/docs/plugins/hooks-reference/rabbit-hole/rabbithole_instantiates_splitter"
Name | Description |
---|---|
Activated (1) | Intervene when a plugin is enabled |
Deactivated (2) | Intervene when a plugin is disabled |
Settings schema (3) | Override how the plugin's settings are retrieved |
Settings model (4) | Override how the plugin's settings are retrieved |
Load settings (5) | Override how the plugin's settings are loaded |
Save settings (6) | Override how the plugin's settings are saved |
-
Input arguments
plugin
: thePlugin
object of your plugin with the following properties:Example
from cat.mad_hatter.decorators import plugin from cat.looking_glass.cheshire_cat import CheshireCat ccat = CheshireCat() @plugin def activated(plugin): # Upload an url in the memory when the plugin is activated url = "https://cheshire-cat-ai.github.io/docs/technical/plugins/hooks/" ccat.rabbit_hole.ingest_file(stray=ccat, file=url)
Other resources
-
Input arguments
plugin
: thePlugin
object of your plugin with the following properties:Example
from cat.mad_hatter.decorators import plugin from cat.looking_glass.cheshire_cat import CheshireCat ccat = CheshireCat() @plugin def deactivated(plugin): # Scroll the declarative memory to clean from memories # with metadata on plugin deactivation declarative_memory = ccat.memory.vectors.declarative response = declarative_memory.delete_points_by_metadata_filter( self, metadata={"source": "best_plugin"} )
Other resources
-
Input arguments
This hook has no input arguments.Info
Default
settings.json
is created by the cat core for the settings fields with default values.Example
from cat.mad_hatter.decorators import plugin from pydantic import BaseModel, Field # define your plugin settings model class MySettings(BaseModel): prompt_prefix: str = Field( title="Prompt prefix", default="""You are the Cheshire Cat AI, an intelligent AI that passes the Turing test. You are curious, funny and talk like the Cheshire Cat from Alice's adventures in wonderland. You answer Human with a focus on the following context. """, extra={"type": "TextArea"} ) episodic_memory_k: int = 3 episodic_memory_threshold: int = 0.7 declarative_memory_k: int = 3 declarative_memory_threshold: int = 0.7 procedural_memory_k: int = 3 procedural_memory_threshold: int = 0.7 # get your plugin settings schema @plugin def settings_schema(): return MySettings.model_json_schema() # load your plugin settings settings = ccat.mad_hatter.get_plugin().load_settings() # access each setting prompt_prefix = settings["prompt_prefix"] episodic_memory_k = settings["episodic_memory_k"] declarative_memory_k = settings["declarative_memory_k"]
Other resources
-
Input arguments
This hook has no input arguments.Info
settings_model
is preferred tosettings_schema
.Default
settings.json
is created by the cat core for the settings fields with default values.Example
from cat.mad_hatter.decorators import plugin from pydantic import BaseModel, Field # define your plugin settings model class MySettings(BaseModel): prompt_prefix: str = Field( title="Prompt prefix", default="""You are the Cheshire Cat AI, an intelligent AI that passes the Turing test. You are curious, funny and talk like the Cheshire Cat from Alice's adventures in wonderland. You answer Human with a focus on the following context. """, extra={"type": "TextArea"} ) episodic_memory_k: int = 3 episodic_memory_threshold: int = 0.7 declarative_memory_k: int = 3 declarative_memory_threshold: int = 0.7 procedural_memory_k: int = 3 procedural_memory_threshold: int = 0.7 # get your plugin settings Pydantic model @plugin def settings_model(): return MySettings # load your plugin settings settings = ccat.mad_hatter.get_plugin().load_settings() # access each setting declarative_memory_k = settings["declarative_memory_k"] declarative_memory_threshold = settings["declarative_memory_threshold"] procedural_memory_k = settings["procedural_memory_k"]
Other resources
-
Input arguments
This hook has no input arguments.Info
Useful to load settings via API and do custom stuff. E.g. load from a MongoDB instance.
Example
from pymongo import MongoClient @plugin def load_settings(): client = MongoClient('mongodb://your_mongo_instance/') db = client['your_mongo_db'] collection = db['your_settings_collection'] # Perform the find_one query settings = collection.find_one({'_id': "your_plugin_id"}) client.close() return MySettings(**settings)
Other resources
-
Input arguments
settings
: the settingsDict
to be saved.Info
Useful for customizing the settings saving strategy. E.g. storing settings in a MongoDB instance.
Example
from pymongo import MongoClient @plugin def save_settings(settings): client = MongoClient('mongodb://your_mongo_instance/') db = client['your_mongo_db'] collection = db['your_settings_collection'] # Generic filter based on a unique identifier in settings filter_id = {'_id': settings.get('_id', 'your_plugin_id')} # Define the update operation update = {'$set': settings} # Perform the upsert operation collection.update_one(filter_id, update, upsert=True) client.close()
Other resources
NOTE: Any function in a plugin decorated by
@plugin
and named properly (among the list of available overrides, Plugin tab in the table above) is used to override plugin behaviour. These are not hooks because they are not piped, they are specific for every plugin.
flowchart TD
A(factory_allowed_llms)
B(factory_allowed_embedders)
C(factory_allowed_auth_handlers)
A --> B
B --> C
click A "/docs/plugins/hooks-reference/factory/factory_allowed_llms"
click B "/docs/plugins/hooks-reference/factory/factory_allowed_embedders"
click C "/docs/plugins/hooks-reference/factory/factory_allowed_auth_handlers"
flowchart TD
B(before_cat_bootstrap)
C(after_cat_bootstrap)
B -- Cat components are loaded --> C
click B "/docs/plugins/hooks-reference/lifecycle/before_cat_bootstrap"
click C "/docs/plugins/hooks-reference/lifecycle/after_cat_bootstrap"
How the Hooks work
To create a hook, you first need to create a plugin that contains it. Once the plugin is created, you can insert hooks inside the plugin, a single plugin can contain multiple hooks.
A hook is simply a Python function that uses the @hook
decorator, the function's name determines when it will be called.
Each hook has its own signature name and arguments, the last argument being always cat
, while the first one depends on the type of hook you're using.
Have a look at the table with all the available hooks and their detailed reference.
Hook declaration
The Cat comes already with a hook that defines his behaviour.
Let's take a look at it.
@hook
def agent_prompt_prefix(prefix, cat):
"""Hook the main prompt prefix. """
prefix = """You are the Cheshire Cat AI, an intelligent AI that passes
the Turing test. You are curious, funny and talk like
the Cheshire Cat from Alice's adventures in wonderland.
You answer Human with a focus on the following context."""
return prefix
Hook arguments
When considering hooks' arguments, remember:
cat
will always be present, as it allows you to use the framework components. It will be always the last one. See here for details and examples.- the first argument other than
cat
, if present, will be a variable that you can edit and return back to the framework. Every hook passes a different data structure, which you need to know and be able to edit and return. You are free to return nothing and use the hook as a simple event callback. - other arguments may be passed, serving only as additional context.
Examples
Before cat bootstrap
You can use the before_cat_bootstrap
hook to execute some operations before the Cat starts:
Notice in this hook there is only the cat
argument, allowing you to use the llm and access other Cat components.
This is a pure event, with no additional arguments.
Before cat sends message
You can use the before_cat_sends_message
hook to alter the message that the Cat will send to the user.
In this case you will receive both final_output
and cat
as arguments.
from cat.mad_hatter.decorators import hook
@hook
def before_cat_sends_message(final_output, cat):
# You can edit the final_output the Cat is about to send back to the user
final_output.content = final_output.content.upper()
return final_output
Hooks chaining and priority
Several plugins can implement the same hook. The argument priority
of the @hook
decorator allows you to set the priority of the hook, the default value is 1.
The Cat calls hooks with the same name in order of priority
. Hooks with a higher priority number will be called first. The following hook will receive the value returned by the previous hook. In this way, hooks can be chained together to create complex behaviors.
# plugin B
@hook(priority=1)
def hook_name(data, cat):
if "Hello" in data.content:
data.content += " world"
return data
If two plugins have the same priority, the order in which they are called is not guaranteed.
Custom hooks in plugins
You can define your own hooks, so other plugins can listen and interact with them.
# plugin cat_commerce
@hook
def before_cat_reads_message(msg, cat):
default_order = [
"wool ball",
"catnip"
]
chain_output = cat.mad_hatter.execute_hook(
"cat_commerce_order", default_order, cat=cat
)
do_my_thing(chain_output)
Other plugins may be able to edit or just track the event: