Skip to content

Commit 0755bee

Browse files
authored
feat: add supports for dify hitl (#2226)
* feat: Implement workflow form handling for paused workflows - Added module-level storage for pending forms to manage state across sessions. - Introduced functions to set, get, and clear pending forms with expiration handling. - Enhanced DifyServiceAPIRunner to support resuming paused workflows via form actions. - Implemented logic to yield human input requests and display appropriate messages. - Updated workflow submission methods to handle paused states and resume actions. - Ensured proper merging of pending form actions with user inputs for seamless interaction. * feat: Add '_routed_by_rule' variable to form action in Lark and Telegram adapters * feat: Enhance Lark and Telegram adapters with new form handling for paused workflows * feat: Enhance TelegramAdapter to handle form action buttons and message threading * feat: Improve TelegramAdapter message handling with enhanced error management and draft message support * feat: Add the function for formatting human input text to support adapters without rich UI. * feat(dingtalk): implement human input card support and card action handling - Add a new module `card_callback.py` to handle card action button clicks from DingTalk. - Introduce `DingTalkCardActionHandler` to process card action callbacks and extract parameters. - Update `DingTalkAdapter` to manage card state and handle form input through a single card template. - Add configuration for `human_input_card_template_id` in `dingtalk.yaml` to specify the template for human input. - Create a new card template `dingtalk_human_input_card.json` for rendering human input prompts and buttons. * feat(dingtalk): enhance human input card functionality with streaming support and active turn management - Updated the DingTalk card template to enable streaming mode and multi-update configuration. - Removed the obsolete delete_card method from DingTalkClient to streamline card management. - Enhanced DingTalkAdapter to manage active turn cards and accumulated streaming text, ensuring a seamless user experience during human input prompts. - Modified the create_message_card method to utilize existing active cards for resumed workflows, preventing duplication. - Improved the _paint_form_on_card method to update existing cards with human input prompts and buttons dynamically. - Updated the dingtalk_human_input_card.json template to reflect the new streaming capabilities and configuration options. * feat(wecom): implement Dify human input pause handling with button interaction support * feat(qqofficial): implement Dify human input button interaction handling and markdown keyboard support * feat(qqofficial): implement one-click QR binding and enhance localization support * feat(discord): implement Discord form view with button interactions for Dify actions * fix(telegram): correct group chat type check and handle oversized callback data for Telegram actions fix(difysvapi): ensure safe access to remove-think configuration in pipeline settings * feat(dify): add support for chatflow app type and enhance human input handling * feat(telegram): add action title feedback for user selections in Telegram messages * feat(lark): enhance LarkAdapter to store form content for resume notices * feat(dingtalk): update display formatting for card content with HTML line breaks * feat(dingtalk): add feedback functionality to cards with 👍/👎 buttons - Implemented feedback state management for cards, allowing users to provide feedback via thumbs up/down buttons. - Enhanced card rendering to include feedback buttons when appropriate. - Registered feedback listeners to handle feedback events and update card states accordingly. - Updated the card template to support dynamic button rendering for feedback actions. - Improved error handling and logging for feedback actions and card updates. * fix: add Avatar component to dingtalk_human_input_card.json for enhanced user interaction * feat(wecom): add optional source block to interactive template cards for enhanced branding * feat(wecom): add functions for template card action extraction and update, enhance button interaction handling * feat(qqofficial): synchronize passive-reply counter with inbound message sequence * feat(qqofficial): add method to identify invisible form placeholder chunks in messages * feat(dingtalk): add download link for human input card template and enhance dynamic form configuration * feat(telegram): enhance message handling with group stream deletion and form placeholder detection * Add unit tests for DingTalk, Lark, WeComBot, and Dify service API runners - Implement tests for DingTalk adapter helper functions including form content cleaning, input extraction, and completed input lines. - Create unit tests for Lark adapter helper functions focusing on input extraction and completed input lines. - Add tests for WeComBot template card functionalities, including event extraction and payload building for human input. - Enhance Dify service API runner tests to cover human input forms, including input collection, action handling, and form snapshot extraction. * feat: Enhance Telegram and QQ Official adapters with select field handling and form action processing - Added support for select fields in Telegram adapter, including option extraction and callback handling. - Implemented form action processing for Telegram callbacks, improving user interaction feedback. - Introduced new helper functions for building keyboards and resolving select button actions in QQ Official adapter. - Enhanced DifyServiceAPIRunner to handle cumulative streaming responses and improve error handling during workflow resumes. - Added unit tests for new functionalities in Telegram and QQ Official adapters, ensuring robust behavior for select fields and form actions. * feat(lark): add functions for current input definitions and visible form content handling feat(qqofficial): update fallback text handling for non-streaming scenarios feat(difysvapi): enhance form content processing for interactive fields and actions test: add unit tests for Lark and QQ Official adapter functionalities * Add tests for DingTalk adapter content processing and markdown formatting - Updated the assertion in `test_dingtalk_completed_input_lines_include_text_and_select_values` to remove unnecessary markdown formatting. - Added new tests to verify that `_dingtalk_clean_form_content` maintains the order of prompts and completed values in various scenarios. - Introduced `test_dingtalk_card_markdown_preserves_internal_line_breaks` to ensure internal line breaks are correctly converted to HTML line breaks. * feat: Refactor input handling and feedback messages across multiple adapters * feat: Update the human-computer interaction template cards, and optimize the prompt information and content display. * feat: Refactor pending form handling to isolate by bot and pipeline * feat: Enhance error handling and caching for Dify and WeCom interactions * feat: Enhance select input handling and validation in Dify API runner and Telegram adapter * feat: Add missing completed input lines handling in DingTalk adapter * feat: Add pipeline_uuid handling across multiple adapters and update related tests
1 parent 3ddebd2 commit 0755bee

43 files changed

Lines changed: 12319 additions & 220 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/build_dingtalk_card_template.py

Lines changed: 1111 additions & 0 deletions
Large diffs are not rendered by default.

src/langbot/libs/dify_service_api/v1/client.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,62 @@ async def workflow_run(
109109
if chunk.startswith('data:'):
110110
yield json.loads(chunk[5:])
111111

112+
async def workflow_submit(
113+
self,
114+
form_token: str,
115+
workflow_run_id: str,
116+
inputs: dict[str, typing.Any],
117+
user: str,
118+
action: str = '',
119+
timeout: float = 120.0,
120+
) -> typing.AsyncGenerator[dict[str, typing.Any], None]:
121+
"""Submit human input to resume a paused workflow, then stream events.
122+
123+
1. POST /form/human_input/{form_token} to submit the form
124+
2. GET /workflow/{task_id}/events to stream the resumed workflow events
125+
"""
126+
127+
headers = {
128+
'Authorization': f'Bearer {self.api_key}',
129+
'Content-Type': 'application/json',
130+
}
131+
132+
async with httpx.AsyncClient(
133+
base_url=self.base_url,
134+
trust_env=True,
135+
timeout=timeout,
136+
) as client:
137+
# Step 1: Submit the form
138+
payload: dict[str, typing.Any] = {
139+
'inputs': inputs if isinstance(inputs, dict) else {},
140+
'user': user,
141+
'action': action,
142+
}
143+
144+
submit_resp = await client.post(
145+
f'/form/human_input/{form_token}',
146+
headers=headers,
147+
json=payload,
148+
)
149+
if submit_resp.status_code != 200:
150+
raise DifyAPIError(f'{submit_resp.status_code} {submit_resp.text}')
151+
152+
# Step 2: Stream resumed workflow events
153+
async with client.stream(
154+
'GET',
155+
f'/workflow/{workflow_run_id}/events',
156+
headers={'Authorization': f'Bearer {self.api_key}'},
157+
params={'user': user},
158+
) as r:
159+
if r.status_code != 200:
160+
body = (await r.aread()).decode(errors='replace')
161+
raise DifyAPIError(f'{r.status_code} {body}')
162+
async for chunk in r.aiter_lines():
163+
if chunk.strip() == '':
164+
continue
165+
if chunk.startswith('data:'):
166+
yield json.loads(chunk[5:])
167+
112168
async def upload_file(
113169
self,
114170
file: httpx._types.FileTypes,

0 commit comments

Comments
 (0)