--- 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: ## 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/.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: ### 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).