mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-29 03:46:37 +08:00
A Workspace installs a plugin once, but that single installation is reused by several product objects: every processor for a Runner, every knowledge base for a KnowledgeEngine / Parser / Retriever, and many conversations or page requests for the other component kinds. The platform storage key carries no processor or knowledge-base dimension, so a plugin must fold that identity into its own keys. - certified-plugins: add "one installation, many usages" with the isolation dimensions per component kind, and "when an installation is revoked" documenting BasePlugin.on_installation_revoked, its trigger set, the absent invocation context and the no-op/failure semantics. - component pages (runner, knowledge-engine, knowledge-retriever, parser, tool, command, event-listener, page): add a short component-specific section with that component's own rules and a link to the contract page. - Align the background-work rule with the SDK contract: the SDK does not track ownership of detached work, so a plugin must keep its own binding-keyed registry and stop it in the revocation hook. All three locales carry the same sections.
227 lines
8.0 KiB
Plaintext
227 lines
8.0 KiB
Plaintext
---
|
|
title: "Component: Command"
|
|
---
|
|
|
|
Command components are triggered by user command messages starting with `!` (or other configured prefixes). Here's an example of triggering the `!help` command:
|
|
|
|
<img width="600" src="/images/zh/plugin/dev/components/command_use.png" />
|
|
|
|
## Adding Command Components
|
|
|
|
A single plugin can contain any number of commands. Execute the command `lbp comp Command` in the plugin directory and follow the prompts to enter the command configuration.
|
|
|
|
```bash
|
|
➜ HelloPlugin > lbp comp Command
|
|
Generating component Command...
|
|
Command name: info
|
|
Command description: Show information of the query
|
|
Component Command generated successfully.
|
|
组件 Command 生成成功。
|
|
```
|
|
|
|
This will generate `info.yaml` and `info.py` files in the `components/commands/` directory. The `.yaml` file defines the basic information for the `!info` command, and the `.py` file is the command handler:
|
|
|
|
```bash
|
|
➜ HelloPlugin > tree
|
|
...
|
|
├── components
|
|
│ ├── __init__.py
|
|
│ ├── commands
|
|
│ │ ├── __init__.py
|
|
│ │ ├── info.py
|
|
│ │ └── info.yaml
|
|
...
|
|
```
|
|
|
|
## Manifest File: Command Component
|
|
|
|
```yaml
|
|
apiVersion: v1 # Do not modify
|
|
kind: Command # Do not modify
|
|
metadata:
|
|
name: info # Command name, users will trigger this command with !info
|
|
label: # Command display name, shown in LangBot UI, supports multilingual
|
|
en_US: Info
|
|
zh_Hans: Info
|
|
description: # Command description, shown in LangBot UI, supports multilingual. Optional.
|
|
en_US: 'Show information of the query'
|
|
zh_Hans: '发送此次消息的详细信息'
|
|
spec:
|
|
execution:
|
|
python:
|
|
path: info.py # Command handler, do not modify
|
|
attr: Info # Command handler class name, matches the class name in info.py
|
|
```
|
|
|
|
## Plugin Processing
|
|
|
|
The following code is generated by default (`components/command/<command_name>.py`). You need to register and implement subcommand processing logic in the `initialize` method of the `Info` class.
|
|
|
|
```python
|
|
# Auto generated by LangBot Plugin SDK.
|
|
# Please refer to https://langbot.app/docs/en/plugin/dev/tutor.html for more details.
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, AsyncGenerator
|
|
|
|
from langbot_plugin.api.definition.components.command.command import Command, Subcommand
|
|
from langbot_plugin.api.entities.builtin.command.context import ExecuteContext, CommandReturn
|
|
|
|
|
|
class Info(Command):
|
|
|
|
async def initialize(self):
|
|
await super().initialize()
|
|
|
|
"Fill with your code here"
|
|
```
|
|
|
|
Adding subcommands:
|
|
|
|
```python
|
|
...
|
|
class Info(Command):
|
|
|
|
async def initialize(self):
|
|
await super().initialize()
|
|
|
|
@self.subcommand(
|
|
name="", # Empty string represents root command
|
|
help="Show information of the query", # Command help information
|
|
usage="info", # Command usage example, displayed in command help
|
|
aliases=["i"], # Command aliases
|
|
)
|
|
async def send(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
|
|
print(context)
|
|
|
|
reply_text = f"Query ID: {context.query_id}\n"
|
|
reply_text += f"command: {context.command}\n"
|
|
reply_text += f"command_text: {context.command_text}\n"
|
|
reply_text += f"params: {context.params}\n"
|
|
reply_text += f"crt_params: {context.crt_params}\n"
|
|
reply_text += f"privilege: {context.privilege}\n"
|
|
reply_text += f"session: {context.session.launcher_type.value}_{context.session.launcher_id}\n"
|
|
|
|
yield CommandReturn(
|
|
text=reply_text,
|
|
)
|
|
|
|
@self.subcommand(
|
|
name="field",
|
|
help="Show information of the field",
|
|
usage="info field",
|
|
aliases=["f"],
|
|
)
|
|
async def field(self, context: ExecuteContext) -> AsyncGenerator[CommandReturn, None]:
|
|
print(context)
|
|
|
|
field_name = context.crt_params[0]
|
|
field_value = getattr(context, field_name)
|
|
|
|
yield CommandReturn(
|
|
text=f"{field_name}: {field_value}",
|
|
)
|
|
```
|
|
|
|
In this code, the `send` function is registered as a subcommand through the `@self.subcommand` decorator, prints the command context (ExecuteContext) information, and concatenates it into a reply message.
|
|
|
|
### Subcommand Registration
|
|
|
|
`name` is the subcommand name. Leaving it empty means handling the main command. If not empty, it matches the second parameter as the command. For example:
|
|
|
|
- `!info` matches the subcommand with name=""
|
|
- `!info field` matches the subcommand with name="field"
|
|
- `!info field value` matches the subcommand with name="field", with `value` as the subcommand parameter
|
|
- Specialy, `name="*"` matches all unmatched first-level subcommands, and passes each section after `info` as parameters, for example `!info 123`, `!info abc`. You can get `['123']` or `['abc']` from `context.crt_params`, depending on the user's input.
|
|
|
|
In subcommand functions, you can read command parameters through the `context` variable.
|
|
|
|
The effect of the above command is shown in the image:
|
|
|
|
<img width="600" src="/images/zh/plugin/dev/components/info_command_demo.png" />
|
|
|
|
### Command Context
|
|
|
|
```python
|
|
|
|
class ExecuteContext(pydantic.BaseModel):
|
|
"""Single command execution context"""
|
|
|
|
query_id: int
|
|
"""Request ID"""
|
|
|
|
session: provider_session.Session
|
|
"""Session object for this message"""
|
|
|
|
command_text: str
|
|
"""Complete command text"""
|
|
|
|
command: str
|
|
"""Command name"""
|
|
|
|
crt_command: str
|
|
"""Current command
|
|
|
|
In multi-level commands, crt_command is the current command, command is the root command.
|
|
Example: !plugin on Webwlkr
|
|
When processing plugin, command is plugin, crt_command is plugin
|
|
When processing on, command is plugin, crt_command is on
|
|
"""
|
|
|
|
params: list[str]
|
|
"""Command parameters
|
|
|
|
Parameter list after splitting the entire command by spaces
|
|
"""
|
|
|
|
crt_params: list[str]
|
|
"""Current command parameters
|
|
|
|
In multi-level commands, crt_params are current command parameters, params are root command parameters.
|
|
Example: !plugin on Webwlkr
|
|
When processing plugin, params is ['on', 'Webwlkr'], crt_params is ['on', 'Webwlkr']
|
|
When processing on, params is ['on', 'Webwlkr'], crt_params is ['Webwlkr']
|
|
"""
|
|
|
|
privilege: int
|
|
"""Session privilege level"""
|
|
|
|
...
|
|
```
|
|
|
|
### Command Return Values
|
|
|
|
Command return values (CommandReturn) currently support returning text, images (image_url, image links), and errors.
|
|
|
|
```python
|
|
yield CommandReturn(
|
|
text=reply_text,
|
|
)
|
|
|
|
yield CommandReturn(
|
|
image_url=image_url,
|
|
)
|
|
```
|
|
|
|
Command processing supports returning multiple messages, so please use `yield` statements to return messages.
|
|
|
|
For specific return values, please refer to the definition of `CommandReturn`: [langbot_plugin.api.entities.builtin.command.context.CommandReturn](https://github.com/langbot-app/langbot-plugin-sdk/tree/main/src/langbot_plugin/api/entities/builtin/command/context.py)
|
|
|
|
## One installation, many usages
|
|
|
|
A single command is triggered **concurrently by several conversations**:
|
|
|
|
- Read config per call; never read it in `initialize()` and cache it on the instance.
|
|
- Never leave command arguments, return values or conversation data on instance fields or module-level variables; `ExecuteContext` is valid only for the current execution.
|
|
- Concurrent triggering means instance fields get interleaved writes — do not use them to hold "current conversation" style state. Data that must survive calls belongs in Host storage, keyed with the conversation identity.
|
|
- If you keep per-installation process-local caches, release them in `on_installation_revoked(binding)`.
|
|
|
|
See [Certified plugins and shared runtime](/en/plugin/certified-plugins) for the full specification.
|
|
|
|
## What's Next
|
|
|
|
You have learned the basic information about command registration and command execution. Next, you can:
|
|
|
|
- See the complete [`ExecuteContext` API reference](/en/plugin/dev/apis/agent-run#command).
|
|
- Check out [Plugin Common APIs](/en/plugin/dev/apis/common).
|