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
This commit is contained in:
Dongchuan Fu
2026-07-13 00:42:46 +08:00
committed by GitHub
parent 3ddebd26ae
commit 0755beebcd
43 changed files with 12320 additions and 221 deletions
@@ -24,7 +24,15 @@ import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Copy, Check, Globe, Info, QrCode } from 'lucide-react';
import {
Copy,
Check,
Globe,
Info,
QrCode,
Download,
ExternalLink,
} from 'lucide-react';
import { copyToClipboard } from '@/app/utils/clipboard';
import {
Tooltip,
@@ -33,6 +41,7 @@ import {
TooltipTrigger,
} from '@/components/ui/tooltip';
import { systemInfo } from '@/app/infra/http';
import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
/**
* Resolve the value referenced by a `show_if.field` string.
@@ -291,6 +300,52 @@ function WebhookUrlField({
);
}
function DownloadLinkField({
label,
description,
url,
filename,
helpUrl,
helpLabel,
}: {
label: string;
description?: string;
url: string;
filename?: string;
helpUrl?: string | null;
helpLabel: string;
}) {
const baseUrl = import.meta.env.VITE_API_BASE_URL || window.location.origin;
const downloadUrl = url.startsWith('http') ? url : `${baseUrl}${url}`;
return (
<FormItem className="min-w-0">
<FormLabel className="break-words">{label}</FormLabel>
<div className="flex min-w-0 flex-wrap items-center gap-2">
<Button asChild variant="outline" size="sm">
<a href={downloadUrl} download={filename}>
<Download className="h-4 w-4" />
{label}
</a>
</Button>
{helpUrl && (
<Button asChild variant="ghost" size="sm">
<a href={helpUrl} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-4 w-4" />
{helpLabel}
</a>
</Button>
)}
</div>
{description && (
<p className="max-w-2xl text-sm break-words text-muted-foreground">
{description}
</p>
)}
</FormItem>
);
}
/**
* Display-only component for `__system.*` fields (e.g. the deployment's
* outbound IPs that the operator must add to a platform's trusted-IP list).
@@ -405,7 +460,7 @@ export default function DynamicFormComponent({
}) {
const isInitialMount = useRef(true);
const previousInitialValues = useRef(initialValues);
const { t } = useTranslation();
const { t, i18n } = useTranslation();
// Normalize a form value according to its field type.
// This ensures legacy/malformed data (e.g. a plain string for
@@ -460,6 +515,7 @@ export default function DynamicFormComponent({
item.type !== 'webhook-url' &&
item.type !== 'embed-code' &&
item.type !== 'qr-code-login' &&
item.type !== 'download-link' &&
!item.name.startsWith(SYSTEM_FIELD_PREFIX),
),
[itemConfigList],
@@ -777,6 +833,30 @@ export default function DynamicFormComponent({
);
}
if (config.type === 'download-link') {
if (!config.url) return null;
return (
<DownloadLinkField
key={config.id}
label={extractI18nObject(config.label)}
description={
config.description
? extractI18nObject(config.description)
: undefined
}
url={config.url}
filename={config.download_filename}
helpUrl={getAdapterDocUrl(config.help_links, i18n.language)}
helpLabel={
config.help_label
? extractI18nObject(config.help_label)
: t('bots.viewAdapterDocs')
}
/>
);
}
// QR code login button (e.g. Feishu one-click create, WeChat scan login)
if (config.type === 'qr-code-login') {
return (
@@ -18,6 +18,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
options?: IDynamicFormItemOption[];
show_if?: IShowIfCondition;
login_platform?: string;
url?: string;
download_filename?: string;
help_links?: Record<string, string>;
help_label?: I18nObject;
constructor(params: IDynamicFormItemSchema) {
this.id = params.id;
@@ -30,6 +34,10 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
this.options = params.options;
this.show_if = params.show_if;
this.login_platform = params.login_platform;
this.url = params.url;
this.download_filename = params.download_filename;
this.help_links = params.help_links;
this.help_label = params.help_label;
}
}