> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veadk.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Feishu

## Overview

`FeishuChannelExtension` bridges inbound messages from a Feishu (Lark) bot into the VeADK `Runner`.

<Note>
  This page treats Feishu as an **inbound message channel**: messages from Feishu users trigger the agent. To let the agent call Feishu capabilities, see [Feishu tools](/productions/veadk/archives/1.0.7/en/components/tools/lark).
</Note>

It listens for the bot's message events and maps Feishu's session identity onto VeADK: the message sender becomes the `Runner`'s user identity, and the thread the message belongs to becomes the session identity; when there is no thread, it falls back to the current group or direct chat as the session. In 1.0.5 it extracts text from plain messages, interactive cards, and merged-forward messages and supports the synchronous Channel SDK lifecycle.

<Warning>
  Thread history and quoted-message retrieval are enabled by default. The extension reads related Feishu messages and sends their content to the model. If the application uses sessions, long-term memory, or logging, the content may also be retained under those policies. Before enabling the bot, confirm that message scopes, model processing, and retention meet your requirements. Disable `include_thread_history` and `include_parent_message` when this context is not required.
</Warning>

When an incoming message belongs to a Feishu thread or is a reply, the extension automatically fetches the thread history or the quoted parent message and prepends that context to the user message before forwarding it to the agent. See [Thread history and quoted messages](#thread-history-and-quoted-messages).

## Environment & prerequisites

Installation:

```bash lines theme={null}
pip install veadk-python[extensions]
```

If you only want this capability, install it on its own:

```bash lines theme={null}
pip install lark-oapi
```

Environment variables:

* `TOOL_FEISHU_CHANNEL_APP_ID`
* `TOOL_FEISHU_CHANNEL_APP_SECRET`
* `TOOL_FEISHU_CHANNEL_TRANSPORT`: defaults to `ws`
* `TOOL_FEISHU_CHANNEL_STREAMING`: whether to enable streaming output, defaults to `false`
* `TOOL_FEISHU_CHANNEL_REACTIONS`: whether to reply with a "received" reaction on incoming messages, defaults to `false`

Or configure in `config.yaml`:

```yaml title="config.yaml" lines theme={null}
tool:
  feishu_channel:
    app_id: cli_xxx
    app_secret: xxx
    transport: ws
    streaming: true
    reactions: true
```

## Usage

```python title="examples/channel/feishu_bot.py" lines theme={null}
import asyncio

from veadk import Agent, Runner
from veadk.extensions import FeishuChannelExtension
from veadk.memory.short_term_memory import ShortTermMemory

agent = Agent(
    name="feishu_agent",
    model_name="doubao-seed-2-1-pro-260628",
    instruction="You are an assistant that talks to users through a Feishu bot.",
)

runner = Runner(
    agent=agent,
    app_name="veadk_feishu_demo",
    user_id="veadk_feishu_default_user",
    short_term_memory=ShortTermMemory(),
)

channel = FeishuChannelExtension(
    runner=runner,
    channel_kwargs={"transport": "ws"},
)


async def main():
    await channel.connect()


if __name__ == "__main__":
    asyncio.run(main())
```

## Parameters

| Parameter | Type | Default | Description |
| :- | :- | :- | :- |
| `runner` | `Runner` | None | Required. VeADK runner that handles inbound Feishu messages. |
| `app_id` | `str \| None` | `TOOL_FEISHU_CHANNEL_APP_ID` | Feishu application ID, with `TOOL_LARK_ENDPOINT` as a compatibility fallback. |
| `app_secret` | `str \| None` | `TOOL_FEISHU_CHANNEL_APP_SECRET` | Feishu application secret, with `TOOL_LARK_API_KEY` as a compatibility fallback. Supply it through an environment variable or secret manager. |
| `channel` | `Any \| None` | `None` | Existing Feishu Channel client. When supplied, the extension does not create a client. |
| `session_id_factory` | `Callable[[Any], str] \| None` | Thread, reply, chat, or message identifier | Custom mapping from a Feishu message to the VeADK `session_id`. |
| `user_id_factory` | `Callable[[Any], str] \| None` | Sender `union_id`, `open_id`, or `user_id` | Custom mapping from a Feishu sender to the VeADK `user_id`. |
| `message_handler` | `Callable \| None` | `None` | Custom message handler. When unset, the extension calls `runner`. It may return a string, an awaitable string, or `None`. |
| `response_formatter` | `Callable[[str], dict[str, str]] \| None` | `{"text": text}` | Converts agent text into the Feishu send payload. |
| `reply_in_thread` | `bool` | `True` | Whether to reply to the original message so output remains in its thread. |
| `ignore_empty_messages` | `bool` | `True` | Whether to ignore messages from which no text can be extracted. |
| `channel_kwargs` | `dict[str, Any] \| None` | `None` | Options passed when creating the Channel client. `transport` reads `TOOL_FEISHU_CHANNEL_TRANSPORT` and otherwise defaults to `ws`. |
| `streaming` | `bool` | `False` | Whether to stream replies. `TOOL_FEISHU_CHANNEL_STREAMING=true` also enables it. |
| `reactions` | `bool` | `False` | Whether to add a received reaction. `TOOL_FEISHU_CHANNEL_REACTIONS=true` also enables it. |
| `include_parent_message` | `bool` | `True` | Whether to fetch and attach the quoted parent message for replies. |
| `include_thread_history` | `bool` | `True` | Whether to fetch and attach thread history for thread messages. |
| `thread_history_limit` | `int` | `20` | Maximum thread-history messages to fetch. Values below `1` are treated as `1`. |

## Thread history and quoted messages

When an incoming message comes from a Feishu thread or is a reply, the extension fetches the relevant context and prepends it to the user message before forwarding it to the agent:

* **Thread history**: when the message belongs to a thread, the extension fetches the thread's history ordered oldest-first (up to `thread_history_limit` messages, excluding the current message) and prepends it as thread context.
* **Quoted message**: when the message is a reply but does not belong to a thread, the extension fetches the quoted parent message; if the parent cannot be fetched, it falls back to the root message of the reply chain and prepends it as quoted context.

Thread history takes precedence over quoted messages: when a thread identifier is present, only the thread history is attached and the parent message is not fetched separately. Fetched message content is rendered back to readable text, covering text, rich-text (post), interactive card, and merged-forward messages.

<Note>
  Fetching thread history and quoted messages requires calling Feishu OpenAPI message endpoints, so `app_id` and `app_secret` must be configured (constructor parameters, or the `TOOL_FEISHU_CHANNEL_APP_ID` / `TOOL_FEISHU_CHANNEL_APP_SECRET` environment variables, falling back to `TOOL_LARK_ENDPOINT` / `TOOL_LARK_API_KEY`). When credentials are not configured the capability is skipped silently without raising an error.
</Note>

### Usage example

Save and configure the `feishu_bot.py` example above, then create the following file in the same directory:

```python title="examples/channel/feishu_bot_thread.py" lines theme={null}
from veadk.extensions import FeishuChannelExtension
from feishu_bot import runner

channel = FeishuChannelExtension(
    runner=runner,
    include_thread_history=True,
    thread_history_limit=30,
    include_parent_message=True,
)
```

Disable the capability so the extension forwards only the current message text:

```python title="examples/channel/feishu_bot_plain.py" lines theme={null}
from veadk.extensions import FeishuChannelExtension
from feishu_bot import runner

channel = FeishuChannelExtension(
    runner=runner,
    include_parent_message=False,
    include_thread_history=False,
)
```

## Notes

* The Feishu WebSocket long-connection mode is used by default, so as long as the bot has subscribed to message events, you can start the connection directly.
* Replies are sent as replies to the original message by default, keeping VeADK output attached to the current Feishu message thread.
* Override the default identity mapping via `session_id_factory` and `user_id_factory`.
* `connect()` and `disconnect()` support both async clients and synchronous Channel SDK clients exposing `start()` / `stop()`.
