docs(plugin): document unified Runner development and APIs

This commit is contained in:
RockChinQ
2026-09-10 18:04:42 +08:00
parent cb26081730
commit 5cbe5eee59
30 changed files with 233 additions and 246 deletions
+11 -14
View File
@@ -2,16 +2,13 @@
title: "Run Context APIs"
---
AgentRunner and EventProcessor use run-scoped APIs to access authorized models, tools, knowledge bases, and context. Neither component exposes `self.plugin`.
Runner provides `ctx.api` bound to the current run ID, context, and resource grants. Custom `run(ctx)` implementations and registered handlers share `RunnerContext`. `self.plugin` still exposes [Common APIs](/en/plugin/dev/apis/common), but does not automatically represent the current run.
## Access
```python
# Inside an EventProcessor handler
# Inside Runner.run(ctx) or a registered handler
api = ctx.api
# Inside AgentRunner.run(ctx)
api = self.get_run_api(ctx)
```
In the examples below, `api` refers to the current run API. Use `await` for the methods below, except synchronous `get_allowed_*` methods and streaming context/iterator APIs. Do not retain or reuse it across runs.
@@ -52,7 +49,7 @@ Check context capabilities through `api.ctx.context.available_apis`. Do not call
## Replies and logs
| EventProcessor method | Purpose |
| Runner method | Purpose |
| --- | --- |
| `await ctx.reply(text)` | Send text to the current event conversation; return the action result. |
| `await ctx.log(text, level="info")` | Record a log at `debug`, `info`, `warning`, or `error` level. |
@@ -69,7 +66,7 @@ async with api.reply_stream() as reply:
result = reply.result
```
EventProcessor can use `ctx.reply_stream()` to also record the action start and result.
Runner can use `ctx.reply_stream()` to also record the action start and result.
`update(text)` takes the **complete text so far**, not a delta, and returns the action result. Normal exit completes the reply. Message events with platform streaming support enabled update the platform message; other replyable events buffer text and send once on successful exit. Errors or cancellation discard buffered partial text; already displayed content is not guaranteed to be retracted. Debug mode simulates platform delivery.
@@ -157,19 +154,19 @@ The following methods require the matching `available_apis` flag and only access
| `run_append_result(result)` | `AgentRunEvent` |
| `run_finalize(run_id=None, status=None, reason=None)` | `AgentRun` |
`run_id=None` selects the current run. EventProcessor handlers finish automatically when they return and usually do not need to append or finalize records manually. AgentRunner reports results with `yield AgentRunResult`; do not also append the same result through `run_append_result`.
`run_id=None` selects the current run. Registered handlers finish automatically when they return. Custom `run(ctx)` implementations can yield `RunnerResult`; do not append the same result again through `run_append_result`.
## Structured interactions (AgentRunner)
## Structured interactions (Runner)
To request confirmation or form input, yield `AgentRunResult.interaction_requested(ctx.run_id, request)`. `request` accepts an `InteractionRequest` object or dictionary. Required fields are `interaction_id`, `title`, and `fallback_text`; optional fields include `kind` (`form`, `confirmation`, `choice`), `fields`, `actions`, and `expires_at`.
To request confirmation or form input, yield `RunnerResult.interaction_requested(ctx.run_id, request)`. `request` accepts an `InteractionRequest` object or dictionary. Required fields are `interaction_id`, `title`, and `fallback_text`; optional fields include `kind` (`form`, `confirmation`, `choice`), `fields`, `actions`, and `expires_at`.
This requires `spec.capabilities.interactions: true`, `spec.permissions.interactions: [request]`, and Host authorization. Inspect `ctx.delivery.interactions` for surface support; submissions return as `interaction.submitted` events. This is a Runner result event, not a method on `ctx.api`, and is not directly exposed by ordinary EventProcessor handlers.
This requires `spec.capabilities.interactions: true`, `spec.permissions.interactions: [request]`, and Host authorization. Inspect `ctx.delivery.interactions` for surface support; submissions return as `interaction.submitted` events. This is a Runner result event, not a method on `ctx.api`, and is yielded from a custom run(ctx) implementation.
## Error handling
```python
from langbot_plugin.api.proxies.agent_run import PermissionDeniedError
from langbot_plugin.api.entities.builtin.agent_runner.errors import AgentAPIException
from langbot_plugin.api.proxies.runner import PermissionDeniedError
from langbot_plugin.api.entities.builtin.runner.errors import AgentAPIException
try:
result = await api.call_tool("event_get_actor", {})
@@ -179,4 +176,4 @@ except AgentAPIException as exc:
await ctx.log(f"{exc.error.code}: {exc.error.message}", level="error")
```
This example runs inside an EventProcessor. `PermissionDeniedError` means a resource, operation, or capability is not granted. `AgentAPIException.error` contains `code`, `message`, `retryable`, and `details`. A timed-out send may already have succeeded; do not resend solely because `retryable` is true.
This example runs inside a Runner. `PermissionDeniedError` means a resource, operation, or capability is not granted. `AgentAPIException.error` contains `code`, `message`, `retryable`, and `details`. A timed-out send may already have succeeded; do not resend solely because `retryable` is true.
+1 -1
View File
@@ -4,7 +4,7 @@ title: "Plugin Common APIs"
LangBot provides a series of APIs for plugins to operate various LangBot modules and control message contexts.
Regular components such as `Command`, `EventListener`, and `Tool` use these APIs through `self.plugin`. `AgentRunner` and `EventProcessor` use [Run Context APIs](/en/plugin/dev/apis/agent-run), without `self.plugin`. See [Platform APIs](/en/plugin/dev/apis/platform) for platform queries and specific actions.
All components can access these LangBot APIs through `self.plugin`. Runner also provides `ctx.api`, bound to the current invocation and its resource grants; see [Run Context APIs](/en/plugin/dev/apis/agent-run). Operations with an explicit bot and target are covered in [Platform APIs](/en/plugin/dev/apis/platform).
## Request API
+1 -1
View File
@@ -62,4 +62,4 @@ In addition to the above entities, there are also entities like `Friend` and `Gr
## Platform APIs
Use [Platform APIs](/en/plugin/dev/apis/platform) to query users, manage groups, and call platform-specific actions. EventProcessor and AgentRunner call authorized platform tools through [Run Context APIs](/en/plugin/dev/apis/agent-run).
Use [Platform APIs](/en/plugin/dev/apis/platform) to query users, manage groups, and call platform-specific actions. Runner call authorized platform tools through [Run Context APIs](/en/plugin/dev/apis/agent-run).
+1 -1
View File
@@ -66,7 +66,7 @@ Only actions declared by the adapter are allowed; this is not unrestricted pass-
## Calling from an event processor
EventProcessor and AgentRunner use run-authorized tools and do not expose the generic `call_platform_api` method. For example, when the event supports actor lookup and grants it:
Runner can also call these platform APIs through `self.plugin`, specifying the bot and target explicitly. To use the current event grants and record actions in its run trace, use `ctx.api` or `ctx.reply()`:
```python
tools = {tool.tool_name for tool in ctx.api.get_allowed_tools()}
+3 -3
View File
@@ -4,8 +4,8 @@ title: "Adding Components"
Plugins consist of one or more components that provide different functionalities to LangBot. Currently supported component types include:
- Plugin processor (EventProcessor): handles platform events in plugin code after an instance is created and bound. See the [tutorial](/en/plugin/dev/components/event-processor).
- Agent runner (AgentRunner): provides execution logic selected in an Agent configuration; it is distinct from a standalone plugin processor.
- Plugin processor (Runner): handles platform events in plugin code after an instance is created and bound. See the [tutorial](/en/plugin/dev/components/runner).
- Agent runner (Runner): provides execution logic selected in an Agent configuration; it is distinct from a standalone plugin processor.
- Event Listeners (EventListener): Listen to events during pipeline execution and modify context or pipelines.
- Commands (Command): Triggered by user command messages starting with `!` (or other configured prefixes).
- Tools (Tool): Called by LLMs during execution of LangBot's built-in Local Agent.
@@ -72,7 +72,7 @@ To remove components, you can delete the corresponding information manually.
For detailed usage of each component, please refer to:
- [EventProcessor](./event-processor)
- [Runner](./runner)
- [EventListener](./event-listener)
- [Command](./command)
- [Tool](./tool)
+1 -1
View File
@@ -3,7 +3,7 @@ title: "Component: Event Listener"
---
<Info>
This component extends Pipeline processing steps. To handle Bot events such as incoming messages and new group members, see [Event Processor](/en/plugin/dev/components/event-processor).
This component extends Pipeline processing steps. To handle Bot events such as incoming messages and new group members, see [Event Processor](/en/plugin/dev/components/runner).
</Info>
LangBot's pipeline generates events during runtime for plugins to hook into and process. Each plugin can only have one event listener (components/event_listener/default.yaml), but can register any number of events within it.
@@ -1,28 +1,45 @@
---
title: "Component: Event Processor"
title: "Component: Runner"
---
The EventProcessor component handles Bot events such as incoming messages and new group members. After installing the plugin, users create a **Plugin processor** in LangBot, select the component, and bind Bot events to it.
Runner implements Agent execution or typed event handlers for a **Plugin processor**. Both styles receive `RunnerContext`. After installation, users select and configure the component in LangBot.
For environment setup, see the [plugin development tutorial](/en/plugin/dev/tutor).
## Adding an Event Processor Component
## Adding a Runner Component
A plugin can contain several event processors. Run `lbp comp EventProcessor` in the plugin directory. Enter `welcome` as the name and `Welcome new members` as the description.
A plugin can contain several event processors. Run `lbp comp Runner` in the plugin directory. Enter `welcome` as the name and `Welcome new members` as the description.
```bash
lbp comp EventProcessor
lbp comp Runner
```
The generated `components/event_processor/welcome.yaml` defines the component, and `welcome.py` contains its implementation.
The generated `components/runner/welcome.yaml` defines the component, and `welcome.py` contains its implementation.
## Manifest File: Event Processor
## Custom execution
To integrate a model or implement an execution loop, declare `spec.usages: [agent]` and yield results from `run(ctx)`:
```python
from langbot_plugin.api.definition.components.runner import Runner, RunnerContext, RunnerResult
from langbot_plugin.api.entities.builtin.provider.message import Message
class Echo(Runner):
async def run(self, ctx: RunnerContext):
yield RunnerResult.run_completed(
ctx.run_id,
message=Message(role="assistant", content=ctx.input.to_text()),
)
```
## Manifest File: Runner
Replace `welcome.yaml` with:
```yaml
apiVersion: langbot/v1
kind: EventProcessor
kind: Runner
metadata:
name: welcome
label:
@@ -34,6 +51,7 @@ metadata:
zh_Hans: 欢迎新成员,并响应 /hello 消息。
ja_JP: 新しいメンバーを歓迎し、/hello に返信します。
spec:
usages: [event]
events:
- group.member_joined
- message.received
@@ -65,14 +83,17 @@ execution:
`spec.events` lists supported events, and `spec.config` defines settings users can edit on the processor detail page. This example handles new members and incoming messages, with a greeting and a reply switch. Sending replies requires the `tool_calling` and `permissions.tools` settings shown above.
`spec.usages` controls where the component can be selected: `agent` for Agents and Pipelines, `event` for Plugin processors. Both can be declared as `[agent, event]`. Event usage requires `spec.events`. The example uses typed handlers; a custom `run(ctx)` can also call `await super().run(ctx)` to dispatch them.
## Plugin Implementation
Register handlers in the `Welcome` class's `initialize` method. Replace `welcome.py` with:
```python
from langbot_plugin.api.definition.components.event_processor import (
EventProcessor,
EventProcessorContext,
from langbot_plugin.api.definition.components.runner import (
Runner,
RunnerContext,
)
from langbot_plugin.api.entities.builtin.platform.events import (
MemberJoinedEvent,
@@ -81,13 +102,13 @@ from langbot_plugin.api.entities.builtin.platform.events import (
from langbot_plugin.api.entities.builtin.platform.message import Plain
class Welcome(EventProcessor):
class Welcome(Runner):
async def initialize(self):
await super().initialize()
@self.handler(MemberJoinedEvent)
async def on_join(ctx: EventProcessorContext):
name = ctx.event.member.nickname or str(ctx.event.member.id)
async def on_join(ctx: RunnerContext):
name = ctx.platform_event.member.nickname or str(ctx.platform_event.member.id)
await ctx.log(f"Member joined: {name}")
if not ctx.config.get("reply_enabled", True):
await ctx.log("Replies are disabled for this processor")
@@ -97,9 +118,9 @@ class Welcome(EventProcessor):
await ctx.log("Welcome action completed")
@self.handler(MessageReceivedEvent)
async def on_message(ctx: EventProcessorContext):
async def on_message(ctx: RunnerContext):
text = "".join(
item.text for item in ctx.event.message_chain if isinstance(item, Plain)
item.text for item in ctx.platform_event.message_chain if isinstance(item, Plain)
).strip()
if text != "/hello":
await ctx.log("Message ignored: expected /hello")
@@ -112,7 +133,7 @@ class Welcome(EventProcessor):
`on_join` welcomes new members, and `on_message` replies to `/hello`. With **Send replies** turned off, they only write logs.
`ctx.event` contains the event data, and `ctx.config` contains this processor's settings. Use `ctx.reply()` to reply and `ctx.log()` to write logs. Processing finishes when the handlers return.
`ctx.platform_event` contains the event data, and `ctx.config` contains this processor's settings. Use `ctx.reply()` to reply and `ctx.log()` to write logs. Processing finishes when the handlers return.
## Streaming Replies
@@ -126,7 +147,7 @@ async with ctx.reply_stream() as reply:
The reply finishes when the `async with` block exits normally. If streaming is unsupported, disabled, or the event is not a message, the Host sends one complete message at the end. Exceptions discard buffered partial text. Debug runs simulate delivery.
This API uses the same permission as `ctx.reply()`. AgentRunner components can use `self.get_run_api(ctx).reply_stream()`; model output text is still not sent automatically.
This API uses the same permission as `ctx.reply()`. Runner components can use `self.get_run_api(ctx).reply_stream()`; model output text is still not sent automatically.
## Registering Events
@@ -168,7 +189,7 @@ After installing the plugin, bind the Bot's **Member joined** and **Message rece
## Next Steps
- Explore more examples in [EventProcessorDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/EventProcessorDemo).
- Explore more examples in [RunnerDemo](https://github.com/langbot-app/langbot-plugin-demo/tree/main/RunnerDemo).
- To extend Pipeline steps such as preprocessing or completion of a model call, use [Event Listener](/en/plugin/dev/components/event-listener).
See [Run Context APIs](/en/plugin/dev/apis/agent-run) for replies, models, tools, history, and state.
+1 -1
View File
@@ -3,7 +3,7 @@ title: "Plugin Development Tutorial"
---
<Info>
To build a plugin for incoming messages, new group members, and other Bot events, complete the basic setup here and continue with the [Event Processor tutorial](/en/plugin/dev/components/event-processor).
To build a plugin for incoming messages, new group members, and other Bot events, complete the basic setup here and continue with the [Event Processor tutorial](/en/plugin/dev/components/runner).
</Info>
## Architecture Foundation