Creating Plugins (Overview)¶
Genie Tooling is designed to be highly extensible through its plugin architecture. Almost every functional component can be replaced or augmented with custom implementations.
This page provides a general overview. For specific plugin types, refer to: * Creating Tool Plugins * Creating RAG Plugins * Creating Other Plugins (for caches, LLM providers, etc.)
Core Plugin Protocol¶
All plugins in Genie Tooling should ideally adhere to the genie_tooling.core.types.Plugin
protocol:
from typing import Protocol, Optional, Dict, Any
class Plugin(Protocol):
@property
def plugin_id(self) -> str:
"""A unique identifier for this plugin instance/type."""
...
async def setup(self, config: Optional[Dict[str, Any]] = None) -> None:
"""
Optional asynchronous setup method.
Called after the plugin is instantiated, with its specific configuration.
"""
pass # Default implementation
async def teardown(self) -> None:
"""
Optional asynchronous teardown method.
Called when the Genie facade is closing down, to release resources.
"""
pass # Default implementation
Key Requirements for a Plugin Class:
-
plugin_id
(Class Attribute):- A unique string identifier for your plugin.
- Convention:
your_plugin_name_v1
(e.g.,my_custom_llm_provider_v1
,advanced_calculator_tool_v1
). - This ID is used in configuration files and for discovery.
-
Implement the Specific Protocol:
- Your plugin class must implement the methods defined by the protocol for its type (e.g.,
Tool
,LLMProviderPlugin
,CacheProviderPlugin
). - These protocols are typically found in the
abc.py
file of the relevant submodule (e.g.,genie_tooling.tools.abc.Tool
).
- Your plugin class must implement the methods defined by the protocol for its type (e.g.,
-
async def setup(self, config: Optional[Dict[str, Any]] = None)
(Optional):- If your plugin needs initialization with configuration values, implement this asynchronous method.
- The
config
dictionary will contain the settings provided for your plugin'splugin_id
in theMiddlewareConfig
(e.g., inllm_provider_configurations["my_custom_llm_v1"]
).
-
async def teardown(self)
(Optional):- If your plugin acquires resources (e.g., network connections, file handles) that need to be released, implement this asynchronous method.
Plugin Discovery¶
Genie's PluginManager
discovers plugins through two primary mechanisms:
-
Entry Points (Recommended for distributable plugins):
- Define an entry point in your package's
pyproject.toml
under the group[tool.poetry.plugins."genie_tooling.plugins"]
. - Example:
- The key is the
plugin_id
Genie will use to refer to your plugin. - The value is the import path to your plugin class.
- Define an entry point in your package's
-
Plugin Development Directories (For local/project-specific plugins):
- Specify a list of directories in
MiddlewareConfig.plugin_dev_dirs
. - The
PluginManager
will scan these directories for Python files (*.py
). - It will attempt to import these files as modules and look for classes that implement the
Plugin
protocol and have aplugin_id
. - Files starting with
_
or.
(e.g.,__init__.py
,_internal_utils.py
) are ignored.
- Specify a list of directories in
Configuration and Usage¶
Once your plugin is discoverable, you can:
* Configure it: Provide settings in the appropriate *_configurations
dictionary within MiddlewareConfig
, keyed by your plugin's plugin_id
.
* Set it as a default: If applicable, set its plugin_id
in fields like default_llm_provider_id
, default_cache_provider_id
, etc., in MiddlewareConfig
or via FeatureSettings
.
* Use it explicitly: Some Genie
facade methods allow specifying a plugin_id
at runtime (e.g., genie.llm.chat(..., provider_id="my_llm_v1")
).
By adhering to these principles, you can create robust and reusable extensions for Genie Tooling.