fix(lark): preserve incoming rich-text code blocks (#2626)

This commit is contained in:
Haoxuan Jiang
2026-10-19 16:26:16 +08:00
committed by GitHub
parent 42f6f7373b
commit 841b4df09d
3 changed files with 80 additions and 2 deletions
@@ -184,7 +184,7 @@ class LarkMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
normalized = LarkMessageConverter._normalize_inbound_content(message, message_content)
for ele in normalized:
tag = ele.get('tag')
if tag in {'text', 'md'}:
if tag in {'text', 'md', 'code_block'}:
text = ele.get('text') or ''
if text:
components.append(platform_message.Plain(text=text))
+1 -1
View File
@@ -726,7 +726,7 @@ class LarkMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
]
for ele in message_content['content']:
if ele['tag'] == 'text':
if ele['tag'] in {'text', 'code_block'}:
lb_msg_list.append(platform_message.Plain(text=ele['text']))
elif ele['tag'] == 'at':
lb_msg_list.append(platform_message.At(target=ele['user_name']))
@@ -0,0 +1,78 @@
"""Lark rich-text code blocks must reach the bot as text instead of being dropped."""
import json
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
from langbot.pkg.platform.adapters.lark.message_converter import LarkMessageConverter
from langbot.pkg.platform.sources.lark import LarkMessageConverter as LegacyLarkMessageConverter
from langbot_plugin.api.entities.builtin.platform import message as platform_message
@pytest.fixture(params=[LegacyLarkMessageConverter, LarkMessageConverter], ids=['legacy', 'omni'])
def converter(request):
return request.param
def post_message(content):
return SimpleNamespace(
message_id='post-msg',
message_type='post',
create_time='1714000000000',
content=json.dumps({'title': '', 'content': content}),
mentions=[],
)
@pytest.mark.asyncio
@pytest.mark.parametrize(
'code_block',
[
{'tag': 'code_block', 'language': 'GO', 'text': 'func main() int64 {\n return 0\n}'},
{'tag': 'code_block', 'language': 'PYTHON', 'text': 'def hello():\n print("你好")\n'},
{'tag': 'code_block', 'text': ' indented\n\nlast line'},
],
ids=['official-go-example', 'python-unicode', 'no-language'],
)
async def test_inbound_post_preserves_code_block_text(converter, code_block):
api_client = MagicMock()
chain = await converter.target2yiri(post_message([[code_block]]), api_client)
assert len(chain) == 2
assert isinstance(chain[0], platform_message.Source)
assert chain[0].id == 'post-msg'
assert isinstance(chain[1], platform_message.Plain)
assert chain[1].text == code_block['text']
assert api_client.mock_calls == []
@pytest.mark.asyncio
async def test_inbound_post_preserves_code_between_text_and_mentions(converter):
api_client = MagicMock()
chain = await converter.target2yiri(
post_message(
[
[{'tag': 'at', 'user_id': 'ou_bot', 'user_name': 'Bot'}, {'tag': 'text', 'text': 'Explain: '}],
[{'tag': 'code_block', 'language': 'PYTHON', 'text': 'print(1 + 2)'}],
[{'tag': 'text', 'text': ' What is the result?'}],
]
),
api_client,
)
assert [type(component) for component in chain] == [
platform_message.Source,
platform_message.At,
platform_message.Plain,
platform_message.Plain,
platform_message.Plain,
]
assert [component.text for component in chain if isinstance(component, platform_message.Plain)] == [
'Explain: ',
'print(1 + 2)',
' What is the result?',
]
assert api_client.mock_calls == []