Feishu Streaming Card Plan
Feishu Streaming Card Plan
This note captures a minimal design for Feishu “streaming output” without assuming native token-by-token text streaming from Feishu message APIs.
Goal
Provide a streaming-like user experience in Feishu by:
- sending an initial interactive card
- progressively updating that card while content is generated
- finalizing the card when generation completes or fails
Why card updates
Current vhttpd and PHP worker capabilities already support:
- sending interactive cards
- updating interactive cards
- callback token updates
- active outbound gateway calls from PHP via
VPhp\VHttpd\VHttpd::gateway('feishu')
This matches the OpenClaw Feishu “streaming” experience more closely than assuming a native streaming text message protocol.
Existing building blocks
Transport and gateway
- Feishu send:
/gateway/feishu/messages
- Feishu update:
- existing websocket upstream
updatecommand execution
- existing websocket upstream
- PHP host calls:
VPhp\VHttpd\VHttpd::gateway('feishu')
PHP semantic layer
VPhp\VHttpd\Upstream\WebSocket\Feishu\Command::sendInteractive(...)VPhp\VHttpd\Upstream\WebSocket\Feishu\Command::updateInteractive(...)VPhp\VHttpd\Upstream\WebSocket\Feishu\Content\InteractiveCardVPhp\VHttpd\Upstream\WebSocket\Feishu\Content\CardHeaderVPhp\VHttpd\Upstream\WebSocket\Feishu\Content\CardMarkdown
Proposed model
Stream session
Introduce a lightweight session object at the PHP layer:
providerinstancechat_idmessage_idstarted_atlast_flush_atfinalized
Lifecycle
start- send an initial placeholder card
- store returned
message_id
append- accumulate generated content chunks
- periodically rebuild the card body
flush- call interactive card update on the same
message_id
- call interactive card update on the same
finalize- write final content
- optionally mark completion state in the card
fail- update card with failure state
Update policy
Do not update on every token.
Recommended first policy:
- coalesce updates
- flush every
300msto1000ms - always flush on completion
This avoids excessive card update calls and keeps the user experience stable.
First implementation scope
If implemented later, keep it intentionally narrow:
- only interactive cards
- only one stream session per output
- message target uses
message_id - no persistence across worker restarts
- no background queue in
vhttpd
Suggested PHP API shape
Possible future PHP-only helper:
$stream = FeishuStreamingCard::start(
instance: 'main',
chatId: 'oc_xxx',
title: 'Thinking...'
);
$stream->appendMarkdown("first chunk");
$stream->appendMarkdown("second chunk");
$stream->flush();
$stream->finalize();
Internally this would use:
VHttpd::gateway('feishu')->send(...)VHttpd::gateway('feishu')->send/update ...
MCP implications
This should not be the first MCP Feishu tool.
Recommended MCP order:
feishu.list_chatsfeishu.send_textfeishu.send_imagefeishu.send_cardfeishu.update_card- only then consider a higher-level streaming card tool
Reason:
- streaming needs session state
- update cadence
- failure handling
- prompt/tool interaction design
These are better designed after the static card tools have stabilized.