mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-11-04 14:52:12 +08:00
fix(lark): stream replies after HITL submissions
This commit is contained in:
@@ -7,6 +7,7 @@ import asyncio
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
@@ -20,6 +21,7 @@ from langbot.pkg.platform.adapters.lark.interaction import (
|
||||
interaction_delivery_capabilities,
|
||||
interaction_event_from_callback,
|
||||
)
|
||||
from langbot.pkg.agent.runner.reply_stream import ReplyStreamRequest, ReplyStreamSession
|
||||
from langbot_plugin.api.definition.abstract.platform.event_logger import AbstractEventLogger
|
||||
from langbot_plugin.api.entities.builtin.platform import entities as platform_entities
|
||||
from langbot_plugin.api.entities.builtin.platform import events as platform_events
|
||||
@@ -729,7 +731,10 @@ async def test_lark_streaming_card_uses_strictly_increasing_sequences():
|
||||
final_card = json.loads(final_request.request_body.card.data)
|
||||
assert final_card['body']['elements'][0] == {'tag': 'markdown', 'content': 'answer'}
|
||||
assert 'header' not in final_card
|
||||
assert final_card['body']['elements'][-1]['columns'][1]['elements'][0]['content'] == '[LangBot](https://langbot.app?utm_source=feishu&utm_medium=bot_card&utm_campaign=langbot)'
|
||||
assert (
|
||||
final_card['body']['elements'][-1]['columns'][1]['elements'][0]['content']
|
||||
== '[LangBot](https://langbot.app?utm_source=feishu&utm_medium=bot_card&utm_campaign=langbot)'
|
||||
)
|
||||
assert not final_card['config'].get('streaming_mode', False)
|
||||
assert 'response-1' not in adapter.card_id_dict
|
||||
assert 'stream-card-1' not in adapter.card_sequence_dict
|
||||
@@ -828,6 +833,43 @@ async def test_lark_streaming_card_falls_back_to_full_card_update_when_stream_cl
|
||||
assert 'stream-card-1' not in adapter.closed_streaming_cards
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
('target_type', 'target_id', 'receive_id_type'),
|
||||
[
|
||||
('person', 'user-1', 'open_id'),
|
||||
('group', 'chat-1', 'chat_id'),
|
||||
],
|
||||
)
|
||||
async def test_lark_hitl_reply_stream_creates_and_updates_new_card(target_type, target_id, receive_id_type):
|
||||
adapter = make_adapter({'enable-stream-reply': True})
|
||||
event = SimpleNamespace(
|
||||
event_type='interaction.submitted',
|
||||
delivery=SimpleNamespace(
|
||||
surface='platform',
|
||||
platform_capabilities={},
|
||||
reply_target={'target_type': target_type, 'target_id': target_id, 'message_id': 'interaction-card'},
|
||||
),
|
||||
)
|
||||
session = ReplyStreamSession(event, adapter)
|
||||
stream_id = uuid4()
|
||||
|
||||
first = await session.apply(ReplyStreamRequest(stream_id=stream_id, operation='update', text='partial'))
|
||||
final = await session.apply(ReplyStreamRequest(stream_id=stream_id, operation='finish', text='complete'))
|
||||
|
||||
assert first['delivery'] == final['delivery'] == 'streaming'
|
||||
request = adapter.api_client.im.v1.message.acreate.await_args.args[0]
|
||||
assert request.receive_id_type == receive_id_type
|
||||
assert request.request_body.receive_id == target_id
|
||||
assert request.request_body.msg_type == 'interactive'
|
||||
assert adapter.api_client.im.v1.message.areply.await_count == 0
|
||||
assert adapter.api_client.cardkit.v1.card_element.acontent.await_count == 1
|
||||
final_card = json.loads(adapter.api_client.cardkit.v1.card.aupdate.await_args.args[0].request_body.card.data)
|
||||
assert final_card['body']['elements'][0]['content'] == 'complete'
|
||||
assert not adapter.card_id_dict
|
||||
await session.close()
|
||||
|
||||
|
||||
def test_lark_interaction_callback_builds_scoped_host_event():
|
||||
callback = SimpleNamespace(
|
||||
event=SimpleNamespace(
|
||||
@@ -991,6 +1033,7 @@ async def test_lark_webhook_rejects_wrong_signature():
|
||||
|
||||
def test_lark_stream_card_separates_multiple_reasoning_rounds():
|
||||
from langbot.pkg.platform.adapters.lark.stream_card import build_card, streaming_text
|
||||
|
||||
content = '<think>first thought</think>first answer<think>second thought</think>final answer'
|
||||
preview = streaming_text(content)
|
||||
assert '<think>' not in preview and '</think>' not in preview
|
||||
@@ -1006,6 +1049,7 @@ def test_lark_stream_card_separates_multiple_reasoning_rounds():
|
||||
|
||||
def test_lark_live_preview_is_bounded_but_final_reasoning_is_complete():
|
||||
from langbot.pkg.platform.adapters.lark.stream_card import build_card, streaming_text
|
||||
|
||||
thought = 'reasoning ' * 200
|
||||
content = '<think>' + thought + '</thi'
|
||||
assert len(streaming_text(content)) < 450
|
||||
@@ -1022,6 +1066,9 @@ async def test_lark_stream_card_creation_is_async_and_branded():
|
||||
request = adapter.api_client.cardkit.v1.card.acreate.await_args.args[0]
|
||||
card = json.loads(request.request_body.data)
|
||||
assert 'header' not in card
|
||||
assert card['body']['elements'][-1]['columns'][1]['elements'][0]['content'] == '[LangBot](https://langbot.app?utm_source=feishu&utm_medium=bot_card&utm_campaign=langbot)'
|
||||
assert (
|
||||
card['body']['elements'][-1]['columns'][1]['elements'][0]['content']
|
||||
== '[LangBot](https://langbot.app?utm_source=feishu&utm_medium=bot_card&utm_campaign=langbot)'
|
||||
)
|
||||
assert card['config']['streaming_mode'] is True
|
||||
assert card['body']['elements'][0]['element_id'] == 'streaming_txt'
|
||||
|
||||
Reference in New Issue
Block a user