Files
takopi/docs/how-to/write-a-plugin.md

149 lines
3.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Write a plugin
Takopi supports entrypoint-based plugins for engines, transports, and commands.
## Checklist
1. Pick a plugin id (must match `^[a-z0-9_]{1,32}$`).
2. Add a Python entrypoint in your packages `pyproject.toml`.
3. Implement a backend object (`BACKEND`) with `id == entrypoint name`.
4. Install your package and validate with `takopi plugins --load`.
## Entrypoint groups
Takopi uses three entrypoint groups:
```toml
[project.entry-points."takopi.engine_backends"]
myengine = "myengine.backend:BACKEND"
[project.entry-points."takopi.transport_backends"]
mytransport = "mytransport.backend:BACKEND"
[project.entry-points."takopi.command_backends"]
mycommand = "mycommand.backend:BACKEND"
```
## Engine backend plugin
An engine backend builds a `Runner` via `build_runner(...)`.
Minimal example:
```py
# myengine/backend.py
from __future__ import annotations
from pathlib import Path
from takopi.api import EngineBackend, EngineConfig, Runner
def build_runner(config: EngineConfig, config_path: Path) -> Runner:
_ = config_path
return MyEngineRunner(config)
BACKEND = EngineBackend(
id="myengine",
build_runner=build_runner,
cli_cmd="myengine",
install_cmd="pip install myengine",
)
```
Engine config is a raw table in `takopi.toml`:
=== "takopi config"
```sh
takopi config set myengine.model "..."
```
=== "toml"
```toml
[myengine]
model = "..."
```
## Transport backend plugin
Transport plugins connect Takopi to other messaging systems (Slack, Discord, …).
For most transports, delegate message handling to `handle_message()` from `takopi.api`.
## Command backend plugin
Command plugins add custom `/command` handlers. They only run when the message starts
with `/<id>` and the id does not collide with engine ids, project aliases, or reserved names.
Minimal example:
```py
# mycommand/backend.py
from __future__ import annotations
from takopi.api import CommandContext, CommandResult
class MyCommand:
id = "hello"
description = "say hello"
async def handle(self, ctx: CommandContext) -> CommandResult | None:
_ = ctx
return CommandResult(text="hello")
BACKEND = MyCommand()
```
### Command plugin configuration
Configure under `[plugins.<id>]`:
=== "takopi config"
```sh
takopi config set plugins.hello.greeting "hello"
```
=== "toml"
```toml
[plugins.hello]
greeting = "hello"
```
The parsed dict is available as `ctx.plugin_config` in `handle()`.
## Enable/disable installed plugins
=== "takopi config"
```sh
takopi config set plugins.enabled '["takopi-transport-slack", "takopi-engine-acme"]'
```
=== "toml"
```toml
[plugins]
enabled = ["takopi-transport-slack", "takopi-engine-acme"]
```
- `enabled = []` (default) means “load all installed plugins”.
- If non-empty, only distributions with matching names are visible.
## Validate discovery and loading
```sh
takopi plugins
takopi plugins --load
```
## Related
- [Plugin system (design)](../explanation/plugin-system.md)
- [Plugin API reference](../reference/plugin-api.md)