diff --git a/scripts/build_dingtalk_card_template.py b/scripts/build_dingtalk_card_template.py index d419e5b9..01e91492 100644 --- a/scripts/build_dingtalk_card_template.py +++ b/scripts/build_dingtalk_card_template.py @@ -35,7 +35,7 @@ def markdown_block(node_id, variable='content'): 'valueType': 'fixed', 'condition': {'op': 'and', 'conditions': []}, }, - 'isStreaming': False, + 'isStreaming': True, 'enableLinkStatPoint': False, 'linkStatPoint': {'type': 'dynamicString', 'content': '', 'i18n': False}, 'linkStatPointParams': [], @@ -552,6 +552,7 @@ def build_editor_data(): return { 'schemaVersion': '3.0.0', 'schema': { + 'config': {'update_multi': True, 'streaming_mode': True}, 'componentsMap': components_map, 'componentsTree': [root], 'i18n': {}, diff --git a/src/langbot/libs/dingtalk_api/api.py b/src/langbot/libs/dingtalk_api/api.py index c5154530..b38fafdd 100644 --- a/src/langbot/libs/dingtalk_api/api.py +++ b/src/langbot/libs/dingtalk_api/api.py @@ -760,36 +760,6 @@ class DingTalkClient: await self.logger.error(f'DingTalk update card error: {traceback.format_exc()}') return False - async def delete_card(self, *, out_track_id: str) -> bool: - """POST /v1.0/card/instances/delete — recall a delivered card. - - Used to retroactively remove the initial streaming chat card when - the workflow turns out to be paused for human input — the prompt - and buttons then live entirely on the dedicated form card. - """ - if not await self.check_access_token(): - await self.get_access_token() - - url = f'{DINGTALK_OPENAPI_BASE}/v1.0/card/instances/delete' - headers = { - 'x-acs-dingtalk-access-token': self.access_token, - 'Content-Type': 'application/json', - } - body = {'outTrackId': out_track_id, 'userIdType': 1} - try: - _stdout_logger.info('DingTalk delete_card request: out_track_id=%s', out_track_id) - async with httpx.AsyncClient() as client: - response = await client.post(url, headers=headers, json=body, timeout=30.0) - _stdout_logger.info( - 'DingTalk delete_card response: status=%d body=%s', - response.status_code, - response.text[:300], - ) - return response.status_code == 200 - except Exception: - _stdout_logger.exception('DingTalk delete_card error') - return False - async def start(self): """启动 WebSocket 连接,监听消息""" self._stopped = False diff --git a/src/langbot/pkg/platform/sources/dingtalk.py b/src/langbot/pkg/platform/sources/dingtalk.py index 4288fd0a..2a31a116 100644 --- a/src/langbot/pkg/platform/sources/dingtalk.py +++ b/src/langbot/pkg/platform/sources/dingtalk.py @@ -181,6 +181,15 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): # user_id_hint, current_text}. Lookup keys for the data-source pull endpoint and # the STREAM card-action callback. card_state: dict + # session_key → out_track_id of the currently-active card for the + # conversation turn. Lets resumed-workflow chunks (which arrive on a + # synthetic event with a fresh resp_message_id) keep updating the same + # card the user clicked instead of getting a new one. + active_turn_card: dict + # session_key → accumulated streaming text for the active turn. Read + # by _paint_form_on_card so the post-pause form keeps the streamed + # context above the new prompt. + active_turn_text: dict ap: typing.Any = None bot_uuid: str = '' @@ -208,6 +217,8 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): logger=logger, card_instance_id_dict={}, card_state={}, + active_turn_card={}, + active_turn_text={}, bot_account_id=bot_account_id, bot=bot, listeners={}, @@ -281,11 +292,14 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): card_instance, card_instance_id = chat_card_entry if content: if form_template_id: - # The form template's MarkdownBlock has `isStreaming: false` - # — the streaming endpoint (PUT /v1.0/card/streaming) does - # not propagate to non-streaming components. Use the full - # update_card_data PUT instead so the content actually - # appears in the card body. + # The card content has already been written via + # update_card_data (in _paint_form_on_card and the + # initial card creation). The streaming endpoint + # (PUT /v1.0/card/streaming) does not propagate + # updates on cards whose content was last set via + # update_card_data — they take different code paths + # on the DingTalk client. Stick with update_card_data + # for the whole turn for consistency. try: await self.bot.update_card_data( out_track_id=card_instance_id, @@ -329,30 +343,54 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): return is_stream async def create_message_card(self, message_id, event): - # When a form template is configured, every card in the conversation - # uses it (chat output, form prompts, post-click states). The chat - # template fallback only kicks in if no form template is configured. form_template_id = (self.config.get('human_input_card_template_id') or '').strip() legacy_template_id = self.config.get('card_template_id', '') - # Synthetic events (e.g. card button clicks) have no inbound chatbot - # message — skip card creation. The lazy-create path in - # reply_message_chunk will spawn a fresh card when the first - # non-empty resume chunk arrives. + # Synthetic events (button clicks): look up the card already in + # active_turn_card so reply_message_chunk can stream to it. if event is None or event.source_platform_object is None: + if form_template_id: + session_key = self._session_key_from_event(event) if event is not None else '' + carry = self.active_turn_card.get(session_key, '') if session_key else '' + if carry: + self.card_instance_id_dict[message_id] = (None, carry) + return True return False if form_template_id: - # Defer card creation to the first non-empty chunk. If the Dify - # workflow pauses immediately for human input without producing - # any LLM text first, no chat card is created at all — only the - # form card gets delivered. Lazy-create lives in - # reply_message_chunk → _lazy_create_resume_chat_card. - return False + # Create one card with the form template, empty buttons, + # pending state. Streaming writes content to it; form pause + # paints buttons onto it. One card per turn, no duplication. + incoming_message = event.source_platform_object.incoming_message + out_track_id = uuid.uuid4().hex + is_group = str(incoming_message.conversation_type) == '2' + if is_group: + open_space_id = f'dtv1.card//IM_GROUP.{incoming_message.conversation_id}' + else: + open_space_id = f'dtv1.card//IM_ROBOT.{incoming_message.sender_staff_id}' + try: + await self.bot.create_and_deliver_card( + card_template_id=form_template_id, + out_track_id=out_track_id, + open_space_id=open_space_id, + is_group=is_group, + card_param_map={'content': '', 'btns': '[]', 'flowStatus': '1'}, + callback_type='STREAM', + ) + except Exception: + if self.ap is not None: + self.ap.logger.exception('DingTalk: create form-template card failed') + return False + self.card_instance_id_dict[message_id] = (None, out_track_id) + session_key = self._session_key_from_event(event) + if session_key: + self.active_turn_card[session_key] = out_track_id + self.active_turn_text[session_key] = '' + return True - # Legacy chat-card path (no form template configured). + # Legacy chat-card path (no form template). incoming_message = event.source_platform_object.incoming_message - card_auto_layout = self.config.get('card_ auto_layout', False) + card_auto_layout = self.config.get('card_auto_layout', False) card_instance, card_instance_id = await self.bot.create_and_card( legacy_template_id, incoming_message, card_auto_layout=card_auto_layout ) @@ -460,12 +498,12 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): message: platform_message.MessageChain, form_data: dict, ) -> None: - """Finalize the current chat card and deliver a new form card. + """Surface human-input prompt + buttons on the active card. - Multi-card flow: every Dify pause spawns its own card. The card the - chat was streaming into (if any) is closed out via streaming_update - with finished=True so its spinner stops; a fresh card is then - delivered carrying the prompt + buttons. + In single-card mode (form_template_id configured): update the + EXISTING card with form buttons so it transitions from streaming + output to prompt+buttons on the same card. In legacy mode: + finalize the chat card and deliver a separate form card. """ if self.ap is not None: self.ap.logger.info( @@ -475,9 +513,25 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): message_id = bot_message.resp_message_id template_id = (self.config.get('human_input_card_template_id') or '').strip() - # Finalize the previous chat card so its spinner stops. Use the - # already-streamed text as the final content (or zero-width space - # when nothing streamed, to satisfy any non-empty-content guards). + if template_id: + # Single-card mode: paint prompt + buttons onto the existing card. + session_key = self._session_key_from_event(message_source) + entry = self.card_instance_id_dict.get(message_id) + out_track_id = entry[1] if entry else None + if not out_track_id and session_key: + out_track_id = self.active_turn_card.get(session_key, '') + if out_track_id: + await self._paint_form_on_card(message_source, out_track_id, form_data, session_key) + self.card_instance_id_dict.pop(message_id, None) + return + + # No existing card (e.g. Dify paused immediately with no LLM + # output before the pause). Create a form card directly. + await self._send_form_card(message_source, form_data, template_id) + self.card_instance_id_dict.pop(message_id, None) + return + + # Legacy mode: finalize the streaming card with text fallback. chat_card_entry = self.card_instance_id_dict.pop(message_id, None) if chat_card_entry is not None: _, chat_out_track_id = chat_card_entry @@ -489,24 +543,93 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): await self.bot.send_card_message(None, chat_out_track_id, text_content or '​', True) except Exception: await self.logger.error(f'DingTalk: finalize chat card before form failed: {traceback.format_exc()}') - # When the chat card uses the form template, also flip flowStatus - # to 3 so it leaves the pending state visibly. - if template_id: - try: - await self.bot.update_card_data( - out_track_id=chat_out_track_id, - card_param_map={'flowStatus': '3'}, - ) - except Exception: - pass - if not template_id: - # No form template configured — fall back to plain text so users - # can still reply with the option number or title. + await self.send_message_text_form(message_source, form_data) + + async def _paint_form_on_card( + self, + message_source: platform_events.MessageEvent, + out_track_id: str, + form_data: dict, + session_key: str, + ) -> None: + """Update an existing card's content + buttons for human-input.""" + actions = list(form_data.get('actions') or []) + node_title = form_data.get('node_title', '') or 'Human Input Required' + form_content = form_data.get('form_content', '') or '' + + # Record form state for the click-handler. + launcher_type, launcher_id, sender_user_id = self._derive_session_descriptor(message_source) + self.card_state[out_track_id] = { + 'session_key': session_key, + 'launcher_type': launcher_type.value, + 'launcher_id': launcher_id, + 'sender_user_id': sender_user_id, + 'form_token': form_data.get('form_token', ''), + 'workflow_run_id': form_data.get('workflow_run_id', ''), + 'actions': actions, + 'node_title': node_title, + 'form_content': form_content, + } + + btns = self._build_btns(actions, out_track_id) + parts: list[str] = [] + prior = self.active_turn_text.get(session_key, '') if session_key else '' + if prior.strip(): + parts.append(prior.rstrip()) + parts.append('---') + if node_title: + parts.append(f'**{node_title}**') + if form_content: + parts.append(form_content) + display_content = '\n\n'.join(parts) or '请选择一个操作以继续。' + + try: + await self.bot.update_card_data( + out_track_id=out_track_id, + card_param_map={ + 'content': display_content, + 'btns': json.dumps(btns, ensure_ascii=False), + 'flowStatus': '3', + }, + ) + except Exception: + if self.ap is not None: + self.ap.logger.exception('DingTalk: paint form on card failed') await self.send_message_text_form(message_source, form_data) return - await self._send_form_card(message_source, form_data, template_id) + if session_key: + self.active_turn_text[session_key] = display_content + + @staticmethod + def _build_btns(actions: list, out_track_id: str) -> list: + btns = [] + for idx, action in enumerate(actions): + action_id = str(action.get('id') or '') + title = str(action.get('title') or action_id or f'选项 {idx + 1}') + style = (action.get('button_style') or '').lower() + if style == 'primary' or (style == '' and idx == 0): + color = 'blue' + elif style == 'danger': + color = 'red' + else: + color = 'gray' + btns.append( + { + 'text': title, + 'color': color, + 'status': 'normal', + 'event': { + 'type': 'sendCardRequest', + 'params': { + 'actionId': action_id, + 'params': {'action_id': action_id, 'out_track_id': out_track_id}, + }, + }, + } + ) + return btns async def _send_form_card( self, @@ -601,11 +724,9 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ) -> typing.Optional[tuple]: """Create a new card for resumed-workflow streaming output. - Used after a button click triggers a synthetic event — no inbound - chatbot message means no card was created upstream, so we spin one - up here when the first non-empty chunk arrives. Prefers the form - template (so empty `btns` keep the layout consistent across the - whole conversation); falls back to the legacy chat template. + Used after a button click triggers a synthetic event — the form + card stays put with the "已选择" notice, and a fresh card is + spawned here for the LLM reply to stream into. """ form_template_id = (self.config.get('human_input_card_template_id') or '').strip() legacy_template_id = (self.config.get('card_template_id') or '').strip() @@ -614,12 +735,6 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): return None out_track_id = uuid.uuid4().hex open_space_id, is_group = self._derive_open_space(message_source) - if self.ap is not None: - self.ap.logger.info( - f'DingTalk _lazy_create_resume_chat_card: out_track_id={out_track_id} ' - f'open_space_id={open_space_id} is_group={is_group} ' - f'using_form_template={bool(form_template_id)}' - ) if form_template_id: card_param_map = {'content': '', 'btns': '[]', 'flowStatus': '1'} card_data_config = None @@ -644,6 +759,12 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): return None entry = (None, out_track_id) self.card_instance_id_dict[message_id] = entry + # Register as the active card so any further chunks on this turn + # (and a subsequent re-pause) land on the same new card. + session_key = self._session_key_from_event(message_source) + if session_key: + self.active_turn_card[session_key] = out_track_id + self.active_turn_text[session_key] = '' return entry async def send_message_text_form( @@ -813,8 +934,11 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): self.ap.logger.exception('DingTalk: enqueue form action query failed') return - # Visual feedback: collapse the form card to a "已选择" notice so - # the user knows the click registered while the workflow resumes. + # Visual feedback on the form card itself: keep the prompt visible, + # add a "已选择" line, remove the buttons. The resumed-workflow + # output lives on a separate new card (lazy-created in + # reply_message_chunk on the synthetic event), so the form card + # stays put as a record of the user's selection. asyncio.create_task( self._mark_card_resolved( out_track_id, @@ -824,6 +948,16 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ) ) + # Crucial: do NOT leave the form card's out_track_id in + # active_turn_card — otherwise create_message_card for the + # synthetic event would reuse it for the resume output, painting + # the LLM reply on top of the "已选择" notice. Clear it so the + # resume goes through the lazy-create path and spawns a fresh card. + session_key = state.get('session_key', '') + if session_key and self.active_turn_card.get(session_key) == out_track_id: + self.active_turn_card.pop(session_key, None) + self.active_turn_text.pop(session_key, None) + # Once consumed, drop the state — the runner clears _PENDING_FORMS too. self.card_state.pop(out_track_id, None) @@ -837,12 +971,9 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): ) -> None: """Update the form card to acknowledge the user's selection. - We rewrite the card content with the original prompt + a green tick - marker, and explicitly clear ``btns`` so the buttons are removed - once chosen. ``flowStatus`` is re-sent because some DingTalk clients - treat the PUT update as a partial *replace* of cardParamMap rather - than a merge — without it, the AICardContainer status containers - would all gate to ``gone`` and the whole card would blank out. + Keeps the original prompt visible, adds a "已选择: X" notice, and + clears the buttons. The card stays as a permanent record of the + choice; the resumed workflow's output goes to a separate new card. """ parts: list[str] = [] if node_title: @@ -863,4 +994,5 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter): }, ) except Exception: - await self.logger.error(f'DingTalk: update form card after click failed: {traceback.format_exc()}') + if self.ap is not None: + self.ap.logger.exception('DingTalk: mark card resolved failed') diff --git a/src/langbot/templates/dingtalk_human_input_card.json b/src/langbot/templates/dingtalk_human_input_card.json index 4c8e1f1a..fec2cef0 100644 --- a/src/langbot/templates/dingtalk_human_input_card.json +++ b/src/langbot/templates/dingtalk_human_input_card.json @@ -1,5 +1,5 @@ { - "editorData": "{\"schemaVersion\":\"3.0.0\",\"schema\":{\"componentsMap\":[{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AIPending\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AIPending\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardStatusContainer\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardStatusContainer\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"BaseText\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"BaseText\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardContent\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardContent\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardContainer\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardContainer\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"ButtonGroup\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"ButtonGroup\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"MarkdownBlock\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"MarkdownBlock\"}],\"componentsTree\":[{\"componentName\":\"AICardContainer\",\"id\":\"node_root\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enablePending\":true,\"enableWriting\":false,\"enableDoing\":false,\"enableFailed\":true,\"summaryContent\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"\"},\"enableTitle\":false,\"flowStatusVar\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"flowStatus\"},\"operationPenalType\":\"custom\",\"enableFlowAbort\":true,\"innerOffset\":0,\"enableGradientBorder\":true,\"cardSizeMode\":\"adaptive\",\"cardSizeHeightMode\":\"adaptive\",\"cardSizeWidthMode\":\"adaptive\",\"cardSizeHeight\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":226,\"variable\":\"\",\"variableType\":\"global\"},\"hasBackground\":false,\"backgroundType\":\"Standard\",\"standardBackgroundColor\":\"gray\",\"backgroundColor\":\"#F6F6F6\",\"darkModeBackgroundColor\":\"#3C3C3C\",\"enableEngineUpgrade\":false,\"enableExposeStatPoint\":false,\"enableDebugTool\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_pending\",\"props\":{\"status\":1,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"处理中状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AIPending\",\"id\":\"node_pending_inner\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"pendingTip\":{\"type\":\"dynamicString\",\"content\":\"处理中...\",\"i18n\":false},\"style\":\"embed\",\"hideIcon\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]},{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_done\",\"props\":{\"status\":3,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"完成状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardContent\",\"id\":\"node_done_content\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"innerOffset\":0,\"disabledWhileForward\":false,\"statPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"statPointParams\":[{\"type\":\"fixed\",\"variable\":\"\",\"value\":\"\",\"name\":\"\",\"variableType\":\"global\",\"id\":\"1\"}],\"margin\":-2,\"transformToEventChain\":false,\"enableStatPoint\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"MarkdownBlock\",\"id\":\"node_text_content\",\"props\":{\"mdVer\":0,\"icon\":{\"type\":\"icon\",\"icon\":\"\",\"iconType\":\"emoji\"},\"content\":{\"variable\":\"content\",\"variableType\":\"global\",\"type\":\"variableValue\"},\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"isStreaming\":false,\"enableLinkStatPoint\":false,\"linkStatPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"linkStatPointParams\":[],\"marginTop\":6,\"marginBottom\":6,\"marginLeft\":12,\"marginRight\":12},\"title\":\"AI 流式富文本\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"},{\"componentName\":\"ButtonGroup\",\"id\":\"node_btn_group\",\"props\":{\"dynamicButtons\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"btns\"},\"marginLeft\":12,\"marginRight\":12,\"marginTop\":6,\"marginBottom\":12,\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"responsiveLayoutWidth\":350,\"buttonsSource\":\"variable\",\"fixedButtonIds\":[],\"fixedButtons\":[],\"enableResponsiveLayout\":false,\"matchContent\":false,\"buttonSpacing\":8,\"margin\":-2,\"innerOffset\":0},\"title\":\"按钮组\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]}]},{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_failed\",\"props\":{\"status\":5,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"失败状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardContent\",\"id\":\"node_failed_content\",\"props\":{\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"innerOffset\":0,\"disabledWhileForward\":false,\"statPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"statPointParams\":[{\"type\":\"fixed\",\"variable\":\"\",\"value\":\"\",\"name\":\"\",\"variableType\":\"global\",\"id\":\"1\"}],\"margin\":-2,\"transformToEventChain\":false,\"enableStatPoint\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"BaseText\",\"id\":\"node_failed_text\",\"props\":{\"text\":{\"i18n\":false,\"type\":\"dynamicString\",\"content\":\"操作失败,请稍后重试。\"},\"hoverText\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"iconType\":\"iconCode\",\"iconFont\":{\"type\":\"icon\",\"icon\":\"\",\"iconType\":\"ddIcon\"},\"icon\":{\"type\":\"dynamicLink\",\"value\":\"\",\"valueType\":\"fixed\",\"variable\":\"\",\"variableType\":\"global\"},\"darkIcon\":{\"type\":\"dynamicLink\",\"value\":\"\",\"valueType\":\"fixed\",\"variable\":\"\",\"variableType\":\"global\"},\"autoWidth\":false,\"maxWidth\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":0,\"variable\":\"\",\"variableType\":\"global\"},\"fixedWidth\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":0,\"variable\":\"\",\"variableType\":\"global\"},\"marginLeft\":10,\"marginRight\":10,\"marginTop\":10,\"marginBottom\":10,\"fontColorType\":\"Standard\",\"enableHighlight\":false,\"maxLine\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":2,\"variable\":\"\",\"variableType\":\"global\"},\"color\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"common_level1_base_color\",\"variable\":\"\",\"variableType\":\"global\"},\"customLightColor\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"#35404b\",\"variable\":\"\",\"variableType\":\"global\"},\"customDarkColor\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"#f6f6f6\",\"variable\":\"\",\"variableType\":\"global\"},\"gravity\":\"center\",\"fontSizeType\":\"Standard\",\"styleType\":\"custom\",\"styleToken\":\"common_body_text_style\",\"size\":\"middle\",\"customFontSize\":15,\"customFontLineHeight\":22,\"bold\":false,\"italic\":false,\"strikeThrough\":false,\"lineHeight\":\"normal\",\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"autoMaxWidth\":false,\"innerOffset\":0,\"enableIcon\":false,\"widthMode\":\"match_parent\",\"margin\":-2},\"title\":\"基础文本\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]}]}]}],\"i18n\":{},\"version\":\"1.0.0\"},\"mockData\":{\"cardData\":{\"flowStatus\":3,\"content\":\"请审核以下报销申请:\\n\\n- 申请人:张三\\n- 金额:¥1,200\\n- 类别:差旅\",\"btns\":[{\"text\":\"通过\",\"color\":\"blue\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"approve\",\"params\":{\"action_id\":\"approve\"}}}},{\"text\":\"驳回\",\"color\":\"gray\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"reject\",\"params\":{\"action_id\":\"reject\"}}}},{\"text\":\"补充资料\",\"color\":\"gray\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"more_info\",\"params\":{\"action_id\":\"more_info\"}}}}]},\"cardPrivateData\":{},\"localData\":{\"flowStatus\":\"\",\"_cardFoldStatusLocalDataKey\":\"\"},\"richTextData\":{}},\"renderContext\":{\"regenerateEnabled\":\"1\",\"regenerateIndex\":\"2\",\"regenerateTotal\":\"5\"},\"editVersion\":0,\"customWidgetInfo\":\"\",\"useCustomWidgetInfo\":false,\"variableList\":[{\"id\":\"content\",\"type\":\"markdown\",\"name\":\"content\",\"description\":\"人工输入提示词(Dify form_content 含可选 node_title 前缀)\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":false},{\"id\":\"flowStatus\",\"type\":\"string\",\"name\":\"flowStatus\",\"description\":\"AI卡片状态:pending(1)、writing(2)、done(3)、failed(5)\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"visible\":false},{\"name\":\"btns\",\"private\":false,\"type\":\"buttonGroup\",\"id\":\"btns\",\"description\":\"动态按钮列表(Dify actions)\",\"editorVarType\":\"variables\",\"disabled\":false,\"schema\":[{\"id\":\"btns.text\",\"type\":\"string\",\"name\":\"text\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮文案\"},{\"id\":\"btns.color\",\"type\":\"string\",\"name\":\"color\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮颜色\"},{\"id\":\"btns.status\",\"type\":\"string\",\"name\":\"status\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮状态\"},{\"id\":\"btns.event\",\"type\":\"dynamicEvent\",\"name\":\"event\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮点击事件\",\"schema\":[{\"id\":\"btns.type\",\"type\":\"string\",\"name\":\"type\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"事件类型:openLink / sendCardRequest\"},{\"id\":\"btns.params\",\"type\":\"object\",\"name\":\"params\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"事件参数\",\"schema\":[{\"id\":\"btns.url\",\"type\":\"string\",\"name\":\"url\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"点击跳转链接(type=openLink)\"},{\"id\":\"btns.actionId\",\"type\":\"string\",\"name\":\"actionId\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"回传请求 id(type=sendCardRequest)\"},{\"id\":\"btns.params\",\"type\":\"object\",\"name\":\"params\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"回传请求参数(type=sendCardRequest)\"}]}]}]}],\"formList\":[],\"customContextList\":[],\"expList\":[],\"localList\":[],\"hsfList\":[],\"lwpList\":[],\"pageData\":{},\"extension\":{\"extendType\":\"AI\",\"aiStatusList\":[3,1,5],\"fileTypeList\":[]}}", + "editorData": "{\"schemaVersion\":\"3.0.0\",\"schema\":{\"componentsMap\":[{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AIPending\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AIPending\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardStatusContainer\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardStatusContainer\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"BaseText\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"BaseText\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardContent\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardContent\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"AICardContainer\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"AICardContainer\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"ButtonGroup\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"ButtonGroup\"},{\"package\":\"@ali/dxComponent\",\"version\":\"1.0.0\",\"exportName\":\"MarkdownBlock\",\"main\":\"./src/index.tsx\",\"destructuring\":false,\"subName\":\"\",\"componentName\":\"MarkdownBlock\"}],\"componentsTree\":[{\"componentName\":\"AICardContainer\",\"id\":\"node_root\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enablePending\":true,\"enableWriting\":false,\"enableDoing\":false,\"enableFailed\":true,\"summaryContent\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"\"},\"enableTitle\":false,\"flowStatusVar\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"flowStatus\"},\"operationPenalType\":\"custom\",\"enableFlowAbort\":true,\"innerOffset\":0,\"enableGradientBorder\":true,\"cardSizeMode\":\"adaptive\",\"cardSizeHeightMode\":\"adaptive\",\"cardSizeWidthMode\":\"adaptive\",\"cardSizeHeight\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":226,\"variable\":\"\",\"variableType\":\"global\"},\"hasBackground\":false,\"backgroundType\":\"Standard\",\"standardBackgroundColor\":\"gray\",\"backgroundColor\":\"#F6F6F6\",\"darkModeBackgroundColor\":\"#3C3C3C\",\"enableEngineUpgrade\":false,\"enableExposeStatPoint\":false,\"enableDebugTool\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_pending\",\"props\":{\"status\":1,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"处理中状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AIPending\",\"id\":\"node_pending_inner\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"pendingTip\":{\"type\":\"dynamicString\",\"content\":\"处理中...\",\"i18n\":false},\"style\":\"embed\",\"hideIcon\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]},{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_done\",\"props\":{\"status\":3,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"完成状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardContent\",\"id\":\"node_done_content\",\"props\":{\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"innerOffset\":0,\"disabledWhileForward\":false,\"statPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"statPointParams\":[{\"type\":\"fixed\",\"variable\":\"\",\"value\":\"\",\"name\":\"\",\"variableType\":\"global\",\"id\":\"1\"}],\"margin\":-2,\"transformToEventChain\":false,\"enableStatPoint\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"MarkdownBlock\",\"id\":\"node_text_content\",\"props\":{\"mdVer\":0,\"icon\":{\"type\":\"icon\",\"icon\":\"\",\"iconType\":\"emoji\"},\"content\":{\"variable\":\"content\",\"variableType\":\"global\",\"type\":\"variableValue\"},\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"isStreaming\":true,\"enableLinkStatPoint\":false,\"linkStatPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"linkStatPointParams\":[],\"marginTop\":6,\"marginBottom\":6,\"marginLeft\":12,\"marginRight\":12},\"title\":\"AI 流式富文本\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"},{\"componentName\":\"ButtonGroup\",\"id\":\"node_btn_group\",\"props\":{\"dynamicButtons\":{\"type\":\"variableValue\",\"variableType\":\"global\",\"variable\":\"btns\"},\"marginLeft\":12,\"marginRight\":12,\"marginTop\":6,\"marginBottom\":12,\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"responsiveLayoutWidth\":350,\"buttonsSource\":\"variable\",\"fixedButtonIds\":[],\"fixedButtons\":[],\"enableResponsiveLayout\":false,\"matchContent\":false,\"buttonSpacing\":8,\"margin\":-2,\"innerOffset\":0},\"title\":\"按钮组\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]}]},{\"componentName\":\"AICardStatusContainer\",\"id\":\"node_status_failed\",\"props\":{\"status\":5,\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"enableExtend\":false,\"autoFoldConfig\":{\"needFold\":true,\"heightLimit\":480,\"foldStatusLocalDataKey\":\"_cardFoldStatusLocalDataKey\"},\"innerOffset\":0,\"enableCollapse\":false,\"margin\":-2},\"title\":\"失败状态\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"AICardContent\",\"id\":\"node_failed_content\",\"props\":{\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"marginLeft\":0,\"marginRight\":0,\"marginTop\":0,\"marginBottom\":0,\"innerOffset\":0,\"disabledWhileForward\":false,\"statPoint\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"statPointParams\":[{\"type\":\"fixed\",\"variable\":\"\",\"value\":\"\",\"name\":\"\",\"variableType\":\"global\",\"id\":\"1\"}],\"margin\":-2,\"transformToEventChain\":false,\"enableStatPoint\":false},\"hidden\":false,\"title\":\"\",\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\",\"children\":[{\"componentName\":\"BaseText\",\"id\":\"node_failed_text\",\"props\":{\"text\":{\"i18n\":false,\"type\":\"dynamicString\",\"content\":\"操作失败,请稍后重试。\"},\"hoverText\":{\"type\":\"dynamicString\",\"content\":\"\",\"i18n\":false},\"iconType\":\"iconCode\",\"iconFont\":{\"type\":\"icon\",\"icon\":\"\",\"iconType\":\"ddIcon\"},\"icon\":{\"type\":\"dynamicLink\",\"value\":\"\",\"valueType\":\"fixed\",\"variable\":\"\",\"variableType\":\"global\"},\"darkIcon\":{\"type\":\"dynamicLink\",\"value\":\"\",\"valueType\":\"fixed\",\"variable\":\"\",\"variableType\":\"global\"},\"autoWidth\":false,\"maxWidth\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":0,\"variable\":\"\",\"variableType\":\"global\"},\"fixedWidth\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":0,\"variable\":\"\",\"variableType\":\"global\"},\"marginLeft\":10,\"marginRight\":10,\"marginTop\":10,\"marginBottom\":10,\"fontColorType\":\"Standard\",\"enableHighlight\":false,\"maxLine\":{\"type\":\"dynamicNumber\",\"valueType\":\"fixed\",\"value\":2,\"variable\":\"\",\"variableType\":\"global\"},\"color\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"common_level1_base_color\",\"variable\":\"\",\"variableType\":\"global\"},\"customLightColor\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"#35404b\",\"variable\":\"\",\"variableType\":\"global\"},\"customDarkColor\":{\"type\":\"dynamicColor\",\"valueType\":\"fixed\",\"value\":\"#f6f6f6\",\"variable\":\"\",\"variableType\":\"global\"},\"gravity\":\"center\",\"fontSizeType\":\"Standard\",\"styleType\":\"custom\",\"styleToken\":\"common_body_text_style\",\"size\":\"middle\",\"customFontSize\":15,\"customFontLineHeight\":22,\"bold\":false,\"italic\":false,\"strikeThrough\":false,\"lineHeight\":\"normal\",\"visible\":{\"type\":\"dynamicVisible\",\"value\":true,\"valueType\":\"fixed\",\"condition\":{\"op\":\"and\",\"conditions\":[]}},\"autoMaxWidth\":false,\"innerOffset\":0,\"enableIcon\":false,\"widthMode\":\"match_parent\",\"margin\":-2},\"title\":\"基础文本\",\"hidden\":false,\"isLocked\":false,\"condition\":true,\"conditionGroup\":\"\"}]}]}]}],\"i18n\":{},\"version\":\"1.0.0\",\"config\":{\"update_multi\":true,\"streaming_mode\":true}},\"mockData\":{\"cardData\":{\"flowStatus\":3,\"content\":\"请审核以下报销申请:\\n\\n- 申请人:张三\\n- 金额:¥1,200\\n- 类别:差旅\",\"btns\":[{\"text\":\"通过\",\"color\":\"blue\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"approve\",\"params\":{\"action_id\":\"approve\"}}}},{\"text\":\"驳回\",\"color\":\"gray\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"reject\",\"params\":{\"action_id\":\"reject\"}}}},{\"text\":\"补充资料\",\"color\":\"gray\",\"status\":\"normal\",\"event\":{\"type\":\"sendCardRequest\",\"params\":{\"actionId\":\"more_info\",\"params\":{\"action_id\":\"more_info\"}}}}]},\"cardPrivateData\":{},\"localData\":{\"flowStatus\":\"\",\"_cardFoldStatusLocalDataKey\":\"\"},\"richTextData\":{}},\"renderContext\":{\"regenerateEnabled\":\"1\",\"regenerateIndex\":\"2\",\"regenerateTotal\":\"5\"},\"editVersion\":0,\"customWidgetInfo\":\"\",\"useCustomWidgetInfo\":false,\"variableList\":[{\"id\":\"content\",\"type\":\"markdown\",\"name\":\"content\",\"description\":\"人工输入提示词(Dify form_content 含可选 node_title 前缀)\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":false},{\"id\":\"flowStatus\",\"type\":\"string\",\"name\":\"flowStatus\",\"description\":\"AI卡片状态:pending(1)、writing(2)、done(3)、failed(5)\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"visible\":false},{\"name\":\"btns\",\"private\":false,\"type\":\"buttonGroup\",\"id\":\"btns\",\"description\":\"动态按钮列表(Dify actions)\",\"editorVarType\":\"variables\",\"disabled\":false,\"schema\":[{\"id\":\"btns.text\",\"type\":\"string\",\"name\":\"text\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮文案\"},{\"id\":\"btns.color\",\"type\":\"string\",\"name\":\"color\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮颜色\"},{\"id\":\"btns.status\",\"type\":\"string\",\"name\":\"status\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮状态\"},{\"id\":\"btns.event\",\"type\":\"dynamicEvent\",\"name\":\"event\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"按钮点击事件\",\"schema\":[{\"id\":\"btns.type\",\"type\":\"string\",\"name\":\"type\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"事件类型:openLink / sendCardRequest\"},{\"id\":\"btns.params\",\"type\":\"object\",\"name\":\"params\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"事件参数\",\"schema\":[{\"id\":\"btns.url\",\"type\":\"string\",\"name\":\"url\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"点击跳转链接(type=openLink)\"},{\"id\":\"btns.actionId\",\"type\":\"string\",\"name\":\"actionId\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"回传请求 id(type=sendCardRequest)\"},{\"id\":\"btns.params\",\"type\":\"object\",\"name\":\"params\",\"private\":false,\"editorVarType\":\"variables\",\"disabled\":true,\"description\":\"回传请求参数(type=sendCardRequest)\"}]}]}]}],\"formList\":[],\"customContextList\":[],\"expList\":[],\"localList\":[],\"hsfList\":[],\"lwpList\":[],\"pageData\":{},\"extension\":{\"extendType\":\"AI\",\"aiStatusList\":[3,1,5],\"fileTypeList\":[]}}", "widgetInfo": "", "type": "im", "mode": "card"