mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-30 04:15:45 +08:00
Restore LangBot branding on DingTalk A2UI cards
This commit is contained in:
@@ -9,12 +9,40 @@ import typing
|
||||
A2UI_CATALOG_ID = 'https://dingtalk.com/card/a2ui/catalogs/public/catalog.json'
|
||||
A2UI_MESSAGE_VERSION = 'v1.0'
|
||||
A2UI_PROTOCOL_VERSION = '1.0'
|
||||
LANGBOT_LOGO_URL = 'https://langbot.app/logo-blue.png'
|
||||
LANGBOT_CARD_URL = 'https://langbot.app?utm_source=dingtalk&utm_medium=bot_card&utm_campaign=langbot'
|
||||
|
||||
|
||||
def binding(path: str) -> dict[str, str]:
|
||||
return {'path': path}
|
||||
|
||||
|
||||
def brand_components() -> list[dict[str, typing.Any]]:
|
||||
return [
|
||||
{
|
||||
'id': 'langbot-brand',
|
||||
'component': 'Row',
|
||||
'children': ['langbot-brand-icon', 'langbot-brand-link'],
|
||||
'align': 'center',
|
||||
'gap': 4,
|
||||
},
|
||||
{
|
||||
'id': 'langbot-brand-icon',
|
||||
'component': 'Image',
|
||||
'url': LANGBOT_LOGO_URL,
|
||||
'variant': 'icon',
|
||||
'fit': 'contain',
|
||||
'previewEnabled': False,
|
||||
},
|
||||
{
|
||||
'id': 'langbot-brand-link',
|
||||
'component': 'Link',
|
||||
'text': 'LangBot',
|
||||
'action': {'functionCall': {'call': 'openUrl', 'args': {'url': LANGBOT_CARD_URL}}},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def serialize_messages(messages: typing.Iterable[dict | str]) -> list[str]:
|
||||
serialized = [
|
||||
message if isinstance(message, str) else json.dumps(message, ensure_ascii=False, separators=(',', ':'))
|
||||
@@ -95,7 +123,7 @@ def text_card_components() -> list[dict[str, typing.Any]]:
|
||||
{
|
||||
'id': 'root',
|
||||
'component': 'Column',
|
||||
'children': ['content'],
|
||||
'children': ['content', 'langbot-brand'],
|
||||
'gap': 8,
|
||||
},
|
||||
{
|
||||
@@ -103,6 +131,7 @@ def text_card_components() -> list[dict[str, typing.Any]]:
|
||||
'component': 'Markdown',
|
||||
'content': binding('/content'),
|
||||
},
|
||||
*brand_components(),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import time
|
||||
import typing
|
||||
import uuid
|
||||
|
||||
from langbot.libs.dingtalk_api.a2ui import binding, create_surface_messages, update_surface_messages
|
||||
from langbot.libs.dingtalk_api.a2ui import brand_components, binding, create_surface_messages, update_surface_messages
|
||||
from langbot.libs.dingtalk_api.dingtalkevent import DingTalkEvent
|
||||
from langbot_plugin.api.entities.builtin.platform import events as platform_events
|
||||
|
||||
@@ -150,6 +150,8 @@ def _build_interaction_messages(
|
||||
)
|
||||
children.append(button_id)
|
||||
|
||||
children.append('langbot-brand')
|
||||
components.extend(brand_components())
|
||||
components.insert(0, {'id': 'root', 'component': 'Column', 'children': children, 'gap': 12})
|
||||
return {'form': form}, components, field_types
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from langbot.libs.dingtalk_api.a2ui import (
|
||||
brand_components,
|
||||
binding,
|
||||
create_surface_messages,
|
||||
create_text_card_messages,
|
||||
@@ -519,6 +520,8 @@ def _dingtalk_a2ui_form(
|
||||
]
|
||||
)
|
||||
children.append(button_id)
|
||||
children.append('langbot-brand')
|
||||
components.extend(brand_components())
|
||||
return data, components
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ def test_dingtalk_dify_input_card_uses_a2ui_binding_and_required_check():
|
||||
assert field['value'] == {'path': '/form/value'}
|
||||
assert button['action']['event']['context']['input'] == {'path': '/form/value'}
|
||||
assert button['checks'][0]['condition']['call'] == 'required'
|
||||
assert components[0]['children'][-1] == 'langbot-brand'
|
||||
assert any(component['id'] == 'langbot-brand-link' for component in components)
|
||||
|
||||
|
||||
def test_dingtalk_extract_select_from_builtin_result_dict():
|
||||
|
||||
@@ -31,6 +31,21 @@ def _response(result=None):
|
||||
return type('Response', (), {'status_code': 200, 'json': lambda self: payload})()
|
||||
|
||||
|
||||
def test_text_card_keeps_brand_when_content_updates():
|
||||
components = create_text_card_messages('surface-id', 'hello')[2]['updateComponents']['components']
|
||||
by_id = {component['id']: component for component in components}
|
||||
|
||||
assert by_id['root']['children'] == ['content', 'langbot-brand']
|
||||
assert by_id['langbot-brand']['children'] == ['langbot-brand-icon', 'langbot-brand-link']
|
||||
assert by_id['langbot-brand-icon']['url'] == 'https://langbot.app/logo-blue.png'
|
||||
assert by_id['langbot-brand-icon']['previewEnabled'] is False
|
||||
assert by_id['langbot-brand-link']['action']['functionCall'] == {
|
||||
'call': 'openUrl',
|
||||
'args': {'url': 'https://langbot.app?utm_source=dingtalk&utm_medium=bot_card&utm_campaign=langbot'},
|
||||
}
|
||||
assert update_text_card_messages('surface-id', 'done')[0]['updateDataModel']['path'] == '/content'
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_ai_card_uses_group_robot_delivery_model():
|
||||
post = AsyncMock(
|
||||
|
||||
@@ -342,6 +342,8 @@ async def test_dingtalk_interaction_delivery_and_callback_event():
|
||||
components = kwargs['a2ui_messages'][2]['updateComponents']['components']
|
||||
button = next(component for component in components if component['component'] == 'Button')
|
||||
assert button['action']['event']['context']['actionId'] == 'lbi:callback-token:a:0'
|
||||
assert components[0]['children'][-1] == 'langbot-brand'
|
||||
assert any(component['id'] == 'langbot-brand-link' for component in components)
|
||||
|
||||
native = dingtalk_card_callback_event(
|
||||
content={'actionId': 'lbi:callback-token:a:0'},
|
||||
|
||||
Reference in New Issue
Block a user