Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions charts/openab-feishu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Under **Permissions & Scopes**, add these scopes:
| `im:message.p2p_msg:readonly` | Read DM messages |
| `im:resource` | Download images/files from messages |
| `contact:user.base:readonly` | Resolve user display names |
| `cardkit:card:write` | Create/update interactive streaming cards (required for card streaming, on by default) |

### 5. Publish

Expand Down Expand Up @@ -189,6 +190,10 @@ helm install my-bot ./charts/openab-feishu \
| `platform.requireMention` | `true` | Require @mention in groups |
| `platform.allowedGroups` | `[]` | Allowed group chat IDs |
| `platform.allowedUsers` | `[]` | Allowed user open_ids |
| `cardStreaming.mode` | `"auto"` | Card streaming: `auto` / `card` / `post` (kill-switch) |
| `cardStreaming.fallbackToPost` | `true` | Fall back to post if a card API call fails |
| `cardStreaming.promoteBytes` | `4000` | Byte threshold for auto-promoting to a card |
| `cardStreaming.idleFinalizeMs` | `3000` | Idle window (ms) before finalizing a card |
| `persistence.enabled` | `true` | Enable PVC for agent state |
| `persistence.size` | `"1Gi"` | PVC size |

Expand Down
9 changes: 9 additions & 0 deletions charts/openab-feishu/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ spec:
- name: FEISHU_ALLOWED_USERS
value: {{ join "," .Values.platform.allowedUsers | quote }}
{{- end }}
# Card streaming (interactive cards for streaming replies)
- name: FEISHU_CARD_STREAMING_MODE
value: {{ .Values.cardStreaming.mode | quote }}
- name: FEISHU_CARD_FALLBACK_TO_POST
value: {{ .Values.cardStreaming.fallbackToPost | quote }}
- name: FEISHU_CARD_PROMOTE_BYTES
value: {{ .Values.cardStreaming.promoteBytes | quote }}
- name: FEISHU_CARD_IDLE_FINALIZE_MS
value: {{ .Values.cardStreaming.idleFinalizeMs | quote }}
volumeMounts:
{{- if .Values.persistence.enabled }}
- name: data
Expand Down
15 changes: 15 additions & 0 deletions charts/openab-feishu/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ platform:
# -- Feishu user open_ids allowed (empty = all users)
allowedUsers: []

# -- Card streaming: render streaming replies as interactive CardKit cards
# (no 20-edit cap, native markdown / table rendering).
cardStreaming:
# -- "auto" (default: short replies stay as post, long / code / table replies
# promote to a card), "card" (always card), or "post" (kill-switch: disable
# card streaming, back to post-only behavior).
mode: "auto"
# -- Fall back to a post message if a card API call fails (recommended).
fallbackToPost: true
# -- Byte threshold at which a plain-text reply auto-promotes to a card.
promoteBytes: 4000
# -- Idle window (ms) after which a streaming card with no further edits is
# finalized (typewriter cursor settles, markdown re-renders).
idleFinalizeMs: 3000

# -- Persistence for agent working directory
persistence:
enabled: true
Expand Down
34 changes: 34 additions & 0 deletions docs/feishu.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ https://your-gateway-host/webhook/feishu
| — | `FEISHU_ALLOW_USER_MESSAGES` | `multibot-mentions` | Thread response mode: `multibot-mentions` / `involved` / `mentions`. See below. |
| `gateway.botUsername` | — | — | Set to bot's `open_id` for @mention gating |
| `gateway.streaming` | — | `false` | Enable streaming (typewriter) mode |
| `cardStreaming.mode` | `FEISHU_CARD_STREAMING_MODE` | `auto` | Card streaming mode: `auto` (short→post, long/code/table→card), `card` (always card), `post` (disable — kill-switch) |
| `cardStreaming.fallbackToPost` | `FEISHU_CARD_FALLBACK_TO_POST` | `true` | Fall back to a post message if a card API call fails |
| `cardStreaming.promoteBytes` | `FEISHU_CARD_PROMOTE_BYTES` | `4000` | Byte threshold for auto-promoting a plain-text reply to a card |
| `cardStreaming.idleFinalizeMs` | `FEISHU_CARD_IDLE_FINALIZE_MS` | `3000` | Idle window (ms) before a streaming card is finalized |

## @mention Gating

Expand Down Expand Up @@ -189,6 +193,36 @@ streaming = true

The gateway platform must support message editing (Feishu/Lark do). Platforms that don't support editing should leave `streaming = false` (default).

## Card Streaming

By default (`FEISHU_CARD_STREAMING_MODE=auto`), streaming replies render as
**interactive CardKit cards** when the content warrants it. Cards have no
20-edit cap (errcode 230072) and render markdown — including **tables** and code
blocks — natively, which a `post` message cannot.

| Mode | Behavior |
|---|---|
| `auto` (default) | Short replies stay as a `post` (native reply UI); long replies, or any reply containing a code fence or a markdown table, promote to a card. |
| `card` | Every reply is sent as a card from the first message. |
| `post` | Card streaming disabled — post-only behavior (the kill-switch). |

Notes:

- **Auto promotion is one-way**: a reply starts as a post and, once promoted,
stays a card. Promotion deletes the post placeholder (shown as "message
recalled" in Feishu) and re-sends as a card. In `card` mode the first reply is
a card from the start, so there is no placeholder and no recall.
- **Finalize**: after ~`FEISHU_CARD_IDLE_FINALIZE_MS` ms with no further edits,
the card is rebuilt as a static card so the typewriter cursor settles and the
markdown re-renders cleanly.
- **Fallback**: if a card API call fails and `FEISHU_CARD_FALLBACK_TO_POST` is
`true` (default), the gateway falls back to the post path (with the edit-cap
recovery), so a reply is never lost.
- **Tables wrapped in a code fence**: agents sometimes wrap a markdown table in a
bare ``` fence for monospace alignment in environments that don't render
tables. On the card path the gateway unwraps a fence whose body is exactly one
table so it renders as a native table.

## Thread (Topic) Replies

When a user replies to a bot message in a group chat, Feishu creates a thread (topic). The bot replies within the same thread, and each thread gets its own independent session.
Expand Down
1 change: 1 addition & 0 deletions gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ subtle = "2"
sha1 = "0.10"
quick-xml = "0.37"
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
parking_lot = "0.12"
urlencoding = "2"

[dev-dependencies]
Expand Down
Loading
Loading