From af2049d0c5ac951032ef9a6a717af650d0f6de73 Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 21:49:33 +0800 Subject: [PATCH 1/8] submodule: update to latest --- libs/dify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/dify b/libs/dify index 79ea944..658157e 160000 --- a/libs/dify +++ b/libs/dify @@ -1 +1 @@ -Subproject commit 79ea94483ef8aa2bd30f088acd0f474ee3c93d16 +Subproject commit 658157e9a1fded47732688083fbdefadeafba784 From 008d5d012427be9dcfa1bda8d1ba1408b7238bfd Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 21:52:33 +0800 Subject: [PATCH 2/8] gen: diff --- .../1.7.1__1.7.2.diff | 1313 +++++++++++++++++ 1 file changed, 1313 insertions(+) create mode 100644 misc/official_api_doc_changes/1.7.1__1.7.2.diff diff --git a/misc/official_api_doc_changes/1.7.1__1.7.2.diff b/misc/official_api_doc_changes/1.7.1__1.7.2.diff new file mode 100644 index 0000000..8a0c809 --- /dev/null +++ b/misc/official_api_doc_changes/1.7.1__1.7.2.diff @@ -0,0 +1,1313 @@ +diff --git a/web/app/(commonLayout)/datasets/template/template.zh.mdx b/web/app/(commonLayout)/datasets/template/template.zh.mdx +index c21ce3bf5f..b7ea889a46 100644 +--- a/web/app/(commonLayout)/datasets/template/template.zh.mdx ++++ b/web/app/(commonLayout)/datasets/template/template.zh.mdx +@@ -25,7 +25,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + ___ +-
++
+ + + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + +@@ -1915,7 +1915,7 @@ ___ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + + +-
++
+ + + +diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx +index a5eea3d193..16a2a36c49 100755 +--- a/web/app/components/develop/template/template.zh.mdx ++++ b/web/app/components/develop/template/template.zh.mdx +@@ -252,6 +252,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + + + --- ++ ++ ++ ++ ++ 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 ++ ++ 文件只能在属于请求应用程序的消息范围内访问。 ++ ++ ### 路径参数 ++ - `file_id` (string) 必需 ++ 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 ++ ++ ### 查询参数 ++ - `as_attachment` (boolean) 可选 ++ 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 ++ ++ ### 响应 ++ 返回带有适当浏览器显示或下载标头的文件内容。 ++ - `Content-Type` 根据文件 MIME 类型设置 ++ - `Content-Length` 文件大小(以字节为单位,如果可用) ++ - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" ++ - `Cache-Control` 用于性能的缓存标头 ++ - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" ++ ++ ### 错误 ++ - 400, `invalid_param`, 参数输入异常 ++ - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 ++ - 404, `file_not_found`, 文件未找到或已被删除 ++ - 500, 服务内部错误 ++ ++ ++ ++ ### 请求示例 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ ++ --header 'Authorization: Bearer {api_key}' ++ ``` ++ ++ ++ ++ ### 作为附件下载 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --output downloaded_file.png ++ ``` ++ ++ ++ ++ ### 响应标头示例 ++ ++ ```http {{ title: 'Headers - 图片预览' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ### 文件下载响应标头 ++ ++ ```http {{ title: 'Headers - 文件下载' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Content-Disposition: attachment; filename*=UTF-8''example.png ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ++--- ++ + + + +diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx +index 30068d93a2..1a7060abc0 100755 +--- a/web/app/components/develop/template/template_advanced_chat.zh.mdx ++++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx +@@ -78,6 +78,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + + (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 + ++ ++ (选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。 ++ + + (选填)链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统会自动生成trace_id。支持以下三种方式传递,具体优先级依次为:
+ - Header:通过 HTTP Header X-Trace-Id 传递,优先级最高。
+@@ -224,6 +227,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + - 400,`provider_not_initialize`,无可用模型凭据配置 + - 400,`provider_quota_exceeded`,模型调用额度不足 + - 400,`model_currently_not_support`,当前模型不可用 ++ - 400,`workflow_not_found`,指定的工作流版本未找到 ++ - 400,`draft_workflow_error`,无法使用草稿工作流版本 ++ - 400,`workflow_id_format_error`,工作流ID格式错误,需要UUID格式 + - 400,`completion_request_error`,文本生成失败 + - 500,服务内部异常 + +@@ -393,6 +399,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + +
+ --- ++ ++ ++ ++ ++ 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 ++ ++ 文件只能在属于请求应用程序的消息范围内访问。 ++ ++ ### 路径参数 ++ - `file_id` (string) 必需 ++ 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 ++ ++ ### 查询参数 ++ - `as_attachment` (boolean) 可选 ++ 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 ++ ++ ### 响应 ++ 返回带有适当浏览器显示或下载标头的文件内容。 ++ - `Content-Type` 根据文件 MIME 类型设置 ++ - `Content-Length` 文件大小(以字节为单位,如果可用) ++ - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" ++ - `Cache-Control` 用于性能的缓存标头 ++ - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" ++ ++ ### 错误 ++ - 400, `invalid_param`, 参数输入异常 ++ - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 ++ - 404, `file_not_found`, 文件未找到或已被删除 ++ - 500, 服务内部错误 ++ ++ ++ ++ ### 请求示例 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ ++ --header 'Authorization: Bearer {api_key}' ++ ``` ++ ++ ++ ++ ### 作为附件下载 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --output downloaded_file.png ++ ``` ++ ++ ++ ++ ### 响应标头示例 ++ ++ ```http {{ title: 'Headers - 图片预览' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ### 文件下载响应标头 ++ ++ ```http {{ title: 'Headers - 文件下载' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Content-Disposition: attachment; filename*=UTF-8''example.png ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ++--- ++ + ++ ++ ++ 更新特定对话变量的值。此端点允许您修改在对话过程中捕获的变量值,同时保留其名称、类型和描述。 ++ ++ ### 路径参数 ++ ++ ++ ++ 包含要更新变量的对话ID。 ++ ++ ++ 要更新的变量ID。 ++ ++ ++ ++ ### 请求体 ++ ++ ++ ++ 变量的新值。必须匹配变量的预期类型(字符串、数字、对象等)。 ++ ++ ++ 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 ++ ++ ++ ++ ### 响应 ++ ++ 返回包含以下内容的更新变量对象: ++ - `id` (string) 变量ID ++ - `name` (string) 变量名称 ++ - `value_type` (string) 变量类型(字符串、数字、对象等) ++ - `value` (any) 更新后的变量值 ++ - `description` (string) 变量描述 ++ - `created_at` (int) 创建时间戳 ++ - `updated_at` (int) 最后更新时间戳 ++ ++ ### 错误 ++ - 400, `Type mismatch: variable expects {expected_type}, but got {actual_type} type`, 值类型与变量的预期类型不匹配 ++ - 404, `conversation_not_exists`, 对话不存在 ++ - 404, `conversation_variable_not_exists`, 变量不存在 ++ ++ ++ ++ ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": "Updated Value", ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ++ ++ ++ ```bash {{ title: '字符串值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": "新的字符串值", ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ```bash {{ title: '数字值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": 42, ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ```bash {{ title: '对象值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": {"product": "Widget", "quantity": 10, "price": 29.99}, ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ++ ++ ```json {{ title: 'Response' }} ++ { ++ "id": "variable-uuid-1", ++ "name": "customer_name", ++ "value_type": "string", ++ "value": "Updated Value", ++ "description": "客户名称(从对话中提取)", ++ "created_at": 1650000000000, ++ "updated_at": 1650000001000 ++ } ++ ``` ++ ++ ++ ++ ++--- ++ + + + +@@ -1101,7 +1302,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + url='/text-to-audio' + method='POST' + title='文字转语音' +- name='#audio' ++ name='#text-to-audio' + /> + + +diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx +index a7127d614b..c975749c10 100644 +--- a/web/app/components/develop/template/template_chat.zh.mdx ++++ b/web/app/components/develop/template/template_chat.zh.mdx +@@ -73,6 +73,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + + (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 + ++ ++ (选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。 ++ + + (选填)链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统会自动生成trace_id。支持以下三种方式传递,具体优先级依次为:
+ - Header:通过 HTTP Header X-Trace-Id 传递,优先级最高。
+@@ -181,6 +184,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + - 400,`provider_not_initialize`,无可用模型凭据配置 + - 400,`provider_quota_exceeded`,模型调用额度不足 + - 400,`model_currently_not_support`,当前模型不可用 ++ - 400,`workflow_not_found`,指定的工作流版本未找到 ++ - 400,`draft_workflow_error`,无法使用草稿工作流版本 ++ - 400,`workflow_id_format_error`,工作流ID格式错误,需要UUID格式 + - 400,`completion_request_error`,文本生成失败 + - 500,服务内部异常 + +@@ -365,6 +371,86 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + +
+ --- ++ ++ ++ ++ ++ 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 ++ ++ 文件只能在属于请求应用程序的消息范围内访问。 ++ ++ ### 路径参数 ++ - `file_id` (string) 必需 ++ 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 ++ ++ ### 查询参数 ++ - `as_attachment` (boolean) 可选 ++ 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 ++ ++ ### 响应 ++ 返回带有适当浏览器显示或下载标头的文件内容。 ++ - `Content-Type` 根据文件 MIME 类型设置 ++ - `Content-Length` 文件大小(以字节为单位,如果可用) ++ - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" ++ - `Cache-Control` 用于性能的缓存标头 ++ - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" ++ ++ ### 错误 ++ - 400, `invalid_param`, 参数输入异常 ++ - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 ++ - 404, `file_not_found`, 文件未找到或已被删除 ++ - 500, 服务内部错误 ++ ++ ++ ++ ### 请求示例 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ ++ --header 'Authorization: Bearer {api_key}' ++ ``` ++ ++ ++ ++ ### 作为附件下载 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --output downloaded_file.png ++ ``` ++ ++ ++ ++ ### 响应标头示例 ++ ++ ```http {{ title: 'Headers - 图片预览' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ### 文件下载响应标头 ++ ++ ```http {{ title: 'Headers - 文件下载' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Content-Disposition: attachment; filename*=UTF-8''example.png ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ++--- ++ + ++ ++ ++ 更新特定对话变量的值。此端点允许您修改在对话过程中捕获的变量值,同时保留其名称、类型和描述。 ++ ++ ### 路径参数 ++ ++ ++ ++ 包含要更新变量的对话ID。 ++ ++ ++ 要更新的变量ID。 ++ ++ ++ ++ ### 请求体 ++ ++ ++ ++ 变量的新值。必须匹配变量的预期类型(字符串、数字、对象等)。 ++ ++ ++ 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 ++ ++ ++ ++ ### 响应 ++ ++ 返回包含以下内容的更新变量对象: ++ - `id` (string) 变量ID ++ - `name` (string) 变量名称 ++ - `value_type` (string) 变量类型(字符串、数字、对象等) ++ - `value` (any) 更新后的变量值 ++ - `description` (string) 变量描述 ++ - `created_at` (int) 创建时间戳 ++ - `updated_at` (int) 最后更新时间戳 ++ ++ ### 错误 ++ - 400, `Type mismatch: variable expects {expected_type}, but got {actual_type} type`, 值类型与变量的预期类型不匹配 ++ - 404, `conversation_not_exists`, 对话不存在 ++ - 404, `conversation_variable_not_exists`, 变量不存在 ++ ++ ++ ++ ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": "Updated Value", ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ++ ++ ++ ```bash {{ title: '字符串值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": "新的字符串值", ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ```bash {{ title: '数字值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": 42, ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ```bash {{ title: '对象值' }} ++ curl -X PUT '${props.appDetail.api_base_url}/conversations/{conversation_id}/variables/{variable_id}' \ ++ --header 'Content-Type: application/json' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --data-raw '{ ++ "value": {"product": "Widget", "quantity": 10, "price": 29.99}, ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ++ ++ ```json {{ title: 'Response' }} ++ { ++ "id": "variable-uuid-1", ++ "name": "customer_name", ++ "value_type": "string", ++ "value": "Updated Value", ++ "description": "客户名称(从对话中提取)", ++ "created_at": 1650000000000, ++ "updated_at": 1650000001000 ++ } ++ ``` ++ ++ ++ ++ ++--- ++ + + + +@@ -1112,7 +1313,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' + url='/text-to-audio' + method='POST' + title='文字转语音' +- name='#audio' ++ name='#text-to-audio' + /> + + +diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx +index 236da62e88..5c00f03a70 100644 +--- a/web/app/components/develop/template/template_workflow.zh.mdx ++++ b/web/app/components/develop/template/template_workflow.zh.mdx +@@ -328,6 +328,235 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 + + --- + ++ ++ ++ ++ 执行指定版本的工作流,通过路径参数指定工作流ID。 ++ ++ ### Path ++ - `workflow_id` (string) Required 工作流ID,用于指定特定版本的工作流 ++ ++ 获取方式:可以在版本历史中查询特定版本的工作流ID。 ++ ++ ### Request Body ++ - `inputs` (object) Required ++ 允许传入 App 定义的各变量值。 ++ inputs 参数包含了多组键值对(Key/Value pairs),每组的键对应一个特定变量,每组的值则是该变量的具体值。变量可以是文件列表类型。 ++ 文件列表类型变量适用于传入文件结合文本理解并回答问题,仅当模型支持该类型文件解析能力时可用。如果该变量是文件列表类型,该变量对应的值应是列表格式,其中每个元素应包含以下内容: ++ - `type` (string) 支持类型: ++ - `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' ++ - `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' ++ - `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' ++ - `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA' ++ - `custom` 具体类型包含:其他文件类型 ++ - `transfer_method` (string) 传递方式,`remote_url` 图片地址 / `local_file` 上传文件 ++ - `url` (string) 图片地址(仅当传递方式为 `remote_url` 时) ++ - `upload_file_id` (string) 上传文件 ID(仅当传递方式为 `local_file` 时) ++ - `response_mode` (string) Required ++ 返回响应模式,支持: ++ - `streaming` 流式模式(推荐)。基于 SSE(**[Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)**)实现类似打字机输出方式的流式返回。 ++ - `blocking` 阻塞模式,等待执行完毕后返回结果。(请求若流程较长可能会被中断)。 ++ 由于 Cloudflare 限制,请求会在 100 秒超时无返回后中断。 ++ - `user` (string) Required ++ 用户标识,用于定义终端用户的身份,方便检索、统计。 ++ 由开发者定义规则,需保证用户标识在应用内唯一。API 无法访问 WebApp 创建的会话。 ++ - `files` (array[object]) 可选 ++ - `trace_id` (string) Optional ++ 链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统将自动生成 `trace_id`。支持以下三种方式传递,具体优先级依次为: ++ 1. Header:推荐通过 HTTP Header `X-Trace-Id` 传递,优先级最高。 ++ 2. Query 参数:通过 URL 查询参数 `trace_id` 传递。 ++ 3. Request Body:通过请求体字段 `trace_id` 传递(即本字段)。 ++ ++ ### Response ++ 当 `response_mode` 为 `blocking` 时,返回 CompletionResponse object。 ++ 当 `response_mode` 为 `streaming`时,返回 ChunkCompletionResponse object 流式序列。 ++ ++ ### CompletionResponse ++ 返回完整的 App 结果,`Content-Type` 为 `application/json` 。 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `data` (object) 详细内容 ++ - `id` (string) workflow 执行 ID ++ - `workflow_id` (string) 关联 Workflow ID ++ - `status` (string) 执行状态, `running` / `succeeded` / `failed` / `stopped` ++ - `outputs` (json) Optional 输出内容 ++ - `error` (string) Optional 错误原因 ++ - `elapsed_time` (float) Optional 耗时(s) ++ - `total_tokens` (int) Optional 总使用 tokens ++ - `total_steps` (int) 总步数(冗余),默认 0 ++ - `created_at` (timestamp) 开始时间 ++ - `finished_at` (timestamp) 结束时间 ++ ++ ### ChunkCompletionResponse ++ 返回 App 输出的流式块,`Content-Type` 为 `text/event-stream`。 ++ 每个流式块均为 data: 开头,块之间以 `\n\n` 即两个换行符分隔,如下所示: ++ ++ ```streaming {{ title: 'Response' }} ++ data: {"event": "text_chunk", "workflow_run_id": "b85e5fc5-751b-454d-b14e-dc5f240b0a31", "task_id": "bd029338-b068-4d34-a331-fc85478922c2", "data": {"text": "\u4e3a\u4e86", "from_variable_selector": ["1745912968134", "text"]}}\n\n ++ ``` ++ ++ 流式块中根据 `event` 不同,结构也不同,包含以下类型: ++ - `event: workflow_started` workflow 开始执行 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `event` (string) 固定为 `workflow_started` ++ - `data` (object) 详细内容 ++ - `id` (string) workflow 执行 ID ++ - `workflow_id` (string) 关联 Workflow ID ++ - `created_at` (timestamp) 开始时间 ++ - `event: node_started` node 开始执行 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `event` (string) 固定为 `node_started` ++ - `data` (object) 详细内容 ++ - `id` (string) workflow 执行 ID ++ - `node_id` (string) 节点 ID ++ - `node_type` (string) 节点类型 ++ - `title` (string) 节点名称 ++ - `index` (int) 执行序号,用于展示 Tracing Node 顺序 ++ - `predecessor_node_id` (string) 前置节点 ID,用于画布展示执行路径 ++ - `inputs` (object) 节点中所有使用到的前置节点变量内容 ++ - `created_at` (timestamp) 开始时间 ++ - `event: text_chunk` 文本片段 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `event` (string) 固定为 `text_chunk` ++ - `data` (object) 详细内容 ++ - `text` (string) 文本内容 ++ - `from_variable_selector` (array) 文本来源路径,帮助开发者了解文本是由哪个节点的哪个变量生成的 ++ - `event: node_finished` node 执行结束,成功失败同一事件中不同状态 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `event` (string) 固定为 `node_finished` ++ - `data` (object) 详细内容 ++ - `id` (string) node 执行 ID ++ - `node_id` (string) 节点 ID ++ - `index` (int) 执行序号,用于展示 Tracing Node 顺序 ++ - `predecessor_node_id` (string) optional 前置节点 ID,用于画布展示执行路径 ++ - `inputs` (object) 节点中所有使用到的前置节点变量内容 ++ - `process_data` (json) Optional 节点过程数据 ++ - `outputs` (json) Optional 输出内容 ++ - `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped` ++ - `error` (string) Optional 错误原因 ++ - `elapsed_time` (float) Optional 耗时(s) ++ - `execution_metadata` (json) 元数据 ++ - `total_tokens` (int) optional 总使用 tokens ++ - `total_price` (decimal) optional 总费用 ++ - `currency` (string) optional 货币,如 `USD` / `RMB` ++ - `created_at` (timestamp) 开始时间 ++ - `event: workflow_finished` workflow 执行结束,成功失败同一事件中不同状态 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `workflow_run_id` (string) workflow 执行 ID ++ - `event` (string) 固定为 `workflow_finished` ++ - `data` (object) 详细内容 ++ - `id` (string) workflow 执行 ID ++ - `workflow_id` (string) 关联 Workflow ID ++ - `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped` ++ - `outputs` (json) Optional 输出内容 ++ - `error` (string) Optional 错误原因 ++ - `elapsed_time` (float) Optional 耗时(s) ++ - `total_tokens` (int) Optional 总使用 tokens ++ - `total_steps` (int) 总步数(冗余),默认 0 ++ - `created_at` (timestamp) 开始时间 ++ - `finished_at` (timestamp) 结束时间 ++ - `event: tts_message` TTS 音频流事件,即:语音合成输出。内容是Mp3格式的音频块,使用 base64 编码后的字符串,播放的时候直接解码即可。(开启自动播放才有此消息) ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `message_id` (string) 消息唯一 ID ++ - `audio` (string) 语音合成之后的音频块使用 Base64 编码之后的文本内容,播放的时候直接 base64 解码送入播放器即可 ++ - `created_at` (int) 创建时间戳,如:1705395332 ++ - `event: tts_message_end` TTS 音频流结束事件,收到这个事件表示音频流返回结束。 ++ - `task_id` (string) 任务 ID,用于请求跟踪和下方的停止响应接口 ++ - `message_id` (string) 消息唯一 ID ++ - `audio` (string) 结束事件是没有音频的,所以这里是空字符串 ++ - `created_at` (int) 创建时间戳,如:1705395332 ++ - `event: ping` 每 10s 一次的 ping 事件,保持连接存活。 ++ ++ ### Errors ++ - 400,`invalid_param`,传入参数异常 ++ - 400,`app_unavailable`,App 配置不可用 ++ - 400,`provider_not_initialize`,无可用模型凭据配置 ++ - 400,`provider_quota_exceeded`,模型调用额度不足 ++ - 400,`model_currently_not_support`,当前模型不可用 ++ - 400,`workflow_not_found`,指定的工作流版本未找到 ++ - 400,`draft_workflow_error`,无法使用草稿工作流版本 ++ - 400,`workflow_id_format_error`,工作流ID格式错误,需要UUID格式 ++ - 400,`workflow_request_error`,workflow 执行失败 ++ - 500,服务内部异常 ++ ++ ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X POST '${props.appDetail.api_base_url}/workflows/{workflow_id}/run' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --header 'Content-Type: application/json' \ ++ --data-raw '{ ++ "inputs": {}, ++ "response_mode": "streaming", ++ "user": "abc-123" ++ }' ++ ``` ++ ++ ++ ```json {{ title: 'File variable example' }} ++ { ++ "inputs": { ++ "{variable_name}": ++ [ ++ { ++ "transfer_method": "local_file", ++ "upload_file_id": "{upload_file_id}", ++ "type": "{document_type}" ++ } ++ ] ++ } ++ } ++ ``` ++ ++ ### Blocking Mode ++ ++ ```json {{ title: 'Response' }} ++ { ++ "workflow_run_id": "djflajgkldjgd", ++ "task_id": "9da23599-e713-473b-982c-4328d4f5c78a", ++ "data": { ++ "id": "fdlsjfjejkghjda", ++ "workflow_id": "fldjaslkfjlsda", ++ "status": "succeeded", ++ "outputs": { ++ "text": "Nice to meet you." ++ }, ++ "error": null, ++ "elapsed_time": 0.875, ++ "total_tokens": 3562, ++ "total_steps": 8, ++ "created_at": 1705407629, ++ "finished_at": 1727807631 ++ } ++ } ++ ``` ++ ++ ### Streaming Mode ++ ++ ```streaming {{ title: 'Response' }} ++ data: {"event": "workflow_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "created_at": 1679586595}} ++ data: {"event": "node_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "created_at": 1679586595}} ++ data: {"event": "node_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "execution_metadata": {"total_tokens": 63127864, "total_price": 2.378, "currency": "USD"}, "created_at": 1679586595}} ++ data: {"event": "workflow_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "total_tokens": 63127864, "total_steps": "1", "created_at": 1679586595, "finished_at": 1679976595}} ++ data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"} ++ data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""} ++ ``` ++ ++ ++ ++ ++--- ++ + + --- + ++ ++ ++ ++ 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 ++ ++ 文件只能在属于请求应用程序的消息范围内访问。 ++ ++ ### 路径参数 ++ - `file_id` (string) 必需 ++ 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 ++ ++ ### 查询参数 ++ - `as_attachment` (boolean) 可选 ++ 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览)。 ++ ++ ### 响应 ++ 返回带有适当浏览器显示或下载标头的文件内容。 ++ - `Content-Type` 根据文件 MIME 类型设置 ++ - `Content-Length` 文件大小(以字节为单位,如果可用) ++ - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" ++ - `Cache-Control` 用于性能的缓存标头 ++ - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" ++ ++ ### 错误 ++ - 400, `invalid_param`, 参数输入异常 ++ - 403, `file_access_denied`, 文件访问被拒绝或文件不属于当前应用程序 ++ - 404, `file_not_found`, 文件未找到或已被删除 ++ - 500, 服务内部错误 ++ ++ ++ ++ ### 请求示例 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview' \ ++ --header 'Authorization: Bearer {api_key}' ++ ``` ++ ++ ++ ++ ### 作为附件下载 ++ ++ ++ ```bash {{ title: 'cURL' }} ++ curl -X GET '${props.appDetail.api_base_url}/files/72fa9618-8f89-4a37-9b33-7e1178a24a67/preview?as_attachment=true' \ ++ --header 'Authorization: Bearer {api_key}' \ ++ --output downloaded_file.png ++ ``` ++ ++ ++ ++ ### 响应标头示例 ++ ++ ```http {{ title: 'Headers - 图片预览' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ### 文件下载响应标头 ++ ++ ```http {{ title: 'Headers - 文件下载' }} ++ Content-Type: image/png ++ Content-Length: 1024 ++ Content-Disposition: attachment; filename*=UTF-8''example.png ++ Cache-Control: public, max-age=3600 ++ ``` ++ ++ ++ ++--- ++ + Date: Fri, 15 Aug 2025 22:02:33 +0800 Subject: [PATCH 3/8] feat: update app_generation --- schema/app_generation.en.yaml | 91 +++++++++++++++++- schema/app_generation.zh.yaml | 92 ++++++++++++++++++- .../overlays/app_generation.en.overlay.yaml | 57 +++++++++++- 3 files changed, 235 insertions(+), 5 deletions(-) diff --git a/schema/app_generation.en.yaml b/schema/app_generation.en.yaml index 1110665..9b5f578 100644 --- a/schema/app_generation.en.yaml +++ b/schema/app_generation.en.yaml @@ -5,8 +5,8 @@ info: Dify API provides a series of interfaces for text generation functionality. It supports text generation applications. All API requests require an application-level API-Key in the Authorization HTTP Header. - official raw document: https://github.com/langgenius/dify/blob/1.5.0/web/app/components/develop/template/template.en.mdx - version: 1.7.1 + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template.en.mdx + version: 1.7.2 servers: - url: 'https://api.dify.ai/v1' description: Dify API Server @@ -532,6 +532,93 @@ paths: code: s3_file_too_large status: 503 message: File exceeds S3 size limit + '/files/{file_id}/preview': + get: + operationId: previewFileByAppGeneration + summary: File Preview + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 'Unique identifier of the file to preview, obtained from the file upload API response' + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: Whether to force file download as attachment. Defaults to false (preview in browser) + responses: + '200': + description: Successful response + content: + application/octet-stream: + schema: + type: string + format: binary + description: File content + headers: + Content-Type: + schema: + type: string + description: Set according to file MIME type + Content-Length: + schema: + type: integer + description: 'File size in bytes, if available' + Content-Disposition: + schema: + type: string + description: Set to 'attachment' if as_attachment=true + Cache-Control: + schema: + type: string + description: Cache headers for performance + Accept-Ranges: + schema: + type: string + description: Set to 'bytes' for audio/video files + '400': + description: Invalid parameter input + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: invalid_param + status: 400 + message: Invalid parameter input + '403': + description: File access denied + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: file_access_denied + status: 403 + message: File access denied or file does not belong to current application + '404': + description: File not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + code: file_not_found + status: 404 + message: File not found or has been deleted + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /info: get: operationId: getApplicationInfoByAppGeneration diff --git a/schema/app_generation.zh.yaml b/schema/app_generation.zh.yaml index 73784c2..522d0c8 100644 --- a/schema/app_generation.zh.yaml +++ b/schema/app_generation.zh.yaml @@ -5,8 +5,8 @@ info: Dify API 提供了一系列接口用于文本生成功能。 支持文本生成型应用。 所有 API 请求都需要在 Authorization HTTP Header 中包含应用级 API-Key。 - 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template.zh.mdx - version: 1.7.1 + 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template.zh.mdx + version: 1.7.2 servers: - url: https://api.dify.ai/v1 @@ -544,6 +544,94 @@ paths: status: 503 message: "文件超出 S3 大小限制" + /files/{file_id}/preview: + get: + operationId: previewFileByAppGeneration + summary: 文件预览 + description: | + 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 + 文件只能在属于请求应用程序的消息范围内访问。 + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 要预览的文件的唯一标识符,从文件上传 API 响应中获得 + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: 是否强制将文件作为附件下载。默认为 false(在浏览器中预览) + responses: + "200": + description: 成功响应 + content: + application/octet-stream: + schema: + type: string + format: binary + description: 文件内容 + headers: + Content-Type: + schema: + type: string + description: 根据文件 MIME 类型设置 + Content-Length: + schema: + type: integer + description: 文件大小(以字节为单位,如果可用) + Content-Disposition: + schema: + type: string + description: 如果 as_attachment=true 则设置为 "attachment" + Cache-Control: + schema: + type: string + description: 用于性能的缓存标头 + Accept-Ranges: + schema: + type: string + description: 对于音频/视频文件设置为 "bytes" + "400": + description: 参数输入异常 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + code: "invalid_param" + status: 400 + message: "参数输入异常" + "403": + description: 文件访问被拒绝 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + code: "file_access_denied" + status: 403 + message: "文件访问被拒绝或文件不属于当前应用程序" + "404": + description: 文件未找到 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + code: "file_not_found" + status: 404 + message: "文件未找到或已被删除" + "500": + description: 服务内部错误 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + /info: get: operationId: getApplicationInfoByAppGeneration diff --git a/schema/overlays/app_generation.en.overlay.yaml b/schema/overlays/app_generation.en.overlay.yaml index c7ddf17..5dcca76 100644 --- a/schema/overlays/app_generation.en.overlay.yaml +++ b/schema/overlays/app_generation.en.overlay.yaml @@ -11,7 +11,7 @@ actions: Dify API provides a series of interfaces for text generation functionality. It supports text generation applications. All API requests require an application-level API-Key in the Authorization HTTP Header. - official raw document: https://github.com/langgenius/dify/blob/1.5.0/web/app/components/develop/template/template.en.mdx + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template.en.mdx - target: $.servers[0] update: @@ -291,6 +291,61 @@ actions: - target: $.paths['/files/upload'].post.responses['503'].description update: "Storage service error" + - target: $.paths['/files/{file_id}/preview'].get + update: + summary: "File Preview" + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + - target: $.paths['/files/{file_id}/preview'].get.parameters[0].description + update: "Unique identifier of the file to preview, obtained from the file upload API response" + + - target: $.paths['/files/{file_id}/preview'].get.parameters[1].description + update: "Whether to force file download as attachment. Defaults to false (preview in browser)" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].description + update: "Successful response" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].content['application/octet-stream'].schema.description + update: "File content" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Type'].description + update: "Set according to file MIME type" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Length'].description + update: "File size in bytes, if available" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Disposition'].description + update: "Set to 'attachment' if as_attachment=true" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Cache-Control'].description + update: "Cache headers for performance" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Accept-Ranges'].description + update: "Set to 'bytes' for audio/video files" + + - target: $.paths['/files/{file_id}/preview'].get.responses['400'].description + update: "Invalid parameter input" + + - target: $.paths['/files/{file_id}/preview'].get.responses['400'].content['application/json'].example.message + update: "Invalid parameter input" + + - target: $.paths['/files/{file_id}/preview'].get.responses['403'].description + update: "File access denied" + + - target: $.paths['/files/{file_id}/preview'].get.responses['403'].content['application/json'].example.message + update: "File access denied or file does not belong to current application" + + - target: $.paths['/files/{file_id}/preview'].get.responses['404'].description + update: "File not found" + + - target: $.paths['/files/{file_id}/preview'].get.responses['404'].content['application/json'].example.message + update: "File not found or has been deleted" + + - target: $.paths['/files/{file_id}/preview'].get.responses['500'].description + update: "Internal server error" + - target: $.paths['/info'].get update: summary: "Get Application Basic Information" From 7ef810ffbe06617ab7aea7388d63ae9923e9f6f6 Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 22:09:33 +0800 Subject: [PATCH 4/8] feat: update app_adv_chat --- schema/app_advanced_chat.en.yaml | 169 +++++++++++++++++- schema/app_advanced_chat.zh.yaml | 169 +++++++++++++++++- .../app_advanced_chat.en.overlay.yaml | 109 ++++++++++- 3 files changed, 438 insertions(+), 9 deletions(-) diff --git a/schema/app_advanced_chat.en.yaml b/schema/app_advanced_chat.en.yaml index ff7fcf9..40e54e0 100644 --- a/schema/app_advanced_chat.en.yaml +++ b/schema/app_advanced_chat.en.yaml @@ -6,8 +6,8 @@ info: Chat applications support session persistence, allowing previous chat history to be used as context for responses, suitable for chat/customer service AI, etc. All API requests require an application-level API-Key in the Authorization HTTP Header. It is strongly recommended that developers store the API-Key on the backend rather than sharing or storing it on the client side to prevent API-Key leakage and financial loss. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_advanced_chat.en.mdx - version: 1.7.1 + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_advanced_chat.en.mdx + version: 1.7.2 servers: - url: 'https://api.dify.ai/v1' description: Dify API Server @@ -350,6 +350,9 @@ paths: type: boolean description: '(Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously.' default: true + workflow_id: + type: string + description: '(Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used.' trace_id: type: string description: '(Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field).' @@ -465,6 +468,76 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '/files/{file_id}/preview': + get: + operationId: previewFileByAppAdvancedChat + summary: File Preview + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 'Unique identifier of the file to preview, obtained from the file upload API response.' + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: Whether to force download the file as an attachment. Default is false (preview in browser). + responses: + '200': + description: File content + content: + '*/*': + schema: + type: string + format: binary + headers: + Content-Type: + description: Set according to file MIME type + schema: + type: string + Content-Length: + description: File size in bytes (if available) + schema: + type: integer + Content-Disposition: + description: Set to 'attachment' if as_attachment=true + schema: + type: string + Cache-Control: + description: Cache headers for performance + schema: + type: string + Accept-Ranges: + description: Set to 'bytes' for audio/video files + schema: + type: string + '400': + description: Invalid parameter input + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: File access denied or file does not belong to current application + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: File not found or deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + $ref: '#/components/responses/Error500' /audio-to-text: post: operationId: convertAudioToTextByAppAdvancedChat @@ -967,7 +1040,7 @@ paths: description: 'File type, image for images' url: type: string - description: Preview image URL + description: 'File preview URL, access files using the file preview API (`/files/{file_id}/preview`)' belongs_to: type: string enum: @@ -1268,6 +1341,96 @@ paths: message: type: string description: Error message + '/conversations/{conversation_id}/variables/{variable_id}': + put: + operationId: updateConversationVariableByAppAdvancedChat + summary: Update Conversation Variable + description: 'Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description.' + parameters: + - name: conversation_id + in: path + required: true + schema: + type: string + description: The conversation ID containing the variable to update. + - name: variable_id + in: path + required: true + schema: + type: string + description: The ID of the variable to update. + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - value + - user + properties: + value: + description: 'New value for the variable. Must match the expected type of the variable (string, number, object, etc.).' + user: + type: string + description: 'User identifier, defined by developer rules, must be unique within the application.' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: Variable ID + name: + type: string + description: Variable name + value_type: + type: string + description: 'Variable type (string, number, object, etc.)' + value: + description: Updated variable value + description: + type: string + description: Variable description + created_at: + type: integer + description: Creation timestamp + updated_at: + type: integer + description: Last update timestamp + '400': + description: Value type mismatch with variable's expected type + content: + application/json: + schema: + type: object + properties: + code: + type: string + description: Error code + message: + type: string + description: Error message + '404': + description: Conversation not found or variable not found + content: + application/json: + schema: + type: object + properties: + code: + type: string + enum: + - conversation_not_exists + - conversation_variable_not_exists + description: Error code + message: + type: string + description: Error message /meta: get: operationId: getAppMetaInfoByAppAdvancedChat diff --git a/schema/app_advanced_chat.zh.yaml b/schema/app_advanced_chat.zh.yaml index 6d50dc0..d7f6fed 100644 --- a/schema/app_advanced_chat.zh.yaml +++ b/schema/app_advanced_chat.zh.yaml @@ -6,8 +6,8 @@ info: 对话应用支持会话持久化,可将之前的聊天记录作为上下文进行回答,可适用于聊天/客服 AI 等。 所有 API 请求都需要在 Authorization HTTP Header 中包含应用级 API-Key。 强烈建议开发者把 API-Key 放在后端存储,而非分享或者放在客户端存储,以免 API-Key 泄露,导致财产损失。 - 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_advanced_chat.zh.mdx - version: 1.7.1 + 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_advanced_chat.zh.mdx + version: 1.7.2 servers: - url: https://api.dify.ai/v1 @@ -358,6 +358,9 @@ paths: type: boolean description: (选填)自动生成标题,默认 `true`。 若设置为 `false`,则可通过调用会话重命名接口并设置 `auto_generate` 为 `true` 实现异步生成标题。 default: true + workflow_id: + type: string + description: (选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。 trace_id: type: string description: (选填)链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统会自动生成trace_id。支持以下三种方式传递,具体优先级依次为:Header:通过 HTTP Header X-Trace-Id 传递,优先级最高。Query 参数:通过 URL 查询参数 trace_id 传递。Request Body:通过请求体字段 trace_id 传递(即本字段)。 @@ -475,6 +478,77 @@ paths: schema: $ref: "#/components/schemas/Error" + /files/{file_id}/preview: + get: + operationId: previewFileByAppAdvancedChat + summary: 文件预览 + description: | + 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 + 文件只能在属于请求应用程序的消息范围内访问。 + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 要预览的文件的唯一标识符,从文件上传 API 响应中获得。 + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: 是否强制将文件作为附件下载。默认为 false(在浏览器中预览)。 + responses: + "200": + description: 文件内容 + content: + '*/*': + schema: + type: string + format: binary + headers: + Content-Type: + description: 根据文件 MIME 类型设置 + schema: + type: string + Content-Length: + description: 文件大小(以字节为单位,如果可用) + schema: + type: integer + Content-Disposition: + description: 如果 as_attachment=true 则设置为 "attachment" + schema: + type: string + Cache-Control: + description: 用于性能的缓存标头 + schema: + type: string + Accept-Ranges: + description: 对于音频/视频文件设置为 "bytes" + schema: + type: string + "400": + description: 参数输入异常 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "403": + description: 文件访问被拒绝或文件不属于当前应用程序 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "404": + description: 文件未找到或已被删除 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + "500": + $ref: "#/components/responses/Error500" + /audio-to-text: post: operationId: convertAudioToTextByAppAdvancedChat @@ -965,7 +1039,7 @@ paths: description: 文件类型,image 图片 url: type: string - description: 预览图片地址 + description: 文件预览地址,使用文件预览 API (`/files/{file_id}/preview`) 访问文件 belongs_to: type: string enum: [user, assistant] @@ -1262,6 +1336,95 @@ paths: type: string description: 错误消息 + /conversations/{conversation_id}/variables/{variable_id}: + put: + operationId: updateConversationVariableByAppAdvancedChat + summary: 更新对话变量 + description: 更新特定对话变量的值。此端点允许您修改在对话过程中捕获的变量值,同时保留其名称、类型和描述。 + parameters: + - name: conversation_id + in: path + required: true + schema: + type: string + description: 包含要更新变量的对话ID。 + - name: variable_id + in: path + required: true + schema: + type: string + description: 要更新的变量ID。 + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - value + - user + properties: + value: + description: 变量的新值。必须匹配变量的预期类型(字符串、数字、对象等)。 + user: + type: string + description: 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 + responses: + "200": + description: 成功响应 + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: 变量ID + name: + type: string + description: 变量名称 + value_type: + type: string + description: 变量类型(字符串、数字、对象等) + value: + description: 更新后的变量值 + description: + type: string + description: 变量描述 + created_at: + type: integer + description: 创建时间戳 + updated_at: + type: integer + description: 最后更新时间戳 + "400": + description: 值类型与变量的预期类型不匹配 + content: + application/json: + schema: + type: object + properties: + code: + type: string + description: 错误代码 + message: + type: string + description: 错误消息 + "404": + description: 对话不存在或变量不存在 + content: + application/json: + schema: + type: object + properties: + code: + type: string + enum: [conversation_not_exists, conversation_variable_not_exists] + description: 错误代码 + message: + type: string + description: 错误消息 + /meta: get: operationId: getAppMetaInfoByAppAdvancedChat diff --git a/schema/overlays/app_advanced_chat.en.overlay.yaml b/schema/overlays/app_advanced_chat.en.overlay.yaml index adb73c8..5446cc8 100644 --- a/schema/overlays/app_advanced_chat.en.overlay.yaml +++ b/schema/overlays/app_advanced_chat.en.overlay.yaml @@ -1,7 +1,7 @@ overlay: 1.0.0 info: title: "English translation overlay for Dify API - Advanced Chat Application" - version: "1.7.1" + version: "1.7.2" extends: ../app_advanced_chat.zh.yaml actions: - target: $.info @@ -12,7 +12,7 @@ actions: Chat applications support session persistence, allowing previous chat history to be used as context for responses, suitable for chat/customer service AI, etc. All API requests require an application-level API-Key in the Authorization HTTP Header. It is strongly recommended that developers store the API-Key on the backend rather than sharing or storing it on the client side to prevent API-Key leakage and financial loss. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_advanced_chat.en.mdx + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_advanced_chat.en.mdx - target: $.servers[0] update: @@ -267,6 +267,10 @@ actions: update: description: "(Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously." + - target: $.paths['/chat-messages'].post.requestBody.content['application/json'].schema.properties.workflow_id + update: + description: "(Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used." + - target: $.paths['/chat-messages'].post.requestBody.content['application/json'].schema.properties.trace_id update: description: "(Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field)." @@ -319,6 +323,46 @@ actions: - target: $.paths['/files/upload'].post.responses['503'].description update: "Storage service error" + - target: $.paths['/files/{file_id}/preview'].get + update: + summary: "File Preview" + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + - target: $.paths['/files/{file_id}/preview'].get.parameters[0].description + update: "Unique identifier of the file to preview, obtained from the file upload API response." + + - target: $.paths['/files/{file_id}/preview'].get.parameters[1].description + update: "Whether to force download the file as an attachment. Default is false (preview in browser)." + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].description + update: "File content" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Type'].description + update: "Set according to file MIME type" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Length'].description + update: "File size in bytes (if available)" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Disposition'].description + update: "Set to 'attachment' if as_attachment=true" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Cache-Control'].description + update: "Cache headers for performance" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Accept-Ranges'].description + update: "Set to 'bytes' for audio/video files" + + - target: $.paths['/files/{file_id}/preview'].get.responses['400'].description + update: "Invalid parameter input" + + - target: $.paths['/files/{file_id}/preview'].get.responses['403'].description + update: "File access denied or file does not belong to current application" + + - target: $.paths['/files/{file_id}/preview'].get.responses['404'].description + update: "File not found or deleted" + - target: $.paths['/audio-to-text'].post update: summary: "Audio to Text" @@ -668,7 +712,7 @@ actions: update: "File type, image for images" - target: $.paths['/messages'].get.responses['200'].content['application/json'].schema.properties.data.items.properties.message_files.items.properties.url.description - update: "Preview image URL" + update: "File preview URL, access files using the file preview API (`/files/{file_id}/preview`)" - target: $.paths['/messages'].get.responses['200'].content['application/json'].schema.properties.data.items.properties.message_files.items.properties.belongs_to.description update: "File owner, user or assistant" @@ -864,6 +908,65 @@ actions: - target: $.paths['/conversations/{conversation_id}/variables'].get.responses['404'].content['application/json'].schema.properties.message.description update: "Error message" + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put + update: + summary: "Update Conversation Variable" + description: "Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.parameters[0].description + update: "The conversation ID containing the variable to update." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.parameters[1].description + update: "The ID of the variable to update." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.requestBody.content['application/json'].schema.properties.value.description + update: "New value for the variable. Must match the expected type of the variable (string, number, object, etc.)." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.requestBody.content['application/json'].schema.properties.user.description + update: "User identifier, defined by developer rules, must be unique within the application." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].description + update: "Successful response" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.id.description + update: "Variable ID" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.name.description + update: "Variable name" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.value_type.description + update: "Variable type (string, number, object, etc.)" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.value.description + update: "Updated variable value" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.description.description + update: "Variable description" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.created_at.description + update: "Creation timestamp" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.updated_at.description + update: "Last update timestamp" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['400'].description + update: "Value type mismatch with variable's expected type" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['400'].content['application/json'].schema.properties.code.description + update: "Error code" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['400'].content['application/json'].schema.properties.message.description + update: "Error message" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['404'].description + update: "Conversation not found or variable not found" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['404'].content['application/json'].schema.properties.code.description + update: "Error code" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['404'].content['application/json'].schema.properties.message.description + update: "Error message" + - target: $.paths['/site'].get update: summary: "Get App WebApp Settings" From 3365509601735ddb1dad0959395bdd1c0ed75e0e Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 22:15:33 +0800 Subject: [PATCH 5/8] feat: update app_chat --- schema/app_chat.en.yaml | 200 ++++++++++++++++++++++- schema/app_chat.zh.yaml | 188 ++++++++++++++++++++- schema/overlays/app_chat.en.overlay.yaml | 96 ++++++++++- 3 files changed, 477 insertions(+), 7 deletions(-) diff --git a/schema/app_chat.en.yaml b/schema/app_chat.en.yaml index 283d14d..f5c75a1 100644 --- a/schema/app_chat.en.yaml +++ b/schema/app_chat.en.yaml @@ -5,8 +5,8 @@ info: Dify API provides a series of interfaces for conversation functionality. It supports conversational applications. All API requests require an application-level API-Key in the Authorization HTTP Header. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_chat.en.mdx - version: 1.7.1 + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_chat.en.mdx + version: 1.7.2 servers: - url: 'https://api.dify.ai/v1' description: Dify API Server @@ -478,6 +478,9 @@ paths: type: boolean description: '(Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously.' default: true + workflow_id: + type: string + description: '(Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used.' trace_id: type: string description: | @@ -625,11 +628,117 @@ paths: - message (string) Error message - event: ping Ping event every 10s to keep the connection alive. '400': - $ref: '#/components/responses/Error400' + description: Request error + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + code: + enum: + - invalid_param + - app_unavailable + - provider_not_initialize + - provider_quota_exceeded + - model_currently_not_support + - workflow_not_found + - draft_workflow_error + - workflow_id_format_error + - completion_request_error '404': $ref: '#/components/responses/Error404' '500': $ref: '#/components/responses/Error500' + '/files/{file_id}/preview': + get: + operationId: previewFileByAppChat + summary: File Preview + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 'Unique identifier of the file to preview, obtained from the file upload API response' + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: Whether to force the file to be downloaded as an attachment. Default is false (preview in browser) + responses: + '200': + description: File content + content: + '*/*': + schema: + type: string + format: binary + headers: + Content-Type: + description: Set according to file MIME type + schema: + type: string + Content-Length: + description: File size in bytes (if available) + schema: + type: integer + Content-Disposition: + description: Set to "attachment" if as_attachment=true + schema: + type: string + Cache-Control: + description: Cache headers for performance + schema: + type: string + Accept-Ranges: + description: Set to "bytes" for audio/video files + schema: + type: string + '400': + description: Invalid parameters + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + code: + enum: + - invalid_param + '403': + description: File access denied or file does not belong to current application + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + code: + enum: + - file_access_denied + '404': + description: File not found or has been deleted + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + code: + enum: + - file_not_found + '500': + $ref: '#/components/responses/Error500' /conversations: get: operationId: getConversationListByAppChat @@ -829,6 +938,91 @@ paths: message: type: string description: Error message + '/conversations/{conversation_id}/variables/{variable_id}': + put: + operationId: updateConversationVariableByAppChat + summary: Update Conversation Variable + description: 'Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions.' + parameters: + - name: conversation_id + in: path + description: ID of the conversation containing the variable to update + required: true + schema: + type: string + - name: variable_id + in: path + description: ID of the variable to update + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - user + - value + properties: + value: + description: 'New value for the variable. Must match the variable''s expected type (string, number, object, etc.)' + user: + type: string + description: 'User identifier, defined by developer rules, must be unique within the application.' + responses: + '200': + description: Successfully updated variable + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: Variable ID + name: + type: string + description: Variable name + value_type: + type: string + description: 'Variable type (string, number, object, etc.)' + value: + description: Updated variable value + description: + type: string + description: Variable description + created_at: + type: integer + description: Creation timestamp + updated_at: + type: integer + description: Last update timestamp + '400': + description: Value type mismatch with variable's expected type + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + message: + example: 'Type mismatch: variable expects string, but got number type' + '404': + description: Conversation not found or variable not found + content: + application/json: + schema: + allOf: + - $ref: '#/components/schemas/Error' + - type: object + properties: + code: + enum: + - conversation_not_exists + - conversation_variable_not_exists /messages: get: operationId: getConversationMessagesByAppChat diff --git a/schema/app_chat.zh.yaml b/schema/app_chat.zh.yaml index 82e35f6..07d481b 100644 --- a/schema/app_chat.zh.yaml +++ b/schema/app_chat.zh.yaml @@ -5,8 +5,8 @@ info: Dify API 提供了一系列接口用于对话功能。 支持对话型应用。 所有 API 请求都需要在 Authorization HTTP Header 中包含应用级 API-Key。 - 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_chat.zh.mdx - version: 1.7.1 + 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_chat.zh.mdx + version: 1.7.2 servers: - url: https://api.dify.ai/v1 @@ -485,6 +485,9 @@ paths: type: boolean description: (选填)自动生成标题,默认 true。若设置为 false,则可通过调用会话重命名接口并设置 auto_generate 为 true 实现异步生成标题。 default: true + workflow_id: + type: string + description: (选填)工作流ID,用于指定特定版本,如果不提供则使用默认的已发布版本。 trace_id: type: string description: | @@ -612,12 +615,107 @@ paths: - message (string) 错误消息 - event: ping 每 10s 一次的 ping 事件,保持连接存活。 "400": - $ref: "#/components/responses/Error400" + description: 请求错误 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + code: + enum: [invalid_param, app_unavailable, provider_not_initialize, provider_quota_exceeded, model_currently_not_support, workflow_not_found, draft_workflow_error, workflow_id_format_error, completion_request_error] "404": $ref: "#/components/responses/Error404" "500": $ref: "#/components/responses/Error500" + /files/{file_id}/preview: + get: + operationId: previewFileByAppChat + summary: 文件预览 + description: | + 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。 + 文件只能在属于请求应用程序的消息范围内访问。 + parameters: + - name: file_id + in: path + required: true + schema: + type: string + description: 要预览的文件的唯一标识符,从文件上传 API 响应中获得 + - name: as_attachment + in: query + required: false + schema: + type: boolean + default: false + description: 是否强制将文件作为附件下载。默认为 false(在浏览器中预览) + responses: + "200": + description: 文件内容 + content: + '*/*': + schema: + type: string + format: binary + headers: + Content-Type: + description: 根据文件 MIME 类型设置 + schema: + type: string + Content-Length: + description: 文件大小(以字节为单位,如果可用) + schema: + type: integer + Content-Disposition: + description: 如果 as_attachment=true 则设置为 "attachment" + schema: + type: string + Cache-Control: + description: 用于性能的缓存标头 + schema: + type: string + Accept-Ranges: + description: 对于音频/视频文件设置为 "bytes" + schema: + type: string + "400": + description: 参数输入异常 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + code: + enum: [invalid_param] + "403": + description: 文件访问被拒绝或文件不属于当前应用程序 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + code: + enum: [file_access_denied] + "404": + description: 文件未找到或已被删除 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + code: + enum: [file_not_found] + "500": + $ref: "#/components/responses/Error500" + /conversations: get: operationId: getConversationListByAppChat @@ -815,6 +913,90 @@ paths: type: string description: 错误消息 + /conversations/{conversation_id}/variables/{variable_id}: + put: + operationId: updateConversationVariableByAppChat + summary: 更新对话变量 + description: 更新特定对话变量的值。此端点允许您修改在对话过程中捕获的变量值,同时保留其名称、类型和描述。 + parameters: + - name: conversation_id + in: path + description: 包含要更新变量的对话ID + required: true + schema: + type: string + - name: variable_id + in: path + description: 要更新的变量ID + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - user + - value + properties: + value: + description: 变量的新值。必须匹配变量的预期类型(字符串、数字、对象等)。 + user: + type: string + description: 用户标识符,由开发人员定义的规则,在应用程序内必须唯一。 + responses: + "200": + description: 成功更新变量 + content: + application/json: + schema: + type: object + properties: + id: + type: string + description: 变量ID + name: + type: string + description: 变量名称 + value_type: + type: string + description: 变量类型(字符串、数字、对象等) + value: + description: 更新后的变量值 + description: + type: string + description: 变量描述 + created_at: + type: integer + description: 创建时间戳 + updated_at: + type: integer + description: 最后更新时间戳 + "400": + description: 值类型与变量的预期类型不匹配 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + message: + example: "Type mismatch: variable expects string, but got number type" + "404": + description: 对话不存在或变量不存在 + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/Error" + - type: object + properties: + code: + enum: [conversation_not_exists, conversation_variable_not_exists] + /messages: get: operationId: getConversationMessagesByAppChat diff --git a/schema/overlays/app_chat.en.overlay.yaml b/schema/overlays/app_chat.en.overlay.yaml index 381303b..8e3c939 100644 --- a/schema/overlays/app_chat.en.overlay.yaml +++ b/schema/overlays/app_chat.en.overlay.yaml @@ -11,7 +11,7 @@ actions: Dify API provides a series of interfaces for conversation functionality. It supports conversational applications. All API requests require an application-level API-Key in the Authorization HTTP Header. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_chat.en.mdx + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_chat.en.mdx - target: $.servers[0] update: @@ -358,6 +358,10 @@ actions: update: description: "(Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously." + - target: $.paths['/chat-messages'].post.requestBody.content['application/json'].schema.properties.workflow_id + update: + description: "(Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used." + - target: $.paths['/chat-messages'].post.requestBody.content['application/json'].schema.properties.trace_id update: description: | @@ -501,6 +505,49 @@ actions: - message (string) Error message - event: ping Ping event every 10s to keep the connection alive. + - target: $.paths['/chat-messages'].post.responses['400'].description + update: "Request error" + + - target: $.paths['/files/{file_id}/preview'].get + update: + summary: "File Preview" + description: | + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + - target: $.paths['/files/{file_id}/preview'].get.parameters[0].description + update: "Unique identifier of the file to preview, obtained from the file upload API response" + + - target: $.paths['/files/{file_id}/preview'].get.parameters[1].description + update: "Whether to force the file to be downloaded as an attachment. Default is false (preview in browser)" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].description + update: "File content" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Type'].description + update: "Set according to file MIME type" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Length'].description + update: "File size in bytes (if available)" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Content-Disposition'].description + update: "Set to \"attachment\" if as_attachment=true" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Cache-Control'].description + update: "Cache headers for performance" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].headers['Accept-Ranges'].description + update: "Set to \"bytes\" for audio/video files" + + - target: $.paths['/files/{file_id}/preview'].get.responses['400'].description + update: "Invalid parameters" + + - target: $.paths['/files/{file_id}/preview'].get.responses['403'].description + update: "File access denied or file does not belong to current application" + + - target: $.paths['/files/{file_id}/preview'].get.responses['404'].description + update: "File not found or has been deleted" + - target: $.paths['/conversations'].get update: summary: "Get Conversation List" @@ -618,6 +665,53 @@ actions: - target: $.paths['/conversations/{conversation_id}/variables'].get.responses['404'].content['application/json'].schema.properties.message.description update: "Error message" + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put + update: + summary: "Update Conversation Variable" + description: "Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.parameters[0].description + update: "ID of the conversation containing the variable to update" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.parameters[1].description + update: "ID of the variable to update" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.requestBody.content['application/json'].schema.properties.value.description + update: "New value for the variable. Must match the variable's expected type (string, number, object, etc.)" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.requestBody.content['application/json'].schema.properties.user.description + update: "User identifier, defined by developer rules, must be unique within the application." + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].description + update: "Successfully updated variable" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.id.description + update: "Variable ID" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.name.description + update: "Variable name" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.value_type.description + update: "Variable type (string, number, object, etc.)" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.value.description + update: "Updated variable value" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.description.description + update: "Variable description" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.created_at.description + update: "Creation timestamp" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['200'].content['application/json'].schema.properties.updated_at.description + update: "Last update timestamp" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['400'].description + update: "Value type mismatch with variable's expected type" + + - target: $.paths['/conversations/{conversation_id}/variables/{variable_id}'].put.responses['404'].description + update: "Conversation not found or variable not found" + - target: $.paths['/messages'].get update: summary: "Get Conversation History Messages" From 41377d46dc11331a31afa3753fd4856ff82d2512 Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 22:20:33 +0800 Subject: [PATCH 6/8] feat: update app_workflow --- schema/app_workflow.en.yaml | 150 +++++++++++++++++- schema/app_workflow.zh.yaml | 151 ++++++++++++++++++- schema/overlays/app_workflow.en.overlay.yaml | 98 +++++++++++- 3 files changed, 391 insertions(+), 8 deletions(-) diff --git a/schema/app_workflow.en.yaml b/schema/app_workflow.en.yaml index 45d4414..89f8f18 100644 --- a/schema/app_workflow.en.yaml +++ b/schema/app_workflow.en.yaml @@ -6,8 +6,8 @@ info: Workflow applications have no session support and are suitable for translation/article writing/summarization AI, etc. All API requests require an application-level API-Key in the Authorization HTTP Header. It is strongly recommended that developers store the API-Key on the backend rather than sharing or storing it on the client to prevent API-Key leakage and financial loss. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_workflow.en.mdx - version: 1.7.1 + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_workflow.en.mdx + version: 1.7.2 servers: - url: 'https://api.dify.ai/v1' description: Dify API Server @@ -508,6 +508,98 @@ paths: $ref: '#/components/responses/Error404' '500': $ref: '#/components/responses/Error500' + '/workflows/{workflow_id}/run': + post: + operationId: runSpecificWorkflow + summary: Execute Specific Workflow + description: 'Execute a specific version of the workflow, specifying the workflow ID through the path parameter' + x-fern-streaming: + format: sse + response: + $ref: '#/components/schemas/WorkflowMessage' + response-stream: + $ref: '#/components/schemas/ChunkWorkflowMessage' + parameters: + - name: workflow_id + in: path + description: 'Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history.' + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - inputs + - response_mode + - user + properties: + inputs: + type: object + description: | + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + response_mode: + type: string + enum: + - streaming + - blocking + description: | + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + user: + type: string + description: | + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + files: + type: array + items: + type: object + description: Optional file list + trace_id: + type: string + description: | + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowMessage' + description: 'When `response_mode` is `blocking`, returns a CompletionResponse object.' + text/event-stream: + schema: + $ref: '#/components/schemas/ChunkWorkflowMessage' + description: | + When `response_mode` is `streaming`, returns a stream of ChunkCompletionResponse objects. + Returns App output stream chunks, Content-Type is text/event-stream. + Each stream chunk starts with data: and chunks are separated by `\n\n` (two newlines). + '400': + $ref: '#/components/responses/Error400' + '404': + $ref: '#/components/responses/Error404' + '500': + $ref: '#/components/responses/Error500' '/workflows/run/{workflow_run_id}': get: operationId: getWorkflowExecutionStatus @@ -845,6 +937,60 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '/files/{file_id}/preview': + get: + operationId: previewFile + summary: File Preview + description: Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application. + parameters: + - name: file_id + in: path + description: 'Unique identifier of the file to preview, obtained from the file upload API response' + required: true + schema: + type: string + - name: as_attachment + in: query + description: Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser) + required: false + schema: + type: boolean + default: false + responses: + '200': + description: Successfully retrieved file content + content: + '*/*': + schema: + type: string + format: binary + description: | + Returns file content with appropriate browser display or download headers. + - `Content-Type` Set according to file MIME type + - `Content-Length` File size in bytes (if available) + - `Content-Disposition` Set to "attachment" if `as_attachment=true` + - `Cache-Control` Cache headers for performance + - `Accept-Ranges` Set to "bytes" for audio/video files + '400': + description: Invalid parameter input + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '403': + description: File access denied or file does not belong to the current application + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '404': + description: File not found or has been deleted + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + $ref: '#/components/responses/Error500' /info: get: operationId: getAppInfo diff --git a/schema/app_workflow.zh.yaml b/schema/app_workflow.zh.yaml index f173b22..7e9f4c9 100644 --- a/schema/app_workflow.zh.yaml +++ b/schema/app_workflow.zh.yaml @@ -6,8 +6,8 @@ info: Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等等。 所有 API 请求都需要在 Authorization HTTP Header 中包含应用级 API-Key。 强烈建议开发者把 API-Key 放在后端存储,而非分享或者放在客户端存储,以免 API-Key 泄露,导致财产损失。 - 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_workflow.zh.mdx - version: 1.7.1 + 官方原始文档见: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_workflow.zh.mdx + version: 1.7.2 servers: - url: https://api.dify.ai/v1 @@ -519,6 +519,98 @@ paths: "500": $ref: "#/components/responses/Error500" + /workflows/{workflow_id}/run: + post: + operationId: runSpecificWorkflow + summary: 执行指定版本 workflow + description: 执行指定版本的工作流,通过路径参数指定工作流ID + x-fern-streaming: + format: sse + response: + $ref: "#/components/schemas/WorkflowMessage" + response-stream: + $ref: "#/components/schemas/ChunkWorkflowMessage" + parameters: + - name: workflow_id + in: path + description: 工作流ID,用于指定特定版本的工作流。获取方式:可以在版本历史中查询特定版本的工作流ID。 + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - inputs + - response_mode + - user + properties: + inputs: + type: object + description: | + 允许传入 App 定义的各变量值。 + inputs 参数包含了多组键值对(Key/Value pairs),每组的键对应一个特定变量,每组的值则是该变量的具体值。变量可以是文件列表类型。 + 文件列表类型变量适用于传入文件结合文本理解并回答问题,仅当模型支持该类型文件解析能力时可用。如果该变量是文件列表类型,该变量对应的值应是列表格式,其中每个元素应包含以下内容: + - `type` (string) 支持类型: + - `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` 具体类型包含:其他文件类型 + - `transfer_method` (string) 传递方式,`remote_url` 图片地址 / `local_file` 上传文件 + - `url` (string) 图片地址(仅当传递方式为 `remote_url` 时) + - `upload_file_id` (string) 上传文件 ID(仅当传递方式为 `local_file` 时) + response_mode: + type: string + enum: [streaming, blocking] + description: | + 返回响应模式,支持: + - `streaming` 流式模式(推荐)。基于 SSE(Server-Sent Events)实现类似打字机输出方式的流式返回。 + - `blocking` 阻塞模式,等待执行完毕后返回结果。(请求若流程较长可能会被中断)。 + 由于 Cloudflare 限制,请求会在 100 秒超时无返回后中断。 + user: + type: string + description: | + 用户标识,用于定义终端用户的身份,方便检索、统计。 + 由开发者定义规则,需保证用户标识在应用内唯一。API 无法访问 WebApp 创建的会话。 + files: + type: array + items: + type: object + description: 可选文件列表 + trace_id: + type: string + description: | + 链路追踪ID。适用于与业务系统已有的trace组件打通,实现端到端分布式追踪等场景。如果未指定,系统将自动生成 `trace_id`。支持以下三种方式传递,具体优先级依次为: + 1. Header:推荐通过 HTTP Header `X-Trace-Id` 传递,优先级最高。 + 2. Query 参数:通过 URL 查询参数 `trace_id` 传递。 + 3. Request Body:通过请求体字段 `trace_id` 传递(即本字段)。 + + responses: + "200": + description: 成功响应 + content: + application/json: + schema: + $ref: "#/components/schemas/WorkflowMessage" + description: 当 `response_mode` 为 `blocking` 时,返回 CompletionResponse object。 + text/event-stream: + schema: + $ref: "#/components/schemas/ChunkWorkflowMessage" + description: | + 当 `response_mode` 为 `streaming`时,返回 ChunkCompletionResponse object 流式序列。 + 返回 App 输出的流式块,Content-Type 为 text/event-stream。 + 每个流式块均为 data: 开头,块之间以 \n\n 即两个换行符分隔。 + "400": + $ref: "#/components/responses/Error400" + "404": + $ref: "#/components/responses/Error404" + "500": + $ref: "#/components/responses/Error500" + /workflows/run/{workflow_run_id}: get: @@ -853,6 +945,61 @@ paths: schema: $ref: "#/components/schemas/Error" + /files/{file_id}/preview: + get: + operationId: previewFile + summary: 文件预览 + description: 预览或下载已上传的文件。此端点允许您访问先前通过文件上传 API 上传的文件。文件只能在属于请求应用程序的消息范围内访问。 + parameters: + - name: file_id + in: path + description: 要预览的文件的唯一标识符,从文件上传 API 响应中获得 + required: true + schema: + type: string + - name: as_attachment + in: query + description: 是否强制将文件作为附件下载。默认为 `false`(在浏览器中预览) + required: false + schema: + type: boolean + default: false + responses: + '200': + description: 成功获取文件内容 + content: + '*/*': + schema: + type: string + format: binary + description: | + 返回带有适当浏览器显示或下载标头的文件内容。 + - `Content-Type` 根据文件 MIME 类型设置 + - `Content-Length` 文件大小(以字节为单位,如果可用) + - `Content-Disposition` 如果 `as_attachment=true` 则设置为 "attachment" + - `Cache-Control` 用于性能的缓存标头 + - `Accept-Ranges` 对于音频/视频文件设置为 "bytes" + '400': + description: 参数输入异常 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '403': + description: 文件访问被拒绝或文件不属于当前应用程序 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '404': + description: 文件未找到或已被删除 + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + '500': + $ref: "#/components/responses/Error500" + /info: get: diff --git a/schema/overlays/app_workflow.en.overlay.yaml b/schema/overlays/app_workflow.en.overlay.yaml index beb7b41..b7cd1e6 100644 --- a/schema/overlays/app_workflow.en.overlay.yaml +++ b/schema/overlays/app_workflow.en.overlay.yaml @@ -747,9 +747,9 @@ actions: - target: $.paths['/info'].get.responses['200'].content['application/json'].schema.properties.author_name.description update: "Author name" - # Add new overlay actions for 1.7.1 updates + # Add new overlay actions for 1.7.2 updates - target: $.info.version - update: "1.7.1" + update: "1.7.2" - target: $.info.description update: | @@ -757,7 +757,7 @@ actions: Workflow applications have no session support and are suitable for translation/article writing/summarization AI, etc. All API requests require an application-level API-Key in the Authorization HTTP Header. It is strongly recommended that developers store the API-Key on the backend rather than sharing or storing it on the client to prevent API-Key leakage and financial loss. - official raw document: https://github.com/langgenius/dify/blob/1.7.1/web/app/components/develop/template/template_workflow.en.mdx + official raw document: https://github.com/langgenius/dify/blob/1.7.2/web/app/components/develop/template/template_workflow.en.mdx - target: $.paths['/workflows/run'].post.requestBody.content['application/json'].schema.properties.files.description update: "Optional file list" @@ -824,4 +824,94 @@ actions: update: "Custom quantity limit, default 3" - target: $.paths['/parameters'].get.responses['200'].content['application/json'].schema.properties.file_upload.properties.custom.properties.transfer_methods.description - update: "Transfer method list: `remote_url`, `local_file`, must choose one" \ No newline at end of file + update: "Transfer method list: `remote_url`, `local_file`, must choose one" + + # New API endpoints for 1.7.2 + - target: $.paths['/workflows/{workflow_id}/run'].post + update: + summary: "Execute Specific Workflow" + description: "Execute a specific version of the workflow, specifying the workflow ID through the path parameter" + + - target: $.paths['/workflows/{workflow_id}/run'].post.parameters[0].description + update: "Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history." + + - target: $.paths['/workflows/{workflow_id}/run'].post.requestBody.content['application/json'].schema.properties.inputs.description + update: | + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + + - target: $.paths['/workflows/{workflow_id}/run'].post.requestBody.content['application/json'].schema.properties.response_mode.description + update: | + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + + - target: $.paths['/workflows/{workflow_id}/run'].post.requestBody.content['application/json'].schema.properties.user.description + update: | + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + + - target: $.paths['/workflows/{workflow_id}/run'].post.requestBody.content['application/json'].schema.properties.files.description + update: "Optional file list" + + - target: $.paths['/workflows/{workflow_id}/run'].post.requestBody.content['application/json'].schema.properties.trace_id.description + update: | + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + + - target: $.paths['/workflows/{workflow_id}/run'].post.responses['200'].description + update: "Successful response" + + - target: $.paths['/workflows/{workflow_id}/run'].post.responses['200'].content['application/json'].schema.description + update: "When `response_mode` is `blocking`, returns a CompletionResponse object." + + - target: $.paths['/workflows/{workflow_id}/run'].post.responses['200'].content['text/event-stream'].schema.description + update: | + When `response_mode` is `streaming`, returns a stream of ChunkCompletionResponse objects. + Returns App output stream chunks, Content-Type is text/event-stream. + Each stream chunk starts with data: and chunks are separated by `\n\n` (two newlines). + + - target: $.paths['/files/{file_id}/preview'].get + update: + summary: "File Preview" + description: "Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application." + + - target: $.paths['/files/{file_id}/preview'].get.parameters[0].description + update: "Unique identifier of the file to preview, obtained from the file upload API response" + + - target: $.paths['/files/{file_id}/preview'].get.parameters[1].description + update: "Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser)" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].description + update: "Successfully retrieved file content" + + - target: $.paths['/files/{file_id}/preview'].get.responses['200'].content['*/*'].schema.description + update: | + Returns file content with appropriate browser display or download headers. + - `Content-Type` Set according to file MIME type + - `Content-Length` File size in bytes (if available) + - `Content-Disposition` Set to "attachment" if `as_attachment=true` + - `Cache-Control` Cache headers for performance + - `Accept-Ranges` Set to "bytes" for audio/video files + + - target: $.paths['/files/{file_id}/preview'].get.responses['400'].description + update: "Invalid parameter input" + + - target: $.paths['/files/{file_id}/preview'].get.responses['403'].description + update: "File access denied or file does not belong to the current application" + + - target: $.paths['/files/{file_id}/preview'].get.responses['404'].description + update: "File not found or has been deleted" \ No newline at end of file From fead44359c70e6db3e1b75eb3e6cf447fb2c68e7 Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 22:24:33 +0800 Subject: [PATCH 7/8] gen: sdk --- src/dify_sdk/advanced_chat/__init__.py | 8 + src/dify_sdk/advanced_chat/client.py | 171 ++++++ src/dify_sdk/advanced_chat/errors/__init__.py | 2 + .../advanced_chat/errors/bad_request_error.py | 5 +- .../advanced_chat/errors/forbidden_error.py | 10 + src/dify_sdk/advanced_chat/raw_client.py | 381 ++++++++++++- src/dify_sdk/advanced_chat/types/__init__.py | 8 + .../types/bad_request_error_body.py | 27 + ...t_response_data_item_message_files_item.py | 2 +- .../types/not_found_error_body.py | 3 +- .../types/not_found_error_body_code.py | 7 + ..._variable_by_app_advanced_chat_response.py | 48 ++ src/dify_sdk/chat/__init__.py | 12 + src/dify_sdk/chat/client.py | 169 ++++++ src/dify_sdk/chat/errors/__init__.py | 2 + src/dify_sdk/chat/errors/bad_request_error.py | 5 +- src/dify_sdk/chat/errors/forbidden_error.py | 10 + src/dify_sdk/chat/raw_client.py | 371 ++++++++++++- src/dify_sdk/chat/types/__init__.py | 10 + .../chat/types/bad_request_error_body.py | 28 + .../chat/types/bad_request_error_body_code.py | 18 + .../chat/types/forbidden_error_body.py | 28 + .../chat/types/not_found_error_body.py | 10 +- .../chat/types/not_found_error_body_code.py | 7 + ...versation_variable_by_app_chat_response.py | 48 ++ src/dify_sdk/generation/__init__.py | 2 + src/dify_sdk/generation/client.py | 65 +++ src/dify_sdk/generation/errors/__init__.py | 2 + .../generation/errors/bad_request_error.py | 5 +- .../generation/errors/forbidden_error.py | 10 + src/dify_sdk/generation/raw_client.py | 206 ++++++- .../knowledge_base/datasets/raw_client.py | 80 +-- .../knowledge_base/documents/raw_client.py | 136 ++--- .../errors/bad_request_error.py | 5 +- .../knowledge_base/errors/forbidden_error.py | 5 +- .../knowledge_base/metadata/raw_client.py | 49 +- .../knowledge_base/models/raw_client.py | 9 +- .../knowledge_base/segments/raw_client.py | 129 +++-- .../knowledge_base/tags/raw_client.py | 57 +- src/dify_sdk/workflow/__init__.py | 4 + src/dify_sdk/workflow/client.py | 226 ++++++++ src/dify_sdk/workflow/errors/__init__.py | 2 + .../workflow/errors/bad_request_error.py | 5 +- .../workflow/errors/forbidden_error.py | 10 + src/dify_sdk/workflow/raw_client.py | 514 +++++++++++++++++- src/dify_sdk/workflow/types/__init__.py | 2 + ...specific_workflow_request_response_mode.py | 5 + 47 files changed, 2620 insertions(+), 298 deletions(-) create mode 100644 src/dify_sdk/advanced_chat/errors/forbidden_error.py create mode 100644 src/dify_sdk/advanced_chat/types/bad_request_error_body.py create mode 100644 src/dify_sdk/advanced_chat/types/not_found_error_body_code.py create mode 100644 src/dify_sdk/advanced_chat/types/update_conversation_variable_by_app_advanced_chat_response.py create mode 100644 src/dify_sdk/chat/errors/forbidden_error.py create mode 100644 src/dify_sdk/chat/types/bad_request_error_body.py create mode 100644 src/dify_sdk/chat/types/bad_request_error_body_code.py create mode 100644 src/dify_sdk/chat/types/forbidden_error_body.py create mode 100644 src/dify_sdk/chat/types/not_found_error_body_code.py create mode 100644 src/dify_sdk/chat/types/update_conversation_variable_by_app_chat_response.py create mode 100644 src/dify_sdk/generation/errors/forbidden_error.py create mode 100644 src/dify_sdk/workflow/errors/forbidden_error.py create mode 100644 src/dify_sdk/workflow/types/run_specific_workflow_request_response_mode.py diff --git a/src/dify_sdk/advanced_chat/__init__.py b/src/dify_sdk/advanced_chat/__init__.py index e16ef28..e1edcf5 100644 --- a/src/dify_sdk/advanced_chat/__init__.py +++ b/src/dify_sdk/advanced_chat/__init__.py @@ -3,6 +3,7 @@ # isort: skip_file from .types import ( + BadRequestErrorBody, ChatCompletionResponse, ChatCompletionResponseMetadata, ChunkChatCompletionResponse, @@ -61,18 +62,21 @@ GetConversationsByAppAdvancedChatResponseDataItem, GetSuggestedQuestionsByAppAdvancedChatResponse, NotFoundErrorBody, + NotFoundErrorBodyCode, RenameConversationByAppAdvancedChatResponse, RetrieverResource, SendChatMessageByAppAdvancedChatRequestResponseMode, SendMessageFeedbackByAppAdvancedChatResponse, StopChatResponseByAppAdvancedChatResponse, UpdateAnnotationByAppAdvancedChatResponse, + UpdateConversationVariableByAppAdvancedChatResponse, UploadedFile, Usage, ) from .errors import ( BadRequestError, ContentTooLargeError, + ForbiddenError, InternalServerError, NotFoundError, ServiceUnavailableError, @@ -81,6 +85,7 @@ __all__ = [ "BadRequestError", + "BadRequestErrorBody", "ChatCompletionResponse", "ChatCompletionResponseMetadata", "ChunkChatCompletionResponse", @@ -95,6 +100,7 @@ "FileInput", "FileInputTransferMethod", "FileInputType", + "ForbiddenError", "GetAnnotationReplyStatusByAppAdvancedChatRequestAction", "GetAnnotationReplyStatusByAppAdvancedChatResponse", "GetAnnotationsListByAppAdvancedChatResponse", @@ -142,6 +148,7 @@ "InternalServerError", "NotFoundError", "NotFoundErrorBody", + "NotFoundErrorBodyCode", "RenameConversationByAppAdvancedChatResponse", "RetrieverResource", "SendChatMessageByAppAdvancedChatRequestResponseMode", @@ -150,6 +157,7 @@ "StopChatResponseByAppAdvancedChatResponse", "UnsupportedMediaTypeError", "UpdateAnnotationByAppAdvancedChatResponse", + "UpdateConversationVariableByAppAdvancedChatResponse", "UploadedFile", "Usage", ] diff --git a/src/dify_sdk/advanced_chat/client.py b/src/dify_sdk/advanced_chat/client.py index 0881cf2..8046227 100644 --- a/src/dify_sdk/advanced_chat/client.py +++ b/src/dify_sdk/advanced_chat/client.py @@ -46,6 +46,9 @@ from .types.send_message_feedback_by_app_advanced_chat_response import SendMessageFeedbackByAppAdvancedChatResponse from .types.stop_chat_response_by_app_advanced_chat_response import StopChatResponseByAppAdvancedChatResponse from .types.update_annotation_by_app_advanced_chat_response import UpdateAnnotationByAppAdvancedChatResponse +from .types.update_conversation_variable_by_app_advanced_chat_response import ( + UpdateConversationVariableByAppAdvancedChatResponse, +) from .types.uploaded_file import UploadedFile # this is used as the default value for optional parameters @@ -77,6 +80,7 @@ def send_chat_message_by_app_advanced_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[FileInput]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[ChunkChatCompletionResponse]: @@ -108,6 +112,9 @@ def send_chat_message_by_app_advanced_chat( auto_generate_name : typing.Optional[bool] (Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field). @@ -135,6 +142,7 @@ def send_chat_message_by_app_advanced_chat( conversation_id=conversation_id, files=files, auto_generate_name=auto_generate_name, + workflow_id=workflow_id, trace_id=trace_id, request_options=request_options, ) as r: @@ -212,6 +220,38 @@ def upload_file_by_app_advanced_chat( ) return _response.data + def preview_file_by_app_advanced_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response. + + as_attachment : typing.Optional[bool] + Whether to force download the file as an attachment. Default is false (preview in browser). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[bytes] + File content + """ + with self._raw_client.preview_file_by_app_advanced_chat( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + yield from r.data + def convert_audio_to_text_by_app_advanced_chat( self, *, @@ -666,6 +706,51 @@ def get_conversation_variables_by_app_advanced_chat( ) return _response.data + def update_conversation_variable_by_app_advanced_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> UpdateConversationVariableByAppAdvancedChatResponse: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description. + + Parameters + ---------- + conversation_id : str + The conversation ID containing the variable to update. + + variable_id : str + The ID of the variable to update. + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UpdateConversationVariableByAppAdvancedChatResponse + Successful response + + Examples + -------- + from dify import DifyApi + client = DifyApi(token="YOUR_TOKEN", ) + client.advanced_chat.update_conversation_variable_by_app_advanced_chat(conversation_id='conversation_id', variable_id='variable_id', value={'key': 'value'} + , user='user', ) + """ + _response = self._raw_client.update_conversation_variable_by_app_advanced_chat( + conversation_id, variable_id, user=user, value=value, request_options=request_options + ) + return _response.data + def get_app_meta_info_by_app_advanced_chat( self, *, request_options: typing.Optional[RequestOptions] = None ) -> GetAppMetaInfoByAppAdvancedChatResponse: @@ -963,6 +1048,7 @@ async def send_chat_message_by_app_advanced_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[FileInput]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[ChunkChatCompletionResponse]: @@ -994,6 +1080,9 @@ async def send_chat_message_by_app_advanced_chat( auto_generate_name : typing.Optional[bool] (Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field). @@ -1024,6 +1113,7 @@ async def main() -> None: conversation_id=conversation_id, files=files, auto_generate_name=auto_generate_name, + workflow_id=workflow_id, trace_id=trace_id, request_options=request_options, ) as r: @@ -1108,6 +1198,39 @@ async def main() -> None: ) return _response.data + async def preview_file_by_app_advanced_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response. + + as_attachment : typing.Optional[bool] + Whether to force download the file as an attachment. Default is false (preview in browser). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[bytes] + File content + """ + async with self._raw_client.preview_file_by_app_advanced_chat( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + async for data in r.data: + yield data + async def convert_audio_to_text_by_app_advanced_chat( self, *, @@ -1598,6 +1721,54 @@ async def main() -> None: ) return _response.data + async def update_conversation_variable_by_app_advanced_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> UpdateConversationVariableByAppAdvancedChatResponse: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description. + + Parameters + ---------- + conversation_id : str + The conversation ID containing the variable to update. + + variable_id : str + The ID of the variable to update. + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UpdateConversationVariableByAppAdvancedChatResponse + Successful response + + Examples + -------- + from dify import AsyncDifyApi + import asyncio + client = AsyncDifyApi(token="YOUR_TOKEN", ) + async def main() -> None: + await client.advanced_chat.update_conversation_variable_by_app_advanced_chat(conversation_id='conversation_id', variable_id='variable_id', value={'key': 'value'} + , user='user', ) + asyncio.run(main()) + """ + _response = await self._raw_client.update_conversation_variable_by_app_advanced_chat( + conversation_id, variable_id, user=user, value=value, request_options=request_options + ) + return _response.data + async def get_app_meta_info_by_app_advanced_chat( self, *, request_options: typing.Optional[RequestOptions] = None ) -> GetAppMetaInfoByAppAdvancedChatResponse: diff --git a/src/dify_sdk/advanced_chat/errors/__init__.py b/src/dify_sdk/advanced_chat/errors/__init__.py index 635aad6..37b13b9 100644 --- a/src/dify_sdk/advanced_chat/errors/__init__.py +++ b/src/dify_sdk/advanced_chat/errors/__init__.py @@ -4,6 +4,7 @@ from .bad_request_error import BadRequestError from .content_too_large_error import ContentTooLargeError +from .forbidden_error import ForbiddenError from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .service_unavailable_error import ServiceUnavailableError @@ -12,6 +13,7 @@ __all__ = [ "BadRequestError", "ContentTooLargeError", + "ForbiddenError", "InternalServerError", "NotFoundError", "ServiceUnavailableError", diff --git a/src/dify_sdk/advanced_chat/errors/bad_request_error.py b/src/dify_sdk/advanced_chat/errors/bad_request_error.py index 5ccb07c..c1e658e 100644 --- a/src/dify_sdk/advanced_chat/errors/bad_request_error.py +++ b/src/dify_sdk/advanced_chat/errors/bad_request_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class BadRequestError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=400, body=body) diff --git a/src/dify_sdk/advanced_chat/errors/forbidden_error.py b/src/dify_sdk/advanced_chat/errors/forbidden_error.py new file mode 100644 index 0000000..674d6b9 --- /dev/null +++ b/src/dify_sdk/advanced_chat/errors/forbidden_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class ForbiddenError(ApiError): + def __init__(self, body: typing.Optional[typing.Any]): + super().__init__(status_code=403, body=body) diff --git a/src/dify_sdk/advanced_chat/raw_client.py b/src/dify_sdk/advanced_chat/raw_client.py index 58ee2a1..d98aadf 100644 --- a/src/dify_sdk/advanced_chat/raw_client.py +++ b/src/dify_sdk/advanced_chat/raw_client.py @@ -15,6 +15,7 @@ from ..core.serialization import convert_and_respect_annotation_metadata from .errors.bad_request_error import BadRequestError from .errors.content_too_large_error import ContentTooLargeError +from .errors.forbidden_error import ForbiddenError from .errors.internal_server_error import InternalServerError from .errors.not_found_error import NotFoundError from .errors.service_unavailable_error import ServiceUnavailableError @@ -60,6 +61,9 @@ from .types.send_message_feedback_by_app_advanced_chat_response import SendMessageFeedbackByAppAdvancedChatResponse from .types.stop_chat_response_by_app_advanced_chat_response import StopChatResponseByAppAdvancedChatResponse from .types.update_annotation_by_app_advanced_chat_response import UpdateAnnotationByAppAdvancedChatResponse +from .types.update_conversation_variable_by_app_advanced_chat_response import ( + UpdateConversationVariableByAppAdvancedChatResponse, +) from .types.uploaded_file import UploadedFile # this is used as the default value for optional parameters @@ -81,6 +85,7 @@ def send_chat_message_by_app_advanced_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[FileInput]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[HttpResponse[typing.Iterator[ChunkChatCompletionResponse]]]: @@ -112,6 +117,9 @@ def send_chat_message_by_app_advanced_chat( auto_generate_name : typing.Optional[bool] (Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field). @@ -136,6 +144,7 @@ def send_chat_message_by_app_advanced_chat( object_=files, annotation=typing.Sequence[FileInput], direction="write" ), "auto_generate_name": auto_generate_name, + "workflow_id": workflow_id, "trace_id": trace_id, }, headers={ @@ -165,9 +174,9 @@ def _iter(): if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -306,9 +315,9 @@ def upload_file_by_app_advanced_chat( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -348,6 +357,100 @@ def upload_file_by_app_advanced_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.contextmanager + def preview_file_by_app_advanced_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response. + + as_attachment : typing.Optional[bool] + Whether to force download the file as an attachment. Default is false (preview in browser). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[HttpResponse[typing.Iterator[bytes]]] + File content + """ + with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + def stream() -> HttpResponse[typing.Iterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return HttpResponse( + response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size)) + ) + _response.read() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield stream() + def convert_audio_to_text_by_app_advanced_chat( self, *, @@ -991,6 +1094,87 @@ def get_conversation_variables_by_app_advanced_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + def update_conversation_variable_by_app_advanced_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[UpdateConversationVariableByAppAdvancedChatResponse]: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description. + + Parameters + ---------- + conversation_id : str + The conversation ID containing the variable to update. + + variable_id : str + The ID of the variable to update. + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[UpdateConversationVariableByAppAdvancedChatResponse] + Successful response + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/variables/{jsonable_encoder(variable_id)}", + method="PUT", + json={ + "value": value, + "user": user, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UpdateConversationVariableByAppAdvancedChatResponse, + parse_obj_as( + type_=UpdateConversationVariableByAppAdvancedChatResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + def get_app_meta_info_by_app_advanced_chat( self, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[GetAppMetaInfoByAppAdvancedChatResponse]: @@ -1372,6 +1556,7 @@ async def send_chat_message_by_app_advanced_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[FileInput]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[ChunkChatCompletionResponse]]]: @@ -1403,6 +1588,9 @@ async def send_chat_message_by_app_advanced_chat( auto_generate_name : typing.Optional[bool] (Optional) Automatically generate title, default `true`. If set to `false`, you can call the conversation rename interface and set `auto_generate` to `true` to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID for specifying a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Three methods are supported with the following priority order: Header: Pass through HTTP Header X-Trace-Id, highest priority. Query parameter: Pass through URL query parameter trace_id. Request Body: Pass through request body field trace_id (this field). @@ -1427,6 +1615,7 @@ async def send_chat_message_by_app_advanced_chat( object_=files, annotation=typing.Sequence[FileInput], direction="write" ), "auto_generate_name": auto_generate_name, + "workflow_id": workflow_id, "trace_id": trace_id, }, headers={ @@ -1456,9 +1645,9 @@ async def _iter(): if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1597,9 +1786,9 @@ async def upload_file_by_app_advanced_chat( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1639,6 +1828,101 @@ async def upload_file_by_app_advanced_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.asynccontextmanager + async def preview_file_by_app_advanced_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response. + + as_attachment : typing.Optional[bool] + Whether to force download the file as an attachment. Default is false (preview in browser). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]] + File content + """ + async with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + async def stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return AsyncHttpResponse( + response=_response, + data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)), + ) + await _response.aread() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield await stream() + async def convert_audio_to_text_by_app_advanced_chat( self, *, @@ -2283,6 +2567,87 @@ async def get_conversation_variables_by_app_advanced_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + async def update_conversation_variable_by_app_advanced_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[UpdateConversationVariableByAppAdvancedChatResponse]: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their name, type, and description. + + Parameters + ---------- + conversation_id : str + The conversation ID containing the variable to update. + + variable_id : str + The ID of the variable to update. + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[UpdateConversationVariableByAppAdvancedChatResponse] + Successful response + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/variables/{jsonable_encoder(variable_id)}", + method="PUT", + json={ + "value": value, + "user": user, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UpdateConversationVariableByAppAdvancedChatResponse, + parse_obj_as( + type_=UpdateConversationVariableByAppAdvancedChatResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + async def get_app_meta_info_by_app_advanced_chat( self, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[GetAppMetaInfoByAppAdvancedChatResponse]: diff --git a/src/dify_sdk/advanced_chat/types/__init__.py b/src/dify_sdk/advanced_chat/types/__init__.py index 67e5e40..37002f8 100644 --- a/src/dify_sdk/advanced_chat/types/__init__.py +++ b/src/dify_sdk/advanced_chat/types/__init__.py @@ -2,6 +2,7 @@ # isort: skip_file +from .bad_request_error_body import BadRequestErrorBody from .chat_completion_response import ChatCompletionResponse from .chat_completion_response_metadata import ChatCompletionResponseMetadata from .chunk_chat_completion_response import ChunkChatCompletionResponse @@ -122,6 +123,7 @@ from .get_conversations_by_app_advanced_chat_response_data_item import GetConversationsByAppAdvancedChatResponseDataItem from .get_suggested_questions_by_app_advanced_chat_response import GetSuggestedQuestionsByAppAdvancedChatResponse from .not_found_error_body import NotFoundErrorBody +from .not_found_error_body_code import NotFoundErrorBodyCode from .rename_conversation_by_app_advanced_chat_response import RenameConversationByAppAdvancedChatResponse from .retriever_resource import RetrieverResource from .send_chat_message_by_app_advanced_chat_request_response_mode import ( @@ -130,10 +132,14 @@ from .send_message_feedback_by_app_advanced_chat_response import SendMessageFeedbackByAppAdvancedChatResponse from .stop_chat_response_by_app_advanced_chat_response import StopChatResponseByAppAdvancedChatResponse from .update_annotation_by_app_advanced_chat_response import UpdateAnnotationByAppAdvancedChatResponse +from .update_conversation_variable_by_app_advanced_chat_response import ( + UpdateConversationVariableByAppAdvancedChatResponse, +) from .uploaded_file import UploadedFile from .usage import Usage __all__ = [ + "BadRequestErrorBody", "ChatCompletionResponse", "ChatCompletionResponseMetadata", "ChunkChatCompletionResponse", @@ -192,12 +198,14 @@ "GetConversationsByAppAdvancedChatResponseDataItem", "GetSuggestedQuestionsByAppAdvancedChatResponse", "NotFoundErrorBody", + "NotFoundErrorBodyCode", "RenameConversationByAppAdvancedChatResponse", "RetrieverResource", "SendChatMessageByAppAdvancedChatRequestResponseMode", "SendMessageFeedbackByAppAdvancedChatResponse", "StopChatResponseByAppAdvancedChatResponse", "UpdateAnnotationByAppAdvancedChatResponse", + "UpdateConversationVariableByAppAdvancedChatResponse", "UploadedFile", "Usage", ] diff --git a/src/dify_sdk/advanced_chat/types/bad_request_error_body.py b/src/dify_sdk/advanced_chat/types/bad_request_error_body.py new file mode 100644 index 0000000..5440d9b --- /dev/null +++ b/src/dify_sdk/advanced_chat/types/bad_request_error_body.py @@ -0,0 +1,27 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class BadRequestErrorBody(UniversalBaseModel): + code: typing.Optional[str] = pydantic.Field(default=None) + """ + Error code + """ + + message: typing.Optional[str] = pydantic.Field(default=None) + """ + Error message + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/dify_sdk/advanced_chat/types/get_conversation_messages_by_app_advanced_chat_response_data_item_message_files_item.py b/src/dify_sdk/advanced_chat/types/get_conversation_messages_by_app_advanced_chat_response_data_item_message_files_item.py index 8475cd9..73d2e73 100644 --- a/src/dify_sdk/advanced_chat/types/get_conversation_messages_by_app_advanced_chat_response_data_item_message_files_item.py +++ b/src/dify_sdk/advanced_chat/types/get_conversation_messages_by_app_advanced_chat_response_data_item_message_files_item.py @@ -22,7 +22,7 @@ class GetConversationMessagesByAppAdvancedChatResponseDataItemMessageFilesItem(U url: typing.Optional[str] = pydantic.Field(default=None) """ - Preview image URL + File preview URL, access files using the file preview API (`/files/{file_id}/preview`) """ belongs_to: typing.Optional[GetConversationMessagesByAppAdvancedChatResponseDataItemMessageFilesItemBelongsTo] = ( diff --git a/src/dify_sdk/advanced_chat/types/not_found_error_body.py b/src/dify_sdk/advanced_chat/types/not_found_error_body.py index b76f064..84e0522 100644 --- a/src/dify_sdk/advanced_chat/types/not_found_error_body.py +++ b/src/dify_sdk/advanced_chat/types/not_found_error_body.py @@ -4,10 +4,11 @@ import pydantic from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .not_found_error_body_code import NotFoundErrorBodyCode class NotFoundErrorBody(UniversalBaseModel): - code: typing.Optional[typing.Literal["conversation_not_exists"]] = pydantic.Field(default=None) + code: typing.Optional[NotFoundErrorBodyCode] = pydantic.Field(default=None) """ Error code """ diff --git a/src/dify_sdk/advanced_chat/types/not_found_error_body_code.py b/src/dify_sdk/advanced_chat/types/not_found_error_body_code.py new file mode 100644 index 0000000..8161f38 --- /dev/null +++ b/src/dify_sdk/advanced_chat/types/not_found_error_body_code.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NotFoundErrorBodyCode = typing.Union[ + typing.Literal["conversation_not_exists", "conversation_variable_not_exists"], typing.Any +] diff --git a/src/dify_sdk/advanced_chat/types/update_conversation_variable_by_app_advanced_chat_response.py b/src/dify_sdk/advanced_chat/types/update_conversation_variable_by_app_advanced_chat_response.py new file mode 100644 index 0000000..f12b8fa --- /dev/null +++ b/src/dify_sdk/advanced_chat/types/update_conversation_variable_by_app_advanced_chat_response.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class UpdateConversationVariableByAppAdvancedChatResponse(UniversalBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable ID + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable name + """ + + value_type: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable type (string, number, object, etc.) + """ + + value: typing.Optional[typing.Optional[typing.Any]] = None + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable description + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Creation timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Last update timestamp + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/dify_sdk/chat/__init__.py b/src/dify_sdk/chat/__init__.py index 421b6a4..22b8a46 100644 --- a/src/dify_sdk/chat/__init__.py +++ b/src/dify_sdk/chat/__init__.py @@ -3,6 +3,8 @@ # isort: skip_file from .types import ( + BadRequestErrorBody, + BadRequestErrorBodyCode, ChatCompletionResponse, ChatCompletionResponseMetadata, ChatHistoryMessage, @@ -23,6 +25,7 @@ FileInput, FileInputTransferMethod, FileInputType, + ForbiddenErrorBody, GetAnnotationReplyStatusByAppChatRequestAction, GetAnnotationReplyStatusByAppChatResponse, GetAnnotationsListByAppChatResponse, @@ -60,6 +63,7 @@ GetConversationVariablesByAppChatResponseDataItem, GetSuggestedQuestionsByAppChatResponse, NotFoundErrorBody, + NotFoundErrorBodyCode, RetrieverResource, SendChatMessageByAppChatRequestFilesItem, SendChatMessageByAppChatRequestFilesItemTransferMethod, @@ -67,12 +71,14 @@ SendMessageFeedbackByAppChatResponse, StopChatResponseByAppChatResponse, UpdateAnnotationByAppChatResponse, + UpdateConversationVariableByAppChatResponse, UploadedFile, Usage, ) from .errors import ( BadRequestError, ContentTooLargeError, + ForbiddenError, InternalServerError, NotFoundError, ServiceUnavailableError, @@ -81,6 +87,8 @@ __all__ = [ "BadRequestError", + "BadRequestErrorBody", + "BadRequestErrorBodyCode", "ChatCompletionResponse", "ChatCompletionResponseMetadata", "ChatHistoryMessage", @@ -102,6 +110,8 @@ "FileInput", "FileInputTransferMethod", "FileInputType", + "ForbiddenError", + "ForbiddenErrorBody", "GetAnnotationReplyStatusByAppChatRequestAction", "GetAnnotationReplyStatusByAppChatResponse", "GetAnnotationsListByAppChatResponse", @@ -141,6 +151,7 @@ "InternalServerError", "NotFoundError", "NotFoundErrorBody", + "NotFoundErrorBodyCode", "RetrieverResource", "SendChatMessageByAppChatRequestFilesItem", "SendChatMessageByAppChatRequestFilesItemTransferMethod", @@ -150,6 +161,7 @@ "StopChatResponseByAppChatResponse", "UnsupportedMediaTypeError", "UpdateAnnotationByAppChatResponse", + "UpdateConversationVariableByAppChatResponse", "UploadedFile", "Usage", ] diff --git a/src/dify_sdk/chat/client.py b/src/dify_sdk/chat/client.py index 9d40c81..b75d022 100644 --- a/src/dify_sdk/chat/client.py +++ b/src/dify_sdk/chat/client.py @@ -31,6 +31,7 @@ from .types.send_message_feedback_by_app_chat_response import SendMessageFeedbackByAppChatResponse from .types.stop_chat_response_by_app_chat_response import StopChatResponseByAppChatResponse from .types.update_annotation_by_app_chat_response import UpdateAnnotationByAppChatResponse +from .types.update_conversation_variable_by_app_chat_response import UpdateConversationVariableByAppChatResponse from .types.uploaded_file import UploadedFile # this is used as the default value for optional parameters @@ -62,6 +63,7 @@ def send_chat_message_by_app_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[SendChatMessageByAppChatRequestFilesItem]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[ChunkChatCompletionResponse]: @@ -94,6 +96,9 @@ def send_chat_message_by_app_chat( auto_generate_name : typing.Optional[bool] (Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID for linking with existing trace components in business systems, enabling end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Supports the following three transmission methods, in order of priority: - Header: Passed through HTTP Header X-Trace-Id, highest priority. @@ -124,11 +129,44 @@ def send_chat_message_by_app_chat( conversation_id=conversation_id, files=files, auto_generate_name=auto_generate_name, + workflow_id=workflow_id, trace_id=trace_id, request_options=request_options, ) as r: yield from r.data + def preview_file_by_app_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Default is false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[bytes] + File content + """ + with self._raw_client.preview_file_by_app_chat( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + yield from r.data + def get_conversation_list_by_app_chat( self, *, @@ -291,6 +329,51 @@ def get_conversation_variables_by_app_chat( ) return _response.data + def update_conversation_variable_by_app_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> UpdateConversationVariableByAppChatResponse: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions. + + Parameters + ---------- + conversation_id : str + ID of the conversation containing the variable to update + + variable_id : str + ID of the variable to update + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UpdateConversationVariableByAppChatResponse + Successfully updated variable + + Examples + -------- + from dify import DifyApi + client = DifyApi(token="YOUR_TOKEN", ) + client.chat.update_conversation_variable_by_app_chat(conversation_id='conversation_id', variable_id='variable_id', value={'key': 'value'} + , user='user', ) + """ + _response = self._raw_client.update_conversation_variable_by_app_chat( + conversation_id, variable_id, user=user, value=value, request_options=request_options + ) + return _response.data + def get_conversation_messages_by_app_chat( self, *, @@ -931,6 +1014,7 @@ async def send_chat_message_by_app_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[SendChatMessageByAppChatRequestFilesItem]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[ChunkChatCompletionResponse]: @@ -963,6 +1047,9 @@ async def send_chat_message_by_app_chat( auto_generate_name : typing.Optional[bool] (Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID for linking with existing trace components in business systems, enabling end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Supports the following three transmission methods, in order of priority: - Header: Passed through HTTP Header X-Trace-Id, highest priority. @@ -996,12 +1083,46 @@ async def main() -> None: conversation_id=conversation_id, files=files, auto_generate_name=auto_generate_name, + workflow_id=workflow_id, trace_id=trace_id, request_options=request_options, ) as r: async for data in r.data: yield data + async def preview_file_by_app_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Default is false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[bytes] + File content + """ + async with self._raw_client.preview_file_by_app_chat( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + async for data in r.data: + yield data + async def get_conversation_list_by_app_chat( self, *, @@ -1176,6 +1297,54 @@ async def main() -> None: ) return _response.data + async def update_conversation_variable_by_app_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> UpdateConversationVariableByAppChatResponse: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions. + + Parameters + ---------- + conversation_id : str + ID of the conversation containing the variable to update + + variable_id : str + ID of the variable to update + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + UpdateConversationVariableByAppChatResponse + Successfully updated variable + + Examples + -------- + from dify import AsyncDifyApi + import asyncio + client = AsyncDifyApi(token="YOUR_TOKEN", ) + async def main() -> None: + await client.chat.update_conversation_variable_by_app_chat(conversation_id='conversation_id', variable_id='variable_id', value={'key': 'value'} + , user='user', ) + asyncio.run(main()) + """ + _response = await self._raw_client.update_conversation_variable_by_app_chat( + conversation_id, variable_id, user=user, value=value, request_options=request_options + ) + return _response.data + async def get_conversation_messages_by_app_chat( self, *, diff --git a/src/dify_sdk/chat/errors/__init__.py b/src/dify_sdk/chat/errors/__init__.py index 635aad6..37b13b9 100644 --- a/src/dify_sdk/chat/errors/__init__.py +++ b/src/dify_sdk/chat/errors/__init__.py @@ -4,6 +4,7 @@ from .bad_request_error import BadRequestError from .content_too_large_error import ContentTooLargeError +from .forbidden_error import ForbiddenError from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .service_unavailable_error import ServiceUnavailableError @@ -12,6 +13,7 @@ __all__ = [ "BadRequestError", "ContentTooLargeError", + "ForbiddenError", "InternalServerError", "NotFoundError", "ServiceUnavailableError", diff --git a/src/dify_sdk/chat/errors/bad_request_error.py b/src/dify_sdk/chat/errors/bad_request_error.py index 5ccb07c..c1e658e 100644 --- a/src/dify_sdk/chat/errors/bad_request_error.py +++ b/src/dify_sdk/chat/errors/bad_request_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class BadRequestError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=400, body=body) diff --git a/src/dify_sdk/chat/errors/forbidden_error.py b/src/dify_sdk/chat/errors/forbidden_error.py new file mode 100644 index 0000000..674d6b9 --- /dev/null +++ b/src/dify_sdk/chat/errors/forbidden_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class ForbiddenError(ApiError): + def __init__(self, body: typing.Optional[typing.Any]): + super().__init__(status_code=403, body=body) diff --git a/src/dify_sdk/chat/raw_client.py b/src/dify_sdk/chat/raw_client.py index 3d82e46..bdc5eff 100644 --- a/src/dify_sdk/chat/raw_client.py +++ b/src/dify_sdk/chat/raw_client.py @@ -15,6 +15,7 @@ from ..core.serialization import convert_and_respect_annotation_metadata from .errors.bad_request_error import BadRequestError from .errors.content_too_large_error import ContentTooLargeError +from .errors.forbidden_error import ForbiddenError from .errors.internal_server_error import InternalServerError from .errors.not_found_error import NotFoundError from .errors.service_unavailable_error import ServiceUnavailableError @@ -45,6 +46,7 @@ from .types.send_message_feedback_by_app_chat_response import SendMessageFeedbackByAppChatResponse from .types.stop_chat_response_by_app_chat_response import StopChatResponseByAppChatResponse from .types.update_annotation_by_app_chat_response import UpdateAnnotationByAppChatResponse +from .types.update_conversation_variable_by_app_chat_response import UpdateConversationVariableByAppChatResponse from .types.uploaded_file import UploadedFile # this is used as the default value for optional parameters @@ -66,6 +68,7 @@ def send_chat_message_by_app_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[SendChatMessageByAppChatRequestFilesItem]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.Iterator[HttpResponse[typing.Iterator[ChunkChatCompletionResponse]]]: @@ -98,6 +101,9 @@ def send_chat_message_by_app_chat( auto_generate_name : typing.Optional[bool] (Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID for linking with existing trace components in business systems, enabling end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Supports the following three transmission methods, in order of priority: - Header: Passed through HTTP Header X-Trace-Id, highest priority. @@ -127,6 +133,7 @@ def send_chat_message_by_app_chat( direction="write", ), "auto_generate_name": auto_generate_name, + "workflow_id": workflow_id, "trace_id": trace_id, }, headers={ @@ -155,6 +162,26 @@ def _iter(): _response.read() if _response.status_code == 400: raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( typing.cast( Error, parse_obj_as( @@ -163,6 +190,80 @@ def _iter(): ), ) ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield stream() + + @contextlib.contextmanager + def preview_file_by_app_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Default is false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[HttpResponse[typing.Iterator[bytes]]] + File content + """ + with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + def stream() -> HttpResponse[typing.Iterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return HttpResponse( + response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size)) + ) + _response.read() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) if _response.status_code == 404: raise NotFoundError( typing.cast( @@ -420,6 +521,87 @@ def get_conversation_variables_by_app_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + def update_conversation_variable_by_app_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[UpdateConversationVariableByAppChatResponse]: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions. + + Parameters + ---------- + conversation_id : str + ID of the conversation containing the variable to update + + variable_id : str + ID of the variable to update + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[UpdateConversationVariableByAppChatResponse] + Successfully updated variable + """ + _response = self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/variables/{jsonable_encoder(variable_id)}", + method="PUT", + json={ + "value": value, + "user": user, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UpdateConversationVariableByAppChatResponse, + parse_obj_as( + type_=UpdateConversationVariableByAppChatResponse, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + def get_conversation_messages_by_app_chat( self, *, @@ -692,9 +874,9 @@ def upload_file_by_app_chat( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1359,6 +1541,7 @@ async def send_chat_message_by_app_chat( conversation_id: typing.Optional[str] = OMIT, files: typing.Optional[typing.Sequence[SendChatMessageByAppChatRequestFilesItem]] = OMIT, auto_generate_name: typing.Optional[bool] = OMIT, + workflow_id: typing.Optional[str] = OMIT, trace_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[ChunkChatCompletionResponse]]]: @@ -1391,6 +1574,9 @@ async def send_chat_message_by_app_chat( auto_generate_name : typing.Optional[bool] (Optional) Whether to automatically generate title, default is true. If set to false, you can call the conversation rename interface and set auto_generate to true to generate a title asynchronously. + workflow_id : typing.Optional[str] + (Optional) Workflow ID, used to specify a specific version. If not provided, the default published version will be used. + trace_id : typing.Optional[str] (Optional) Trace ID for linking with existing trace components in business systems, enabling end-to-end distributed tracing scenarios. If not specified, the system will automatically generate a trace_id. Supports the following three transmission methods, in order of priority: - Header: Passed through HTTP Header X-Trace-Id, highest priority. @@ -1420,6 +1606,7 @@ async def send_chat_message_by_app_chat( direction="write", ), "auto_generate_name": auto_generate_name, + "workflow_id": workflow_id, "trace_id": trace_id, }, headers={ @@ -1448,6 +1635,26 @@ async def _iter(): await _response.aread() if _response.status_code == 400: raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( typing.cast( Error, parse_obj_as( @@ -1456,6 +1663,81 @@ async def _iter(): ), ) ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield await stream() + + @contextlib.asynccontextmanager + async def preview_file_by_app_chat( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Default is false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]] + File content + """ + async with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + async def stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return AsyncHttpResponse( + response=_response, + data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)), + ) + await _response.aread() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) if _response.status_code == 404: raise NotFoundError( typing.cast( @@ -1713,6 +1995,87 @@ async def get_conversation_variables_by_app_chat( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + async def update_conversation_variable_by_app_chat( + self, + conversation_id: str, + variable_id: str, + *, + user: str, + value: typing.Optional[typing.Any] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[UpdateConversationVariableByAppChatResponse]: + """ + Update the value of a specific conversation variable. This endpoint allows you to modify variable values captured during conversations while preserving their names, types, and descriptions. + + Parameters + ---------- + conversation_id : str + ID of the conversation containing the variable to update + + variable_id : str + ID of the variable to update + + user : str + User identifier, defined by developer rules, must be unique within the application. + + value : typing.Optional[typing.Any] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[UpdateConversationVariableByAppChatResponse] + Successfully updated variable + """ + _response = await self._client_wrapper.httpx_client.request( + f"conversations/{jsonable_encoder(conversation_id)}/variables/{jsonable_encoder(variable_id)}", + method="PUT", + json={ + "value": value, + "user": user, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + UpdateConversationVariableByAppChatResponse, + parse_obj_as( + type_=UpdateConversationVariableByAppChatResponse, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + async def get_conversation_messages_by_app_chat( self, *, @@ -1985,9 +2348,9 @@ async def upload_file_by_app_chat( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/chat/types/__init__.py b/src/dify_sdk/chat/types/__init__.py index b4683ee..bec40bb 100644 --- a/src/dify_sdk/chat/types/__init__.py +++ b/src/dify_sdk/chat/types/__init__.py @@ -2,6 +2,8 @@ # isort: skip_file +from .bad_request_error_body import BadRequestErrorBody +from .bad_request_error_body_code import BadRequestErrorBodyCode from .chat_completion_response import ChatCompletionResponse from .chat_completion_response_metadata import ChatCompletionResponseMetadata from .chat_history_message import ChatHistoryMessage @@ -22,6 +24,7 @@ from .file_input import FileInput from .file_input_transfer_method import FileInputTransferMethod from .file_input_type import FileInputType +from .forbidden_error_body import ForbiddenErrorBody from .get_annotation_reply_status_by_app_chat_request_action import GetAnnotationReplyStatusByAppChatRequestAction from .get_annotation_reply_status_by_app_chat_response import GetAnnotationReplyStatusByAppChatResponse from .get_annotations_list_by_app_chat_response import GetAnnotationsListByAppChatResponse @@ -97,6 +100,7 @@ from .get_conversation_variables_by_app_chat_response_data_item import GetConversationVariablesByAppChatResponseDataItem from .get_suggested_questions_by_app_chat_response import GetSuggestedQuestionsByAppChatResponse from .not_found_error_body import NotFoundErrorBody +from .not_found_error_body_code import NotFoundErrorBodyCode from .retriever_resource import RetrieverResource from .send_chat_message_by_app_chat_request_files_item import SendChatMessageByAppChatRequestFilesItem from .send_chat_message_by_app_chat_request_files_item_transfer_method import ( @@ -106,10 +110,13 @@ from .send_message_feedback_by_app_chat_response import SendMessageFeedbackByAppChatResponse from .stop_chat_response_by_app_chat_response import StopChatResponseByAppChatResponse from .update_annotation_by_app_chat_response import UpdateAnnotationByAppChatResponse +from .update_conversation_variable_by_app_chat_response import UpdateConversationVariableByAppChatResponse from .uploaded_file import UploadedFile from .usage import Usage __all__ = [ + "BadRequestErrorBody", + "BadRequestErrorBodyCode", "ChatCompletionResponse", "ChatCompletionResponseMetadata", "ChatHistoryMessage", @@ -130,6 +137,7 @@ "FileInput", "FileInputTransferMethod", "FileInputType", + "ForbiddenErrorBody", "GetAnnotationReplyStatusByAppChatRequestAction", "GetAnnotationReplyStatusByAppChatResponse", "GetAnnotationsListByAppChatResponse", @@ -167,6 +175,7 @@ "GetConversationVariablesByAppChatResponseDataItem", "GetSuggestedQuestionsByAppChatResponse", "NotFoundErrorBody", + "NotFoundErrorBodyCode", "RetrieverResource", "SendChatMessageByAppChatRequestFilesItem", "SendChatMessageByAppChatRequestFilesItemTransferMethod", @@ -174,6 +183,7 @@ "SendMessageFeedbackByAppChatResponse", "StopChatResponseByAppChatResponse", "UpdateAnnotationByAppChatResponse", + "UpdateConversationVariableByAppChatResponse", "UploadedFile", "Usage", ] diff --git a/src/dify_sdk/chat/types/bad_request_error_body.py b/src/dify_sdk/chat/types/bad_request_error_body.py new file mode 100644 index 0000000..77c3061 --- /dev/null +++ b/src/dify_sdk/chat/types/bad_request_error_body.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class BadRequestErrorBody(UniversalBaseModel): + message: typing.Optional[typing.Optional[typing.Any]] = None + code: str = pydantic.Field() + """ + Error code + """ + + status: int = pydantic.Field() + """ + HTTP status code + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/dify_sdk/chat/types/bad_request_error_body_code.py b/src/dify_sdk/chat/types/bad_request_error_body_code.py new file mode 100644 index 0000000..e4453c2 --- /dev/null +++ b/src/dify_sdk/chat/types/bad_request_error_body_code.py @@ -0,0 +1,18 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +BadRequestErrorBodyCode = typing.Union[ + typing.Literal[ + "invalid_param", + "app_unavailable", + "provider_not_initialize", + "provider_quota_exceeded", + "model_currently_not_support", + "workflow_not_found", + "draft_workflow_error", + "workflow_id_format_error", + "completion_request_error", + ], + typing.Any, +] diff --git a/src/dify_sdk/chat/types/forbidden_error_body.py b/src/dify_sdk/chat/types/forbidden_error_body.py new file mode 100644 index 0000000..7e13c13 --- /dev/null +++ b/src/dify_sdk/chat/types/forbidden_error_body.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class ForbiddenErrorBody(UniversalBaseModel): + code: typing.Optional[typing.Literal["file_access_denied"]] = None + status: int = pydantic.Field() + """ + HTTP status code + """ + + message: str = pydantic.Field() + """ + Error message description + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/dify_sdk/chat/types/not_found_error_body.py b/src/dify_sdk/chat/types/not_found_error_body.py index b76f064..44e14ee 100644 --- a/src/dify_sdk/chat/types/not_found_error_body.py +++ b/src/dify_sdk/chat/types/not_found_error_body.py @@ -4,17 +4,19 @@ import pydantic from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .not_found_error_body_code import NotFoundErrorBodyCode class NotFoundErrorBody(UniversalBaseModel): - code: typing.Optional[typing.Literal["conversation_not_exists"]] = pydantic.Field(default=None) + code: typing.Optional[NotFoundErrorBodyCode] = None + status: int = pydantic.Field() """ - Error code + HTTP status code """ - message: typing.Optional[str] = pydantic.Field(default=None) + message: str = pydantic.Field() """ - Error message + Error message description """ if IS_PYDANTIC_V2: diff --git a/src/dify_sdk/chat/types/not_found_error_body_code.py b/src/dify_sdk/chat/types/not_found_error_body_code.py new file mode 100644 index 0000000..8161f38 --- /dev/null +++ b/src/dify_sdk/chat/types/not_found_error_body_code.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +NotFoundErrorBodyCode = typing.Union[ + typing.Literal["conversation_not_exists", "conversation_variable_not_exists"], typing.Any +] diff --git a/src/dify_sdk/chat/types/update_conversation_variable_by_app_chat_response.py b/src/dify_sdk/chat/types/update_conversation_variable_by_app_chat_response.py new file mode 100644 index 0000000..f4f3141 --- /dev/null +++ b/src/dify_sdk/chat/types/update_conversation_variable_by_app_chat_response.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel + + +class UpdateConversationVariableByAppChatResponse(UniversalBaseModel): + id: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable ID + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable name + """ + + value_type: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable type (string, number, object, etc.) + """ + + value: typing.Optional[typing.Optional[typing.Any]] = None + description: typing.Optional[str] = pydantic.Field(default=None) + """ + Variable description + """ + + created_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Creation timestamp + """ + + updated_at: typing.Optional[int] = pydantic.Field(default=None) + """ + Last update timestamp + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/dify_sdk/generation/__init__.py b/src/dify_sdk/generation/__init__.py index 9b8e1c6..165cbaa 100644 --- a/src/dify_sdk/generation/__init__.py +++ b/src/dify_sdk/generation/__init__.py @@ -63,6 +63,7 @@ from .errors import ( BadRequestError, ContentTooLargeError, + ForbiddenError, InternalServerError, NotFoundError, ServiceUnavailableError, @@ -84,6 +85,7 @@ "FileInput", "FileInputTransferMethod", "FileInputType", + "ForbiddenError", "GetAnnotationReplyStatusByAppGenerationRequestAction", "GetAnnotationReplyStatusByAppGenerationResponse", "GetAnnotationsListByAppGenerationResponse", diff --git a/src/dify_sdk/generation/client.py b/src/dify_sdk/generation/client.py index 2c3efc0..eaf620a 100644 --- a/src/dify_sdk/generation/client.py +++ b/src/dify_sdk/generation/client.py @@ -154,6 +154,38 @@ def upload_file_by_app_generation( ) return _response.data + def preview_file_by_app_generation( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force file download as attachment. Defaults to false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[bytes] + Successful response + """ + with self._raw_client.preview_file_by_app_generation( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + yield from r.data + def get_application_info_by_app_generation( self, *, request_options: typing.Optional[RequestOptions] = None ) -> GetApplicationInfoByAppGenerationResponse: @@ -744,6 +776,39 @@ async def main() -> None: ) return _response.data + async def preview_file_by_app_generation( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force file download as attachment. Defaults to false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[bytes] + Successful response + """ + async with self._raw_client.preview_file_by_app_generation( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + async for data in r.data: + yield data + async def get_application_info_by_app_generation( self, *, request_options: typing.Optional[RequestOptions] = None ) -> GetApplicationInfoByAppGenerationResponse: diff --git a/src/dify_sdk/generation/errors/__init__.py b/src/dify_sdk/generation/errors/__init__.py index 635aad6..37b13b9 100644 --- a/src/dify_sdk/generation/errors/__init__.py +++ b/src/dify_sdk/generation/errors/__init__.py @@ -4,6 +4,7 @@ from .bad_request_error import BadRequestError from .content_too_large_error import ContentTooLargeError +from .forbidden_error import ForbiddenError from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .service_unavailable_error import ServiceUnavailableError @@ -12,6 +13,7 @@ __all__ = [ "BadRequestError", "ContentTooLargeError", + "ForbiddenError", "InternalServerError", "NotFoundError", "ServiceUnavailableError", diff --git a/src/dify_sdk/generation/errors/bad_request_error.py b/src/dify_sdk/generation/errors/bad_request_error.py index 5ccb07c..c1e658e 100644 --- a/src/dify_sdk/generation/errors/bad_request_error.py +++ b/src/dify_sdk/generation/errors/bad_request_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class BadRequestError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=400, body=body) diff --git a/src/dify_sdk/generation/errors/forbidden_error.py b/src/dify_sdk/generation/errors/forbidden_error.py new file mode 100644 index 0000000..674d6b9 --- /dev/null +++ b/src/dify_sdk/generation/errors/forbidden_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class ForbiddenError(ApiError): + def __init__(self, body: typing.Optional[typing.Any]): + super().__init__(status_code=403, body=body) diff --git a/src/dify_sdk/generation/raw_client.py b/src/dify_sdk/generation/raw_client.py index 14c4263..459f10d 100644 --- a/src/dify_sdk/generation/raw_client.py +++ b/src/dify_sdk/generation/raw_client.py @@ -15,6 +15,7 @@ from ..core.serialization import convert_and_respect_annotation_metadata from .errors.bad_request_error import BadRequestError from .errors.content_too_large_error import ContentTooLargeError +from .errors.forbidden_error import ForbiddenError from .errors.internal_server_error import InternalServerError from .errors.not_found_error import NotFoundError from .errors.service_unavailable_error import ServiceUnavailableError @@ -145,9 +146,9 @@ def _iter(): if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -233,9 +234,9 @@ def upload_file_by_app_generation( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -275,6 +276,100 @@ def upload_file_by_app_generation( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.contextmanager + def preview_file_by_app_generation( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force file download as attachment. Defaults to false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[HttpResponse[typing.Iterator[bytes]]] + Successful response + """ + with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + def stream() -> HttpResponse[typing.Iterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return HttpResponse( + response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size)) + ) + _response.read() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield stream() + def get_application_info_by_app_generation( self, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[GetApplicationInfoByAppGenerationResponse]: @@ -1029,9 +1124,9 @@ async def _iter(): if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1117,9 +1212,9 @@ async def upload_file_by_app_generation( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1159,6 +1254,101 @@ async def upload_file_by_app_generation( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.asynccontextmanager + async def preview_file_by_app_generation( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. + Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force file download as attachment. Defaults to false (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]] + Successful response + """ + async with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + async def stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return AsyncHttpResponse( + response=_response, + data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)), + ) + await _response.aread() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield await stream() + async def get_application_info_by_app_generation( self, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[GetApplicationInfoByAppGenerationResponse]: diff --git a/src/dify_sdk/knowledge_base/datasets/raw_client.py b/src/dify_sdk/knowledge_base/datasets/raw_client.py index 38479f8..6245f65 100644 --- a/src/dify_sdk/knowledge_base/datasets/raw_client.py +++ b/src/dify_sdk/knowledge_base/datasets/raw_client.py @@ -80,9 +80,9 @@ def get_dataset_list( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -193,9 +193,9 @@ def create_empty_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -252,9 +252,9 @@ def view_knowledge_base_details( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -262,9 +262,9 @@ def view_knowledge_base_details( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -303,9 +303,9 @@ def delete_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -313,9 +313,9 @@ def delete_dataset( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -412,9 +412,9 @@ def update_knowledge_base_details( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -422,9 +422,9 @@ def update_knowledge_base_details( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -497,9 +497,9 @@ def retrieve_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -507,9 +507,9 @@ def retrieve_dataset( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -572,9 +572,9 @@ async def get_dataset_list( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -685,9 +685,9 @@ async def create_empty_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -744,9 +744,9 @@ async def view_knowledge_base_details( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -754,9 +754,9 @@ async def view_knowledge_base_details( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -795,9 +795,9 @@ async def delete_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -805,9 +805,9 @@ async def delete_dataset( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -904,9 +904,9 @@ async def update_knowledge_base_details( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -914,9 +914,9 @@ async def update_knowledge_base_details( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -989,9 +989,9 @@ async def retrieve_dataset( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -999,9 +999,9 @@ async def retrieve_dataset( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/knowledge_base/documents/raw_client.py b/src/dify_sdk/knowledge_base/documents/raw_client.py index f3f11b7..ecedd98 100644 --- a/src/dify_sdk/knowledge_base/documents/raw_client.py +++ b/src/dify_sdk/knowledge_base/documents/raw_client.py @@ -119,9 +119,9 @@ def create_document_by_text( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -209,9 +209,9 @@ def create_document_by_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -323,9 +323,9 @@ def update_document_by_text( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -333,9 +333,9 @@ def update_document_by_text( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -411,9 +411,9 @@ def update_document_by_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -421,9 +421,9 @@ def update_document_by_file( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -493,9 +493,9 @@ def get_document_indexing_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -503,9 +503,9 @@ def get_document_indexing_status( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -572,9 +572,9 @@ def get_document_list( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -635,9 +635,9 @@ def get_document_detail( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -645,9 +645,9 @@ def get_document_detail( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -699,9 +699,9 @@ def delete_document( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -709,9 +709,9 @@ def delete_document( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -776,9 +776,9 @@ def batch_update_document_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -786,9 +786,9 @@ def batch_update_document_status( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -838,9 +838,9 @@ def get_upload_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -848,9 +848,9 @@ def get_upload_file( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -942,9 +942,9 @@ async def create_document_by_text( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1032,9 +1032,9 @@ async def create_document_by_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1146,9 +1146,9 @@ async def update_document_by_text( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1156,9 +1156,9 @@ async def update_document_by_text( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1234,9 +1234,9 @@ async def update_document_by_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1244,9 +1244,9 @@ async def update_document_by_file( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1316,9 +1316,9 @@ async def get_document_indexing_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1326,9 +1326,9 @@ async def get_document_indexing_status( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1395,9 +1395,9 @@ async def get_document_list( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1458,9 +1458,9 @@ async def get_document_detail( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1468,9 +1468,9 @@ async def get_document_detail( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1522,9 +1522,9 @@ async def delete_document( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1532,9 +1532,9 @@ async def delete_document( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1599,9 +1599,9 @@ async def batch_update_document_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1609,9 +1609,9 @@ async def batch_update_document_status( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1661,9 +1661,9 @@ async def get_upload_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1671,9 +1671,9 @@ async def get_upload_file( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/knowledge_base/errors/bad_request_error.py b/src/dify_sdk/knowledge_base/errors/bad_request_error.py index 5ccb07c..c1e658e 100644 --- a/src/dify_sdk/knowledge_base/errors/bad_request_error.py +++ b/src/dify_sdk/knowledge_base/errors/bad_request_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class BadRequestError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=400, body=body) diff --git a/src/dify_sdk/knowledge_base/errors/forbidden_error.py b/src/dify_sdk/knowledge_base/errors/forbidden_error.py index 4569e4f..674d6b9 100644 --- a/src/dify_sdk/knowledge_base/errors/forbidden_error.py +++ b/src/dify_sdk/knowledge_base/errors/forbidden_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class ForbiddenError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=403, body=body) diff --git a/src/dify_sdk/knowledge_base/metadata/raw_client.py b/src/dify_sdk/knowledge_base/metadata/raw_client.py index 91dfa15..c7b4371 100644 --- a/src/dify_sdk/knowledge_base/metadata/raw_client.py +++ b/src/dify_sdk/knowledge_base/metadata/raw_client.py @@ -11,7 +11,6 @@ from ...core.request_options import RequestOptions from ...core.serialization import convert_and_respect_annotation_metadata from ..errors.bad_request_error import BadRequestError -from ..types.error import Error from .types.create_metadata_response import CreateMetadataResponse from .types.list_dataset_metadata_response import ListDatasetMetadataResponse from .types.toggle_built_in_metadata_request_action import ToggleBuiltInMetadataRequestAction @@ -63,9 +62,9 @@ def list_dataset_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -126,9 +125,9 @@ def create_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -170,9 +169,9 @@ def delete_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -232,9 +231,9 @@ def update_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -280,9 +279,9 @@ def toggle_built_in_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -339,9 +338,9 @@ def update_documents_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -393,9 +392,9 @@ async def list_dataset_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -456,9 +455,9 @@ async def create_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -500,9 +499,9 @@ async def delete_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -562,9 +561,9 @@ async def update_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -610,9 +609,9 @@ async def toggle_built_in_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -669,9 +668,9 @@ async def update_documents_metadata( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/knowledge_base/models/raw_client.py b/src/dify_sdk/knowledge_base/models/raw_client.py index 5cc2485..e9b6364 100644 --- a/src/dify_sdk/knowledge_base/models/raw_client.py +++ b/src/dify_sdk/knowledge_base/models/raw_client.py @@ -9,7 +9,6 @@ from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ..errors.bad_request_error import BadRequestError -from ..types.error import Error from .types.get_embedding_models_response import GetEmbeddingModelsResponse @@ -51,9 +50,9 @@ def get_embedding_models( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -102,9 +101,9 @@ async def get_embedding_models( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/knowledge_base/segments/raw_client.py b/src/dify_sdk/knowledge_base/segments/raw_client.py index 679a284..831ab18 100644 --- a/src/dify_sdk/knowledge_base/segments/raw_client.py +++ b/src/dify_sdk/knowledge_base/segments/raw_client.py @@ -12,7 +12,6 @@ from ...core.serialization import convert_and_respect_annotation_metadata from ..errors.bad_request_error import BadRequestError from ..errors.forbidden_error import ForbiddenError -from ..types.error import Error from .types.create_segments_request_segments_item import CreateSegmentsRequestSegmentsItem from .types.create_segments_response import CreateSegmentsResponse from .types.get_datasets_dataset_id_documents_document_id_segments_segment_id_child_chunks_response import ( @@ -103,9 +102,9 @@ def get_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -171,9 +170,9 @@ def create_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -181,9 +180,9 @@ def create_segments( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -258,9 +257,9 @@ def query_document_child_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -329,9 +328,9 @@ def create_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -339,9 +338,9 @@ def create_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -395,9 +394,9 @@ def delete_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -405,9 +404,9 @@ def delete_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -480,9 +479,9 @@ def update_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -490,9 +489,9 @@ def update_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -550,9 +549,9 @@ def get_segment_detail( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -560,9 +559,9 @@ def get_segment_detail( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -632,9 +631,9 @@ def update_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -642,9 +641,9 @@ def update_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -694,9 +693,9 @@ def delete_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -704,9 +703,9 @@ def delete_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -787,9 +786,9 @@ async def get_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -855,9 +854,9 @@ async def create_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -865,9 +864,9 @@ async def create_segments( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -942,9 +941,9 @@ async def query_document_child_segments( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1013,9 +1012,9 @@ async def create_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1023,9 +1022,9 @@ async def create_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1079,9 +1078,9 @@ async def delete_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1089,9 +1088,9 @@ async def delete_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1164,9 +1163,9 @@ async def update_document_child_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1174,9 +1173,9 @@ async def update_document_child_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1234,9 +1233,9 @@ async def get_segment_detail( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1244,9 +1243,9 @@ async def get_segment_detail( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1316,9 +1315,9 @@ async def update_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1326,9 +1325,9 @@ async def update_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1378,9 +1377,9 @@ async def delete_segment( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1388,9 +1387,9 @@ async def delete_segment( if _response.status_code == 403: raise ForbiddenError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/knowledge_base/tags/raw_client.py b/src/dify_sdk/knowledge_base/tags/raw_client.py index 6fff10d..34d2c54 100644 --- a/src/dify_sdk/knowledge_base/tags/raw_client.py +++ b/src/dify_sdk/knowledge_base/tags/raw_client.py @@ -10,7 +10,6 @@ from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ..errors.bad_request_error import BadRequestError -from ..types.error import Error from ..types.knowledge_tag import KnowledgeTag from .types.get_dataset_tags_response import GetDatasetTagsResponse @@ -56,9 +55,9 @@ def get_knowledge_tags( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -112,9 +111,9 @@ def create_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -160,9 +159,9 @@ def delete_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -220,9 +219,9 @@ def update_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -272,9 +271,9 @@ def bind_dataset_to_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -324,9 +323,9 @@ def unbind_dataset_from_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -373,9 +372,9 @@ def get_dataset_tags( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -424,9 +423,9 @@ async def get_knowledge_tags( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -480,9 +479,9 @@ async def create_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -528,9 +527,9 @@ async def delete_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -588,9 +587,9 @@ async def update_knowledge_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -640,9 +639,9 @@ async def bind_dataset_to_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -692,9 +691,9 @@ async def unbind_dataset_from_tag( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -741,9 +740,9 @@ async def get_dataset_tags( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/workflow/__init__.py b/src/dify_sdk/workflow/__init__.py index de3f5a5..9e9f9c0 100644 --- a/src/dify_sdk/workflow/__init__.py +++ b/src/dify_sdk/workflow/__init__.py @@ -41,6 +41,7 @@ GetWorkflowLogsResponseDataItemWorkflowRun, GetWorkflowLogsResponseDataItemWorkflowRunStatus, RetrieverResource, + RunSpecificWorkflowRequestResponseMode, RunWorkflowRequestResponseMode, StopWorkflowResponse, UploadFileResponse, @@ -52,6 +53,7 @@ from .errors import ( BadRequestError, ContentTooLargeError, + ForbiddenError, InternalServerError, NotFoundError, ServiceUnavailableError, @@ -72,6 +74,7 @@ "FileInput", "FileInputTransferMethod", "FileInputType", + "ForbiddenError", "GetAppInfoResponse", "GetAppParametersResponse", "GetAppParametersResponseFileUpload", @@ -102,6 +105,7 @@ "InternalServerError", "NotFoundError", "RetrieverResource", + "RunSpecificWorkflowRequestResponseMode", "RunWorkflowRequestResponseMode", "ServiceUnavailableError", "StopWorkflowResponse", diff --git a/src/dify_sdk/workflow/client.py b/src/dify_sdk/workflow/client.py index d81da41..be83cb7 100644 --- a/src/dify_sdk/workflow/client.py +++ b/src/dify_sdk/workflow/client.py @@ -13,6 +13,7 @@ from .types.get_workflow_execution_status_response import GetWorkflowExecutionStatusResponse from .types.get_workflow_logs_request_status import GetWorkflowLogsRequestStatus from .types.get_workflow_logs_response import GetWorkflowLogsResponse +from .types.run_specific_workflow_request_response_mode import RunSpecificWorkflowRequestResponseMode from .types.run_workflow_request_response_mode import RunWorkflowRequestResponseMode from .types.stop_workflow_response import StopWorkflowResponse from .types.upload_file_response import UploadFileResponse @@ -111,6 +112,86 @@ def run_workflow( ) as r: yield from r.data + def run_specific_workflow( + self, + workflow_id: str, + *, + inputs: typing.Dict[str, typing.Optional[typing.Any]], + response_mode: RunSpecificWorkflowRequestResponseMode, + user: str, + files: typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[ChunkWorkflowMessage]: + """ + Execute a specific version of the workflow, specifying the workflow ID through the path parameter + + Parameters + ---------- + workflow_id : str + Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history. + + inputs : typing.Dict[str, typing.Optional[typing.Any]] + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + + response_mode : RunSpecificWorkflowRequestResponseMode + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + + user : str + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + + files : typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] + Optional file list + + trace_id : typing.Optional[str] + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[ChunkWorkflowMessage] + Successful response + + Examples + -------- + from dify import DifyApi + client = DifyApi(token="YOUR_TOKEN", ) + response = client.workflow.run_specific_workflow(workflow_id='workflow_id', inputs={'key': 'value' + }, response_mode="streaming", user='user', ) + for chunk in response: + yield chunk + """ + with self._raw_client.run_specific_workflow( + workflow_id, + inputs=inputs, + response_mode=response_mode, + user=user, + files=files, + trace_id=trace_id, + request_options=request_options, + ) as r: + yield from r.data + def get_workflow_execution_status( self, workflow_run_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> GetWorkflowExecutionStatusResponse: @@ -260,6 +341,35 @@ def upload_file( _response = self._raw_client.upload_file(file=file, user=user, request_options=request_options) return _response.data + def preview_file( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[bytes] + Successfully retrieved file content + """ + with self._raw_client.preview_file(file_id, as_attachment=as_attachment, request_options=request_options) as r: + yield from r.data + def get_app_info(self, *, request_options: typing.Optional[RequestOptions] = None) -> GetAppInfoResponse: """ Used to get basic information about the application @@ -426,6 +536,90 @@ async def main() -> None: async for data in r.data: yield data + async def run_specific_workflow( + self, + workflow_id: str, + *, + inputs: typing.Dict[str, typing.Optional[typing.Any]], + response_mode: RunSpecificWorkflowRequestResponseMode, + user: str, + files: typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[ChunkWorkflowMessage]: + """ + Execute a specific version of the workflow, specifying the workflow ID through the path parameter + + Parameters + ---------- + workflow_id : str + Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history. + + inputs : typing.Dict[str, typing.Optional[typing.Any]] + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + + response_mode : RunSpecificWorkflowRequestResponseMode + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + + user : str + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + + files : typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] + Optional file list + + trace_id : typing.Optional[str] + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[ChunkWorkflowMessage] + Successful response + + Examples + -------- + from dify import AsyncDifyApi + import asyncio + client = AsyncDifyApi(token="YOUR_TOKEN", ) + async def main() -> None: + response = await client.workflow.run_specific_workflow(workflow_id='workflow_id', inputs={'key': 'value' + }, response_mode="streaming", user='user', ) + async for chunk in response: + yield chunk + asyncio.run(main()) + """ + async with self._raw_client.run_specific_workflow( + workflow_id, + inputs=inputs, + response_mode=response_mode, + user=user, + files=files, + trace_id=trace_id, + request_options=request_options, + ) as r: + async for data in r.data: + yield data + async def get_workflow_execution_status( self, workflow_run_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> GetWorkflowExecutionStatusResponse: @@ -589,6 +783,38 @@ async def main() -> None: _response = await self._raw_client.upload_file(file=file, user=user, request_options=request_options) return _response.data + async def preview_file( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[bytes]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[bytes] + Successfully retrieved file content + """ + async with self._raw_client.preview_file( + file_id, as_attachment=as_attachment, request_options=request_options + ) as r: + async for data in r.data: + yield data + async def get_app_info(self, *, request_options: typing.Optional[RequestOptions] = None) -> GetAppInfoResponse: """ Used to get basic information about the application diff --git a/src/dify_sdk/workflow/errors/__init__.py b/src/dify_sdk/workflow/errors/__init__.py index ccae61f..4b2a0bb 100644 --- a/src/dify_sdk/workflow/errors/__init__.py +++ b/src/dify_sdk/workflow/errors/__init__.py @@ -4,6 +4,7 @@ from .bad_request_error import BadRequestError from .content_too_large_error import ContentTooLargeError +from .forbidden_error import ForbiddenError from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .service_unavailable_error import ServiceUnavailableError @@ -13,6 +14,7 @@ __all__ = [ "BadRequestError", "ContentTooLargeError", + "ForbiddenError", "InternalServerError", "NotFoundError", "ServiceUnavailableError", diff --git a/src/dify_sdk/workflow/errors/bad_request_error.py b/src/dify_sdk/workflow/errors/bad_request_error.py index 5ccb07c..c1e658e 100644 --- a/src/dify_sdk/workflow/errors/bad_request_error.py +++ b/src/dify_sdk/workflow/errors/bad_request_error.py @@ -1,9 +1,10 @@ # This file was auto-generated by Fern from our API Definition. +import typing + from ...core.api_error import ApiError -from ..types.error import Error class BadRequestError(ApiError): - def __init__(self, body: Error): + def __init__(self, body: typing.Optional[typing.Any]): super().__init__(status_code=400, body=body) diff --git a/src/dify_sdk/workflow/errors/forbidden_error.py b/src/dify_sdk/workflow/errors/forbidden_error.py new file mode 100644 index 0000000..674d6b9 --- /dev/null +++ b/src/dify_sdk/workflow/errors/forbidden_error.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.api_error import ApiError + + +class ForbiddenError(ApiError): + def __init__(self, body: typing.Optional[typing.Any]): + super().__init__(status_code=403, body=body) diff --git a/src/dify_sdk/workflow/raw_client.py b/src/dify_sdk/workflow/raw_client.py index 718c3f2..1c28fae 100644 --- a/src/dify_sdk/workflow/raw_client.py +++ b/src/dify_sdk/workflow/raw_client.py @@ -5,6 +5,7 @@ import typing from json.decoder import JSONDecodeError +import httpx_sse from .. import core from ..core.api_error import ApiError from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper @@ -14,6 +15,7 @@ from ..core.request_options import RequestOptions from .errors.bad_request_error import BadRequestError from .errors.content_too_large_error import ContentTooLargeError +from .errors.forbidden_error import ForbiddenError from .errors.internal_server_error import InternalServerError from .errors.not_found_error import NotFoundError from .errors.service_unavailable_error import ServiceUnavailableError @@ -27,6 +29,7 @@ from .types.get_workflow_execution_status_response import GetWorkflowExecutionStatusResponse from .types.get_workflow_logs_request_status import GetWorkflowLogsRequestStatus from .types.get_workflow_logs_response import GetWorkflowLogsResponse +from .types.run_specific_workflow_request_response_mode import RunSpecificWorkflowRequestResponseMode from .types.run_workflow_request_response_mode import RunWorkflowRequestResponseMode from .types.stop_workflow_response import StopWorkflowResponse from .types.upload_file_response import UploadFileResponse @@ -139,6 +142,26 @@ def _iter(): _response.read() if _response.status_code == 400: raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( typing.cast( Error, parse_obj_as( @@ -147,6 +170,120 @@ def _iter(): ), ) ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield stream() + + @contextlib.contextmanager + def run_specific_workflow( + self, + workflow_id: str, + *, + inputs: typing.Dict[str, typing.Optional[typing.Any]], + response_mode: RunSpecificWorkflowRequestResponseMode, + user: str, + files: typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[ChunkWorkflowMessage]]]: + """ + Execute a specific version of the workflow, specifying the workflow ID through the path parameter + + Parameters + ---------- + workflow_id : str + Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history. + + inputs : typing.Dict[str, typing.Optional[typing.Any]] + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + + response_mode : RunSpecificWorkflowRequestResponseMode + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + + user : str + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + + files : typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] + Optional file list + + trace_id : typing.Optional[str] + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.Iterator[HttpResponse[typing.Iterator[ChunkWorkflowMessage]]] + Successful response + """ + with self._client_wrapper.httpx_client.stream( + f"workflows/{jsonable_encoder(workflow_id)}/run", + method="POST", + json={ + "inputs": inputs, + "response_mode": response_mode, + "user": user, + "files": files, + "trace_id": trace_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) as _response: + + def stream() -> HttpResponse[typing.Iterator[ChunkWorkflowMessage]]: + try: + if 200 <= _response.status_code < 300: + + def _iter(): + _event_source = httpx_sse.EventSource(_response) + for _sse in _event_source.iter_sse(): + if _sse.data == None: + return + try: + yield _sse.data + except Exception: + pass + return + + return HttpResponse(response=_response, data=_iter()) + _response.read() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) if _response.status_code == 404: raise NotFoundError( typing.cast( @@ -213,9 +350,9 @@ def get_workflow_execution_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -302,9 +439,9 @@ def stop_workflow( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -412,9 +549,9 @@ def get_workflow_logs( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -504,9 +641,9 @@ def upload_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -566,6 +703,99 @@ def upload_file( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.contextmanager + def preview_file( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.Iterator[HttpResponse[typing.Iterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.Iterator[HttpResponse[typing.Iterator[bytes]]] + Successfully retrieved file content + """ + with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + def stream() -> HttpResponse[typing.Iterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return HttpResponse( + response=_response, data=(_chunk for _chunk in _response.iter_bytes(chunk_size=_chunk_size)) + ) + _response.read() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield stream() + def get_app_info( self, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[GetAppInfoResponse]: @@ -600,9 +830,9 @@ def get_app_info( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -676,9 +906,9 @@ def get_app_parameters( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -752,9 +982,9 @@ def get_app_site( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -899,6 +1129,26 @@ async def _iter(): await _response.aread() if _response.status_code == 400: raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( typing.cast( Error, parse_obj_as( @@ -907,6 +1157,120 @@ async def _iter(): ), ) ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield await stream() + + @contextlib.asynccontextmanager + async def run_specific_workflow( + self, + workflow_id: str, + *, + inputs: typing.Dict[str, typing.Optional[typing.Any]], + response_mode: RunSpecificWorkflowRequestResponseMode, + user: str, + files: typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] = OMIT, + trace_id: typing.Optional[str] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[ChunkWorkflowMessage]]]: + """ + Execute a specific version of the workflow, specifying the workflow ID through the path parameter + + Parameters + ---------- + workflow_id : str + Workflow ID, used to specify a specific version of the workflow. How to obtain: You can query the specific version workflow ID in the version history. + + inputs : typing.Dict[str, typing.Optional[typing.Any]] + Allows passing values for variables defined in the App. + The inputs parameter contains multiple key/value pairs, where each key corresponds to a specific variable and each value is the specific value for that variable. Variables can be of file list type. + File list type variables are suitable for passing files combined with text understanding to answer questions, only available when the model supports parsing capabilities for that type of file. If the variable is a file list type, the value corresponding to the variable should be in list format, where each element should include the following: + - `type` (string) Supported types: + - `document` Specific types include: 'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB' + - `image` Specific types include: 'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' + - `audio` Specific types include: 'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' + - `video` Specific types include: 'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` Specific types include: other file types + - `transfer_method` (string) Transfer method, `remote_url` image address / `local_file` uploaded file + - `url` (string) Image address (only when the transfer method is `remote_url`) + - `upload_file_id` (string) Upload file ID (only when the transfer method is `local_file`) + + response_mode : RunSpecificWorkflowRequestResponseMode + Response mode, supports: + - `streaming` Streaming mode (recommended). Implements streaming return similar to typewriter output based on SSE (Server-Sent Events). + - `blocking` Blocking mode, waits for execution to complete before returning results. (Requests may be interrupted if the process is lengthy). + Due to Cloudflare limitations, requests will be interrupted after 100 seconds of timeout with no response. + + user : str + User identifier, used to define the identity of the end user, for easy retrieval and statistics. + Rules defined by the developer, the user identifier must be unique within the application. API cannot access sessions created by WebApp. + + files : typing.Optional[typing.Sequence[typing.Dict[str, typing.Optional[typing.Any]]]] + Optional file list + + trace_id : typing.Optional[str] + Tracing ID. Suitable for integrating with existing trace components in business systems to achieve end-to-end distributed tracing. If not specified, the system will automatically generate a `trace_id`. Supports the following three transmission methods, with priorities as follows: + 1. Header: Recommended to pass through HTTP Header `X-Trace-Id`, highest priority. + 2. Query parameter: Pass through URL query parameter `trace_id`. + 3. Request Body: Pass through request body field `trace_id` (this field). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Yields + ------ + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[ChunkWorkflowMessage]]] + Successful response + """ + async with self._client_wrapper.httpx_client.stream( + f"workflows/{jsonable_encoder(workflow_id)}/run", + method="POST", + json={ + "inputs": inputs, + "response_mode": response_mode, + "user": user, + "files": files, + "trace_id": trace_id, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) as _response: + + async def stream() -> AsyncHttpResponse[typing.AsyncIterator[ChunkWorkflowMessage]]: + try: + if 200 <= _response.status_code < 300: + + async def _iter(): + _event_source = httpx_sse.EventSource(_response) + async for _sse in _event_source.aiter_sse(): + if _sse.data == None: + return + try: + yield _sse.data + except Exception: + pass + return + + return AsyncHttpResponse(response=_response, data=_iter()) + await _response.aread() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) if _response.status_code == 404: raise NotFoundError( typing.cast( @@ -973,9 +1337,9 @@ async def get_workflow_execution_status( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1062,9 +1426,9 @@ async def stop_workflow( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1172,9 +1536,9 @@ async def get_workflow_logs( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1264,9 +1628,9 @@ async def upload_file( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1326,6 +1690,100 @@ async def upload_file( raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response.text) raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + @contextlib.asynccontextmanager + async def preview_file( + self, + file_id: str, + *, + as_attachment: typing.Optional[bool] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]]: + """ + Preview or download uploaded files. This endpoint allows you to access files previously uploaded through the file upload API. Files can only be accessed within the message scope belonging to the requesting application. + + Parameters + ---------- + file_id : str + Unique identifier of the file to preview, obtained from the file upload API response + + as_attachment : typing.Optional[bool] + Whether to force the file to be downloaded as an attachment. Defaults to `false` (preview in browser) + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. You can pass in configuration such as `chunk_size`, and more to customize the request and response. + + Returns + ------- + typing.AsyncIterator[AsyncHttpResponse[typing.AsyncIterator[bytes]]] + Successfully retrieved file content + """ + async with self._client_wrapper.httpx_client.stream( + f"files/{jsonable_encoder(file_id)}/preview", + method="GET", + params={ + "as_attachment": as_attachment, + }, + request_options=request_options, + ) as _response: + + async def stream() -> AsyncHttpResponse[typing.AsyncIterator[bytes]]: + try: + if 200 <= _response.status_code < 300: + _chunk_size = request_options.get("chunk_size", None) if request_options is not None else None + return AsyncHttpResponse( + response=_response, + data=(_chunk async for _chunk in _response.aiter_bytes(chunk_size=_chunk_size)), + ) + await _response.aread() + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + parse_obj_as( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 500: + raise InternalServerError( + typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError( + headers=dict(_response.headers), status_code=_response.status_code, body=_response.text + ) + raise ApiError(headers=dict(_response.headers), status_code=_response.status_code, body=_response_json) + + yield await stream() + async def get_app_info( self, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[GetAppInfoResponse]: @@ -1360,9 +1818,9 @@ async def get_app_info( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1436,9 +1894,9 @@ async def get_app_parameters( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) @@ -1512,9 +1970,9 @@ async def get_app_site( if _response.status_code == 400: raise BadRequestError( typing.cast( - Error, + typing.Optional[typing.Any], parse_obj_as( - type_=Error, # type: ignore + type_=typing.Optional[typing.Any], # type: ignore object_=_response.json(), ), ) diff --git a/src/dify_sdk/workflow/types/__init__.py b/src/dify_sdk/workflow/types/__init__.py index ce5ac8c..6efb149 100644 --- a/src/dify_sdk/workflow/types/__init__.py +++ b/src/dify_sdk/workflow/types/__init__.py @@ -54,6 +54,7 @@ from .get_workflow_logs_response_data_item_workflow_run import GetWorkflowLogsResponseDataItemWorkflowRun from .get_workflow_logs_response_data_item_workflow_run_status import GetWorkflowLogsResponseDataItemWorkflowRunStatus from .retriever_resource import RetrieverResource +from .run_specific_workflow_request_response_mode import RunSpecificWorkflowRequestResponseMode from .run_workflow_request_response_mode import RunWorkflowRequestResponseMode from .stop_workflow_response import StopWorkflowResponse from .upload_file_response import UploadFileResponse @@ -101,6 +102,7 @@ "GetWorkflowLogsResponseDataItemWorkflowRun", "GetWorkflowLogsResponseDataItemWorkflowRunStatus", "RetrieverResource", + "RunSpecificWorkflowRequestResponseMode", "RunWorkflowRequestResponseMode", "StopWorkflowResponse", "UploadFileResponse", diff --git a/src/dify_sdk/workflow/types/run_specific_workflow_request_response_mode.py b/src/dify_sdk/workflow/types/run_specific_workflow_request_response_mode.py new file mode 100644 index 0000000..e1f5f39 --- /dev/null +++ b/src/dify_sdk/workflow/types/run_specific_workflow_request_response_mode.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RunSpecificWorkflowRequestResponseMode = typing.Union[typing.Literal["streaming", "blocking"], typing.Any] From 9c89a915beda924557a97cd7937191791a1e9717 Mon Sep 17 00:00:00 2001 From: straydragon Date: Fri, 15 Aug 2025 22:30:33 +0800 Subject: [PATCH 8/8] bump: version --- README.md | 12 ++++++------ README.zh.md | 12 ++++++------ pyproject.toml | 2 +- schema/knowledge_base.en.yaml | 2 +- schema/knowledge_base.zh.yaml | 2 +- schema/overlays/knowledge_base.en.overlay.yaml | 2 +- uv.lock | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index c12ccee..729f716 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Dify x OpenAPI -[![Dify Version Support](https://img.shields.io/badge/Support_Dify_Version-1.7.1-blue)](https://github.com/langgenius/dify) +[![Dify Version Support](https://img.shields.io/badge/Support_Dify_Version-1.7.2-blue)](https://github.com/langgenius/dify) [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Package Manager: uv](https://img.shields.io/badge/package%20manager-uv-black)](https://github.com/astral-sh/uv) @@ -39,7 +39,7 @@ Provides OpenAPI Schema for [Dify](https://github.com/langgenius/dify) API, whic > [!tip] > This indicates that the API has passed at least one test case request. If you find any API errors, feel free to submit an issue or PR! -- Knowledge Base: [OpenAPI Schema(中文)](./schema/knowledge_base.zh.yaml) | [OpenAPI Schema(English)](./schema/knowledge_base.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.1/web/app/(commonLayout)/datasets/template) +- Knowledge Base: [OpenAPI Schema(中文)](./schema/knowledge_base.zh.yaml) | [OpenAPI Schema(English)](./schema/knowledge_base.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.2/web/app/(commonLayout)/datasets/template) - [x] POST /datasets - Create empty knowledge base - [x] POST /datasets/{dataset_id} - Update knowledge base - [x] GET /datasets/{dataset_id}/documents - Get document list @@ -71,7 +71,7 @@ Provides OpenAPI Schema for [Dify](https://github.com/langgenius/dify) API, whic - [x] POST /datasets/{dataset_id}/tags - Query dataset bound tags - [x] POST /datasets/{dataset_id}/retrieval - Retrieve with metadata filtering conditions -- Chat Application: [OpenAPI Schema(中文)](./schema/app_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_chat.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- Chat Application: [OpenAPI Schema(中文)](./schema/app_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_chat.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /chat-messages - Send conversation message - [x] POST /files/upload - Upload file - [x] POST /messages/{message_id}/feedbacks - Message feedback @@ -84,7 +84,7 @@ Provides OpenAPI Schema for [Dify](https://github.com/langgenius/dify) API, whic - [x] GET /info - Get application basic information - [x] GET /parameters - Get application parameters -- Advanced Chat Application: [OpenAPI Schema(中文)](./schema/app_advanced_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_advanced_chat.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- Advanced Chat Application: [OpenAPI Schema(中文)](./schema/app_advanced_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_advanced_chat.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /audio-to-text - Speech to text - [x] POST /text-to-audio - Text to speech - [x] GET /apps/annotations - Get annotation list @@ -100,10 +100,10 @@ Provides OpenAPI Schema for [Dify](https://github.com/langgenius/dify) API, whic - [x] GET /parameters - Get application parameters -- Text Generation Application: [OpenAPI Schema(中文)](./schema/app_generation.zh.yaml) | [OpenAPI Schema(English)](./schema/app_generation.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- Text Generation Application: [OpenAPI Schema(中文)](./schema/app_generation.zh.yaml) | [OpenAPI Schema(English)](./schema/app_generation.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /completion-messages - Send message -- Workflow Application: [OpenAPI Schema(中文)](./schema/app_workflow.zh.yaml) | [OpenAPI Schema(English)](./schema/app_workflow.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- Workflow Application: [OpenAPI Schema(中文)](./schema/app_workflow.zh.yaml) | [OpenAPI Schema(English)](./schema/app_workflow.en.yaml) | [Official Documentation Source](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /workflows/run - Execute workflow - [x] GET /workflows/run/{workflow_run_id} - Get workflow execution status - [x] POST /workflows/tasks/{task_id}/stop - Stop response diff --git a/README.zh.md b/README.zh.md index 4f1bd01..00a6dea 100644 --- a/README.zh.md +++ b/README.zh.md @@ -1,6 +1,6 @@ # Dify x OpenAPI -[![Dify Version Support](https://img.shields.io/badge/Support_Dify_Version-1.7.1-blue)](https://github.com/langgenius/dify) +[![Dify Version Support](https://img.shields.io/badge/Support_Dify_Version-1.7.2-blue)](https://github.com/langgenius/dify) [![Code style: ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff) [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Package Manager: uv](https://img.shields.io/badge/package%20manager-uv-black)](https://github.com/astral-sh/uv) @@ -41,7 +41,7 @@ > [!tip] > 这里指至少可以通过一次测试用例请求, 如果你发现有哪些API错误, 欢迎提issue或者pr! -- 知识库: [OpenAPI Schema(中文)](./schema/knowledge_base.zh.yaml) | [OpenAPI Schema(English)](./schema/knowledge_base.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.1/web/app/(commonLayout)/datasets/template) +- 知识库: [OpenAPI Schema(中文)](./schema/knowledge_base.zh.yaml) | [OpenAPI Schema(English)](./schema/knowledge_base.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.2/web/app/(commonLayout)/datasets/template) - [x] POST /datasets - 创建空知识库 - [x] POST /datasets/{dataset_id} - 更新知识库 - [x] GET /datasets/{dataset_id}/documents - 获取文档列表 @@ -73,7 +73,7 @@ - [x] POST /datasets/{dataset_id}/tags - 查询知识库已绑定的标签 - [x] POST /datasets/{dataset_id}/retrieval - 带元数据过滤条件的检索 -- 聊天应用: [OpenAPI Schema(中文)](./schema/app_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_chat.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- 聊天应用: [OpenAPI Schema(中文)](./schema/app_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_chat.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /chat-messages - 发送对话消息 - [x] POST /files/upload - 上传文件 - [x] POST /messages/{message_id}/feedbacks - 消息反馈 @@ -86,7 +86,7 @@ - [x] GET /info - 获取应用基本信息 - [x] GET /parameters - 获取应用参数 -- 高级聊天应用: [OpenAPI Schema(中文)](./schema/app_advanced_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_advanced_chat.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- 高级聊天应用: [OpenAPI Schema(中文)](./schema/app_advanced_chat.zh.yaml) | [OpenAPI Schema(English)](./schema/app_advanced_chat.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /audio-to-text - 语音转文字 - [x] POST /text-to-audio - 文字转语音 - [x] GET /apps/annotations - 获取标注列表 @@ -101,10 +101,10 @@ - [x] GET /info - 获取应用基本信息 - [x] GET /parameters - 获取应用参数(包含文字转语音设置) -- 文本生成应用: [OpenAPI Schema(中文)](./schema/app_generation.zh.yaml) | [OpenAPI Schema(English)](./schema/app_generation.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- 文本生成应用: [OpenAPI Schema(中文)](./schema/app_generation.zh.yaml) | [OpenAPI Schema(English)](./schema/app_generation.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /completion-messages - 发送消息 -- 工作流应用: [OpenAPI Schema(中文)](./schema/app_workflow.zh.yaml) | [OpenAPI Schema(English)](./schema/app_workflow.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.1/web/app/components/develop/template) +- 工作流应用: [OpenAPI Schema(中文)](./schema/app_workflow.zh.yaml) | [OpenAPI Schema(English)](./schema/app_workflow.en.yaml) | [官方文档源码](https://github.com/langgenius/dify/tree/1.7.2/web/app/components/develop/template) - [x] POST /workflows/run - 执行工作流 - [x] GET /workflows/run/{workflow_run_id} - 获取工作流执行状态 - [x] POST /workflows/tasks/{task_id}/stop - 停止响应 diff --git a/pyproject.toml b/pyproject.toml index 3118f25..0e7b660 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dify-openapi" -version = "1.7.1" +version = "1.7.2" description = "a client for testing and validating dify openapi schema" readme = "README.md" authors = [{ name = "straydragon", email = "straydragonl@foxmail.com" }] diff --git a/schema/knowledge_base.en.yaml b/schema/knowledge_base.en.yaml index d71f148..cd51776 100644 --- a/schema/knowledge_base.en.yaml +++ b/schema/knowledge_base.en.yaml @@ -4,7 +4,7 @@ info: description: | Dify Knowledge Base API provides a series of interfaces for managing knowledge bases, documents, and retrieval functions. All API requests require a knowledge base level API-Key in the Authorization HTTP Header. - version: 1.7.1 + version: 1.7.2 servers: - url: 'https://api.dify.ai/v1' description: Dify API Server diff --git a/schema/knowledge_base.zh.yaml b/schema/knowledge_base.zh.yaml index 45849cd..5e4f40a 100644 --- a/schema/knowledge_base.zh.yaml +++ b/schema/knowledge_base.zh.yaml @@ -4,7 +4,7 @@ info: description: | Dify 知识库 API 提供了一系列接口用于管理知识库、文档和检索功能。 所有 API 请求都需要在 Authorization HTTP Header 中包含知识库级 API-Key。 - version: 1.7.1 + version: 1.7.2 servers: - url: https://api.dify.ai/v1 diff --git a/schema/overlays/knowledge_base.en.overlay.yaml b/schema/overlays/knowledge_base.en.overlay.yaml index ca35d63..aada412 100644 --- a/schema/overlays/knowledge_base.en.overlay.yaml +++ b/schema/overlays/knowledge_base.en.overlay.yaml @@ -1,7 +1,7 @@ overlay: 1.0.0 info: title: "English translation overlay for Dify Knowledge Base API" - version: "1.7.1" + version: "1.7.2" extends: ../knowledge_base.zh.yaml actions: - target: $.info diff --git a/uv.lock b/uv.lock index f434d81..0ce68f2 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.13'", @@ -158,7 +158,7 @@ wheels = [ [[package]] name = "dify-openapi" -version = "1.7.1" +version = "1.7.2" source = { editable = "." } dependencies = [ { name = "httpx" },