diff --git a/docs.json b/docs.json index b8f64ac2d..7966b7c5d 100644 --- a/docs.json +++ b/docs.json @@ -65,7 +65,14 @@ "en/use-dify/nodes/agent", "en/use-dify/nodes/question-classifier", "en/use-dify/nodes/ifelse", - "en/use-dify/nodes/human-input", + { + "group": "Human Input", + "expanded": false, + "root": "en/use-dify/nodes/human-input", + "pages": [ + "en/use-dify/nodes/hitl-api-integration-flow" + ] + }, "en/use-dify/nodes/iteration", "en/use-dify/nodes/loop", "en/use-dify/nodes/code", @@ -498,7 +505,14 @@ "zh/use-dify/nodes/agent", "zh/use-dify/nodes/question-classifier", "zh/use-dify/nodes/ifelse", - "zh/use-dify/nodes/human-input", + { + "group": "人工介入", + "expanded": false, + "root": "zh/use-dify/nodes/human-input", + "pages": [ + "zh/use-dify/nodes/hitl-api-integration-flow" + ] + }, "zh/use-dify/nodes/iteration", "zh/use-dify/nodes/loop", "zh/use-dify/nodes/code", @@ -931,7 +945,14 @@ "ja/use-dify/nodes/agent", "ja/use-dify/nodes/question-classifier", "ja/use-dify/nodes/ifelse", - "ja/use-dify/nodes/human-input", + { + "group": "人間の入力", + "expanded": false, + "root": "ja/use-dify/nodes/human-input", + "pages": [ + "ja/use-dify/nodes/hitl-api-integration-flow" + ] + }, "ja/use-dify/nodes/iteration", "ja/use-dify/nodes/loop", "ja/use-dify/nodes/code", diff --git a/en/api-reference/openapi_chatflow.json b/en/api-reference/openapi_chatflow.json index 6c1e08125..e02d09f11 100644 --- a/en/api-reference/openapi_chatflow.json +++ b/en/api-reference/openapi_chatflow.json @@ -141,7 +141,7 @@ }, "humanInputPause": { "summary": "Response Example - Human Input pause", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -2778,7 +2778,7 @@ "Human Input" ], "summary": "Get Human Input Form", - "description": "Retrieve a paused Human Input form's contents using the `form_token` from a `human_input_required` event. Requires **WebApp** delivery.", + "description": "Retrieve a paused Human Input form's contents. Requires WebApp delivery.\n\nSee the [API Integration Flow](/en/use-dify/nodes/hitl-api-integration-flow) for the end-to-end sequence of calling Human Input endpoints.", "operationId": "getChatflowHumanInputForm", "parameters": [ { @@ -2788,7 +2788,7 @@ "schema": { "type": "string" }, - "description": "Access token for the paused form, returned in the `human_input_required` event from [Send Chat Message](/api-reference/chatflows/send-chat-message) in streaming mode." + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." } ], "responses": { @@ -2810,7 +2810,7 @@ "properties": { "type": { "type": "string", - "description": "Form input control type. Available values: `text_input` (single-line text field) and `paragraph` (multi-line text area)." + "description": "Form input control type. Available values: `paragraph` (multi-line text input), `select` (single-choice from a list), `file` (single file upload), and `file-list` (multiple file uploads)." }, "output_variable_name": { "type": "string", @@ -2819,7 +2819,7 @@ "default": { "type": "object", "nullable": true, - "description": "Default value resolved from the workflow context. `null` when no default is configured.", + "description": "Raw default-value configuration for `paragraph` inputs. The client should not resolve this directly; use `resolved_default_values` to display defaults. `null` for other input types or when no default is configured.", "properties": { "type": { "type": "string", @@ -2837,6 +2837,70 @@ "description": "Literal default value when `type` is `constant`. Always a string." } } + }, + "option_source": { + "type": "object", + "description": "Source of options for `select` inputs. Present only when `type` is `select`.", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "Origin of the options. `constant` means `value` lists the options literally; `variable` means `selector` points to an `array[string]` workflow variable that provides them." + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Variable reference path when `type` is `variable`." + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Literal option list when `type` is `constant`." + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "File categories the recipient may upload. Present for `file` and `file-list` inputs. Values: `image`, `document`, `audio`, `video`, `custom`." + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allowed file extensions when `allowed_file_types` includes `custom`. Include the leading `.` in each extension, for example `.md`. Present for `file` and `file-list` inputs." + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "Upload methods the recipient may use. Values: `local_file`, `remote_url`. Present for `file` and `file-list` inputs." + }, + "number_limits": { + "type": "integer", + "description": "Maximum number of files the recipient may upload. Present only for `file-list` inputs." } } }, @@ -2847,7 +2911,7 @@ "additionalProperties": { "type": "string" }, - "description": "Pre-filled default values, keyed by input `output_variable_name`. All values are stringified." + "description": "Pre-rendered values to display in the form. Keyed by input `output_variable_name`. Populated for `paragraph` inputs whose default resolves from a workflow variable; empty for inputs with no resolvable default. Display these values; do not re-resolve `default` on the client. All values are stringified." }, "user_actions": { "type": "array", @@ -2887,17 +2951,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -2959,6 +3038,23 @@ } } } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } } } }, @@ -2967,7 +3063,7 @@ "Human Input" ], "summary": "Submit Human Input Form", - "description": "Submit the recipient's response to a paused Human Input form. The workflow resumes on acceptance; use [Stream Workflow Events](/api-reference/chatflows/stream-workflow-events) to follow subsequent events. Requires **WebApp** delivery.", + "description": "Submit the recipient's response to a paused Human Input form. The workflow resumes on acceptance; follow subsequent events via the Stream Workflow Events endpoint. Requires WebApp delivery.", "operationId": "submitChatflowHumanInputForm", "parameters": [ { @@ -2977,7 +3073,7 @@ "schema": { "type": "string" }, - "description": "Access token for the paused form, returned in the `human_input_required` event from [Send Chat Message](/api-reference/chatflows/send-chat-message) in streaming mode." + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." } ], "requestBody": { @@ -2994,8 +3090,95 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "Recipient-supplied values keyed by input `output_variable_name`." + "description": "Submitted values keyed by each input's `output_variable_name`. The value shape depends on the input's `type`. See the schema tabs for each shape.", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph or select", + "type": "string", + "description": "Value shape for `paragraph` (free text) and `select` (one chosen option) inputs." + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "How the backend resolves this file. `local_file` references a file already uploaded via the Upload Local File or Upload Remote File endpoint. `remote_url` tells the backend to fetch the file from the given URL at submit time." + }, + "upload_file_id": { + "type": "string", + "description": "Required when `transfer_method` is `local_file`. The `id` returned by the Upload Local File or Upload Remote File endpoint." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Required when `transfer_method` is `remote_url`. A publicly reachable HTTPS URL of the file to fetch." + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "Optional file category override. When set, must be consistent with the form input's `allowed_file_types`." + } + }, + "description": "Value shape for `file` inputs. Submit a single file mapping object." + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "How the backend resolves this file. `local_file` references a file already uploaded via the Upload Local File or Upload Remote File endpoint. `remote_url` tells the backend to fetch the file from the given URL at submit time." + }, + "upload_file_id": { + "type": "string", + "description": "Required when `transfer_method` is `local_file`. The `id` returned by the Upload Local File or Upload Remote File endpoint." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Required when `transfer_method` is `remote_url`. A publicly reachable HTTPS URL of the file to fetch." + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "Optional file category override. When set, must be consistent with the form input's `allowed_file_types`." + } + } + }, + "description": "Value shape for `file-list` inputs. Submit an array of file mapping objects." + } + ] + } }, "action": { "type": "string", @@ -3014,7 +3197,19 @@ "summary": "Request Example", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "Looks good to ship", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -3107,6 +3302,576 @@ } } } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Issue Upload Token", + "description": "Issue a short-lived upload token for a paused Human Input form. Requires WebApp delivery.", + "operationId": "issueChatflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." + } + ], + "responses": { + "200": { + "description": "Upload token issued successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "Pass as a `Bearer` token on [Upload Local File](/api-reference/human-input/upload-local-file) and [Upload Remote File](/api-reference/human-input/upload-remote-file) to attach files to the form's `file` or `file-list` inputs." + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp (seconds) after which `upload_token` is no longer accepted." + } + } + }, + "examples": { + "success": { + "summary": "Response Example", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found` : Form not found.", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "Form not found" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many form requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Upload Local File", + "description": "Upload a local file to a paused Human Input form.", + "operationId": "uploadChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "File upload request. Requires multipart/form-data.", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "The file to upload. Must conform to the form input's `allowed_file_types` and `allowed_file_extensions`." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "File uploaded successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reference this value as `upload_file_id` in `file` and `file-list` inputs on [Submit Human Input Form](/api-reference/human-input/submit-human-input-form)." + }, + "name": { + "type": "string", + "description": "Original file name." + }, + "size": { + "type": "integer", + "description": "File size in bytes." + }, + "extension": { + "type": "string", + "nullable": true, + "description": "File extension." + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "MIME type of the file." + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "Identifier of the user the file was uploaded for." + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "Unix timestamp (seconds) when the file was created." + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "Signed URL for previewing the file. `null` if not generated." + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "Original source URL, if the file was fetched remotely. `null` for local uploads." + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "Identifier of the user that owns the upload." + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "Tenant the upload belongs to." + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "Always `null` for HITL uploads." + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "Internal storage key." + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "Response Example", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded` : No file was provided in the request.\n- `too_many_files` : Only one file is allowed per request.\n- `filename_not_exists_error` : The uploaded file has no filename.\n- `file_extension_blocked` : The file extension is blocked for security reasons.\n- `invalid_upload_token` : The `Authorization` header was malformed.", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "Please upload your file." + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "Only one file is allowed." + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "The specified filename does not exist." + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "The file extension is blocked for security reasons." + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "Invalid upload token." + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` : Upload token is required.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "Upload token is required." + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` : Upload token is invalid or expired.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "Upload token is invalid or expired." + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` : File size exceeded.", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "File size exceeded." + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` : File type not allowed.", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "File type not allowed." + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Upload Remote File", + "description": "Fetch a remote URL and attach the resulting file to a paused Human Input form.", + "operationId": "uploadRemoteChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "Publicly reachable URL of the file to fetch and attach." + } + } + }, + "examples": { + "remoteUpload": { + "summary": "Request Example", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "File fetched and attached successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reference this value as `upload_file_id` in `file` and `file-list` inputs on [Submit Human Input Form](/api-reference/human-input/submit-human-input-form)." + }, + "name": { + "type": "string", + "description": "File name derived from the remote URL or response headers." + }, + "size": { + "type": "integer", + "description": "File size in bytes." + }, + "extension": { + "type": "string", + "nullable": true, + "description": "File extension." + }, + "url": { + "type": "string", + "nullable": true, + "description": "Signed URL for fetching the file's contents." + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "MIME type of the file." + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "Identifier of the user the file was uploaded for." + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "Unix timestamp (seconds) when the file was created." + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "Response Example", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error` : Failed to fetch the remote URL.\n- `file_extension_blocked` : The file extension is blocked for security reasons.\n- `invalid_upload_token` : The `Authorization` header was malformed.", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "Failed to fetch file from https://example.com/report.pdf: connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "The file extension is blocked for security reasons." + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "Invalid upload token." + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` : Upload token is required.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "Upload token is required." + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` : Upload token is invalid or expired.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "Upload token is invalid or expired." + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` : Remote file exceeded the size limit.", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "File size exceeded." + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` : File type not allowed.", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "File type not allowed." + } + } + } + } + } } } } @@ -3217,6 +3982,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key authentication. For all API requests, include your API Key in the `Authorization` HTTP Header, prefixed with `Bearer `. Example: `Authorization: Bearer {API_KEY}`. **Strongly recommend storing your API Key on the server-side, not shared or stored on the client-side, to avoid possible API-Key leakage that can lead to serious consequences.**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "Short-lived upload token for attaching files to a Human Input form. Obtain via [Issue Upload Token](/api-reference/human-input/issue-upload-token) and pass as `Authorization: Bearer hitl_upload_{token}`." } }, "responses": { @@ -5928,7 +6699,7 @@ "properties": { "type": { "type": "string", - "description": "Form input control type. Available values: `text_input` (single-line text field) and `paragraph` (multi-line text area)." + "description": "Form input control type. Available values: `paragraph` (multi-line text input), `select` (single-choice from a list), `file` (single file upload), and `file-list` (multiple file uploads)." }, "output_variable_name": { "type": "string", @@ -5991,7 +6762,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "Access token for [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form). `null` when the Human Input node uses Email or Console delivery (the Service API can only operate on WebApp-delivered forms)." + "description": "Access token for [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form). `null` when the Human Input node uses any non-WebApp delivery method (the Service API can only operate on WebApp-delivered forms)." }, "resolved_default_values": { "type": "object", @@ -6609,7 +7380,7 @@ "properties": { "type": { "type": "string", - "description": "`text_input` for single-line text, `paragraph` for multi-line text." + "description": "Available values: `paragraph`, `select`, `file`, `file-list`." }, "output_variable_name": { "type": "string", diff --git a/en/api-reference/openapi_workflow.json b/en/api-reference/openapi_workflow.json index 1327c257c..728ee2a55 100644 --- a/en/api-reference/openapi_workflow.json +++ b/en/api-reference/openapi_workflow.json @@ -128,7 +128,7 @@ }, "humanInputPause": { "summary": "Response Example - Human Input pause", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -317,7 +317,7 @@ "examples": { "humanInputPause": { "summary": "Response Example - Human Input pause", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -1556,7 +1556,7 @@ "Human Input" ], "summary": "Get Human Input Form", - "description": "Retrieve a paused Human Input form's contents using the `form_token` from a `human_input_required` event. Requires **WebApp** delivery.", + "description": "Retrieve a paused Human Input form's contents. Requires WebApp delivery.\n\nSee the [API Integration Flow](/en/use-dify/nodes/hitl-api-integration-flow) for the end-to-end sequence of calling Human Input endpoints.", "operationId": "getWorkflowHumanInputForm", "parameters": [ { @@ -1566,7 +1566,7 @@ "schema": { "type": "string" }, - "description": "Access token for the paused form, returned in the `human_input_required` event from [Run Workflow](/api-reference/workflows/run-workflow) in streaming mode." + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." } ], "responses": { @@ -1588,7 +1588,7 @@ "properties": { "type": { "type": "string", - "description": "Form input control type. Available values: `text_input` (single-line text field) and `paragraph` (multi-line text area)." + "description": "Form input control type. Available values: `paragraph` (multi-line text input), `select` (single-choice from a list), `file` (single file upload), and `file-list` (multiple file uploads)." }, "output_variable_name": { "type": "string", @@ -1597,7 +1597,7 @@ "default": { "type": "object", "nullable": true, - "description": "Default value resolved from the workflow context. `null` when no default is configured.", + "description": "Raw default-value configuration for `paragraph` inputs. The client should not resolve this directly; use `resolved_default_values` to display defaults. `null` for other input types or when no default is configured.", "properties": { "type": { "type": "string", @@ -1615,6 +1615,70 @@ "description": "Literal default value when `type` is `constant`. Always a string." } } + }, + "option_source": { + "type": "object", + "description": "Source of options for `select` inputs. Present only when `type` is `select`.", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "Origin of the options. `constant` means `value` lists the options literally; `variable` means `selector` points to an `array[string]` workflow variable that provides them." + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Variable reference path when `type` is `variable`." + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Literal option list when `type` is `constant`." + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "File categories the recipient may upload. Present for `file` and `file-list` inputs. Values: `image`, `document`, `audio`, `video`, `custom`." + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Allowed file extensions when `allowed_file_types` includes `custom`. Include the leading `.` in each extension, for example `.md`. Present for `file` and `file-list` inputs." + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "Upload methods the recipient may use. Values: `local_file`, `remote_url`. Present for `file` and `file-list` inputs." + }, + "number_limits": { + "type": "integer", + "description": "Maximum number of files the recipient may upload. Present only for `file-list` inputs." } } }, @@ -1625,7 +1689,7 @@ "additionalProperties": { "type": "string" }, - "description": "Pre-filled default values, keyed by input `output_variable_name`. All values are stringified." + "description": "Pre-rendered values to display in the form. Keyed by input `output_variable_name`. Populated for `paragraph` inputs whose default resolves from a workflow variable; empty for inputs with no resolvable default. Display these values; do not re-resolve `default` on the client. All values are stringified." }, "user_actions": { "type": "array", @@ -1665,17 +1729,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -1737,6 +1816,23 @@ } } } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } } } }, @@ -1745,7 +1841,7 @@ "Human Input" ], "summary": "Submit Human Input Form", - "description": "Submit the recipient's response to a paused Human Input form. The workflow resumes on acceptance; use [Stream Workflow Events](/api-reference/workflows/stream-workflow-events) to follow subsequent events. Requires **WebApp** delivery.", + "description": "Submit the recipient's response to a paused Human Input form. The workflow resumes on acceptance; follow subsequent events via the Stream Workflow Events endpoint. Requires WebApp delivery.", "operationId": "submitWorkflowHumanInputForm", "parameters": [ { @@ -1755,7 +1851,7 @@ "schema": { "type": "string" }, - "description": "Access token for the paused form, returned in the `human_input_required` event from [Run Workflow](/api-reference/workflows/run-workflow) in streaming mode." + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." } ], "requestBody": { @@ -1772,8 +1868,95 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "Recipient-supplied values keyed by input `output_variable_name`." + "description": "Submitted values keyed by each input's `output_variable_name`. The value shape depends on the input's `type`. See the schema tabs for each shape.", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph or select", + "type": "string", + "description": "Value shape for `paragraph` (free text) and `select` (one chosen option) inputs." + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "How the backend resolves this file. `local_file` references a file already uploaded via the Upload Local File or Upload Remote File endpoint. `remote_url` tells the backend to fetch the file from the given URL at submit time." + }, + "upload_file_id": { + "type": "string", + "description": "Required when `transfer_method` is `local_file`. The `id` returned by the Upload Local File or Upload Remote File endpoint." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Required when `transfer_method` is `remote_url`. A publicly reachable HTTPS URL of the file to fetch." + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "Optional file category override. When set, must be consistent with the form input's `allowed_file_types`." + } + }, + "description": "Value shape for `file` inputs. Submit a single file mapping object." + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "How the backend resolves this file. `local_file` references a file already uploaded via the Upload Local File or Upload Remote File endpoint. `remote_url` tells the backend to fetch the file from the given URL at submit time." + }, + "upload_file_id": { + "type": "string", + "description": "Required when `transfer_method` is `local_file`. The `id` returned by the Upload Local File or Upload Remote File endpoint." + }, + "url": { + "type": "string", + "format": "uri", + "description": "Required when `transfer_method` is `remote_url`. A publicly reachable HTTPS URL of the file to fetch." + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "Optional file category override. When set, must be consistent with the form input's `allowed_file_types`." + } + } + }, + "description": "Value shape for `file-list` inputs. Submit an array of file mapping objects." + } + ] + } }, "action": { "type": "string", @@ -1792,7 +1975,19 @@ "summary": "Request Example", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "Looks good to ship", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -1885,6 +2080,576 @@ } } } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Issue Upload Token", + "description": "Issue a short-lived upload token for a paused Human Input form. Requires WebApp delivery.", + "operationId": "issueWorkflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Access token for the paused form, returned in the `human_input_required` event from the Run Workflow or Send Chat Message endpoint in streaming mode." + } + ], + "responses": { + "200": { + "description": "Upload token issued successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "Pass as a `Bearer` token on [Upload Local File](/api-reference/human-input/upload-local-file) and [Upload Remote File](/api-reference/human-input/upload-remote-file) to attach files to the form's `file` or `file-list` inputs." + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp (seconds) after which `upload_token` is no longer accepted." + } + } + }, + "examples": { + "success": { + "summary": "Response Example", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found` : Form not found.", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "Form not found" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` : Too many form requests from this client. Try again later.", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "Too many form requests. Please try again later." + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Upload Local File", + "description": "Upload a local file to a paused Human Input form.", + "operationId": "uploadWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "File upload request. Requires multipart/form-data.", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "The file to upload. Must conform to the form input's `allowed_file_types` and `allowed_file_extensions`." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "File uploaded successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reference this value as `upload_file_id` in `file` and `file-list` inputs on [Submit Human Input Form](/api-reference/human-input/submit-human-input-form)." + }, + "name": { + "type": "string", + "description": "Original file name." + }, + "size": { + "type": "integer", + "description": "File size in bytes." + }, + "extension": { + "type": "string", + "nullable": true, + "description": "File extension." + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "MIME type of the file." + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "Identifier of the user the file was uploaded for." + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "Unix timestamp (seconds) when the file was created." + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "Signed URL for previewing the file. `null` if not generated." + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "Original source URL, if the file was fetched remotely. `null` for local uploads." + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "Identifier of the user that owns the upload." + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "Tenant the upload belongs to." + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "Always `null` for HITL uploads." + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "Internal storage key." + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "Response Example", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded` : No file was provided in the request.\n- `too_many_files` : Only one file is allowed per request.\n- `filename_not_exists_error` : The uploaded file has no filename.\n- `file_extension_blocked` : The file extension is blocked for security reasons.\n- `invalid_upload_token` : The `Authorization` header was malformed.", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "Please upload your file." + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "Only one file is allowed." + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "The specified filename does not exist." + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "The file extension is blocked for security reasons." + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "Invalid upload token." + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` : Upload token is required.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "Upload token is required." + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` : Upload token is invalid or expired.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "Upload token is invalid or expired." + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` : File size exceeded.", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "File size exceeded." + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` : File type not allowed.", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "File type not allowed." + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "Human Input" + ], + "summary": "Upload Remote File", + "description": "Fetch a remote URL and attach the resulting file to a paused Human Input form.", + "operationId": "uploadRemoteWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "Publicly reachable URL of the file to fetch and attach." + } + } + }, + "examples": { + "remoteUpload": { + "summary": "Request Example", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "File fetched and attached successfully.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Reference this value as `upload_file_id` in `file` and `file-list` inputs on [Submit Human Input Form](/api-reference/human-input/submit-human-input-form)." + }, + "name": { + "type": "string", + "description": "File name derived from the remote URL or response headers." + }, + "size": { + "type": "integer", + "description": "File size in bytes." + }, + "extension": { + "type": "string", + "nullable": true, + "description": "File extension." + }, + "url": { + "type": "string", + "nullable": true, + "description": "Signed URL for fetching the file's contents." + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "MIME type of the file." + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "Identifier of the user the file was uploaded for." + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "Unix timestamp (seconds) when the file was created." + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "Response Example", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error` : Failed to fetch the remote URL.\n- `file_extension_blocked` : The file extension is blocked for security reasons.\n- `invalid_upload_token` : The `Authorization` header was malformed.", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "Failed to fetch file from https://example.com/report.pdf: connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "The file extension is blocked for security reasons." + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "Invalid upload token." + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` : Upload token is required.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "Upload token is required." + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` : Upload token is invalid or expired.", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "Upload token is invalid or expired." + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` : Remote file exceeded the size limit.", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "File size exceeded." + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` : File type not allowed.", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "File type not allowed." + } + } + } + } + } } } } @@ -1995,6 +2760,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key authentication. For all API requests, include your API Key in the `Authorization` HTTP Header, prefixed with `Bearer `. Example: `Authorization: Bearer {API_KEY}`. **Strongly recommend storing your API Key on the server-side, not shared or stored on the client-side, to avoid possible API-Key leakage that can lead to serious consequences.**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "Short-lived upload token for attaching files to a Human Input form. Obtain via [Issue Upload Token](/api-reference/human-input/issue-upload-token) and pass as `Authorization: Bearer hitl_upload_{token}`." } }, "responses": { @@ -4162,7 +4933,7 @@ "properties": { "type": { "type": "string", - "description": "Form input control type. Available values: `text_input` (single-line text field) and `paragraph` (multi-line text area)." + "description": "Form input control type. Available values: `paragraph` (multi-line text input), `select` (single-choice from a list), `file` (single file upload), and `file-list` (multiple file uploads)." }, "output_variable_name": { "type": "string", @@ -4225,7 +4996,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "Access token for [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form). `null` when the Human Input node uses Email or Console delivery (the Service API can only operate on WebApp-delivered forms)." + "description": "Access token for [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form). `null` when the Human Input node uses any non-WebApp delivery method (the Service API can only operate on WebApp-delivered forms)." }, "resolved_default_values": { "type": "object", diff --git a/en/self-host/configuration/environments.mdx b/en/self-host/configuration/environments.mdx index 8493c1599..8a865af91 100644 --- a/en/self-host/configuration/environments.mdx +++ b/en/self-host/configuration/environments.mdx @@ -1153,7 +1153,7 @@ The sandbox is a separate service that runs Python, JavaScript, and Jinja2 code | `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | Maximum wall-clock time in seconds per workflow run. Exceeding this terminates the workflow. | | `WORKFLOW_CALL_MAX_DEPTH` | `5` | Maximum depth for nested workflow-calls-workflow. Prevents infinite recursion. | | `MAX_VARIABLE_SIZE` | `204800` | Maximum size in bytes (200 KB) for a single workflow variable. | -| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | Maximum number of files that can be uploaded in a single workflow execution. | +| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | Upper bound for the max-files setting on a single file-upload field (e.g., a User Input File List). The node panel's max-files slider is capped at this value; raise to allow larger per-field configurations. | | `WORKFLOW_NODE_EXECUTION_STORAGE` | `rdbms` | Where workflow node execution records are stored. `rdbms` stores everything in the database. `hybrid` stores new data in object storage and reads from both. | | `DSL_EXPORT_ENCRYPT_DATASET_ID` | `true` | Encrypt dataset IDs when exporting DSL files. Set to `false` to export plain IDs for easier cross-environment import. | diff --git a/en/use-dify/nodes/hitl-api-integration-flow.mdx b/en/use-dify/nodes/hitl-api-integration-flow.mdx new file mode 100644 index 000000000..361ab24a2 --- /dev/null +++ b/en/use-dify/nodes/hitl-api-integration-flow.mdx @@ -0,0 +1,123 @@ +--- +title: Human Input API Integration Flow +sidebarTitle: API Integration Flow +description: End-to-end sequence for handling a paused Human Input form via the API +--- + +When a workflow reaches a Human Input node, it pauses and emits a `human_input_required` event in the streaming response. The event carries a `form_token` that your integration uses to drive the form lifecycle until the workflow resumes. + +For per-endpoint reference, see the [Human Input API](/api-reference/human-input/get-human-input-form). + +## Sequence + +The sequence below applies to both Workflow and Chatflow apps. Only the entry endpoint in Step 1 and the resume endpoint in Step 6 differ between the two. + + + + + 1. Call [Run Workflow](/api-reference/workflows/run-workflow) (Workflow apps) or [Send Chat Message](/api-reference/chatflows/send-chat-message) (Chatflow apps). + 2. Watch for the `human_input_required` event in the SSE stream and capture `form_token`, `form_id`, and `task_id` from the payload. + + You'll use `task_id` in Step 6 if the stream closes before the workflow resumes. + + + Call [Get Human Input Form](/api-reference/human-input/get-human-input-form) with `form_token`. The response includes the rendered Markdown, input field definitions, available actions, and any pre-filled default values. Render the form for the recipient. + + + If the form has `file` or `file-list` inputs, call [Issue Upload Token](/api-reference/human-input/issue-upload-token) for a short-lived `upload_token`. Reuse the token across multiple uploads on the same form. + + + For each file the recipient attaches, call one of: + + - [Upload Local File](/api-reference/human-input/upload-local-file) for files from the recipient's device (multipart). + - [Upload Remote File](/api-reference/human-input/upload-remote-file) for files at a public URL that you want the backend to fetch and validate before submission. + + Both return an `id` you'll reference as `upload_file_id` in the submit payload. + + + Call [Submit Human Input Form](/api-reference/human-input/submit-human-input-form) with the recipient's input values and the selected action. + + File inputs accept either a `{transfer_method: local_file, upload_file_id}` mapping (from Step 4) or an inline `{transfer_method: remote_url, url}` mapping. See [Upload First vs. Inline Remote URL](#upload-first-vs-inline-remote-url) for the trade-off. + + On success, the workflow resumes along the matching action branch. + + + If the original SSE stream closed, reopen it via the [Workflow](/api-reference/workflows/stream-workflow-events) or [Chatflow](/api-reference/chatflows/stream-workflow-events) Stream Workflow Events API with the `task_id` from Step 1. + + + +## Upload First vs. Inline Remote URL + +Both submission patterns work for file inputs: + +- **Pre-upload, then reference `upload_file_id`** (recommended) + + The backend validates file size, type, and extension at upload time, so the recipient gets immediate feedback and can retry before committing the whole submission. + +- **Submit inline with `transfer_method: remote_url`** + + The backend fetches the file at submit time. Faster to integrate, but any size, type, or fetch failure rejects the entire submission, forcing the recipient to redo other fields. + + + For interactive forms with recipient feedback, prefer the pre-upload pattern. The trade-off only pays off when the integration is fully programmatic and no human is waiting to retype anything. + + +## Authentication + +The form-facing endpoints, [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form), are unauthenticated. The `form_token` itself is the credential, and the endpoints are rate-limited per client IP. + +The file-upload endpoints require the `Bearer` `upload_token` from Step 3. + +## Delivery Method Requirement + +The Human Input API works only with forms delivered via the Human Input node's WebApp method. Email-only delivery doesn't expose a `form_token`. + +## Example: File-Attached Submission + +This example uses a form with a `feedback` paragraph input, an `attachments` file-list input, and `approve` / `revise` actions. + +1. Call [Get Human Input Form](/api-reference/human-input/get-human-input-form) to get the form definition: + + ```http + GET /form/human_input/ + ``` + +2. Call [Issue Upload Token](/api-reference/human-input/issue-upload-token) to get an upload token: + + ```http + POST /form/human_input//upload-token + ``` + + Returns `{"upload_token": "hitl_upload_...", "expires_at": ...}`. + +3. For each local file, call [Upload Local File](/api-reference/human-input/upload-local-file): + + ```http + POST /form/human_input/files/upload + Authorization: Bearer hitl_upload_... + Content-Type: multipart/form-data + + file= + ``` + + Returns `{"id": "1a77f0df-...", ...}`. + +4. Call [Submit Human Input Form](/api-reference/human-input/submit-human-input-form) with the recipient's input and selected action: + + ```http + POST /form/human_input/ + Content-Type: application/json + + { + "inputs": { + "feedback": "Looks good to ship", + "attachments": [ + {"transfer_method": "local_file", "upload_file_id": "1a77f0df-..."} + ] + }, + "action": "approve", + "user": "abc-123" + } + ``` + + Returns `{}`. The workflow resumes along the `approve` branch. diff --git a/en/use-dify/nodes/human-input.mdx b/en/use-dify/nodes/human-input.mdx index 4b4476f42..35ac048c5 100644 --- a/en/use-dify/nodes/human-input.mdx +++ b/en/use-dify/nodes/human-input.mdx @@ -1,15 +1,15 @@ --- title: Human Input -description: Pause workflows to request human input, review, or decisions +description: Pause workflows to request human input --- The Human Input node pauses workflows at key points to deliver a customizable request form. Recipients can use the form to review information, provide input, and choose from predefined decisions that determine how the workflow proceeds. -By embedding human judgement directly where it matters, you can **balance automated efficiency with human oversight**. +By embedding human judgement directly where it matters, you can *balance automated efficiency with human oversight*. - - ![Request Form Example](/images/use-dify/workflow/human-input-request-form-example.png) - + + For a workflow design example, see [Example: Content Review Workflow](#example-content-review-workflow). + ## Configuration @@ -27,10 +27,10 @@ Configure the following to define how the node requests and processes human inpu Choose the channel through which the request is delivered. Currently available methods: -- **Web app**: Displays the request form to the WebApp end user. Not available in workflows started by a Trigger. +- **Webapp**: Displays the request form to the WebApp end user. Not available in workflows started by a Trigger. - External clients can also retrieve and submit WebApp forms through the Service API. See [Get Human Input Form](/api-reference/human-input/get-human-input-form). + External clients can drive the WebApp form lifecycle through the Service API. See [API Integration Flow](/en/use-dify/nodes/hitl-api-integration-flow). - **Email**: Sends a request link via email to specific workspace members, external email addresses, or every member of the workspace. Anyone with the link can respond, no Dify account required. @@ -51,19 +51,29 @@ Customize the form recipients see and interact with: Reference workflow variables to show dynamic content, such as AI-generated text for review or any needed contextual information from upstream nodes. + In WebApp delivery, the form itself displays to end users. Any variables you reference render their values directly in the form, so **no Answer or Output node is needed before the Human Input node**. + Reasoning models emit their thinking process alongside the final answer. Referencing the `text` output variable shows both by default. To show only the answer, toggle on **Enable Reasoning Tag Separation** for the corresponding LLM node. -- **Collect input with input fields** +- **Collect input with form fields** - Input fields can start empty or pre-filled with variables (e.g., LLM output to refine) or static text (e.g., example or default values) that recipients can edit. + Add fields into the request form to capture different types of input from recipients. Each field becomes a variable for downstream use. + + For example, in a blog review workflow, you can pass recipient feedback to a downstream LLM node for content revision. - Each input field becomes a variable for downstream use. For instance, pass edited content for further processing or send feedback to an LLM for content revision. + | Field Type | Description | + |:-----------|:------------| + | Paragraph | Text input. Can start empty, or pre-filled with variables (e.g., LLM output to refine) or static text (examples or default values).



No maximum length, but very long inputs may exceed downstream LLM context windows. | + | Select | Single-choice selection from a list of options. Define options manually, or reference an `array[string]` variable to use its items as options. | + | Single File / File List | Single or multiple file uploads. On self-hosted deployments, file upload limits are tunable via environment variables:
  • `UPLOAD_FILE_SIZE_LIMIT`, `UPLOAD_IMAGE_FILE_SIZE_LIMIT`, `UPLOAD_VIDEO_FILE_SIZE_LIMIT`, and `UPLOAD_AUDIO_FILE_SIZE_LIMIT` cap per-file size by extension.
  • `WORKFLOW_FILE_UPLOAD_LIMIT` caps the max files a File List field can be configured to accept.
See [Environment Variables](/en/self-host/configuration/environments) for defaults.
| -After the recipient responds, the form content with all values filled in is available downstream as the `__rendered_content` variable. + Only Paragraph is optional; Select, Single File, and File List are mandatory. The form's action buttons stay disabled until all mandatory fields are filled. + +After the recipient responds, the form content with all values filled in is available downstream as the `__rendered_content` variable. File field values render as plain-text placeholders: `[file]` for Single File and `[N files]` for File List. ### User Action @@ -90,3 +100,89 @@ Configure how long the request stays open before it expires. The default is 3 da If no recipient responds before the timeout, the workflow follows the timeout branch from the node. Wire this branch to a fallback path, such as a notification or a retry loop. If no timeout branch is connected, the workflow ends. + +## Example: Content Review Workflow + +
+ + ![Workflow Example](/images/use-dify/workflow/human-input-workflow-example.png) + + + ![Request Form Example](/images/use-dify/workflow/human-input-request-form-example.png) + +
+ +This workflow drafts a blog post from the `topic` and `language` that a workflow initiator inputs, emails the draft to a reviewer, and finalizes the output based on the reviewer's choice. + +It is designed around three things the reviewer should be able to do: + +1. **See the AI-generated draft**: Reference the upstream LLM node's `text` variable in the form so the rendered form displays the draft directly. + +2. **Edit the draft directly if needed**: Add a Paragraph field named `edits` in the form, pre-filled with the same `text` variable, so the reviewer sees the draft as starting content and can edit in place. + + Because blog posts are long, the form's Markdown display (point 1) reads better than a Paragraph field on its own. For shorter content, the pre-filled Paragraph field alone can handle both reading and editing. + +3. **Provide feedback for an AI revision**: + + 1. Add a Paragraph field named `feedback` in the form for the reviewer's feedback. + 2. Connect two downstream LLM nodes in sequence: + 1. A Regenerate node that takes the original draft `text` and the reviewer's `feedback` to produce a revised draft. + 2. A Check Revision node that takes `feedback` and the revised draft to verify whether the revision addresses the feedback. The verified result is what flows downstream. + +On the received request form, the reviewer fills the relevant Paragraph fields (or leaves them blank) based on their judgment, then clicks the matching action button. Each action wires to a different output: + +- **Approve**: the original draft from the upstream LLM +- **Apply Edit**: the reviewer's edited content from the `edits` field +- **Regenerate**: the revised draft from the downstream LLM pipeline + + + + **System** + + ```text wrap + Write a marketing blog post around the given topic in the specified language. + ``` + + **User** + + ```text + Topic: {{#user_input.topic#}} + Language: {{#user_input.language#}} + ``` + + + + **System** + + ```text wrap + Regenerate the draft based on user feedback. + ``` + + **User** + + ```text + Draft: {{#generate_draft.text#}} + User Feedback: {{#human_input.feedback#}} + ``` + + + + **System** + + ```text wrap + Check whether the draft below addresses the user's feedback. Return the draft unchanged if it does; revise it to address the feedback if it doesn't. + ``` + + **User** + + ```text + User Feedback: {{#human_input.feedback#}} + Regenerated Draft: {{#regenerate.text#}} + ``` + + + + + \ No newline at end of file diff --git a/images/use-dify/workflow/human-input-request-form-example.png b/images/use-dify/workflow/human-input-request-form-example.png index c607bf82e..14c597c8a 100644 Binary files a/images/use-dify/workflow/human-input-request-form-example.png and b/images/use-dify/workflow/human-input-request-form-example.png differ diff --git a/images/use-dify/workflow/human-input-workflow-example.png b/images/use-dify/workflow/human-input-workflow-example.png new file mode 100644 index 000000000..5a55d2329 Binary files /dev/null and b/images/use-dify/workflow/human-input-workflow-example.png differ diff --git a/ja/api-reference/openapi_chatflow.json b/ja/api-reference/openapi_chatflow.json index cf1c85557..f13a69d1f 100644 --- a/ja/api-reference/openapi_chatflow.json +++ b/ja/api-reference/openapi_chatflow.json @@ -141,7 +141,7 @@ }, "humanInputPause": { "summary": "レスポンス例 - 人間の入力での一時停止", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -2778,7 +2778,7 @@ "人間の入力" ], "summary": "人間の入力フォームを取得", - "description": "`human_input_required` イベントの `form_token` を用いて、一時停止中の人間の入力フォームの内容を取得します。**WebApp** 配信が必要です。", + "description": "一時停止中の人間の入力フォームの内容を取得します。 WebApp 配信が必要です。\n\n人間の入力エンドポイントを呼び出す一連の流れについては、[API 連携フロー](/ja/use-dify/nodes/hitl-api-integration-flow) を参照してください。", "operationId": "getChatflowHumanInputForm", "parameters": [ { @@ -2788,7 +2788,7 @@ "schema": { "type": "string" }, - "description": "一時停止フォームのアクセストークン。ストリーミングモードで [チャットメッセージを送信](/api-reference/チャットフロー/チャットメッセージを送信) が返す `human_input_required` イベントから取得します。" + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" } ], "responses": { @@ -2801,7 +2801,7 @@ "properties": { "form_content": { "type": "string", - "description": "ワークフロー変数が置換された、事前レンダリング済みのフォーム本文。" + "description": "ワークフロー変数を埋め込んだ、事前レンダリング済みのフォーム本文。" }, "inputs": { "type": "array", @@ -2810,33 +2810,97 @@ "properties": { "type": { "type": "string", - "description": "フォーム入力コントロールの種類。使用可能な値:`text_input`(単一行テキスト)と `paragraph`(複数行テキスト)。" + "description": "フォーム入力コントロールの種類。利用可能な値: `paragraph` (複数行テキスト入力)、 `select` (リストからの単一選択)、 `file` (単一ファイルアップロード)、 `file-list` (複数ファイルアップロード)。" }, "output_variable_name": { "type": "string", - "description": "この入力の送信値をワークフロー内で参照する変数名。送信時の `inputs` オブジェクトのキーに対応します。" + "description": "ワークフロー内でこの入力の送信値を参照する際の変数名。送信リクエストの `inputs` オブジェクトのキーに対応します。" }, "default": { "type": "object", "nullable": true, - "description": "ワークフローコンテキストから解決されるデフォルト値。デフォルトが設定されていない場合は `null`。", + "description": "`paragraph` 入力の生のデフォルト値設定。クライアントはこのフィールドを直接解決すべきではありません。デフォルトの表示には `resolved_default_values` を使用してください。その他の入力タイプ、またはデフォルト値が未設定の場合は `null` になります。", "properties": { "type": { "type": "string", - "description": "デフォルト値のソース。`constant` は `value` をリテラル文字列として使用します。`variable` は `selector` がワークフロー変数を指します。" + "description": "デフォルト値のソース。 `constant` は `value` をリテラル文字列として使用することを示します。 `variable` は `selector` がワークフロー変数を指すことを示します。" }, "selector": { "type": "array", "items": { "type": "string" }, - "description": "`type` が `variable` の場合の変数参照パス(例:`[\"node_id\", \"var_name\"]`)。少なくとも 2 要素を含みます。" + "description": "`type` が `variable` の場合の変数参照パス(例: `[\"node_id\", \"var_name\"]`)。少なくとも 2 つの要素を含む必要があります。" }, "value": { "type": "string", - "description": "`type` が `constant` の場合のリテラルデフォルト値。常に文字列です。" + "description": "`type` が `constant` の場合のリテラルなデフォルト値。常に文字列です。" } } + }, + "option_source": { + "type": "object", + "description": "`select` 入力の選択肢のソース。 `type` が `select` の場合のみ含まれます。", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "選択肢のソース。 `constant` は `value` に選択肢を直接列挙することを示します。 `variable` は `selector` が選択肢を提供する `array[string]` 型のワークフロー変数を指すことを示します。" + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`type` が `variable` の場合の変数参照パス。" + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`type` が `constant` の場合のリテラルな選択肢リスト。" + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "受信者がアップロード可能なファイルカテゴリ。 `file` および `file-list` 入力に含まれます。値: `image`、 `document`、 `audio`、 `video`、 `custom`。" + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`allowed_file_types` に `custom` が含まれる場合に許可されるファイル拡張子。各拡張子には先頭の `.` を含めてください。たとえば `.md` です。`file` および `file-list` 入力に含まれます。" + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "受信者が使用できるアップロード方法。値: `local_file`、 `remote_url`。 `file` および `file-list` 入力に含まれます。" + }, + "number_limits": { + "type": "integer", + "description": "受信者がアップロードできるファイルの最大数。 `file-list` 入力のみに含まれます。" } } }, @@ -2847,7 +2911,7 @@ "additionalProperties": { "type": "string" }, - "description": "事前入力デフォルト値。入力の `output_variable_name` をキーとします。すべての値は文字列化されます。" + "description": "フォームに表示するための事前計算済みデフォルト値。入力の `output_variable_name` をキーとします。デフォルト値がワークフロー変数から解決可能な `paragraph` 入力にのみ設定されます。解決可能なデフォルトがない入力では空になります。クライアントはこれらの値をそのまま表示してください。 `default` をクライアント側で再解決する必要はありません。すべての値は文字列化されています。" }, "user_actions": { "type": "array", @@ -2858,7 +2922,7 @@ "type": "string", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "description": "アクションボタンの識別子。受信者がこのボタンを選択したら、`action` として [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) に渡します。" + "description": "アクションボタンの識別子。受信者がこのボタンを選択したときに、[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `action` として渡します。" }, "title": { "type": "string", @@ -2867,7 +2931,7 @@ }, "button_style": { "type": "string", - "description": "ボタンの視覚スタイル。使用可能な値:`primary`、`default`、`accent`、`ghost`。" + "description": "ボタンの視覚スタイル。利用可能な値: `primary`、 `default`、 `accent`、 `ghost`。" } } }, @@ -2876,7 +2940,7 @@ "expiration_time": { "type": "integer", "format": "int64", - "description": "Unix タイムスタンプ(秒)。これを過ぎるとフォームは送信できなくなります。" + "description": "Unix タイムスタンプ(秒)。この時刻以降、フォームは送信できなくなります。" } } }, @@ -2887,17 +2951,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -2919,7 +2998,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found` :フォームが見つかりません。", "content": { "application/json": { "examples": { @@ -2928,7 +3007,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "フォームが見つかりません" } } } @@ -2936,7 +3015,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted` :フォームは送信済みです。フォームはワンショットで、最初の応答が有効になります(送信したユーザーは問いません)。\n- `human_input_form_expired` :送信が到達する前にフォームの有効期限が切れています。", "content": { "application/json": { "examples": { @@ -2945,7 +3024,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは他のユーザーによって送信済みです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -2953,7 +3032,24 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは有効期限切れです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" } } } @@ -2967,7 +3063,7 @@ "人間の入力" ], "summary": "人間の入力フォームを送信", - "description": "一時停止中の人間の入力フォームに対する受信者の応答を送信します。フォームが受理されるとワークフローが再開するため、後続のイベントは [ワークフローイベントをストリーム](/api-reference/チャットフロー/ワークフローイベントをストリーム) で受信してください。**WebApp** 配信が必要です。", + "description": "一時停止中の人間の入力フォームに受信者の応答を送信します。受理されるとワークフローが再開され、後続のイベントはワークフローイベントをストリーミングエンドポイントで追跡できます。 WebApp 配信が必要です。", "operationId": "submitChatflowHumanInputForm", "parameters": [ { @@ -2977,7 +3073,7 @@ "schema": { "type": "string" }, - "description": "一時停止フォームのアクセストークン。ストリーミングモードで [チャットメッセージを送信](/api-reference/チャットフロー/チャットメッセージを送信) が返す `human_input_required` イベントから取得します。" + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" } ], "requestBody": { @@ -2994,12 +3090,99 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "受信者が提供した値。入力の `output_variable_name` をキーとします。" + "description": "各入力の `output_variable_name` をキーとする送信値。値の形は入力の `type` によって異なります。形ごとのスキーマタブを参照してください。", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph または select", + "type": "string", + "description": "`paragraph`(自由テキスト)と `select`(選ばれた 1 つの選択肢)入力の値の形。" + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "バックエンドがこのファイルをどのように解決するか。`local_file` は、[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントで既にアップロードされたファイルを参照します。`remote_url` は、指定された URL から送信時にファイルを取得するようバックエンドに指示します。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` が `local_file` のとき必須。[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントから返される `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` が `remote_url` のとき必須。公開アクセス可能な HTTPS URL です。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "省略可能なファイルカテゴリの上書き。設定する場合、フォーム入力の `allowed_file_types` と整合している必要があります。" + } + }, + "description": "`file` 入力の値の形。1 つのファイルマッピングオブジェクトを送信します。" + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "バックエンドがこのファイルをどのように解決するか。`local_file` は、[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントで既にアップロードされたファイルを参照します。`remote_url` は、指定された URL から送信時にファイルを取得するようバックエンドに指示します。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` が `local_file` のとき必須。[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントから返される `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` が `remote_url` のとき必須。公開アクセス可能な HTTPS URL です。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "省略可能なファイルカテゴリの上書き。設定する場合、フォーム入力の `allowed_file_types` と整合している必要があります。" + } + } + }, + "description": "`file-list` 入力の値の形。ファイルマッピングオブジェクトの配列を送信します。" + } + ] + } }, "action": { "type": "string", - "description": "受信者が選択したアクションボタンの ID。[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) で返される `user_actions` リストに含まれるいずれかの `id` 値と一致する必要があります。", + "description": "受信者が選択したアクションボタンの ID。フォームの `user_actions` リスト([人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) エンドポイントから返される)にある `id` のいずれかと一致する必要があります。", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, @@ -3014,7 +3197,19 @@ "summary": "リクエスト例", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "公開して問題ありません", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -3026,7 +3221,7 @@ }, "responses": { "200": { - "description": "フォームの送信に成功しました。レスポンスボディは空オブジェクトです。", + "description": "フォームの送信に成功しました。レスポンスボディは空のオブジェクトです。", "content": { "application/json": { "schema": { @@ -3042,7 +3237,7 @@ } }, "400": { - "description": "- `bad_request` : Form recipient type is invalid.\n- `invalid_form_data` : Submission failed validation against the form definition.", + "description": "- `bad_request` :フォーム受信者のタイプが無効です。\n- `invalid_form_data` :送信内容がフォーム定義の検証に失敗しました。", "content": { "application/json": { "examples": { @@ -3051,7 +3246,7 @@ "value": { "status": 400, "code": "bad_request", - "message": "Form recipient type is invalid" + "message": "フォーム受信者のタイプが無効です" } }, "invalid_form_data": { @@ -3059,7 +3254,7 @@ "value": { "status": 400, "code": "invalid_form_data", - "message": "Missing required inputs: comment" + "message": "必須入力が不足しています:comment" } } } @@ -3067,7 +3262,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found` :フォームが見つかりません。", "content": { "application/json": { "examples": { @@ -3076,7 +3271,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "フォームが見つかりません" } } } @@ -3084,7 +3279,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted` :フォームは送信済みです。フォームはワンショットで、最初の応答が有効になります(送信したユーザーは問いません)。\n- `human_input_form_expired` :送信が到達する前にフォームの有効期限が切れています。", "content": { "application/json": { "examples": { @@ -3093,7 +3288,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは他のユーザーによって送信済みです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -3101,7 +3296,577 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは有効期限切れです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "アップロードトークンを発行", + "description": "一時停止中の人間の入力フォームに対して、短期有効なアップロードトークンを発行します。 WebApp 配信が必要です。", + "operationId": "issueChatflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" + } + ], + "responses": { + "200": { + "description": "アップロードトークンの発行に成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "`Bearer` トークンとして渡し、フォームの `file` または `file-list` 入力にファイルを添付します。送信先は [ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード) または [リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード) エンドポイントです。" + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix タイムスタンプ(秒)。この時刻以降、 `upload_token` は受け付けられなくなります。" + } + } + }, + "examples": { + "success": { + "summary": "レスポンス例", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found` :フォームが見つかりません。", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "フォームが見つかりません" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのフォームリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "ローカルファイルをアップロード", + "description": "一時停止中の人間の入力フォームにローカルファイルを 1 つアップロードします。", + "operationId": "uploadChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "ファイルアップロードリクエスト。multipart/form-data が必要です。", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "アップロードするファイル。フォーム入力の `allowed_file_types` と `allowed_file_extensions` に従う必要があります。" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "ファイルのアップロードに成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `file` および `file-list` 入力で、この値を `upload_file_id` として参照します。" + }, + "name": { + "type": "string", + "description": "元のファイル名。" + }, + "size": { + "type": "integer", + "description": "ファイルサイズ(バイト)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "ファイル拡張子。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "ファイルの MIME タイプ。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "ファイルのアップロード対象ユーザーの識別子。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "ファイル作成時の Unix タイムスタンプ(秒)。" + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "ファイルプレビュー用の署名付き URL。生成されていない場合は `null`。" + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "リモートから取得した場合の元のソース URL。ローカルアップロードの場合は `null`。" + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "アップロードを所有するユーザーの識別子。" + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "アップロードが属するテナント。" + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "人間の入力のアップロードでは常に `null`。" + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "内部ストレージキー。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "レスポンス例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded` :リクエストにファイルが含まれていません。\n- `too_many_files` :リクエストごとに 1 ファイルのみ許可されます。\n- `filename_not_exists_error` :アップロードされたファイルにファイル名がありません。\n- `file_extension_blocked` :セキュリティ上の理由により、このファイル拡張子はブロックされています。\n- `invalid_upload_token` : `Authorization` ヘッダの形式が不正です。", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "ファイルをアップロードしてください。" + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "1 ファイルのみ許可されています。" + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "指定されたファイル名は存在しません。" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "セキュリティ上の理由により、このファイル拡張子はブロックされています。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効です。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` :アップロードトークンが必要です。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "アップロードトークンが必要です。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` :アップロードトークンが無効、または有効期限切れです。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効、または有効期限切れです。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` :ファイルサイズが上限を超えています。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "ファイルサイズが上限を超えています。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` :許可されていないファイルタイプです。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "許可されていないファイルタイプです。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "リモートファイルをアップロード", + "description": "リモート URL を取得し、得られたファイルを一時停止中の人間の入力フォームに添付します。", + "operationId": "uploadRemoteChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "取得して添付するファイルの、公開アクセス可能な URL。" + } + } + }, + "examples": { + "remoteUpload": { + "summary": "リクエスト例", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "リモートファイルの取得と添付に成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `file` および `file-list` 入力で、この値を `upload_file_id` として参照します。" + }, + "name": { + "type": "string", + "description": "リモート URL またはレスポンスヘッダから導出されたファイル名。" + }, + "size": { + "type": "integer", + "description": "ファイルサイズ(バイト)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "ファイル拡張子。" + }, + "url": { + "type": "string", + "nullable": true, + "description": "ファイル内容を取得するための署名付き URL。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "ファイルの MIME タイプ。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "ファイルのアップロード対象ユーザーの識別子。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "ファイル作成時の Unix タイムスタンプ(秒)。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "レスポンス例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error` :リモート URL の取得に失敗しました。\n- `file_extension_blocked` :セキュリティ上の理由により、このファイル拡張子はブロックされています。\n- `invalid_upload_token` : `Authorization` ヘッダの形式が不正です。", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "https://example.com/report.pdf からのファイル取得に失敗しました:connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "セキュリティ上の理由により、このファイル拡張子はブロックされています。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効です。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` :アップロードトークンが必要です。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "アップロードトークンが必要です。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` :アップロードトークンが無効、または有効期限切れです。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効、または有効期限切れです。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` :リモートファイルがサイズ上限を超えています。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "ファイルサイズが上限を超えています。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` :許可されていないファイルタイプです。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "許可されていないファイルタイプです。" } } } @@ -3217,6 +3982,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key 認証です。すべての API リクエストにおいて、`Authorization` HTTP ヘッダーに `Bearer ` プレフィックスを付けた API Key を含めてください。例:`Authorization: Bearer {API_KEY}`。**API Key はサーバーサイドに保存し、クライアントサイドで共有・保存しないことを強く推奨します。API Key の漏洩は深刻な結果につながる可能性があります。**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "人間の入力フォームへのファイル添付に使う短期のアップロードトークン。[アップロードトークンを発行](/api-reference/人間の入力/アップロードトークンを発行) エンドポイントから取得し、 `Authorization: Bearer hitl_upload_{token}` の形式で渡します。" } }, "responses": { @@ -5928,7 +6699,7 @@ "properties": { "type": { "type": "string", - "description": "フォーム入力コントロールの種類。使用可能な値:`text_input`(単一行テキスト)と `paragraph`(複数行テキスト)。" + "description": "フォーム入力コントロールの種類。利用可能な値: `paragraph` (複数行テキスト入力)、 `select` (リストからの単一選択)、 `file` (単一ファイルアップロード)、 `file-list` (複数ファイルアップロード)。" }, "output_variable_name": { "type": "string", @@ -5991,7 +6762,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) と [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) のアクセストークン。人間の入力ノードがメール配信またはコンソール配信を使用する場合は `null` になります(Service API は WebApp 配信のフォームのみ操作できます)。" + "description": "[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) と [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) のアクセストークン。人間の入力ノードが WebApp 以外の配信方法を使用している場合は `null` です(Service API は WebApp 配信のフォームのみ操作できます)。" }, "resolved_default_values": { "type": "object", @@ -6609,7 +7380,7 @@ "properties": { "type": { "type": "string", - "description": "`text_input` は単一行テキスト、`paragraph` は複数行テキストです。" + "description": "利用可能な値: `paragraph` 、 `select` 、 `file` 、 `file-list` 。" }, "output_variable_name": { "type": "string", diff --git a/ja/api-reference/openapi_workflow.json b/ja/api-reference/openapi_workflow.json index 65ce4712e..52b08a51b 100644 --- a/ja/api-reference/openapi_workflow.json +++ b/ja/api-reference/openapi_workflow.json @@ -128,7 +128,7 @@ }, "humanInputPause": { "summary": "レスポンス例 - 人間の入力での一時停止", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -317,7 +317,7 @@ "examples": { "humanInputPause": { "summary": "レスポンス例 - 人間の入力での一時停止", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -1556,7 +1556,7 @@ "人間の入力" ], "summary": "人間の入力フォームを取得", - "description": "`human_input_required` イベントの `form_token` を用いて、一時停止中の人間の入力フォームの内容を取得します。**WebApp** 配信が必要です。", + "description": "一時停止中の人間の入力フォームの内容を取得します。 WebApp 配信が必要です。\n\n人間の入力エンドポイントを呼び出す一連の流れについては、[API 連携フロー](/ja/use-dify/nodes/hitl-api-integration-flow) を参照してください。", "operationId": "getWorkflowHumanInputForm", "parameters": [ { @@ -1566,7 +1566,7 @@ "schema": { "type": "string" }, - "description": "一時停止フォームのアクセストークン。ストリーミングモードで [ワークフローを実行](/api-reference/ワークフロー/ワークフローを実行) が返す `human_input_required` イベントから取得します。" + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" } ], "responses": { @@ -1579,7 +1579,7 @@ "properties": { "form_content": { "type": "string", - "description": "ワークフロー変数が置換された、事前レンダリング済みのフォーム本文。" + "description": "ワークフロー変数を埋め込んだ、事前レンダリング済みのフォーム本文。" }, "inputs": { "type": "array", @@ -1588,33 +1588,97 @@ "properties": { "type": { "type": "string", - "description": "フォーム入力コントロールの種類。使用可能な値:`text_input`(単一行テキスト)と `paragraph`(複数行テキスト)。" + "description": "フォーム入力コントロールの種類。利用可能な値: `paragraph` (複数行テキスト入力)、 `select` (リストからの単一選択)、 `file` (単一ファイルアップロード)、 `file-list` (複数ファイルアップロード)。" }, "output_variable_name": { "type": "string", - "description": "この入力の送信値をワークフロー内で参照する変数名。送信時の `inputs` オブジェクトのキーに対応します。" + "description": "ワークフロー内でこの入力の送信値を参照する際の変数名。送信リクエストの `inputs` オブジェクトのキーに対応します。" }, "default": { "type": "object", "nullable": true, - "description": "ワークフローコンテキストから解決されるデフォルト値。デフォルトが設定されていない場合は `null`。", + "description": "`paragraph` 入力の生のデフォルト値設定。クライアントはこのフィールドを直接解決すべきではありません。デフォルトの表示には `resolved_default_values` を使用してください。その他の入力タイプ、またはデフォルト値が未設定の場合は `null` になります。", "properties": { "type": { "type": "string", - "description": "デフォルト値のソース。`constant` は `value` をリテラル文字列として使用します。`variable` は `selector` がワークフロー変数を指します。" + "description": "デフォルト値のソース。 `constant` は `value` をリテラル文字列として使用することを示します。 `variable` は `selector` がワークフロー変数を指すことを示します。" }, "selector": { "type": "array", "items": { "type": "string" }, - "description": "`type` が `variable` の場合の変数参照パス(例:`[\"node_id\", \"var_name\"]`)。少なくとも 2 要素を含みます。" + "description": "`type` が `variable` の場合の変数参照パス(例: `[\"node_id\", \"var_name\"]`)。少なくとも 2 つの要素を含む必要があります。" }, "value": { "type": "string", - "description": "`type` が `constant` の場合のリテラルデフォルト値。常に文字列です。" + "description": "`type` が `constant` の場合のリテラルなデフォルト値。常に文字列です。" } } + }, + "option_source": { + "type": "object", + "description": "`select` 入力の選択肢のソース。 `type` が `select` の場合のみ含まれます。", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "選択肢のソース。 `constant` は `value` に選択肢を直接列挙することを示します。 `variable` は `selector` が選択肢を提供する `array[string]` 型のワークフロー変数を指すことを示します。" + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`type` が `variable` の場合の変数参照パス。" + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`type` が `constant` の場合のリテラルな選択肢リスト。" + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "受信者がアップロード可能なファイルカテゴリ。 `file` および `file-list` 入力に含まれます。値: `image`、 `document`、 `audio`、 `video`、 `custom`。" + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "`allowed_file_types` に `custom` が含まれる場合に許可されるファイル拡張子。各拡張子には先頭の `.` を含めてください。たとえば `.md` です。`file` および `file-list` 入力に含まれます。" + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "受信者が使用できるアップロード方法。値: `local_file`、 `remote_url`。 `file` および `file-list` 入力に含まれます。" + }, + "number_limits": { + "type": "integer", + "description": "受信者がアップロードできるファイルの最大数。 `file-list` 入力のみに含まれます。" } } }, @@ -1625,7 +1689,7 @@ "additionalProperties": { "type": "string" }, - "description": "事前入力デフォルト値。入力の `output_variable_name` をキーとします。すべての値は文字列化されます。" + "description": "フォームに表示するための事前計算済みデフォルト値。入力の `output_variable_name` をキーとします。デフォルト値がワークフロー変数から解決可能な `paragraph` 入力にのみ設定されます。解決可能なデフォルトがない入力では空になります。クライアントはこれらの値をそのまま表示してください。 `default` をクライアント側で再解決する必要はありません。すべての値は文字列化されています。" }, "user_actions": { "type": "array", @@ -1636,7 +1700,7 @@ "type": "string", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "description": "アクションボタンの識別子。受信者がこのボタンを選択したら、`action` として [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) に渡します。" + "description": "アクションボタンの識別子。受信者がこのボタンを選択したときに、[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `action` として渡します。" }, "title": { "type": "string", @@ -1645,7 +1709,7 @@ }, "button_style": { "type": "string", - "description": "ボタンの視覚スタイル。使用可能な値:`primary`、`default`、`accent`、`ghost`。" + "description": "ボタンの視覚スタイル。利用可能な値: `primary`、 `default`、 `accent`、 `ghost`。" } } }, @@ -1654,7 +1718,7 @@ "expiration_time": { "type": "integer", "format": "int64", - "description": "Unix タイムスタンプ(秒)。これを過ぎるとフォームは送信できなくなります。" + "description": "Unix タイムスタンプ(秒)。この時刻以降、フォームは送信できなくなります。" } } }, @@ -1665,17 +1729,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -1697,7 +1776,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found` :フォームが見つかりません。", "content": { "application/json": { "examples": { @@ -1706,7 +1785,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "フォームが見つかりません" } } } @@ -1714,7 +1793,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted` :フォームは送信済みです。フォームはワンショットで、最初の応答が有効になります(送信したユーザーは問いません)。\n- `human_input_form_expired` :送信が到達する前にフォームの有効期限が切れています。", "content": { "application/json": { "examples": { @@ -1723,7 +1802,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは他のユーザーによって送信済みです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -1731,7 +1810,24 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは有効期限切れです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" } } } @@ -1745,7 +1841,7 @@ "人間の入力" ], "summary": "人間の入力フォームを送信", - "description": "一時停止中の人間の入力フォームに対する受信者の応答を送信します。フォームが受理されるとワークフローが再開するため、後続のイベントは [ワークフローイベントをストリーム](/api-reference/ワークフロー/ワークフローイベントをストリーム) で受信してください。**WebApp** 配信が必要です。", + "description": "一時停止中の人間の入力フォームに受信者の応答を送信します。受理されるとワークフローが再開され、後続のイベントはワークフローイベントをストリーミングエンドポイントで追跡できます。 WebApp 配信が必要です。", "operationId": "submitWorkflowHumanInputForm", "parameters": [ { @@ -1755,7 +1851,7 @@ "schema": { "type": "string" }, - "description": "一時停止フォームのアクセストークン。ストリーミングモードで [ワークフローを実行](/api-reference/ワークフロー/ワークフローを実行) が返す `human_input_required` イベントから取得します。" + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" } ], "requestBody": { @@ -1772,12 +1868,99 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "受信者が提供した値。入力の `output_variable_name` をキーとします。" + "description": "各入力の `output_variable_name` をキーとする送信値。値の形は入力の `type` によって異なります。形ごとのスキーマタブを参照してください。", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph または select", + "type": "string", + "description": "`paragraph`(自由テキスト)と `select`(選ばれた 1 つの選択肢)入力の値の形。" + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "バックエンドがこのファイルをどのように解決するか。`local_file` は、[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントで既にアップロードされたファイルを参照します。`remote_url` は、指定された URL から送信時にファイルを取得するようバックエンドに指示します。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` が `local_file` のとき必須。[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントから返される `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` が `remote_url` のとき必須。公開アクセス可能な HTTPS URL です。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "省略可能なファイルカテゴリの上書き。設定する場合、フォーム入力の `allowed_file_types` と整合している必要があります。" + } + }, + "description": "`file` 入力の値の形。1 つのファイルマッピングオブジェクトを送信します。" + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "バックエンドがこのファイルをどのように解決するか。`local_file` は、[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントで既にアップロードされたファイルを参照します。`remote_url` は、指定された URL から送信時にファイルを取得するようバックエンドに指示します。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` が `local_file` のとき必須。[ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード)または[リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード)エンドポイントから返される `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` が `remote_url` のとき必須。公開アクセス可能な HTTPS URL です。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "省略可能なファイルカテゴリの上書き。設定する場合、フォーム入力の `allowed_file_types` と整合している必要があります。" + } + } + }, + "description": "`file-list` 入力の値の形。ファイルマッピングオブジェクトの配列を送信します。" + } + ] + } }, "action": { "type": "string", - "description": "受信者が選択したアクションボタンの ID。[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) で返される `user_actions` リストに含まれるいずれかの `id` 値と一致する必要があります。", + "description": "受信者が選択したアクションボタンの ID。フォームの `user_actions` リスト([人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) エンドポイントから返される)にある `id` のいずれかと一致する必要があります。", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, @@ -1792,7 +1975,19 @@ "summary": "リクエスト例", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "公開して問題ありません", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -1804,7 +1999,7 @@ }, "responses": { "200": { - "description": "フォームの送信に成功しました。レスポンスボディは空オブジェクトです。", + "description": "フォームの送信に成功しました。レスポンスボディは空のオブジェクトです。", "content": { "application/json": { "schema": { @@ -1820,7 +2015,7 @@ } }, "400": { - "description": "- `bad_request` : Form recipient type is invalid.\n- `invalid_form_data` : Submission failed validation against the form definition.", + "description": "- `bad_request` :フォーム受信者のタイプが無効です。\n- `invalid_form_data` :送信内容がフォーム定義の検証に失敗しました。", "content": { "application/json": { "examples": { @@ -1829,7 +2024,7 @@ "value": { "status": 400, "code": "bad_request", - "message": "Form recipient type is invalid" + "message": "フォーム受信者のタイプが無効です" } }, "invalid_form_data": { @@ -1837,7 +2032,7 @@ "value": { "status": 400, "code": "invalid_form_data", - "message": "Missing required inputs: comment" + "message": "必須入力が不足しています:comment" } } } @@ -1845,7 +2040,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found` :フォームが見つかりません。", "content": { "application/json": { "examples": { @@ -1854,7 +2049,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "フォームが見つかりません" } } } @@ -1862,7 +2057,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted` :フォームは送信済みです。フォームはワンショットで、最初の応答が有効になります(送信したユーザーは問いません)。\n- `human_input_form_expired` :送信が到達する前にフォームの有効期限が切れています。", "content": { "application/json": { "examples": { @@ -1871,7 +2066,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは他のユーザーによって送信済みです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -1879,7 +2074,577 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "このフォームは有効期限切れです。form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "アップロードトークンを発行", + "description": "一時停止中の人間の入力フォームに対して、短期有効なアップロードトークンを発行します。 WebApp 配信が必要です。", + "operationId": "issueWorkflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "一時停止中のフォームへのアクセストークン。ストリーミングモードのワークフローを実行エンドポイントまたはチャットメッセージを送信エンドポイントが返す `human_input_required` イベントから取得します。" + } + ], + "responses": { + "200": { + "description": "アップロードトークンの発行に成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "`Bearer` トークンとして渡し、フォームの `file` または `file-list` 入力にファイルを添付します。送信先は [ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード) または [リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード) エンドポイントです。" + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix タイムスタンプ(秒)。この時刻以降、 `upload_token` は受け付けられなくなります。" + } + } + }, + "examples": { + "success": { + "summary": "レスポンス例", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found` :フォームが見つかりません。", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "フォームが見つかりません" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded` :このクライアントからのフォームリクエストが多すぎます。しばらく経ってから再試行してください。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "フォームリクエストが多すぎます。しばらく経ってから再試行してください。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "ローカルファイルをアップロード", + "description": "一時停止中の人間の入力フォームにローカルファイルを 1 つアップロードします。", + "operationId": "uploadWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "ファイルアップロードリクエスト。multipart/form-data が必要です。", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "アップロードするファイル。フォーム入力の `allowed_file_types` と `allowed_file_extensions` に従う必要があります。" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "ファイルのアップロードに成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `file` および `file-list` 入力で、この値を `upload_file_id` として参照します。" + }, + "name": { + "type": "string", + "description": "元のファイル名。" + }, + "size": { + "type": "integer", + "description": "ファイルサイズ(バイト)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "ファイル拡張子。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "ファイルの MIME タイプ。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "ファイルのアップロード対象ユーザーの識別子。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "ファイル作成時の Unix タイムスタンプ(秒)。" + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "ファイルプレビュー用の署名付き URL。生成されていない場合は `null`。" + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "リモートから取得した場合の元のソース URL。ローカルアップロードの場合は `null`。" + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "アップロードを所有するユーザーの識別子。" + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "アップロードが属するテナント。" + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "人間の入力のアップロードでは常に `null`。" + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "内部ストレージキー。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "レスポンス例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded` :リクエストにファイルが含まれていません。\n- `too_many_files` :リクエストごとに 1 ファイルのみ許可されます。\n- `filename_not_exists_error` :アップロードされたファイルにファイル名がありません。\n- `file_extension_blocked` :セキュリティ上の理由により、このファイル拡張子はブロックされています。\n- `invalid_upload_token` : `Authorization` ヘッダの形式が不正です。", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "ファイルをアップロードしてください。" + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "1 ファイルのみ許可されています。" + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "指定されたファイル名は存在しません。" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "セキュリティ上の理由により、このファイル拡張子はブロックされています。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効です。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` :アップロードトークンが必要です。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "アップロードトークンが必要です。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` :アップロードトークンが無効、または有効期限切れです。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効、または有効期限切れです。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` :ファイルサイズが上限を超えています。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "ファイルサイズが上限を超えています。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` :許可されていないファイルタイプです。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "許可されていないファイルタイプです。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "人間の入力" + ], + "summary": "リモートファイルをアップロード", + "description": "リモート URL を取得し、得られたファイルを一時停止中の人間の入力フォームに添付します。", + "operationId": "uploadRemoteWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "取得して添付するファイルの、公開アクセス可能な URL。" + } + } + }, + "examples": { + "remoteUpload": { + "summary": "リクエスト例", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "リモートファイルの取得と添付に成功しました。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "[人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントの `file` および `file-list` 入力で、この値を `upload_file_id` として参照します。" + }, + "name": { + "type": "string", + "description": "リモート URL またはレスポンスヘッダから導出されたファイル名。" + }, + "size": { + "type": "integer", + "description": "ファイルサイズ(バイト)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "ファイル拡張子。" + }, + "url": { + "type": "string", + "nullable": true, + "description": "ファイル内容を取得するための署名付き URL。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "ファイルの MIME タイプ。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "ファイルのアップロード対象ユーザーの識別子。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "ファイル作成時の Unix タイムスタンプ(秒)。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "レスポンス例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error` :リモート URL の取得に失敗しました。\n- `file_extension_blocked` :セキュリティ上の理由により、このファイル拡張子はブロックされています。\n- `invalid_upload_token` : `Authorization` ヘッダの形式が不正です。", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "https://example.com/report.pdf からのファイル取得に失敗しました:connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "セキュリティ上の理由により、このファイル拡張子はブロックされています。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効です。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token` :アップロードトークンが必要です。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "アップロードトークンが必要です。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token` :アップロードトークンが無効、または有効期限切れです。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "アップロードトークンが無効、または有効期限切れです。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large` :リモートファイルがサイズ上限を超えています。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "ファイルサイズが上限を超えています。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type` :許可されていないファイルタイプです。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "許可されていないファイルタイプです。" } } } @@ -1995,6 +2760,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key 認証です。すべての API リクエストにおいて、`Authorization` HTTP ヘッダーに `Bearer ` プレフィックスを付けた API Key を含めてください。例:`Authorization: Bearer {API_KEY}`。**API Key はサーバーサイドに保存し、クライアントサイドで共有・保存しないことを強く推奨します。API Key の漏洩は深刻な結果につながる可能性があります。**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "人間の入力フォームへのファイル添付に使う短期のアップロードトークン。[アップロードトークンを発行](/api-reference/人間の入力/アップロードトークンを発行) エンドポイントから取得し、 `Authorization: Bearer hitl_upload_{token}` の形式で渡します。" } }, "responses": { @@ -4162,7 +4933,7 @@ "properties": { "type": { "type": "string", - "description": "フォーム入力コントロールの種類。使用可能な値:`text_input`(単一行テキスト)と `paragraph`(複数行テキスト)。" + "description": "フォーム入力コントロールの種類。利用可能な値: `paragraph` (複数行テキスト入力)、 `select` (リストからの単一選択)、 `file` (単一ファイルアップロード)、 `file-list` (複数ファイルアップロード)。" }, "output_variable_name": { "type": "string", @@ -4225,7 +4996,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) と [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) のアクセストークン。人間の入力ノードがメール配信またはコンソール配信を使用する場合は `null` になります(Service API は WebApp 配信のフォームのみ操作できます)。" + "description": "[人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) と [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) のアクセストークン。人間の入力ノードが WebApp 以外の配信方法を使用している場合は `null` です(Service API は WebApp 配信のフォームのみ操作できます)。" }, "resolved_default_values": { "type": "object", diff --git a/ja/self-host/configuration/environments.mdx b/ja/self-host/configuration/environments.mdx index 30935c51e..6fe166337 100644 --- a/ja/self-host/configuration/environments.mdx +++ b/ja/self-host/configuration/environments.mdx @@ -1153,7 +1153,7 @@ Dify はアカウント招待、パスワードリセット、ログインコー | `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | ワークフロー実行ごとの最大実行時間(秒)。超過するとワークフローが終了されます。 | | `WORKFLOW_CALL_MAX_DEPTH` | `5` | ネストされたワークフロー呼び出しの最大深度。無限再帰を防止します。 | | `MAX_VARIABLE_SIZE` | `204800` | 単一のワークフロー変数の最大サイズ(バイト、200 KB)。 | -| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | 単一のワークフロー実行でアップロードできる最大ファイル数。 | +| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | 1 つのファイルアップロードフィールド(ユーザー入力ノードのファイル変数など)における最大ファイル数設定の上限です。ノードパネルの最大ファイル数スライダーはこの値で制限されます。フィールドごとの設定をより大きくするには、この値を上げます。 | | `WORKFLOW_NODE_EXECUTION_STORAGE` | `rdbms` | ワークフローノード実行レコードの保存先。`rdbms`はすべてをデータベースに保存します。`hybrid` は新しいデータをオブジェクトストレージに保存し、両方から読み取ります。 | | `DSL_EXPORT_ENCRYPT_DATASET_ID` | `true` | DSL ファイルエクスポート時にデータセット ID を暗号化。クロス環境インポートを容易にするためにプレーン ID をエクスポートする場合は `false` に設定してください。 | diff --git a/ja/use-dify/nodes/hitl-api-integration-flow.mdx b/ja/use-dify/nodes/hitl-api-integration-flow.mdx new file mode 100644 index 000000000..e0caf50d6 --- /dev/null +++ b/ja/use-dify/nodes/hitl-api-integration-flow.mdx @@ -0,0 +1,125 @@ +--- +title: 人間の入力 API 連携フロー +sidebarTitle: API 連携フロー +description: 一時停止中の人間の入力フォームを API で扱うエンドツーエンドのフロー +--- + + ⚠️ このドキュメントは AI によって自動翻訳されています。不正確な部分がある場合は、[英語版](/en/use-dify/nodes/human-input/integration-flow) を参照してください。 + +ワークフローが人間の入力ノードに到達すると一時停止し、ストリーミングレスポンスから `human_input_required` イベントが送出されます。このイベントが運ぶ `form_token` を使って、ワークフローが再開するまでフォームのライフサイクルを進めます。 + +各エンドポイントのリファレンスは [人間の入力 API](/api-reference/人間の入力/人間の入力フォームを取得) を参照してください。 + +## フロー + +以下のフローは Workflow アプリと Chatflow アプリの両方に共通します。両者は手順 1 のエントリエンドポイントと手順 6 の再開エンドポイントだけが異なります。 + + + + + 1. Workflow アプリは [ワークフローを実行](/api-reference/ワークフロー/ワークフローを実行) を呼び出します。Chatflow アプリは [チャットメッセージを送信](/api-reference/チャットフロー/チャットメッセージを送信) を呼び出します。 + 2. SSE ストリームで `human_input_required` イベントを監視し、ペイロードから `form_token`、`form_id`、`task_id` を取得します。 + + SSE ストリームがワークフロー再開前に切断された場合、手順 6 で `task_id` を使います。 + + + `form_token` を指定して [人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) エンドポイントを呼び出します。レスポンスにはレンダリング済みの Markdown、入力フィールド定義、利用可能なアクション、事前入力済みのデフォルト値が含まれます。フォームを受信者に表示します。 + + + フォームに `file` または `file-list` 入力が含まれる場合のみ実行します。[アップロードトークンを発行](/api-reference/人間の入力/アップロードトークンを発行) エンドポイントを呼び出すと、短期有効な `upload_token` が返ります。同じフォームに対する複数のアップロードでこのトークンを再利用できます。 + + + 受信者が添付するファイルごとに、次のいずれかを呼び出します。 + + - [ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード) エンドポイント。受信者のデバイス上のファイル用(multipart)。 + - [リモートファイルをアップロード](/api-reference/人間の入力/リモートファイルをアップロード) エンドポイント。送信前にバックエンドで取得・検証したい公開 URL のファイル用。 + + どちらも `id` を返します。送信時にこの値を `upload_file_id` として参照します。 + + + [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントを呼び出し、受信者の入力値と選択されたアクションを送ります。 + + ファイル入力には、`{transfer_method: local_file, upload_file_id}` マッピング(手順 4 の結果)と、インラインの `{transfer_method: remote_url, url}` マッピングのどちらも使えます。トレードオフの詳細は [事前アップロードかインラインリモート URL か](#事前アップロードかインラインリモート-url-か) を参照してください。 + + 送信に成功すると、ワークフローは該当するアクション分岐から再開します。 + + + 元の SSE ストリームが切断されている場合は、手順 1 の `task_id` を使います。[Workflow](/api-reference/ワークフロー/ワークフローイベントをストリーム) または [Chatflow](/api-reference/チャットフロー/ワークフローイベントをストリーム) のワークフローイベントをストリームエンドポイントに再接続します。 + + + +## 事前アップロードかインラインリモート URL か + +ファイル入力には 2 つの送信パターンがあります。 + +- **事前アップロードしてから `upload_file_id` で送信**(推奨) + + バックエンドは、ファイルサイズ・タイプ・拡張子をアップロード時に検証します。受信者はエラーをすぐに確認でき、送信前に再試行できます。 + +- **`transfer_method: remote_url` でインライン送信** + + バックエンドは送信時にファイルを取得します。実装は速いですが、サイズ・タイプ・取得の失敗があると送信全体が拒否され、他のフィールドを再入力する必要が出る場合があります。 + + + 受信者からのフィードバックが伴うインタラクティブなフォームでは、事前アップロードのパターンを推奨します。完全に自動化されたインテグレーションで、誰も再入力を待たされない場合のみ、インラインのトレードオフが見合います。 + + +## 認証 + +フォーム向けの [人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) と [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントは認証不要です。`form_token` 自体がアクセス資格情報として機能し、これらのエンドポイントはクライアント IP ごとにレート制限されます。 + +ファイルアップロードエンドポイントは、手順 3 で取得した `Bearer` `upload_token` を必要とします。 + +## 配信方法の前提 + +人間の入力 API は、人間の入力ノードで WebApp 配信方法が設定されたフォームのみを対象とします。メール配信のみのフォームは、API に `form_token` を公開しません。 + +## 例:ファイル添付付きの送信 + +このフォームには、`feedback` 段落入力、`attachments` ファイルリスト入力、`approve` / `revise` アクションがあるものとします。 + +1. [人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) エンドポイントを呼び出してフォーム定義を取得します。 + + ```http + GET /form/human_input/ + ``` + +2. [アップロードトークンを発行](/api-reference/人間の入力/アップロードトークンを発行) エンドポイントを呼び出してアップロードトークンを取得します。 + + ```http + POST /form/human_input//upload-token + ``` + + `{"upload_token": "hitl_upload_...", "expires_at": ...}` が返ります。 + +3. ローカルファイルごとに [ローカルファイルをアップロード](/api-reference/人間の入力/ローカルファイルをアップロード) エンドポイントを呼び出します。 + + ```http + POST /form/human_input/files/upload + Authorization: Bearer hitl_upload_... + Content-Type: multipart/form-data + + file= + ``` + + `{"id": "1a77f0df-...", ...}` が返ります。 + +4. [人間の入力フォームを送信](/api-reference/人間の入力/人間の入力フォームを送信) エンドポイントを呼び出し、受信者の入力と選択されたアクションを送ります。 + + ```http + POST /form/human_input/ + Content-Type: application/json + + { + "inputs": { + "feedback": "公開して問題ありません", + "attachments": [ + {"transfer_method": "local_file", "upload_file_id": "1a77f0df-..."} + ] + }, + "action": "approve", + "user": "abc-123" + } + ``` + + `{}` が返ります。ワークフローは `approve` 分岐から再開します。 diff --git a/ja/use-dify/nodes/human-input.mdx b/ja/use-dify/nodes/human-input.mdx index ccc125704..3be85399e 100644 --- a/ja/use-dify/nodes/human-input.mdx +++ b/ja/use-dify/nodes/human-input.mdx @@ -1,38 +1,38 @@ --- title: 人間の入力 -description: ワークフローを一時停止して、人間の入力、レビュー、または意思決定を要求します +description: ワークフローを一時停止して人間の入力を要求します --- ⚠️ このドキュメントは AI によって自動翻訳されています。不正確な部分がある場合は、[英語版](/en/use-dify/nodes/human-input) を参照してください。 人間の入力ノードは、ワークフローを重要なポイントで一時停止し、カスタマイズ可能なリクエストフォームを配信します。受信者はフォームを使って情報を確認し、入力を提供し、ワークフローの進行方法を決定する事前定義された選択肢から選ぶことができます。 -重要な場面に人間の判断を直接組み込むことで、 **自動化による効率性と人間による監視のバランス** をとることができます。 +重要な場面に人間の判断を直接組み込むことで、 **自動化による効率と人間による監視のバランス** をとることができます。 - - ![リクエストフォームの例](/images/use-dify/workflow/human-input-request-form-example.png) - + + ワークフロー設計の完全な例については、[例:コンテンツレビューワークフロー](#example-content-review-workflow) を参照してください。 + ## 設定 -ノードがどのように人間の入力を要求し処理するかを定義するために、以下を設定します: +ノードがどのように人間の入力を要求し処理するかを定義するために、以下を設定します。 -- **配信方法**: リクエストフォームがどのように受信者に届くか。 +- **配信方法**:リクエストフォームがどのように受信者に届くか。 - **フォーム内容**: 受信者に表示される情報と、操作できる内容。 -- **ユーザーアクション**: 受信者が行える決定と、それに応じたワークフローの進行方法。 +- **ユーザーアクション**:受信者が行える決定と、それに応じたワークフローの進行方法。 -- **タイムアウト戦略**: 待機時間と、受信者が応答しなかった場合の処理。 +- **タイムアウト戦略**:待機時間と、受信者が応答しなかった場合の処理。 ### 配信方法 -リクエストを配信するチャネルを選択します。現在利用可能な方法は以下の通りです: +リクエストを配信するチャネルを選択します。現在利用可能な方法は次のとおりです。 -- **WebApp** :WebApp のエンドユーザーにリクエストフォームを表示します。トリガーで開始されたワークフローでは利用できません。 +- **Webapp** :WebApp のエンドユーザーにリクエストフォームを表示します。トリガーで開始されたワークフローでは利用できません。 - 外部クライアントは Service API 経由で WebApp のフォームを取得・送信することもできます。詳細は [人間の入力フォームを取得](/api-reference/人間の入力/人間の入力フォームを取得) を参照してください。 + 外部クライアントは Service API 経由で WebApp フォームのライフサイクルを駆動できます。詳細は [API 連携フロー](/ja/use-dify/nodes/hitl-api-integration-flow) を参照してください。 - **メール** :特定のワークスペースメンバー、外部のメールアドレス、またはワークスペース全員にリクエストリンクをメールで送信します。リンクを持っている人は誰でも応答でき、Dify アカウントは不要です。 @@ -53,25 +53,41 @@ description: ワークフローを一時停止して、人間の入力、レビ ワークフロー変数を参照して、レビュー用の AI 生成テキストや上流ノードの必要なコンテキスト情報などの動的コンテンツを表示します。 + WebApp 配信方法では、フォーム自体がエンドユーザーに直接表示されます。参照した変数はフォーム内にそのまま値として描画されるため、 **人間の入力ノードの前に回答や出力ノードを追加する必要はありません** 。 + 推論モデルは最終回答とともに思考プロセスも出力します。`text` 出力変数を参照するとデフォルトで両方が表示されます。 - 回答のみを表示するには、対応する LLM ノードで **推論タグの分離を有効にする** (Enable Reasoning Tag Separation)をオンにします。 + 回答のみを表示するには、対応する LLM ノードで **推論タグの分離を有効にする** をオンにします。 -- **入力フィールドによる入力の収集** +- **フォームフィールドによる入力の収集** + + リクエストフォームにフィールドを追加して、受信者からのさまざまなタイプの入力を取得します。各フィールドは下流で使用できる変数になります。 - 入力フィールドは、空の状態から始めることも、変数(例:修正する LLM の出力)や静的テキスト(例:サンプルやデフォルト値)を事前に入れておくこともでき、受信者が編集できます。 + たとえば、ブログレビューワークフローでは、受信者のフィードバックを下流の LLM ノードに渡してコンテンツの修正に使用できます。 - 各入力フィールドは下流で使用するための変数となります。例えば、編集されたコンテンツをさらなる処理に渡したり、コンテンツ修正のためのフィードバックを LLM に送信したりできます。 + | フィールドタイプ | 説明 | + |:----------------|:-----| + | 段落 | テキスト入力。空のまま開始することも、変数(例:修正対象の LLM 出力)や静的テキスト(例やデフォルト値)で事前入力することもできます。



最大長の制限はありませんが、非常に長い入力は下流の LLM のコンテキストウィンドウを超える可能性があります。 | + | 選択 | 選択肢のリストから 1 つを選びます。選択肢を手動で定義するか、`array[string]` 変数を参照してその項目を選択肢として使用します。 | + | 単一ファイル / ファイルリスト | 単一または複数のファイルアップロード。セルフホスト環境では、ファイルアップロードの上限を環境変数で調整できます。`UPLOAD_FILE_SIZE_LIMIT`、`UPLOAD_IMAGE_FILE_SIZE_LIMIT`、`UPLOAD_VIDEO_FILE_SIZE_LIMIT`、`UPLOAD_AUDIO_FILE_SIZE_LIMIT` は拡張子ごとの 1 ファイルのサイズ上限を設定します。`WORKFLOW_FILE_UPLOAD_LIMIT` はファイルリストフィールドで受け付けられる最大ファイル数の上限を設定します。デフォルト値については [環境変数](/ja/self-host/configuration/environments) を参照してください。 | -受信者が応答すると、すべての値が入力されたフォーム内容は下流変数 `__rendered_content` として利用できます。 + 段落のみ任意で、選択、単一ファイル、ファイルリストは必須です。すべての必須フィールドが入力されるまで、フォームのアクションボタンは無効のままになります。 + +受信者が応答すると、すべての値が入力されたフォーム内容は下流変数 `__rendered_content` として利用できます。ファイル系フィールドの値はプレーンテキストのプレースホルダーとして表示されます。単一ファイルは `[file]`、ファイルリストは `[N files]` です。 ### ユーザーアクション 受信者がクリックできる決定ボタンを定義します。各ボタンはワークフローを異なる実行パスにルーティングします。 -例えば、`投稿` ブランチはコンテンツの公開をトリガーするノードにつながります。`再生成` ブランチはコンテンツを修正するために LLM ノードにループバックするような構成が可能です。 +たとえば、`Post` ブランチはコンテンツの公開をトリガーするノードにつながり、`Regenerate` ブランチはコンテンツを修正するために LLM ノードへループバックします。 + +各ボタンには表示タイトルとアクション ID があります。ボタンがクリックされると、その ID が `__action_id`、タイトル(ボタンテキスト)が `__action_value` として下流に公開されます。 + + + ![アクションボタンの設定](/images/use-dify/workflow/human-input-action-button-config.png) + 各ボタンには表示タイトルとアクション ID があります。ボタンがクリックされると、その ID は下流変数 `__action_id`、タイトル(ボタンテキスト)は `__action_value` として利用できます。 @@ -82,7 +98,7 @@ description: ワークフローを一時停止して、人間の入力、レビ プリセットのボタンスタイルを使用して、アクションを視覚的に区別します。 - 例えば、`承認` のような重要なアクションには目立つスタイルを使用し、二次的なオプションには控えめなスタイルを使用します。 + たとえば、`Approve` のような重要なアクションには目立つスタイルを、二次的なオプションには控えめなスタイルを使用します。 ### タイムアウト戦略 @@ -92,3 +108,89 @@ description: ワークフローを一時停止して、人間の入力、レビ タイムアウトまでに受信者が応答しなかった場合、ワークフローはノードのタイムアウトブランチに進みます。このブランチをフォールバックパス(通知の送信やリトライループなど)に接続してください。 タイムアウトブランチが接続されていない場合、ワークフローは終了します。 + +## 例:コンテンツレビューワークフロー + +
+ + ![ワークフローの例](/images/use-dify/workflow/human-input-workflow-example.png) + + + ![メールで配信されるリクエストフォーム](/images/use-dify/workflow/human-input-request-form-example.png) + +
+ +このワークフローでは、ワークフローの起動者が入力した `topic` と `language` からブログ記事の草稿を作成します。その草稿をメールでレビュアーに送信し、レビュアーの選択に応じて最終出力を確定します。 + +このノードでレビュアーが行えるようにすべき次の 3 つを軸に設計されています。 + +1. **AI が生成した草稿を確認する**:フォームで上流の LLM ノードの `text` 変数を参照し、レンダリングされたフォームに草稿の本文をそのまま表示させます。 + +2. **必要に応じて草稿を直接編集する**:フォームに `edits` という段落フィールドを追加し、同じ `text` 変数で事前入力します。これによりレビュアーは草稿を出発点として、その場で編集できます。 + + ブログ記事は通常長いため、フォームの Markdown 表示(第 1 点)の方が、単独の段落フィールドよりも読みやすくなります。コンテンツが短い場合は、事前入力した段落フィールド 1 つで閲覧と編集の両方を兼ねられます。 + +3. **AI による修正のためにフィードバックを提供する**: + + 1. レビュアーのフィードバックを集めるため、フォームに `feedback` という段落フィールドを追加します。 + 2. 下流の LLM ノードを 2 つ順に接続します。 + 1. Regenerate ノード:元の草稿 `text` とレビュアーの `feedback` を受け取って修正された草稿を生成します。 + 2. Check Revision ノード:`feedback` と修正された草稿を受け取り、修正がフィードバックに沿っているかを確認します。検証された結果が最終出力として下流に流れます。 + +レビュアーは受け取ったリクエストフォームで、判断に基づいて該当する段落フィールドを入力(または空のまま)し、対応するアクションボタンをクリックします。各アクションは異なる出力に接続されます。 + +- **Approve**:上流の LLM からの元の草稿 +- **Apply Edit**:`edits` フィールドでレビュアーが編集した内容 +- **Regenerate**:下流の LLM パイプラインからの修正草稿 + + + + **System** + + ```text wrap + Write a marketing blog post around the given topic in the specified language. + ``` + + **User** + + ```text + Topic: {{#user_input.topic#}} + Language: {{#user_input.language#}} + ``` + + + + **System** + + ```text wrap + Regenerate the draft based on user feedback. + ``` + + **User** + + ```text + Draft: {{#generate_draft.text#}} + User Feedback: {{#human_input.feedback#}} + ``` + + + + **System** + + ```text wrap + Check whether the draft below addresses the user's feedback. Return the draft unchanged if it does; revise it to address the feedback if it doesn't. + ``` + + **User** + + ```text + User Feedback: {{#human_input.feedback#}} + Regenerated Draft: {{#regenerate.text#}} + ``` + + + + + diff --git a/writing-guides/glossary.md b/writing-guides/glossary.md index f647fced3..f797e79f5 100644 --- a/writing-guides/glossary.md +++ b/writing-guides/glossary.md @@ -314,6 +314,8 @@ Terms in this section must match the Dify product interface exactly. When these | Select | 下拉选项 | 選択 | app-debug.variableConfig.select | Variable type | | Number | 数字 | 数値 | app-debug.variableConfig.number | Variable type | | Checkbox | 复选框 | チェックボックス | app-debug.variableConfig.checkbox | Variable type | +| Single File | 单文件 | 単一ファイル | app-debug.variableConfig.single-file | Variable type; one file upload | +| File List | 文件列表 | ファイルリスト | app-debug.variableConfig.multi-files | Variable type; multiple file uploads | | API-based Variable | 基于 API 的变量 | API ベースの変数 | app-debug.variableConfig.apiBasedVar | Variable type | | Label Name | 显示名称 | ラベル名 | app-debug.variableConfig.labelName | Display name field for variables | diff --git a/zh/api-reference/openapi_chatflow.json b/zh/api-reference/openapi_chatflow.json index b815a39ee..1baefd5bb 100644 --- a/zh/api-reference/openapi_chatflow.json +++ b/zh/api-reference/openapi_chatflow.json @@ -141,7 +141,7 @@ }, "humanInputPause": { "summary": "响应示例 - 人工介入暂停", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -2778,7 +2778,7 @@ "人工介入" ], "summary": "获取人工介入表单", - "description": "使用 `human_input_required` 事件返回的 `form_token` 获取暂停中的人工介入表单内容。需要 **WebApp** 送达方式。", + "description": "获取暂停中的人工介入表单内容。需要 WebApp 发送方式。\n\n调用人工介入接口的端到端流程,参见 [API 集成流程](/zh/use-dify/nodes/hitl-api-integration-flow)。", "operationId": "getChatflowHumanInputForm", "parameters": [ { @@ -2788,7 +2788,7 @@ "schema": { "type": "string" }, - "description": "暂停表单的访问令牌,由流式模式下 [发送对话消息](/api-reference/对话流/发送对话消息) 返回的 `human_input_required` 事件提供。" + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" } ], "responses": { @@ -2810,7 +2810,7 @@ "properties": { "type": { "type": "string", - "description": "表单输入控件类型。可用值:`text_input`(单行文本输入框)和 `paragraph`(多行文本区域)。" + "description": "表单输入控件类型。可用值:`paragraph`(多行文本输入)、`select`(从列表中单选)、`file`(单个文件上传)和 `file-list`(多个文件上传)。" }, "output_variable_name": { "type": "string", @@ -2819,7 +2819,7 @@ "default": { "type": "object", "nullable": true, - "description": "从工作流上下文解析得到的默认值。未配置默认值时为 `null`。", + "description": "`paragraph` 输入的原始默认值配置。客户端不应直接解析此字段,请使用 `resolved_default_values` 来展示默认值。其他输入类型或未配置默认值时为 `null`。", "properties": { "type": { "type": "string", @@ -2830,13 +2830,77 @@ "items": { "type": "string" }, - "description": "当 `type` 为 `variable` 时,变量引用路径(例如 `[\"node_id\", \"var_name\"]`)。至少包含两个元素。" + "description": "当 `type` 为 `variable` 时的变量引用路径(例如 `[\"node_id\", \"var_name\"]`)。至少包含两个元素。" }, "value": { "type": "string", "description": "当 `type` 为 `constant` 时的字面默认值。始终为字符串。" } } + }, + "option_source": { + "type": "object", + "description": "`select` 输入的选项来源。仅当 `type` 为 `select` 时出现。", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "选项来源。`constant` 表示 `value` 直接列出选项;`variable` 表示 `selector` 指向提供选项的 `array[string]` 工作流变量。" + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `type` 为 `variable` 时的变量引用路径。" + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `type` 为 `constant` 时的字面选项列表。" + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "接收人可上传的文件类别。`file` 和 `file-list` 输入会包含此字段。可用值:`image`、`document`、`audio`、`video`、`custom`。" + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `allowed_file_types` 包含 `custom` 时允许的文件扩展名。每个扩展名都需要包含前导 `.`,例如 `.md`。`file` 和 `file-list` 输入会包含此字段。" + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "接收人可使用的上传方式。可用值:`local_file`、`remote_url`。`file` 和 `file-list` 输入会包含此字段。" + }, + "number_limits": { + "type": "integer", + "description": "接收人可上传的最大文件数。仅 `file-list` 输入会包含此字段。" } } }, @@ -2847,7 +2911,7 @@ "additionalProperties": { "type": "string" }, - "description": "预填充默认值,按输入的 `output_variable_name` 分组。所有值均为字符串。" + "description": "用于在表单中展示的预渲染默认值,按输入的 `output_variable_name` 分组。仅当 `paragraph` 输入的默认值可由工作流变量解析时填充;无可解析默认值的输入为空。客户端请直接展示这些值,无需在前端再次解析 `default`。所有值均为字符串。" }, "user_actions": { "type": "array", @@ -2858,16 +2922,16 @@ "type": "string", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "description": "操作按钮的标识。当接收者选择该按钮时,作为 `action` 传入 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单)。" + "description": "操作按钮的标识。当接收人选择该按钮时,作为 `action` 传入 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单)。" }, "title": { "type": "string", "maxLength": 100, - "description": "显示给接收者的按钮文本。" + "description": "显示给接收人的按钮文本。" }, "button_style": { "type": "string", - "description": "按钮视觉样式。可用值:`primary`、`default`、`accent`、`ghost`。" + "description": "按钮的视觉样式。可用值:`primary`、`default`、`accent`、`ghost`。" } } }, @@ -2876,7 +2940,7 @@ "expiration_time": { "type": "integer", "format": "int64", - "description": "Unix 时间戳(秒),超过后无法再提交该表单。" + "description": "Unix 时间戳(秒),超过该时间后表单将不可提交。" } } }, @@ -2887,17 +2951,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -2919,7 +2998,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found`:未找到表单。", "content": { "application/json": { "examples": { @@ -2928,7 +3007,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "未找到表单" } } } @@ -2936,7 +3015,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted`:表单已被提交。表单为一次性使用;无论由哪位用户提交,首个响应即生效。\n- `human_input_form_expired`:在提交到达之前表单已过期。", "content": { "application/json": { "examples": { @@ -2945,7 +3024,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已被其他用户提交,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -2953,7 +3032,24 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已过期,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" } } } @@ -2967,7 +3063,7 @@ "人工介入" ], "summary": "提交人工介入表单", - "description": "提交接收者对暂停中人工介入表单的响应。表单被接受后工作流恢复执行;可订阅 [流式获取工作流事件](/api-reference/对话流/流式获取工作流事件) 接收后续事件。需要 **WebApp** 送达方式。", + "description": "向暂停中的人工介入表单提交接收人的响应。接受后工作流将继续;可通过订阅工作流事件接口跟踪后续事件。需要 WebApp 发送方式。", "operationId": "submitChatflowHumanInputForm", "parameters": [ { @@ -2977,7 +3073,7 @@ "schema": { "type": "string" }, - "description": "暂停表单的访问令牌,由流式模式下 [发送对话消息](/api-reference/对话流/发送对话消息) 返回的 `human_input_required` 事件提供。" + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" } ], "requestBody": { @@ -2994,12 +3090,99 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "接收者提交的值,按输入的 `output_variable_name` 分组。" + "description": "按各输入的 `output_variable_name` 提交的值。值的形态取决于输入的 `type`。各形态详见下方的 schema 标签页。", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph 或 select", + "type": "string", + "description": "`paragraph`(自由文本)与 `select`(已选选项)输入的值形态。" + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "后端如何获取此文件。`local_file` 引用通过[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口已上传的文件。`remote_url` 让后端在提交时从给定 URL 抓取文件。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` 为 `local_file` 时必填。由[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口返回的 `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` 为 `remote_url` 时必填。需为公开可访问的 HTTPS URL。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "可选的文件类别覆盖项。设置时必须与表单输入的 `allowed_file_types` 保持一致。" + } + }, + "description": "`file` 输入的值形态。提交单个文件映射对象。" + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "后端如何获取此文件。`local_file` 引用通过[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口已上传的文件。`remote_url` 让后端在提交时从给定 URL 抓取文件。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` 为 `local_file` 时必填。由[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口返回的 `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` 为 `remote_url` 时必填。需为公开可访问的 HTTPS URL。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "可选的文件类别覆盖项。设置时必须与表单输入的 `allowed_file_types` 保持一致。" + } + } + }, + "description": "`file-list` 输入的值形态。提交文件映射对象的数组。" + } + ] + } }, "action": { "type": "string", - "description": "接收者选择的操作按钮 ID。必须与 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 返回的 `user_actions` 列表中的某个 `id` 值匹配。", + "description": "接收人选择的操作按钮 ID。必须与表单的 `user_actions` 列表(由 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 接口返回)中的某个 `id` 值匹配。", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, @@ -3014,7 +3197,19 @@ "summary": "请求示例", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "可以发布", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -3042,7 +3237,7 @@ } }, "400": { - "description": "- `bad_request` : Form recipient type is invalid.\n- `invalid_form_data` : Submission failed validation against the form definition.", + "description": "- `bad_request`:表单接收人类型无效。\n- `invalid_form_data`:提交内容未通过表单定义的校验。", "content": { "application/json": { "examples": { @@ -3051,7 +3246,7 @@ "value": { "status": 400, "code": "bad_request", - "message": "Form recipient type is invalid" + "message": "表单接收人类型无效" } }, "invalid_form_data": { @@ -3059,7 +3254,7 @@ "value": { "status": 400, "code": "invalid_form_data", - "message": "Missing required inputs: comment" + "message": "缺少必填输入:comment" } } } @@ -3067,7 +3262,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found`:未找到表单。", "content": { "application/json": { "examples": { @@ -3076,7 +3271,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "未找到表单" } } } @@ -3084,7 +3279,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted`:表单已被提交。表单为一次性使用;无论由哪位用户提交,首个响应即生效。\n- `human_input_form_expired`:在提交到达之前表单已过期。", "content": { "application/json": { "examples": { @@ -3093,7 +3288,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已被其他用户提交,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -3101,7 +3296,577 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已过期,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "签发上传令牌", + "description": "为暂停中的人工介入表单签发一个短期有效的上传令牌。需要 WebApp 发送方式。", + "operationId": "issueChatflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" + } + ], + "responses": { + "200": { + "description": "上传令牌签发成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "在 [上传本地文件](/api-reference/人工介入/上传本地文件) 和 [上传远程文件](/api-reference/人工介入/上传远程文件) 接口中作为 `Bearer` 令牌使用,用于向表单的 `file` 或 `file-list` 输入附加文件。" + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix 时间戳(秒),超过该时间后 `upload_token` 将不再被接受。" + } + } + }, + "examples": { + "success": { + "summary": "响应示例", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found`:未找到表单。", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "未找到表单" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的表单请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "上传本地文件", + "description": "向暂停中的人工介入表单上传单个本地文件。", + "operationId": "uploadChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "文件上传请求,需使用 multipart/form-data。", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "要上传的文件。必须符合表单输入的 `allowed_file_types` 和 `allowed_file_extensions` 配置。" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "文件上传成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "在 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口的 `file` 和 `file-list` 输入中,将该值作为 `upload_file_id` 引用。" + }, + "name": { + "type": "string", + "description": "原始文件名。" + }, + "size": { + "type": "integer", + "description": "文件大小(字节)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "文件扩展名。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "文件的 MIME 类型。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "文件归属的用户标识。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "文件创建时的 Unix 时间戳(秒)。" + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "用于预览文件的签名 URL。未生成时为 `null`。" + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "若文件来自远程拉取,则为原始来源 URL;本地上传时为 `null`。" + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "上传归属用户的标识。" + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "上传归属的租户。" + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "对人工介入上传始终为 `null`。" + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "内部存储键。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "响应示例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded`:请求中未提供文件。\n- `too_many_files`:每个请求仅允许一个文件。\n- `filename_not_exists_error`:上传的文件没有文件名。\n- `file_extension_blocked`:出于安全考虑,该文件扩展名被禁止。\n- `invalid_upload_token`:`Authorization` 请求头格式有误。", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "请上传文件。" + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "仅允许上传一个文件。" + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "指定的文件名不存在。" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "出于安全考虑,该文件扩展名被禁止。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "上传令牌无效。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token`:需要提供上传令牌。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "需要提供上传令牌。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token`:上传令牌无效或已过期。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "上传令牌无效或已过期。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large`:文件大小超出限制。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "文件大小超出限制。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type`:不允许的文件类型。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "不允许的文件类型。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "上传远程文件", + "description": "拉取远程 URL 并将结果文件附加到暂停中的人工介入表单。", + "operationId": "uploadRemoteChatflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "可公开访问的文件 URL,用于拉取并附加。" + } + } + }, + "examples": { + "remoteUpload": { + "summary": "请求示例", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "远程文件拉取并附加成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "在 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口的 `file` 和 `file-list` 输入中,将该值作为 `upload_file_id` 引用。" + }, + "name": { + "type": "string", + "description": "从远程 URL 或响应头派生的文件名。" + }, + "size": { + "type": "integer", + "description": "文件大小(字节)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "文件扩展名。" + }, + "url": { + "type": "string", + "nullable": true, + "description": "用于获取文件内容的签名 URL。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "文件的 MIME 类型。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "文件归属的用户标识。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "文件创建时的 Unix 时间戳(秒)。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "响应示例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error`:拉取远程 URL 失败。\n- `file_extension_blocked`:出于安全考虑,该文件扩展名被禁止。\n- `invalid_upload_token`:`Authorization` 请求头格式有误。", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "拉取 https://example.com/report.pdf 失败:connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "出于安全考虑,该文件扩展名被禁止。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "上传令牌无效。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token`:需要提供上传令牌。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "需要提供上传令牌。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token`:上传令牌无效或已过期。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "上传令牌无效或已过期。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large`:远程文件大小超出限制。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "文件大小超出限制。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type`:不允许的文件类型。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "不允许的文件类型。" } } } @@ -3217,6 +3982,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key 认证。对于所有 API 请求,请在 `Authorization` HTTP 头中包含您的 API Key,并加上 `Bearer ` 前缀。示例:`Authorization: Bearer {API_KEY}`。**强烈建议将 API Key 存储在服务端,不要在客户端共享或存储,以避免 API Key 泄漏导致严重后果。**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "用于向人工介入表单附加文件的短期上传令牌。通过 [签发上传令牌](/api-reference/人工介入/签发上传令牌) 接口获取,并以 `Authorization: Bearer hitl_upload_{token}` 形式传入。" } }, "responses": { @@ -5928,7 +6699,7 @@ "properties": { "type": { "type": "string", - "description": "表单输入控件类型。可用值:`text_input`(单行文本输入框)和 `paragraph`(多行文本区域)。" + "description": "表单输入控件类型。可用值:`paragraph`(多行文本输入)、`select`(从列表中单选)、`file`(单个文件上传)和 `file-list`(多个文件上传)。" }, "output_variable_name": { "type": "string", @@ -5991,7 +6762,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "[获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 和 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 的访问令牌。当人工介入节点使用邮件或控制台送达时为 `null`(Service API 仅能处理 WebApp 送达的表单)。" + "description": "[获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 和 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 的访问令牌。当人工介入节点使用非 WebApp 的发送方式时为 `null`(Service API 仅能处理 WebApp 发送的表单)。" }, "resolved_default_values": { "type": "object", @@ -6609,7 +7380,7 @@ "properties": { "type": { "type": "string", - "description": "`text_input` 为单行文本,`paragraph` 为多行文本。" + "description": "可用值:`paragraph`、`select`、`file`、`file-list`。" }, "output_variable_name": { "type": "string", diff --git a/zh/api-reference/openapi_workflow.json b/zh/api-reference/openapi_workflow.json index 6acc5872e..1b6076484 100644 --- a/zh/api-reference/openapi_workflow.json +++ b/zh/api-reference/openapi_workflow.json @@ -128,7 +128,7 @@ }, "humanInputPause": { "summary": "响应示例 - 人工介入暂停", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -317,7 +317,7 @@ "examples": { "humanInputPause": { "summary": "响应示例 - 人工介入暂停", - "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"text_input\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" + "value": "data: {\"event\": \"workflow_started\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"workflow_id\": \"7c3e33d4-2a8b-4e5f-9b1a-d3c6e8f12345\", \"inputs\": {\"draft\": \"Hello\"}, \"created_at\": 1705407629, \"reason\": \"initial\"}} data: {\"event\": \"human_input_required\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"form_id\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\", \"form_token\": \"tok_abc123\", \"node_id\": \"approval_node\", \"node_title\": \"Approval\", \"form_content\": \"Please review the draft.\", \"inputs\": [{\"type\": \"paragraph\", \"output_variable_name\": \"comment\", \"default\": null}], \"actions\": [{\"id\": \"approve\", \"title\": \"Approve\", \"button_style\": \"primary\"}], \"display_in_ui\": false, \"resolved_default_values\": {\"comment\": \"\"}, \"expiration_time\": 1705494029}} data: {\"event\": \"workflow_paused\", \"task_id\": \"c3800678-a077-43df-a102-53f23ed20b88\", \"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"data\": {\"workflow_run_id\": \"fb47b2e6-5e43-4f90-be01-d5c5a088d156\", \"status\": \"paused\", \"created_at\": 1705407629, \"elapsed_time\": 0.5}}" } } } @@ -1556,7 +1556,7 @@ "人工介入" ], "summary": "获取人工介入表单", - "description": "使用 `human_input_required` 事件返回的 `form_token` 获取暂停中的人工介入表单内容。需要 **WebApp** 送达方式。", + "description": "获取暂停中的人工介入表单内容。需要 WebApp 发送方式。\n\n调用人工介入接口的端到端流程,参见 [API 集成流程](/zh/use-dify/nodes/hitl-api-integration-flow)。", "operationId": "getWorkflowHumanInputForm", "parameters": [ { @@ -1566,7 +1566,7 @@ "schema": { "type": "string" }, - "description": "暂停表单的访问令牌,由流式模式下 [执行工作流](/api-reference/工作流/执行工作流) 返回的 `human_input_required` 事件提供。" + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" } ], "responses": { @@ -1588,7 +1588,7 @@ "properties": { "type": { "type": "string", - "description": "表单输入控件类型。可用值:`text_input`(单行文本输入框)和 `paragraph`(多行文本区域)。" + "description": "表单输入控件类型。可用值:`paragraph`(多行文本输入)、`select`(从列表中单选)、`file`(单个文件上传)和 `file-list`(多个文件上传)。" }, "output_variable_name": { "type": "string", @@ -1597,7 +1597,7 @@ "default": { "type": "object", "nullable": true, - "description": "从工作流上下文解析得到的默认值。未配置默认值时为 `null`。", + "description": "`paragraph` 输入的原始默认值配置。客户端不应直接解析此字段,请使用 `resolved_default_values` 来展示默认值。其他输入类型或未配置默认值时为 `null`。", "properties": { "type": { "type": "string", @@ -1608,13 +1608,77 @@ "items": { "type": "string" }, - "description": "当 `type` 为 `variable` 时,变量引用路径(例如 `[\"node_id\", \"var_name\"]`)。至少包含两个元素。" + "description": "当 `type` 为 `variable` 时的变量引用路径(例如 `[\"node_id\", \"var_name\"]`)。至少包含两个元素。" }, "value": { "type": "string", "description": "当 `type` 为 `constant` 时的字面默认值。始终为字符串。" } } + }, + "option_source": { + "type": "object", + "description": "`select` 输入的选项来源。仅当 `type` 为 `select` 时出现。", + "properties": { + "type": { + "type": "string", + "enum": [ + "variable", + "constant" + ], + "description": "选项来源。`constant` 表示 `value` 直接列出选项;`variable` 表示 `selector` 指向提供选项的 `array[string]` 工作流变量。" + }, + "selector": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `type` 为 `variable` 时的变量引用路径。" + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `type` 为 `constant` 时的字面选项列表。" + } + } + }, + "allowed_file_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ] + }, + "description": "接收人可上传的文件类别。`file` 和 `file-list` 输入会包含此字段。可用值:`image`、`document`、`audio`、`video`、`custom`。" + }, + "allowed_file_extensions": { + "type": "array", + "items": { + "type": "string" + }, + "description": "当 `allowed_file_types` 包含 `custom` 时允许的文件扩展名。每个扩展名都需要包含前导 `.`,例如 `.md`。`file` 和 `file-list` 输入会包含此字段。" + }, + "allowed_file_upload_methods": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ] + }, + "description": "接收人可使用的上传方式。可用值:`local_file`、`remote_url`。`file` 和 `file-list` 输入会包含此字段。" + }, + "number_limits": { + "type": "integer", + "description": "接收人可上传的最大文件数。仅 `file-list` 输入会包含此字段。" } } }, @@ -1625,7 +1689,7 @@ "additionalProperties": { "type": "string" }, - "description": "预填充默认值,按输入的 `output_variable_name` 分组。所有值均为字符串。" + "description": "用于在表单中展示的预渲染默认值,按输入的 `output_variable_name` 分组。仅当 `paragraph` 输入的默认值可由工作流变量解析时填充;无可解析默认值的输入为空。客户端请直接展示这些值,无需在前端再次解析 `default`。所有值均为字符串。" }, "user_actions": { "type": "array", @@ -1636,16 +1700,16 @@ "type": "string", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$", - "description": "操作按钮的标识。当接收者选择该按钮时,作为 `action` 传入 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单)。" + "description": "操作按钮的标识。当接收人选择该按钮时,作为 `action` 传入 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单)。" }, "title": { "type": "string", "maxLength": 100, - "description": "显示给接收者的按钮文本。" + "description": "显示给接收人的按钮文本。" }, "button_style": { "type": "string", - "description": "按钮视觉样式。可用值:`primary`、`default`、`accent`、`ghost`。" + "description": "按钮的视觉样式。可用值:`primary`、`default`、`accent`、`ghost`。" } } }, @@ -1654,7 +1718,7 @@ "expiration_time": { "type": "integer", "format": "int64", - "description": "Unix 时间戳(秒),超过后无法再提交该表单。" + "description": "Unix 时间戳(秒),超过该时间后表单将不可提交。" } } }, @@ -1665,17 +1729,32 @@ "form_content": "Please review the draft and confirm or request changes.", "inputs": [ { - "type": "text_input", - "output_variable_name": "comment", + "type": "paragraph", + "output_variable_name": "feedback", "default": { "type": "constant", "selector": [], "value": "" } + }, + { + "type": "file-list", + "output_variable_name": "attachments", + "default": null, + "allowed_file_types": [ + "document", + "image" + ], + "allowed_file_extensions": [], + "allowed_file_upload_methods": [ + "local_file", + "remote_url" + ], + "number_limits": 5 } ], "resolved_default_values": { - "comment": "" + "feedback": "" }, "user_actions": [ { @@ -1697,7 +1776,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found`:未找到表单。", "content": { "application/json": { "examples": { @@ -1706,7 +1785,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "未找到表单" } } } @@ -1714,7 +1793,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted`:表单已被提交。表单为一次性使用;无论由哪位用户提交,首个响应即生效。\n- `human_input_form_expired`:在提交到达之前表单已过期。", "content": { "application/json": { "examples": { @@ -1723,7 +1802,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已被其他用户提交,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -1731,7 +1810,24 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已过期,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" } } } @@ -1745,7 +1841,7 @@ "人工介入" ], "summary": "提交人工介入表单", - "description": "提交接收者对暂停中人工介入表单的响应。表单被接受后工作流恢复执行;可订阅 [流式获取工作流事件](/api-reference/工作流/流式获取工作流事件) 接收后续事件。需要 **WebApp** 送达方式。", + "description": "向暂停中的人工介入表单提交接收人的响应。接受后工作流将继续;可通过订阅工作流事件接口跟踪后续事件。需要 WebApp 发送方式。", "operationId": "submitWorkflowHumanInputForm", "parameters": [ { @@ -1755,7 +1851,7 @@ "schema": { "type": "string" }, - "description": "暂停表单的访问令牌,由流式模式下 [执行工作流](/api-reference/工作流/执行工作流) 返回的 `human_input_required` 事件提供。" + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" } ], "requestBody": { @@ -1772,12 +1868,99 @@ "properties": { "inputs": { "type": "object", - "additionalProperties": true, - "description": "接收者提交的值,按输入的 `output_variable_name` 分组。" + "description": "按各输入的 `output_variable_name` 提交的值。值的形态取决于输入的 `type`。各形态详见下方的 schema 标签页。", + "additionalProperties": { + "oneOf": [ + { + "title": "paragraph 或 select", + "type": "string", + "description": "`paragraph`(自由文本)与 `select`(已选选项)输入的值形态。" + }, + { + "title": "file", + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "后端如何获取此文件。`local_file` 引用通过[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口已上传的文件。`remote_url` 让后端在提交时从给定 URL 抓取文件。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` 为 `local_file` 时必填。由[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口返回的 `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` 为 `remote_url` 时必填。需为公开可访问的 HTTPS URL。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "可选的文件类别覆盖项。设置时必须与表单输入的 `allowed_file_types` 保持一致。" + } + }, + "description": "`file` 输入的值形态。提交单个文件映射对象。" + }, + { + "title": "file-list", + "type": "array", + "items": { + "type": "object", + "required": [ + "transfer_method" + ], + "properties": { + "transfer_method": { + "type": "string", + "enum": [ + "local_file", + "remote_url" + ], + "description": "后端如何获取此文件。`local_file` 引用通过[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口已上传的文件。`remote_url` 让后端在提交时从给定 URL 抓取文件。" + }, + "upload_file_id": { + "type": "string", + "description": "`transfer_method` 为 `local_file` 时必填。由[上传本地文件](/api-reference/人工介入/上传本地文件)或[上传远程文件](/api-reference/人工介入/上传远程文件)接口返回的 `id`。" + }, + "url": { + "type": "string", + "format": "uri", + "description": "`transfer_method` 为 `remote_url` 时必填。需为公开可访问的 HTTPS URL。" + }, + "type": { + "type": "string", + "enum": [ + "image", + "document", + "audio", + "video", + "custom" + ], + "description": "可选的文件类别覆盖项。设置时必须与表单输入的 `allowed_file_types` 保持一致。" + } + } + }, + "description": "`file-list` 输入的值形态。提交文件映射对象的数组。" + } + ] + } }, "action": { "type": "string", - "description": "接收者选择的操作按钮 ID。必须与 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 返回的 `user_actions` 列表中的某个 `id` 值匹配。", + "description": "接收人选择的操作按钮 ID。必须与表单的 `user_actions` 列表(由 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 接口返回)中的某个 `id` 值匹配。", "maxLength": 20, "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, @@ -1792,7 +1975,19 @@ "summary": "请求示例", "value": { "inputs": { - "comment": "Looks good to ship" + "feedback": "可以发布", + "attachments": [ + { + "transfer_method": "local_file", + "upload_file_id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "type": "document" + }, + { + "transfer_method": "remote_url", + "url": "https://example.com/report.pdf", + "type": "document" + } + ] }, "action": "approve", "user": "abc-123" @@ -1820,7 +2015,7 @@ } }, "400": { - "description": "- `bad_request` : Form recipient type is invalid.\n- `invalid_form_data` : Submission failed validation against the form definition.", + "description": "- `bad_request`:表单接收人类型无效。\n- `invalid_form_data`:提交内容未通过表单定义的校验。", "content": { "application/json": { "examples": { @@ -1829,7 +2024,7 @@ "value": { "status": 400, "code": "bad_request", - "message": "Form recipient type is invalid" + "message": "表单接收人类型无效" } }, "invalid_form_data": { @@ -1837,7 +2032,7 @@ "value": { "status": 400, "code": "invalid_form_data", - "message": "Missing required inputs: comment" + "message": "缺少必填输入:comment" } } } @@ -1845,7 +2040,7 @@ } }, "404": { - "description": "`not_found` : Form not found.", + "description": "`not_found`:未找到表单。", "content": { "application/json": { "examples": { @@ -1854,7 +2049,7 @@ "value": { "status": 404, "code": "not_found", - "message": "Form not found" + "message": "未找到表单" } } } @@ -1862,7 +2057,7 @@ } }, "412": { - "description": "- `human_input_form_submitted` : Form already submitted. Forms are one-shot; the first response wins regardless of which user submits it.\n- `human_input_form_expired` : The form's expiration time passed before submission arrived.", + "description": "- `human_input_form_submitted`:表单已被提交。表单为一次性使用;无论由哪位用户提交,首个响应即生效。\n- `human_input_form_expired`:在提交到达之前表单已过期。", "content": { "application/json": { "examples": { @@ -1871,7 +2066,7 @@ "value": { "status": 412, "code": "human_input_form_submitted", - "message": "This form has already been submitted by another user, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已被其他用户提交,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" } }, "human_input_form_expired": { @@ -1879,7 +2074,577 @@ "value": { "status": 412, "code": "human_input_form_expired", - "message": "This form has expired, form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + "message": "该表单已过期,form_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/{form_token}/upload-token": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "签发上传令牌", + "description": "为暂停中的人工介入表单签发一个短期有效的上传令牌。需要 WebApp 发送方式。", + "operationId": "issueWorkflowHumanInputUploadToken", + "parameters": [ + { + "name": "form_token", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "暂停表单的访问令牌,由流式模式下执行工作流或发送对话消息接口返回的 `human_input_required` 事件提供。" + } + ], + "responses": { + "200": { + "description": "上传令牌签发成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "upload_token": { + "type": "string", + "description": "在 [上传本地文件](/api-reference/人工介入/上传本地文件) 和 [上传远程文件](/api-reference/人工介入/上传远程文件) 接口中作为 `Bearer` 令牌使用,用于向表单的 `file` 或 `file-list` 输入附加文件。" + }, + "expires_at": { + "type": "integer", + "format": "int64", + "description": "Unix 时间戳(秒),超过该时间后 `upload_token` 将不再被接受。" + } + } + }, + "examples": { + "success": { + "summary": "响应示例", + "value": { + "upload_token": "hitl_upload_dGhpcy1pcy1hbi1leGFtcGxlLXRva2Vu", + "expires_at": 1745510400 + } + } + } + } + } + }, + "404": { + "description": "`not_found`:未找到表单。", + "content": { + "application/json": { + "examples": { + "not_found": { + "summary": "not_found", + "value": { + "status": 404, + "code": "not_found", + "message": "未找到表单" + } + } + } + } + } + }, + "429": { + "description": "`web_form_rate_limit_exceeded`:该客户端的表单请求过多,请稍后重试。", + "content": { + "application/json": { + "examples": { + "web_form_rate_limit_exceeded": { + "summary": "web_form_rate_limit_exceeded", + "value": { + "status": 429, + "code": "web_form_rate_limit_exceeded", + "message": "表单请求过多,请稍后重试。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/upload": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "上传本地文件", + "description": "向暂停中的人工介入表单上传单个本地文件。", + "operationId": "uploadWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "description": "文件上传请求,需使用 multipart/form-data。", + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "要上传的文件。必须符合表单输入的 `allowed_file_types` 和 `allowed_file_extensions` 配置。" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "文件上传成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "在 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口的 `file` 和 `file-list` 输入中,将该值作为 `upload_file_id` 引用。" + }, + "name": { + "type": "string", + "description": "原始文件名。" + }, + "size": { + "type": "integer", + "description": "文件大小(字节)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "文件扩展名。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "文件的 MIME 类型。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "文件归属的用户标识。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "文件创建时的 Unix 时间戳(秒)。" + }, + "preview_url": { + "type": "string", + "nullable": true, + "description": "用于预览文件的签名 URL。未生成时为 `null`。" + }, + "source_url": { + "type": "string", + "nullable": true, + "description": "若文件来自远程拉取,则为原始来源 URL;本地上传时为 `null`。" + }, + "original_url": { + "type": "string", + "nullable": true + }, + "user_id": { + "type": "string", + "nullable": true, + "description": "上传归属用户的标识。" + }, + "tenant_id": { + "type": "string", + "nullable": true, + "description": "上传归属的租户。" + }, + "conversation_id": { + "type": "string", + "nullable": true, + "description": "对人工介入上传始终为 `null`。" + }, + "file_key": { + "type": "string", + "nullable": true, + "description": "内部存储键。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "响应示例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "draft-review.pdf", + "size": 204800, + "extension": "pdf", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629, + "preview_url": null, + "source_url": null, + "original_url": null, + "user_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "tenant_id": "11223344-5566-7788-99aa-bbccddeeff00", + "conversation_id": null, + "file_key": "uploads/draft-review.pdf" + } + } + } + } + } + }, + "400": { + "description": "- `no_file_uploaded`:请求中未提供文件。\n- `too_many_files`:每个请求仅允许一个文件。\n- `filename_not_exists_error`:上传的文件没有文件名。\n- `file_extension_blocked`:出于安全考虑,该文件扩展名被禁止。\n- `invalid_upload_token`:`Authorization` 请求头格式有误。", + "content": { + "application/json": { + "examples": { + "no_file_uploaded": { + "summary": "no_file_uploaded", + "value": { + "status": 400, + "code": "no_file_uploaded", + "message": "请上传文件。" + } + }, + "too_many_files": { + "summary": "too_many_files", + "value": { + "status": 400, + "code": "too_many_files", + "message": "仅允许上传一个文件。" + } + }, + "filename_not_exists_error": { + "summary": "filename_not_exists_error", + "value": { + "status": 400, + "code": "filename_not_exists_error", + "message": "指定的文件名不存在。" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "出于安全考虑,该文件扩展名被禁止。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "上传令牌无效。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token`:需要提供上传令牌。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "需要提供上传令牌。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token`:上传令牌无效或已过期。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "上传令牌无效或已过期。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large`:文件大小超出限制。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "文件大小超出限制。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type`:不允许的文件类型。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "不允许的文件类型。" + } + } + } + } + } + } + } + } + }, + "/form/human_input/files/remote-upload": { + "post": { + "tags": [ + "人工介入" + ], + "summary": "上传远程文件", + "description": "拉取远程 URL 并将结果文件附加到暂停中的人工介入表单。", + "operationId": "uploadRemoteWorkflowHumanInputFile", + "security": [ + { + "HumanInputUploadToken": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "url" + ], + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "可公开访问的文件 URL,用于拉取并附加。" + } + } + }, + "examples": { + "remoteUpload": { + "summary": "请求示例", + "value": { + "url": "https://example.com/report.pdf" + } + } + } + } + } + }, + "responses": { + "201": { + "description": "远程文件拉取并附加成功。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "在 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口的 `file` 和 `file-list` 输入中,将该值作为 `upload_file_id` 引用。" + }, + "name": { + "type": "string", + "description": "从远程 URL 或响应头派生的文件名。" + }, + "size": { + "type": "integer", + "description": "文件大小(字节)。" + }, + "extension": { + "type": "string", + "nullable": true, + "description": "文件扩展名。" + }, + "url": { + "type": "string", + "nullable": true, + "description": "用于获取文件内容的签名 URL。" + }, + "mime_type": { + "type": "string", + "nullable": true, + "description": "文件的 MIME 类型。" + }, + "created_by": { + "type": "string", + "nullable": true, + "description": "文件归属的用户标识。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "nullable": true, + "description": "文件创建时的 Unix 时间戳(秒)。" + } + } + }, + "examples": { + "uploadSuccess": { + "summary": "响应示例", + "value": { + "id": "1a77f0df-c0e6-461c-987c-e72526f341ee", + "name": "report.pdf", + "size": 204800, + "extension": "pdf", + "url": "https://files.example.com/signed/report.pdf?sig=abc123", + "mime_type": "application/pdf", + "created_by": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", + "created_at": 1705407629 + } + } + } + } + } + }, + "400": { + "description": "- `remote_file_upload_error`:拉取远程 URL 失败。\n- `file_extension_blocked`:出于安全考虑,该文件扩展名被禁止。\n- `invalid_upload_token`:`Authorization` 请求头格式有误。", + "content": { + "application/json": { + "examples": { + "remote_file_upload_error": { + "summary": "remote_file_upload_error", + "value": { + "status": 400, + "code": "remote_file_upload_error", + "message": "拉取 https://example.com/report.pdf 失败:connection refused" + } + }, + "file_extension_blocked": { + "summary": "file_extension_blocked", + "value": { + "status": 400, + "code": "file_extension_blocked", + "message": "出于安全考虑,该文件扩展名被禁止。" + } + }, + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 400, + "code": "invalid_upload_token", + "message": "上传令牌无效。" + } + } + } + } + } + }, + "401": { + "description": "`invalid_upload_token`:需要提供上传令牌。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 401, + "code": "invalid_upload_token", + "message": "需要提供上传令牌。" + } + } + } + } + } + }, + "403": { + "description": "`invalid_upload_token`:上传令牌无效或已过期。", + "content": { + "application/json": { + "examples": { + "invalid_upload_token": { + "summary": "invalid_upload_token", + "value": { + "status": 403, + "code": "invalid_upload_token", + "message": "上传令牌无效或已过期。" + } + } + } + } + } + }, + "413": { + "description": "`file_too_large`:远程文件大小超出限制。", + "content": { + "application/json": { + "examples": { + "file_too_large": { + "summary": "file_too_large", + "value": { + "status": 413, + "code": "file_too_large", + "message": "文件大小超出限制。" + } + } + } + } + } + }, + "415": { + "description": "`unsupported_file_type`:不允许的文件类型。", + "content": { + "application/json": { + "examples": { + "unsupported_file_type": { + "summary": "unsupported_file_type", + "value": { + "status": 415, + "code": "unsupported_file_type", + "message": "不允许的文件类型。" } } } @@ -1995,6 +2760,12 @@ "scheme": "bearer", "bearerFormat": "API_KEY", "description": "API Key 认证。对于所有 API 请求,请在 `Authorization` HTTP 头中包含您的 API Key,并加上 `Bearer ` 前缀。示例:`Authorization: Bearer {API_KEY}`。**强烈建议将 API Key 存储在服务端,不要在客户端共享或存储,以避免 API Key 泄漏导致严重后果。**" + }, + "HumanInputUploadToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "HITL_UPLOAD_TOKEN", + "description": "用于向人工介入表单附加文件的短期上传令牌。通过 [签发上传令牌](/api-reference/人工介入/签发上传令牌) 接口获取,并以 `Authorization: Bearer hitl_upload_{token}` 形式传入。" } }, "responses": { @@ -4162,7 +4933,7 @@ "properties": { "type": { "type": "string", - "description": "表单输入控件类型。可用值:`text_input`(单行文本输入框)和 `paragraph`(多行文本区域)。" + "description": "表单输入控件类型。可用值:`paragraph`(多行文本输入)、`select`(从列表中单选)、`file`(单个文件上传)和 `file-list`(多个文件上传)。" }, "output_variable_name": { "type": "string", @@ -4225,7 +4996,7 @@ "form_token": { "type": "string", "nullable": true, - "description": "[获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 和 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 的访问令牌。当人工介入节点使用邮件或控制台送达时为 `null`(Service API 仅能处理 WebApp 送达的表单)。" + "description": "[获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 和 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 的访问令牌。当人工介入节点使用非 WebApp 的发送方式时为 `null`(Service API 仅能处理 WebApp 发送的表单)。" }, "resolved_default_values": { "type": "object", diff --git a/zh/self-host/configuration/environments.mdx b/zh/self-host/configuration/environments.mdx index f023a7770..f6aa4f6d0 100644 --- a/zh/self-host/configuration/environments.mdx +++ b/zh/self-host/configuration/environments.mdx @@ -1153,7 +1153,7 @@ Dify 发送邮件用于账户邀请、密码重置、登录验证码和人工输 | `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | 每次工作流运行的最大实际运行时间(秒)。超过此值将终止工作流。 | | `WORKFLOW_CALL_MAX_DEPTH` | `5` | 嵌套工作流调用工作流的最大深度。防止无限递归。 | | `MAX_VARIABLE_SIZE` | `204800` | 单个工作流变量的最大大小(字节,200 KB)。 | -| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | 单次工作流执行中可上传的最大文件数。 | +| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | 单个文件上传字段(如用户输入节点的 File List)中最大文件数设置的上限。节点面板的最大文件数滑块以此值为上限;调高此值可允许更大的单字段配置。 | | `WORKFLOW_NODE_EXECUTION_STORAGE` | `rdbms` | 工作流节点执行记录的存储位置。`rdbms` 将所有内容存储在数据库中。`hybrid` 将新数据存储在对象存储中,并从两者读取。 | | `DSL_EXPORT_ENCRYPT_DATASET_ID` | `true` | 导出 DSL 文件时加密数据集 ID。设为 `false` 以导出明文 ID,便于跨环境导入。 | diff --git a/zh/use-dify/nodes/hitl-api-integration-flow.mdx b/zh/use-dify/nodes/hitl-api-integration-flow.mdx new file mode 100644 index 000000000..190886b9c --- /dev/null +++ b/zh/use-dify/nodes/hitl-api-integration-flow.mdx @@ -0,0 +1,125 @@ +--- +title: 人工介入 API 集成流程 +sidebarTitle: API 集成流程 +description: 通过 API 处理暂停中的人工介入表单的端到端流程 +--- + + ⚠️ 本文档由 AI 自动翻译。如有任何不准确之处,请参考 [英文原版](/en/use-dify/nodes/human-input/integration-flow)。 + +当工作流执行到人工介入节点时会暂停,并在流式响应中发出 `human_input_required` 事件。该事件携带 `form_token`,集成方可凭其驱动整个表单生命周期,直到工作流恢复执行。 + +各接口的字段参考,详见 [人工介入 API](/api-reference/人工介入/获取人工介入表单)。 + +## 流程 + +下方流程同时适用于 Workflow 和 Chatflow 应用。两者仅在第 1 步的入口接口与第 6 步的恢复接口上有所不同。 + + + + + 1. Workflow 应用调用 [执行工作流](/api-reference/工作流/执行工作流),Chatflow 应用调用 [发送对话消息](/api-reference/对话流/发送对话消息)。 + 2. 在 SSE 流中关注 `human_input_required` 事件,从 payload 中提取 `form_token`、`form_id` 和 `task_id`。 + + 若 SSE 流在工作流恢复前关闭,第 6 步会用到 `task_id`。 + + + 使用 `form_token` 调用 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 接口。响应包含渲染后的 Markdown、输入字段定义、可用操作以及已预填的默认值。将表单呈现给接收人。 + + + 若表单包含 `file` 或 `file-list` 输入,调用 [签发上传令牌](/api-reference/人工介入/签发上传令牌) 接口获取短期有效的 `upload_token`。同一表单的多次上传可复用此令牌。 + + + 针对接收人附加的每个文件,调用其中之一: + + - [上传本地文件](/api-reference/人工介入/上传本地文件) 接口处理来自接收人设备的文件(multipart)。 + - [上传远程文件](/api-reference/人工介入/上传远程文件) 接口处理你希望后端在提交前抓取并校验的公开 URL。 + + 两者都会返回一个 `id`,提交时作为 `upload_file_id` 引用。 + + + 调用 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口,附上接收人输入的值和所选操作。 + + 文件输入既可使用 `{transfer_method: local_file, upload_file_id}` 映射(来自第 4 步),也可在提交时直接使用 `{transfer_method: remote_url, url}` 映射。两种模式的权衡详见 [先上传 vs 直接提交远程 URL](#先上传-vs-直接提交远程-url)。 + + 提交成功后,工作流沿匹配的操作分支恢复执行。 + + + 若原 SSE 流已关闭,使用第 1 步中的 `task_id` 调用 [Workflow](/api-reference/工作流/流式获取工作流事件) 或 [Chatflow](/api-reference/对话流/流式获取工作流事件) 的订阅工作流事件接口重新连接。 + + + +## 先上传 vs 直接提交远程 URL + +文件输入支持两种提交模式: + +- **先上传,再使用 `upload_file_id`**(推荐) + + 后端在上传阶段即校验文件大小、类型和扩展名,接收人可立即看到错误并在提交前重试。 + +- **使用 `transfer_method: remote_url` 直接提交** + + 后端在提交阶段抓取文件。集成更快,但任何大小、类型或抓取失败都会拒绝整次提交,接收人可能需要重填其他字段。 + + + 对于需要接收人反馈的交互式表单,优先选择先上传模式。仅当流程完全程序化、无需任何人重新输入时,直接提交模式的代价才能被抵消。 + + +## 鉴权 + +面向表单的 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 和 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口不需要鉴权。`form_token` 本身即访问凭证,相关接口按客户端 IP 限流。 + +文件上传接口需要在 Authorization 头中使用第 3 步获取的 `Bearer` `upload_token`。 + +## 发送方式要求 + +人工介入 API 仅支持通过人工介入节点的 WebApp 发送方式投递的表单。仅通过邮件投递的表单不会向 API 暴露 `form_token`。 + +## 示例:带文件的提交 + +本示例的表单包含一个 `feedback` 段落输入、一个 `attachments` 文件列表输入,以及 `approve` / `revise` 两个操作。 + +1. 调用 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单) 接口获取表单定义: + + ```http + GET /form/human_input/ + ``` + +2. 调用 [签发上传令牌](/api-reference/人工介入/签发上传令牌) 接口获取上传令牌: + + ```http + POST /form/human_input//upload-token + ``` + + 返回 `{"upload_token": "hitl_upload_...", "expires_at": ...}`。 + +3. 针对每个本地文件,调用 [上传本地文件](/api-reference/人工介入/上传本地文件) 接口: + + ```http + POST /form/human_input/files/upload + Authorization: Bearer hitl_upload_... + Content-Type: multipart/form-data + + file= + ``` + + 返回 `{"id": "1a77f0df-...", ...}`。 + +4. 调用 [提交人工介入表单](/api-reference/人工介入/提交人工介入表单) 接口,附上接收人输入与所选操作: + + ```http + POST /form/human_input/ + Content-Type: application/json + + { + "inputs": { + "feedback": "可以发布", + "attachments": [ + {"transfer_method": "local_file", "upload_file_id": "1a77f0df-..."} + ] + }, + "action": "approve", + "user": "abc-123" + } + ``` + + 返回 `{}`。工作流沿 `approve` 分支恢复执行。 diff --git a/zh/use-dify/nodes/human-input.mdx b/zh/use-dify/nodes/human-input.mdx index e3217039d..0188daef8 100644 --- a/zh/use-dify/nodes/human-input.mdx +++ b/zh/use-dify/nodes/human-input.mdx @@ -1,17 +1,17 @@ --- title: 人工介入 -description: 暂停工作流以请求人工输入、审核或决策 +description: 暂停工作流以请求人工输入 --- ⚠️ 本文档由 AI 自动翻译。如有任何不准确之处,请参考 [英文原版](/en/use-dify/nodes/human-input)。 -人工介入节点在关键点暂停工作流,发送可自定义的请求表单。接收人可使用表单审核信息、提供输入,并从预定义的决策中进行选择,这些决策决定工作流如何继续。 +人工介入节点在关键节点暂停工作流,发送可自定义的请求表单。接收人可使用表单审核信息、提供输入,并从预定义的决策中进行选择,这些决策决定工作流如何继续。 -通过在关键环节直接嵌入人工判断,你可在 **自动化效率与人工监督之间取得平衡**。 +通过在关键环节嵌入人工判断,你可在 **自动化效率与人工监督之间取得平衡** 。 - - ![请求表单示例](/images/use-dify/workflow/human-input-request-form-example.png) - + + 工作流设计示例,参考 [示例:内容审核工作流](#example-content-review-workflow)。 + ## 配置 @@ -21,7 +21,7 @@ description: 暂停工作流以请求人工输入、审核或决策 - **表单内容**:接收人将看到什么信息以及他们可以与之交互的内容。 -- **用户动作**:接收人可以做出哪些决策,以及工作流相应如何进行。 +- **用户动作**:接收人可以做出哪些决策,以及工作流将如何相应地进行。 - **超时策略**:等待多久以及如果无人响应会发生什么。 @@ -29,51 +29,61 @@ description: 暂停工作流以请求人工输入、审核或决策 选择发送请求的渠道。当前可用的方式: -- **WebApp**:向 WebApp 终端用户显示请求表单。在由触发器启动的工作流中不可用。 +- **Webapp**:向 WebApp 终端用户显示请求表单。在由触发器启动的工作流中不可用。 - 外部客户端也可通过 Service API 获取并提交 WebApp 表单。详见 [获取人工介入表单](/api-reference/人工介入/获取人工介入表单)。 + 外部客户端可通过 Service API 驱动 WebApp 表单的生命周期。详见 [API 集成流程](/zh/use-dify/nodes/hitl-api-integration-flow)。 - **邮件**:将请求链接发送至特定工作区成员、外部邮箱地址或工作区全体成员。任何持有链接的人都可响应,无需 Dify 账户。 - 无论采用何种发送方式,请求将在收到第一个响应后关闭。 + 无论采用何种发送方式,请求都将在收到第一个响应后关闭。 ### 表单内容 -自定义接收人看到并与之交互的表单: +自定义接收人将看到并可与之交互的表单: - **使用 Markdown 格式化结构** - 使用标题、列表、粗体文本、链接和其他 Markdown 元素清晰地呈现信息。 + 使用标题、列表、粗体、链接和其他 Markdown 元素清晰地呈现信息。 - **使用变量显示动态数据** - 引用工作流变量以显示动态内容,例如供审核的 AI 生成文本或上游节点的任何上下文信息。 + 引用工作流变量以显示动态内容,例如供审核的 AI 生成文本或来自上游节点的任何上下文信息。 + + 在 WebApp 发送方式下,表单本身会直接展示给终端用户。表单中引用的任何变量的值都将直接显示,因此 **无需在人工介入节点之前再添加直接回复或输出节点**。 - 推理模型在最终答案旁输出其思维过程。引用 `text` 输出变量时默认显示两者。 + 推理模型会在最终答案之外输出思维过程。引用 `text` 输出变量时,默认同时显示两者。 - 要仅显示答案,为相应的 LLM 节点开启 **思维链分离渲染**(Enable Reasoning Tag Separation)。 + 如需仅显示答案,为对应的 LLM 节点开启 **启用推理标签分离**。 -- **使用输入字段收集输入** +- **使用表单字段收集输入** - 输入字段可以是空的,也可以预填变量(例如,需优化的 LLM 输出)或静态文本(例如,示例或默认值),接收人可对其进行编辑。 + 在请求表单中添加字段,以获取来自接收人的不同类型的输入。每个字段都会成为下游可用的变量。 - 每个输入字段都成为供下游使用的变量。例如,将编辑后的内容传递给后续处理,或将反馈发送给 LLM 进行内容修订。 + 例如,在博客审核工作流中,你可以将接收人反馈传递给下游 LLM 节点用于内容修订。 -接收人响应后,填入所有值的表单内容可作为下游变量 `__rendered_content` 使用。 + | 字段类型 | 描述 | + |:---------|:-----| + | 段落 | 文本输入。可为空,也可预填变量(例如,需优化的 LLM 输出)或静态文本(示例或默认值),供接收人进行编辑。



无最大长度限制,但过长的输入可能超出下游 LLM 的上下文窗口。 | + | 下拉选项 | 从选项列表中进行单选。可手动定义选项,或引用上游的某个 `array[string]` 变量以将其中的值作为选项。 | + | 单文件 / 文件列表 | 单个或多个文件上传。在自托管部署中,文件上传限制可通过环境变量进行调整:
  • `UPLOAD_FILE_SIZE_LIMIT`、`UPLOAD_IMAGE_FILE_SIZE_LIMIT`、`UPLOAD_VIDEO_FILE_SIZE_LIMIT` 和 `UPLOAD_AUDIO_FILE_SIZE_LIMIT` 按扩展名限制单个文件大小。
  • `WORKFLOW_FILE_UPLOAD_LIMIT` 限制文件列表字段可配置的最大文件数。
详见 [环境变量](/zh/self-host/configuration/environments)。
| + + 仅段落为选填;下拉选项、单文件和文件列表均为必填。在所有必填字段填写完毕之前,表单的动作按钮将无法点击。 + +接收人响应后,填入所有值的表单内容可作为 `__rendered_content` 变量供下游使用。文件字段的值会以纯文本占位符呈现:单文件为 `[file]`,文件列表为 `[N files]`。 ### 用户动作 定义接收人可点击的决策按钮,每个按钮将工作流路由到不同的执行路径。 -例如,`发布` 分支可能会通向触发内容发布的节点,而 `重新生成` 分支可能会循环回 LLM 节点以修改内容。 +例如,`Post` 分支可能会通向触发内容发布的节点,而 `Regenerate` 分支可能会循环回 LLM 节点以修改内容。 -每个按钮包含显示标题和动作 ID。按钮被点击时,其 ID 可作为 `__action_id`、标题(按钮文本)可作为 `__action_value` 在下游使用。 +每个按钮包含显示标题和动作 ID。按钮被点击时,其 ID 可作为 `__action_id`、标题(按钮文本)可作为 `__action_value` 供下游使用。 ![动作按钮配置](/images/use-dify/workflow/human-input-action-button-config.png) @@ -82,13 +92,99 @@ description: 暂停工作流以请求人工输入、审核或决策 使用预设按钮样式在视觉上区分动作。 - 例如,对 `批准` 等关键动作使用醒目的样式,对次要选项使用较淡的样式。 + 例如,对 `Approve` 等关键动作使用醒目的样式,对次要选项使用较淡的样式。 ### 超时策略 -配置请求保持开启的时长。默认 3 天。 +配置请求保持开启的时长,默认 3 天。 -如果在超时前没有接收人响应,工作流将沿节点的超时分支继续。将该分支连接到后备路径,例如发送通知或重试循环。 +如果在超时前没有接收人响应,工作流将沿节点的超时分支继续。可将该分支连接到后备路径,例如发送通知或重试。 如果未连接超时分支,工作流将结束。 + +## 示例:内容审核工作流 + +
+ + ![工作流示例](/images/use-dify/workflow/human-input-workflow-example.png) + + + ![通过邮件发送的请求表单](/images/use-dify/workflow/human-input-request-form-example.png) + +
+ +该工作流根据工作流发起者输入的 `topic` 和 `language` 起草一篇博客文章,将草稿通过邮件发送给审核人,并根据审核人的选择确定最终输出。 + +它围绕审核人在此节点上应能完成的三件事进行设计: + +1. **查看 AI 生成的草稿**:在表单中引用上游 LLM 节点的 `text` 变量,让渲染后的表单直接显示草稿。 + +2. **按需编辑草稿**:在表单中添加一个段落输入字段 `edits`,并预填同一个 `text` 变量,让审核人以草稿为起点就地修改。 + + 由于博客文章通常较长,由表单以 Markdown 渲染呈现草稿(即第 1 点)比段落输入字段的阅读体验更好。若内容较短,单独使用预填的段落字段即可让接收人同时进行阅读与编辑。 + +3. **提供反馈以由 AI 修订**: + + 1. 在表单中添加一个段落输入字段 `feedback`,用于收集审核人的反馈。 + 2. 依次连接两个下游 LLM 节点: + 1. Regenerate 节点,用于根据原始草稿 `text` 和审核人反馈 `feedback` 生成修订后的草稿。 + 2. Check Revision 节点,用于根据 `feedback` 和 Regenerate 节点生成的修订草稿检查修订内容是否与反馈一致。检查后的结果会作为最终输出流向下游。 + +审核人在收到的请求表单中,根据判断填写相应的段落字段(或留空),然后点击对应的动作按钮。每个动作连接到不同的输出: + +- **Approve**:来自上游 LLM 的原始草稿 +- **Apply Edit**:来自第一个段落字段 `edits`,即由审核人编辑过的内容 +- **Regenerate**:来自经下游 LLM 节点处理过的修订草稿 + + + + **System** + + ```text wrap + Write a marketing blog post around the given topic in the specified language. + ``` + + **User** + + ```text + Topic: {{#user_input.topic#}} + Language: {{#user_input.language#}} + ``` + + + + **System** + + ```text wrap + Regenerate the draft based on user feedback. + ``` + + **User** + + ```text + Draft: {{#generate_draft.text#}} + User Feedback: {{#human_input.feedback#}} + ``` + + + + **System** + + ```text wrap + Check whether the draft below addresses the user's feedback. Return the draft unchanged if it does; revise it to address the feedback if it doesn't. + ``` + + **User** + + ```text + User Feedback: {{#human_input.feedback#}} + Regenerated Draft: {{#regenerate.text#}} + ``` + + + + +