From 659d51a2da3601e58d41d83ffd6b7b7467b168e3 Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:43:49 +0800 Subject: [PATCH 01/14] fix: complete file_upload schema in OpenAPI templates (#22700) (#22719) --- web/app/components/base/features/types.ts | 16 ++++++++++ .../develop/template/template.en.mdx | 29 ++++++++++++++--- .../develop/template/template.ja.mdx | 29 ++++++++++++++--- .../develop/template/template.zh.mdx | 29 ++++++++++++++--- .../template/template_advanced_chat.en.mdx | 29 ++++++++++++++--- .../template/template_advanced_chat.ja.mdx | 29 ++++++++++++++--- .../template/template_advanced_chat.zh.mdx | 29 ++++++++++++++--- .../develop/template/template_chat.en.mdx | 29 ++++++++++++++--- .../develop/template/template_chat.ja.mdx | 31 +++++++++++++++---- .../develop/template/template_chat.zh.mdx | 29 ++++++++++++++--- .../develop/template/template_workflow.en.mdx | 29 ++++++++++++++--- .../develop/template/template_workflow.ja.mdx | 29 ++++++++++++++--- .../develop/template/template_workflow.zh.mdx | 29 ++++++++++++++--- 13 files changed, 305 insertions(+), 61 deletions(-) diff --git a/web/app/components/base/features/types.ts b/web/app/components/base/features/types.ts index 83f876383d..56bd7829ad 100644 --- a/web/app/components/base/features/types.ts +++ b/web/app/components/base/features/types.ts @@ -35,6 +35,22 @@ export type FileUpload = { number_limits?: number transfer_methods?: TransferMethod[] } + document?: EnabledOrDisabled & { + number_limits?: number + transfer_methods?: TransferMethod[] + } + audio?: EnabledOrDisabled & { + number_limits?: number + transfer_methods?: TransferMethod[] + } + video?: EnabledOrDisabled & { + number_limits?: number + transfer_methods?: TransferMethod[] + } + custom?: EnabledOrDisabled & { + number_limits?: number + transfer_methods?: TransferMethod[] + } allowed_file_types?: string[] allowed_file_extensions?: string[] allowed_file_upload_methods?: TransferMethod[] diff --git a/web/app/components/develop/template/template.en.mdx b/web/app/components/develop/template/template.en.mdx index f178645a8e..3fdb782628 100755 --- a/web/app/components/develop/template/template.en.mdx +++ b/web/app/components/develop/template/template.en.mdx @@ -580,11 +580,30 @@ The text generation application offers non-session support and is ideal for tran - `default` (string) Default value - `options` (array[string]) Option values - `file_upload` (object) File upload configuration - - `image` (object) Image settings - Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) Whether it is enabled - - `number_limits` (int) Image number limit, default is 3 - - `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one + - `document` (object) Document settings + Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Document number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `image` (object) Image settings + Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Image number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `audio` (object) Audio settings + Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Audio number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `video` (object) Video settings + Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Video number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `custom` (object) Custom settings + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Custom number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. - `system_parameters` (object) System parameters - `file_size_limit` (int) Document upload size limit (MB) - `image_file_size_limit` (int) Image file upload size limit (MB) diff --git a/web/app/components/develop/template/template.ja.mdx b/web/app/components/develop/template/template.ja.mdx index 4dbefca8f8..238a921fb5 100755 --- a/web/app/components/develop/template/template.ja.mdx +++ b/web/app/components/develop/template/template.ja.mdx @@ -578,11 +578,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `default` (string) デフォルト値 - `options` (array[string]) オプション値 - `file_upload` (object) ファイルアップロード設定 - - `image` (object) 画像設定 - 現在は画像タイプのみ対応:`png`、`jpg`、`jpeg`、`webp`、`gif` - - `enabled` (bool) 有効かどうか - - `number_limits` (int) 画像数制限、デフォルトは3 - - `transfer_methods` (array[string]) 転送方法リスト、remote_url、local_file、いずれかを選択 + - `document` (object) ドキュメント設定 + 現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ドキュメント数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `image` (object) 画像設定 + 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) 画像数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `audio` (object) オーディオ設定 + 現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) オーディオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `video` (object) ビデオ設定 + 現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ビデオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `custom` (object) カスタム設定 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) カスタム数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 - `system_parameters` (object) システムパラメータ - `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB) - `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB) diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx index 4af5a28050..a5eea3d193 100755 --- a/web/app/components/develop/template/template.zh.mdx +++ b/web/app/components/develop/template/template.zh.mdx @@ -552,11 +552,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `default` (string) 默认值 - `options` (array[string]) 选项值 - `file_upload` (object) 文件上传配置 - - `image` (object) 图片设置 - 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 是否开启 - - `number_limits` (int) 图片数量限制,默认 3 - - `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个 + - `document` (object) 文档设置 + 当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 文档数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `image` (object) 图片设置 + 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 图片数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `audio` (object) 音频设置 + 当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 音频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `video` (object) 视频设置 + 当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 视频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `custom` (object) 自定义设置 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 自定义数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 - `system_parameters` (object) 系统参数 - `file_size_limit` (int) 文档上传大小限制 (MB) - `image_file_size_limit` (int) 图片文件上传大小限制(MB) diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index faecd3d1cd..adba404a64 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -1197,11 +1197,30 @@ Chat applications support session persistence, allowing previous chat history to - `default` (string) Default value - `options` (array[string]) Option values - `file_upload` (object) File upload configuration - - `image` (object) Image settings - Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) Whether it is enabled - - `number_limits` (int) Image number limit, default is 3 - - `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one + - `document` (object) Document settings + Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Document number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `image` (object) Image settings + Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Image number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `audio` (object) Audio settings + Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Audio number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `video` (object) Video settings + Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Video number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `custom` (object) Custom settings + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Custom number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. - `system_parameters` (object) System parameters - `file_size_limit` (int) Document upload size limit (MB) - `image_file_size_limit` (int) Image file upload size limit (MB) diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index 5ce54a61d2..2e57d5e20c 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -1197,11 +1197,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `default` (string) デフォルト値 - `options` (array[string]) オプション値 - `file_upload` (object) ファイルアップロード設定 - - `image` (object) 画像設定 - 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 有効かどうか - - `number_limits` (int) 画像数の制限、デフォルトは3 - - `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります + - `document` (object) ドキュメント設定 + 現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ドキュメント数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `image` (object) 画像設定 + 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) 画像数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `audio` (object) オーディオ設定 + 現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) オーディオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `video` (object) ビデオ設定 + 現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ビデオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `custom` (object) カスタム設定 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) カスタム数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 - `system_parameters` (object) システムパラメータ - `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB) - `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB) 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 7a69ee60aa..8955396ad9 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -1229,11 +1229,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `default` (string) 默认值 - `options` (array[string]) 选项值 - `file_upload` (object) 文件上传配置 - - `image` (object) 图片设置 - 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 是否开启 - - `number_limits` (int) 图片数量限制,默认 3 - - `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个 + - `document` (object) 文档设置 + 当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 文档数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `image` (object) 图片设置 + 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 图片数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `audio` (object) 音频设置 + 当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 音频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `video` (object) 视频设置 + 当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 视频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `custom` (object) 自定义设置 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 自定义数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 - `system_parameters` (object) 系统参数 - `file_size_limit` (int) Document upload size limit (MB) - `image_file_size_limit` (int) Image file upload size limit (MB) diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index c95471160e..73d1fa1b41 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -1234,11 +1234,30 @@ Chat applications support session persistence, allowing previous chat history to - `default` (string) Default value - `options` (array[string]) Option values - `file_upload` (object) File upload configuration - - `image` (object) Image settings - Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) Whether it is enabled - - `number_limits` (int) Image number limit, default is 3 - - `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one + - `document` (object) Document settings + Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Document number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `image` (object) Image settings + Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Image number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `audio` (object) Audio settings + Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Audio number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `video` (object) Video settings + Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Video number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `custom` (object) Custom settings + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Custom number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. - `system_parameters` (object) System parameters - `file_size_limit` (int) Document upload size limit (MB) - `image_file_size_limit` (int) Image file upload size limit (MB) diff --git a/web/app/components/develop/template/template_chat.ja.mdx b/web/app/components/develop/template/template_chat.ja.mdx index 8368326e40..45c970a9f2 100644 --- a/web/app/components/develop/template/template_chat.ja.mdx +++ b/web/app/components/develop/template/template_chat.ja.mdx @@ -1224,12 +1224,31 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `required` (bool) 必須かどうか - `default` (string) デフォルト値 - `options` (array[string]) オプション値 - - `file_upload` (object) ファイルアップロード構成 - - `image` (object) 画像設定 - 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 有効かどうか - - `number_limits` (int) 画像数の制限、デフォルトは3 - - `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります + - `file_upload` (object) ファイルアップロード設定 + - `document` (object) ドキュメント設定 + 現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ドキュメント数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `image` (object) 画像設定 + 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) 画像数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `audio` (object) オーディオ設定 + 現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) オーディオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `video` (object) ビデオ設定 + 現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ビデオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `custom` (object) カスタム設定 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) カスタム数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 - `system_parameters` (object) システムパラメータ - `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB) - `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB) diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index 325470ac62..8573408c36 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -1237,11 +1237,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `default` (string) 默认值 - `options` (array[string]) 选项值 - `file_upload` (object) 文件上传配置 - - `image` (object) 图片设置 - 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 是否开启 - - `number_limits` (int) 图片数量限制,默认 3 - - `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个 + - `document` (object) 文档设置 + 当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 文档数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `image` (object) 图片设置 + 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 图片数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `audio` (object) 音频设置 + 当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 音频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `video` (object) 视频设置 + 当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 视频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `custom` (object) 自定义设置 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 自定义数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 - `system_parameters` (object) 系统参数 - `file_size_limit` (int) 文档上传大小限制 (MB) - `image_file_size_limit` (int) 图片文件上传大小限制(MB) diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index 77409c1284..23ff2bbb55 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -690,11 +690,30 @@ Workflow applications offers non-session support and is ideal for translation, a - `default` (string) Default value - `options` (array[string]) Option values - `file_upload` (object) File upload configuration - - `image` (object) Image settings - Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) Whether it is enabled - - `number_limits` (int) Image number limit, default is 3 - - `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one + - `document` (object) Document settings + Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Document number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `image` (object) Image settings + Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Image number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `audio` (object) Audio settings + Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Audio number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `video` (object) Video settings + Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`. + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Video number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. + - `custom` (object) Custom settings + - `enabled` (bool) Whether it is enabled + - `number_limits` (int) Custom number limit, default is 3 + - `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one. - `system_parameters` (object) System parameters - `file_size_limit` (int) Document upload size limit (MB) - `image_file_size_limit` (int) Image file upload size limit (MB) diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index a83e21aef7..287eb87f45 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -691,11 +691,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `default` (string) デフォルト値 - `options` (array[string]) オプション値 - `file_upload` (object) ファイルアップロード設定 - - `image` (object) 画像設定 - 現在サポートされている画像タイプのみ:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 有効かどうか - - `number_limits` (int) 画像数の制限、デフォルトは3 - - `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります + - `document` (object) ドキュメント設定 + 現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ドキュメント数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `image` (object) 画像設定 + 現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) 画像数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `audio` (object) オーディオ設定 + 現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) オーディオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `video` (object) ビデオ設定 + 現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) ビデオ数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 + - `custom` (object) カスタム設定 + - `enabled` (bool) 有効かどうか + - `number_limits` (int) カスタム数の上限。デフォルトは 3 + - `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。 - `system_parameters` (object) システムパラメータ - `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB) - `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB) diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index 42922610da..105eca0700 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -678,11 +678,30 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 - `default` (string) 默认值 - `options` (array[string]) 选项值 - `file_upload` (object) 文件上传配置 - - `image` (object) 图片设置 - 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif` - - `enabled` (bool) 是否开启 - - `number_limits` (int) 图片数量限制,默认 3 - - `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个 + - `document` (object) 文档设置 + 当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 文档数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `image` (object) 图片设置 + 当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 图片数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `audio` (object) 音频设置 + 当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 音频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `video` (object) 视频设置 + 当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 视频数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 + - `custom` (object) 自定义设置 + - `enabled` (bool) 是否启用 + - `number_limits` (int) 自定义数量限制,默认为 3 + - `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。 - `system_parameters` (object) 系统参数 - `file_size_limit` (int) 文档上传大小限制 (MB) - `image_file_size_limit` (int) 图片文件上传大小限制(MB) From 5d5fa888575ad6eaa9b446fb4101fd62eb5016bc Mon Sep 17 00:00:00 2001 From: NFish Date: Mon, 21 Jul 2025 18:07:49 +0800 Subject: [PATCH 02/14] fix: the text/icon shows wrong color in darkmode (#22724) --- .../components/app/app-access-control/access-control-dialog.tsx | 2 +- web/app/components/app/app-access-control/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/app/app-access-control/access-control-dialog.tsx b/web/app/components/app/app-access-control/access-control-dialog.tsx index e3e013fbd4..72dd33c72e 100644 --- a/web/app/components/app/app-access-control/access-control-dialog.tsx +++ b/web/app/components/app/app-access-control/access-control-dialog.tsx @@ -47,7 +47,7 @@ const AccessControlDialog = ({ >
close()} className="absolute right-5 top-5 z-10 flex h-8 w-8 cursor-pointer items-center justify-center"> - +
{children}
diff --git a/web/app/components/app/app-access-control/index.tsx b/web/app/components/app/app-access-control/index.tsx index 13faaea957..e8e2358e0e 100644 --- a/web/app/components/app/app-access-control/index.tsx +++ b/web/app/components/app/app-access-control/index.tsx @@ -72,7 +72,7 @@ export default function AccessControl(props: AccessControlProps) {
-

{t('app.accessControlDialog.accessLabel')}

+

{t('app.accessControlDialog.accessLabel')}

From 308f1340dd4ff2012815d7ad788fec7d2b438a2e Mon Sep 17 00:00:00 2001 From: Maries Date: Mon, 21 Jul 2025 20:18:19 +0800 Subject: [PATCH 03/14] fix: migrations circle dependency (#22731) --- .../versions/2025_05_15_1635-16081485540c_.py | 41 ------------------- ...w_draft_varaibles_add_node_execution_id.py | 2 +- 2 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 api/migrations/versions/2025_05_15_1635-16081485540c_.py diff --git a/api/migrations/versions/2025_05_15_1635-16081485540c_.py b/api/migrations/versions/2025_05_15_1635-16081485540c_.py deleted file mode 100644 index f55730bfb2..0000000000 --- a/api/migrations/versions/2025_05_15_1635-16081485540c_.py +++ /dev/null @@ -1,41 +0,0 @@ -"""empty message - -Revision ID: 16081485540c -Revises: d28f2004b072 -Create Date: 2025-05-15 16:35:39.113777 - -""" -from alembic import op -import models as models -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '16081485540c' -down_revision = '2adcbe1f5dfb' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('tenant_plugin_auto_upgrade_strategies', - sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False), - sa.Column('tenant_id', models.types.StringUUID(), nullable=False), - sa.Column('strategy_setting', sa.String(length=16), server_default='fix_only', nullable=False), - sa.Column('upgrade_time_of_day', sa.Integer(), nullable=False), - sa.Column('upgrade_mode', sa.String(length=16), server_default='exclude', nullable=False), - sa.Column('exclude_plugins', sa.ARRAY(sa.String(length=255)), nullable=False), - sa.Column('include_plugins', sa.ARRAY(sa.String(length=255)), nullable=False), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), - sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), - sa.PrimaryKeyConstraint('id', name='tenant_plugin_auto_upgrade_strategy_pkey'), - sa.UniqueConstraint('tenant_id', name='unique_tenant_plugin_auto_upgrade_strategy') - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('tenant_plugin_auto_upgrade_strategies') - # ### end Alembic commands ### diff --git a/api/migrations/versions/2025_06_06_1424-4474872b0ee6_workflow_draft_varaibles_add_node_execution_id.py b/api/migrations/versions/2025_06_06_1424-4474872b0ee6_workflow_draft_varaibles_add_node_execution_id.py index 47ac27511e..d7a5d116c9 100644 --- a/api/migrations/versions/2025_06_06_1424-4474872b0ee6_workflow_draft_varaibles_add_node_execution_id.py +++ b/api/migrations/versions/2025_06_06_1424-4474872b0ee6_workflow_draft_varaibles_add_node_execution_id.py @@ -12,7 +12,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '4474872b0ee6' -down_revision = '16081485540c' +down_revision = '2adcbe1f5dfb' branch_labels = None depends_on = None From 29f0a9ab94a7a34a4e9aecef7e6d273e3f869a01 Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 21 Jul 2025 21:14:38 +0800 Subject: [PATCH 04/14] Fix incorrect mcp method_name (#22736) --- api/core/mcp/mcp_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/mcp/mcp_client.py b/api/core/mcp/mcp_client.py index f7aa7bbd7b..5fe52c008a 100644 --- a/api/core/mcp/mcp_client.py +++ b/api/core/mcp/mcp_client.py @@ -69,7 +69,7 @@ class MCPClient: parsed_url = urlparse(self.server_url) path = parsed_url.path or "" - method_name = path.removesuffix("/").lower() + method_name = path.rstrip("/").split("/")[-1] if path else "" if method_name in connection_methods: client_factory = connection_methods[method_name] self.connect_server(client_factory, method_name) From e9893f1518ad1c440ae6dd5b7669774d8065dd48 Mon Sep 17 00:00:00 2001 From: jiangbo721 <365065261@qq.com> Date: Tue, 22 Jul 2025 02:40:40 +0800 Subject: [PATCH 05/14] chore: use 'json_list' instead of 'json' to prevent ambiguity (#22739) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 刘江波 --- api/core/workflow/nodes/agent/agent_node.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index 704eb6a3ac..8cf33ac81e 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -486,7 +486,7 @@ class AgentNode(BaseNode): text = "" files: list[File] = [] - json: list[dict] = [] + json_list: list[dict] = [] agent_logs: list[AgentLogEvent] = [] agent_execution_metadata: Mapping[WorkflowNodeExecutionMetadataKey, Any] = {} @@ -564,7 +564,7 @@ class AgentNode(BaseNode): if key in WorkflowNodeExecutionMetadataKey.__members__.values() } if message.message.json_object is not None: - json.append(message.message.json_object) + json_list.append(message.message.json_object) elif message.type == ToolInvokeMessage.MessageType.LINK: assert isinstance(message.message, ToolInvokeMessage.TextMessage) stream_text = f"Link: {message.message.text}\n" @@ -676,8 +676,8 @@ class AgentNode(BaseNode): } ) # Step 2: normalize JSON into {"data": [...]}.change json to list[dict] - if json: - json_output.extend(json) + if json_list: + json_output.extend(json_list) else: json_output.append({"data": []}) From f70ff72a5856e1648e8f5c3ed031ca123f9a406d Mon Sep 17 00:00:00 2001 From: kentaka347 <72369527+kentaka347@users.noreply.github.com> Date: Tue, 22 Jul 2025 03:43:12 +0900 Subject: [PATCH 06/14] chore: Add fonts-noto-cjk dependency for pypdfium2 (#22359) Co-authored-by: kentaka347 --- api/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/Dockerfile b/api/Dockerfile index 7e4997507f..8c7a1717b9 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -47,6 +47,8 @@ RUN \ curl nodejs libgmp-dev libmpfr-dev libmpc-dev \ # For Security expat libldap-2.5-0 perl libsqlite3-0 zlib1g \ + # install fonts to support the use of tools like pypdfium2 + fonts-noto-cjk \ # install a package to improve the accuracy of guessing mime type and file extension media-types \ # install libmagic to support the use of python-magic guess MIMETYPE From b5599b294592b0dbc3f6c150ad1c332f26012cb9 Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Tue, 22 Jul 2025 08:11:01 +0800 Subject: [PATCH 07/14] fix: prevent panel width localStorage pollution during viewport compression (#22745) (#22747) --- .../components/workflow-panel/index.spec.tsx | 178 ++++++++++++++++++ .../_base/components/workflow-panel/index.tsx | 16 +- .../panel/debug-and-preview/index.spec.tsx | 145 ++++++++++++++ .../panel/debug-and-preview/index.tsx | 9 +- 4 files changed, 340 insertions(+), 8 deletions(-) create mode 100644 web/app/components/workflow/nodes/_base/components/workflow-panel/index.spec.tsx create mode 100644 web/app/components/workflow/panel/debug-and-preview/index.spec.tsx diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.spec.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.spec.tsx new file mode 100644 index 0000000000..5c6ffb7a52 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.spec.tsx @@ -0,0 +1,178 @@ +/** + * Workflow Panel Width Persistence Tests + * Tests for GitHub issue #22745: Panel width persistence bug fix + */ + +import '@testing-library/jest-dom' + +type PanelWidthSource = 'user' | 'system' + +// Mock localStorage for testing +const createMockLocalStorage = () => { + const storage: Record = {} + return { + getItem: jest.fn((key: string) => storage[key] || null), + setItem: jest.fn((key: string, value: string) => { + storage[key] = value + }), + removeItem: jest.fn((key: string) => { + delete storage[key] + }), + clear: jest.fn(() => { + Object.keys(storage).forEach(key => delete storage[key]) + }), + get storage() { return { ...storage } }, + } +} + +// Core panel width logic extracted from the component +const createPanelWidthManager = (storageKey: string) => { + return { + updateWidth: (width: number, source: PanelWidthSource = 'user') => { + const newValue = Math.max(400, Math.min(width, 800)) + if (source === 'user') + localStorage.setItem(storageKey, `${newValue}`) + + return newValue + }, + getStoredWidth: () => { + const stored = localStorage.getItem(storageKey) + return stored ? Number.parseFloat(stored) : 400 + }, + } +} + +describe('Workflow Panel Width Persistence', () => { + let mockLocalStorage: ReturnType + + beforeEach(() => { + mockLocalStorage = createMockLocalStorage() + Object.defineProperty(globalThis, 'localStorage', { + value: mockLocalStorage, + writable: true, + }) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('Node Panel Width Management', () => { + const storageKey = 'workflow-node-panel-width' + + it('should save user resize to localStorage', () => { + const manager = createPanelWidthManager(storageKey) + + const result = manager.updateWidth(500, 'user') + + expect(result).toBe(500) + expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, '500') + }) + + it('should not save system compression to localStorage', () => { + const manager = createPanelWidthManager(storageKey) + + const result = manager.updateWidth(200, 'system') + + expect(result).toBe(400) // Respects minimum width + expect(localStorage.setItem).not.toHaveBeenCalled() + }) + + it('should enforce minimum width of 400px', () => { + const manager = createPanelWidthManager(storageKey) + + // User tries to set below minimum + const userResult = manager.updateWidth(300, 'user') + expect(userResult).toBe(400) + expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, '400') + + // System compression below minimum + const systemResult = manager.updateWidth(150, 'system') + expect(systemResult).toBe(400) + expect(localStorage.setItem).toHaveBeenCalledTimes(1) // Only user call + }) + + it('should preserve user preferences during system compression', () => { + localStorage.setItem(storageKey, '600') + const manager = createPanelWidthManager(storageKey) + + // System compresses panel + manager.updateWidth(200, 'system') + + // User preference should remain unchanged + expect(localStorage.getItem(storageKey)).toBe('600') + }) + }) + + describe('Bug Scenario Reproduction', () => { + it('should reproduce original bug behavior (for comparison)', () => { + const storageKey = 'workflow-node-panel-width' + + // Original buggy behavior - always saves regardless of source + const buggyUpdate = (width: number) => { + localStorage.setItem(storageKey, `${width}`) + return Math.max(400, width) + } + + localStorage.setItem(storageKey, '500') // User preference + buggyUpdate(200) // System compression pollutes localStorage + + expect(localStorage.getItem(storageKey)).toBe('200') // Bug: corrupted state + }) + + it('should verify fix prevents localStorage pollution', () => { + const storageKey = 'workflow-node-panel-width' + const manager = createPanelWidthManager(storageKey) + + localStorage.setItem(storageKey, '500') // User preference + manager.updateWidth(200, 'system') // System compression + + expect(localStorage.getItem(storageKey)).toBe('500') // Fix: preserved state + }) + }) + + describe('Edge Cases', () => { + it('should handle multiple rapid operations correctly', () => { + const manager = createPanelWidthManager('workflow-node-panel-width') + + // Rapid system adjustments + manager.updateWidth(300, 'system') + manager.updateWidth(250, 'system') + manager.updateWidth(180, 'system') + + // Single user adjustment + manager.updateWidth(550, 'user') + + expect(localStorage.setItem).toHaveBeenCalledTimes(1) + expect(localStorage.setItem).toHaveBeenCalledWith('workflow-node-panel-width', '550') + }) + + it('should handle corrupted localStorage gracefully', () => { + localStorage.setItem('workflow-node-panel-width', '150') // Below minimum + const manager = createPanelWidthManager('workflow-node-panel-width') + + const storedWidth = manager.getStoredWidth() + expect(storedWidth).toBe(150) // Returns raw value + + // User can correct the preference + const correctedWidth = manager.updateWidth(500, 'user') + expect(correctedWidth).toBe(500) + expect(localStorage.getItem('workflow-node-panel-width')).toBe('500') + }) + }) + + describe('TypeScript Type Safety', () => { + it('should enforce source parameter type', () => { + const manager = createPanelWidthManager('workflow-node-panel-width') + + // Valid source values + manager.updateWidth(500, 'user') + manager.updateWidth(500, 'system') + + // Default to 'user' + manager.updateWidth(500) + + expect(localStorage.setItem).toHaveBeenCalledTimes(2) // user + default + }) + }) +}) diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index d50b99e213..93fab83172 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -99,15 +99,18 @@ const BasePanel: FC = ({ return Math.max(available, 400) }, [workflowCanvasWidth, otherPanelWidth]) - const updateNodePanelWidth = useCallback((width: number) => { + const updateNodePanelWidth = useCallback((width: number, source: 'user' | 'system' = 'user') => { // Ensure the width is within the min and max range const newValue = Math.max(400, Math.min(width, maxNodePanelWidth)) - localStorage.setItem('workflow-node-panel-width', `${newValue}`) + + if (source === 'user') + localStorage.setItem('workflow-node-panel-width', `${newValue}`) + setNodePanelWidth(newValue) }, [maxNodePanelWidth, setNodePanelWidth]) const handleResize = useCallback((width: number) => { - updateNodePanelWidth(width) + updateNodePanelWidth(width, 'user') }, [updateNodePanelWidth]) const { @@ -121,7 +124,10 @@ const BasePanel: FC = ({ onResize: debounce(handleResize), }) - const debounceUpdate = debounce(updateNodePanelWidth) + const debounceUpdate = debounce((width: number) => { + updateNodePanelWidth(width, 'system') + }) + useEffect(() => { if (!workflowCanvasWidth) return @@ -132,7 +138,7 @@ const BasePanel: FC = ({ const target = Math.max(workflowCanvasWidth - otherPanelWidth - reservedCanvasWidth, 400) debounceUpdate(target) } - }, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, updateNodePanelWidth]) + }, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, debounceUpdate]) const { handleNodeSelect } = useNodesInteractions() const { nodesReadOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/panel/debug-and-preview/index.spec.tsx b/web/app/components/workflow/panel/debug-and-preview/index.spec.tsx new file mode 100644 index 0000000000..1ac70d1ab3 --- /dev/null +++ b/web/app/components/workflow/panel/debug-and-preview/index.spec.tsx @@ -0,0 +1,145 @@ +/** + * Debug and Preview Panel Width Persistence Tests + * Tests for GitHub issue #22745: Panel width persistence bug fix + */ + +import '@testing-library/jest-dom' + +type PanelWidthSource = 'user' | 'system' + +// Mock localStorage for testing +const createMockLocalStorage = () => { + const storage: Record = {} + return { + getItem: jest.fn((key: string) => storage[key] || null), + setItem: jest.fn((key: string, value: string) => { + storage[key] = value + }), + removeItem: jest.fn((key: string) => { + delete storage[key] + }), + clear: jest.fn(() => { + Object.keys(storage).forEach(key => delete storage[key]) + }), + get storage() { return { ...storage } }, + } +} + +// Preview panel width logic +const createPreviewPanelManager = () => { + const storageKey = 'debug-and-preview-panel-width' + + return { + updateWidth: (width: number, source: PanelWidthSource = 'user') => { + const newValue = Math.max(400, Math.min(width, 800)) + if (source === 'user') + localStorage.setItem(storageKey, `${newValue}`) + + return newValue + }, + getStoredWidth: () => { + const stored = localStorage.getItem(storageKey) + return stored ? Number.parseFloat(stored) : 400 + }, + } +} + +describe('Debug and Preview Panel Width Persistence', () => { + let mockLocalStorage: ReturnType + + beforeEach(() => { + mockLocalStorage = createMockLocalStorage() + Object.defineProperty(globalThis, 'localStorage', { + value: mockLocalStorage, + writable: true, + }) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('Preview Panel Width Management', () => { + it('should save user resize to localStorage', () => { + const manager = createPreviewPanelManager() + + const result = manager.updateWidth(450, 'user') + + expect(result).toBe(450) + expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '450') + }) + + it('should not save system compression to localStorage', () => { + const manager = createPreviewPanelManager() + + const result = manager.updateWidth(300, 'system') + + expect(result).toBe(400) // Respects minimum width + expect(localStorage.setItem).not.toHaveBeenCalled() + }) + + it('should behave identically to Node Panel', () => { + const manager = createPreviewPanelManager() + + // Both user and system operations should behave consistently + manager.updateWidth(500, 'user') + expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '500') + + manager.updateWidth(200, 'system') + expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500') + }) + }) + + describe('Dual Panel Scenario', () => { + it('should maintain independence from Node Panel', () => { + localStorage.setItem('workflow-node-panel-width', '600') + localStorage.setItem('debug-and-preview-panel-width', '450') + + const manager = createPreviewPanelManager() + + // System compresses preview panel + manager.updateWidth(200, 'system') + + // Only preview panel storage key should be unaffected + expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('450') + expect(localStorage.getItem('workflow-node-panel-width')).toBe('600') + }) + + it('should handle F12 scenario consistently', () => { + const manager = createPreviewPanelManager() + + // User sets preference + manager.updateWidth(500, 'user') + expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500') + + // F12 opens causing viewport compression + manager.updateWidth(180, 'system') + + // User preference preserved + expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500') + }) + }) + + describe('Consistency with Node Panel', () => { + it('should enforce same minimum width rules', () => { + const manager = createPreviewPanelManager() + + // Same 400px minimum as Node Panel + const result = manager.updateWidth(300, 'user') + expect(result).toBe(400) + expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '400') + }) + + it('should use same source parameter pattern', () => { + const manager = createPreviewPanelManager() + + // Default to 'user' when source not specified + manager.updateWidth(500) + expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '500') + + // Explicit 'system' source + manager.updateWidth(300, 'system') + expect(localStorage.setItem).toHaveBeenCalledTimes(1) // Only user call + }) + }) +}) diff --git a/web/app/components/workflow/panel/debug-and-preview/index.tsx b/web/app/components/workflow/panel/debug-and-preview/index.tsx index ff09f48625..baf4c21dcd 100644 --- a/web/app/components/workflow/panel/debug-and-preview/index.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/index.tsx @@ -53,8 +53,9 @@ const DebugAndPreview = () => { const nodePanelWidth = useStore(s => s.nodePanelWidth) const panelWidth = useStore(s => s.previewPanelWidth) const setPanelWidth = useStore(s => s.setPreviewPanelWidth) - const handleResize = useCallback((width: number) => { - localStorage.setItem('debug-and-preview-panel-width', `${width}`) + const handleResize = useCallback((width: number, source: 'user' | 'system' = 'user') => { + if (source === 'user') + localStorage.setItem('debug-and-preview-panel-width', `${width}`) setPanelWidth(width) }, [setPanelWidth]) const maxPanelWidth = useMemo(() => { @@ -74,7 +75,9 @@ const DebugAndPreview = () => { triggerDirection: 'left', minWidth: 400, maxWidth: maxPanelWidth, - onResize: debounce(handleResize), + onResize: debounce((width: number) => { + handleResize(width, 'user') + }), }) return ( From db09e7386ff81cb1206da4b8c0b4408f248764c7 Mon Sep 17 00:00:00 2001 From: Jason Young <44939412+farion1231@users.noreply.github.com> Date: Tue, 22 Jul 2025 08:12:38 +0800 Subject: [PATCH 08/14] test: add comprehensive unit tests for AuthType (#22742) --- .../services/auth/test_auth_type.py | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 api/tests/unit_tests/services/auth/test_auth_type.py diff --git a/api/tests/unit_tests/services/auth/test_auth_type.py b/api/tests/unit_tests/services/auth/test_auth_type.py new file mode 100644 index 0000000000..94073f451e --- /dev/null +++ b/api/tests/unit_tests/services/auth/test_auth_type.py @@ -0,0 +1,150 @@ +import pytest + +from services.auth.auth_type import AuthType + + +class TestAuthType: + """Test cases for AuthType enum""" + + def test_auth_type_is_str_enum(self): + """Test that AuthType is properly a StrEnum""" + assert issubclass(AuthType, str) + assert hasattr(AuthType, "__members__") + + def test_auth_type_has_expected_values(self): + """Test that all expected auth types exist with correct values""" + expected_values = { + "FIRECRAWL": "firecrawl", + "WATERCRAWL": "watercrawl", + "JINA": "jinareader", + } + + # Verify all expected members exist + for member_name, expected_value in expected_values.items(): + assert hasattr(AuthType, member_name) + assert getattr(AuthType, member_name).value == expected_value + + # Verify no extra members exist + assert len(AuthType) == len(expected_values) + + @pytest.mark.parametrize( + ("auth_type", "expected_string"), + [ + (AuthType.FIRECRAWL, "firecrawl"), + (AuthType.WATERCRAWL, "watercrawl"), + (AuthType.JINA, "jinareader"), + ], + ) + def test_auth_type_string_representation(self, auth_type, expected_string): + """Test string representation of auth types""" + assert str(auth_type) == expected_string + assert auth_type.value == expected_string + + @pytest.mark.parametrize( + ("auth_type", "compare_value", "expected_result"), + [ + (AuthType.FIRECRAWL, "firecrawl", True), + (AuthType.WATERCRAWL, "watercrawl", True), + (AuthType.JINA, "jinareader", True), + (AuthType.FIRECRAWL, "FIRECRAWL", False), # Case sensitive + (AuthType.FIRECRAWL, "watercrawl", False), + (AuthType.JINA, "jina", False), # Full value mismatch + ], + ) + def test_auth_type_comparison(self, auth_type, compare_value, expected_result): + """Test auth type comparison with strings""" + assert (auth_type == compare_value) is expected_result + + def test_auth_type_iteration(self): + """Test that AuthType can be iterated over""" + auth_types = list(AuthType) + assert len(auth_types) == 3 + assert AuthType.FIRECRAWL in auth_types + assert AuthType.WATERCRAWL in auth_types + assert AuthType.JINA in auth_types + + def test_auth_type_membership(self): + """Test membership checking for AuthType""" + assert "firecrawl" in [auth.value for auth in AuthType] + assert "watercrawl" in [auth.value for auth in AuthType] + assert "jinareader" in [auth.value for auth in AuthType] + assert "invalid" not in [auth.value for auth in AuthType] + + def test_auth_type_invalid_attribute_access(self): + """Test accessing non-existent auth type raises AttributeError""" + with pytest.raises(AttributeError): + _ = AuthType.INVALID_TYPE + + def test_auth_type_immutability(self): + """Test that enum values cannot be modified""" + # In Python 3.11+, enum members are read-only + with pytest.raises(AttributeError): + AuthType.FIRECRAWL = "modified" + + def test_auth_type_from_value(self): + """Test creating AuthType from string value""" + assert AuthType("firecrawl") == AuthType.FIRECRAWL + assert AuthType("watercrawl") == AuthType.WATERCRAWL + assert AuthType("jinareader") == AuthType.JINA + + # Test invalid value + with pytest.raises(ValueError) as exc_info: + AuthType("invalid_auth_type") + assert "invalid_auth_type" in str(exc_info.value) + + def test_auth_type_name_property(self): + """Test the name property of enum members""" + assert AuthType.FIRECRAWL.name == "FIRECRAWL" + assert AuthType.WATERCRAWL.name == "WATERCRAWL" + assert AuthType.JINA.name == "JINA" + + @pytest.mark.parametrize( + "auth_type", + [AuthType.FIRECRAWL, AuthType.WATERCRAWL, AuthType.JINA], + ) + def test_auth_type_isinstance_checks(self, auth_type): + """Test isinstance checks for auth types""" + assert isinstance(auth_type, AuthType) + assert isinstance(auth_type, str) + assert isinstance(auth_type.value, str) + + def test_auth_type_hash(self): + """Test that auth types are hashable and can be used in sets/dicts""" + auth_set = {AuthType.FIRECRAWL, AuthType.WATERCRAWL, AuthType.JINA} + assert len(auth_set) == 3 + + auth_dict = { + AuthType.FIRECRAWL: "firecrawl_handler", + AuthType.WATERCRAWL: "watercrawl_handler", + AuthType.JINA: "jina_handler", + } + assert auth_dict[AuthType.FIRECRAWL] == "firecrawl_handler" + + def test_auth_type_json_serializable(self): + """Test that auth types can be JSON serialized""" + import json + + auth_data = { + "provider": AuthType.FIRECRAWL, + "enabled": True, + } + + # Should serialize to string value + json_str = json.dumps(auth_data, default=str) + assert '"provider": "firecrawl"' in json_str + + def test_auth_type_matches_factory_usage(self): + """Test that all AuthType values are handled by ApiKeyAuthFactory""" + # This test verifies that the enum values match what's expected + # by the factory implementation + from services.auth.api_key_auth_factory import ApiKeyAuthFactory + + for auth_type in AuthType: + # Should not raise ValueError for valid auth types + try: + auth_class = ApiKeyAuthFactory.get_apikey_auth_factory(auth_type) + assert auth_class is not None + except ImportError: + # It's OK if the actual auth implementation doesn't exist + # We're just testing that the enum value is recognized + pass From 58d92970a9129438b25f38c27b39e2ba4217f18e Mon Sep 17 00:00:00 2001 From: issac2e <90555819+issac2e@users.noreply.github.com> Date: Tue, 22 Jul 2025 08:21:41 +0800 Subject: [PATCH 09/14] Optimize tencent_vector knowledge base deletion error handling with batch processing support (#22726) Co-authored-by: liuchen15 Co-authored-by: crazywoola <427733928@qq.com> --- .../rag/datasource/vdb/tencent/tencent_vector.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/api/core/rag/datasource/vdb/tencent/tencent_vector.py b/api/core/rag/datasource/vdb/tencent/tencent_vector.py index 75afe0cdb8..84746d23ea 100644 --- a/api/core/rag/datasource/vdb/tencent/tencent_vector.py +++ b/api/core/rag/datasource/vdb/tencent/tencent_vector.py @@ -206,9 +206,19 @@ class TencentVector(BaseVector): def delete_by_ids(self, ids: list[str]) -> None: if not ids: return - self._client.delete( - database_name=self._client_config.database, collection_name=self.collection_name, document_ids=ids - ) + + total_count = len(ids) + batch_size = self._client_config.max_upsert_batch_size + batch = math.ceil(total_count / batch_size) + + for j in range(batch): + start_idx = j * batch_size + end_idx = min(total_count, (j + 1) * batch_size) + batch_ids = ids[start_idx:end_idx] + + self._client.delete( + database_name=self._client_config.database, collection_name=self.collection_name, document_ids=batch_ids + ) def delete_by_metadata_field(self, key: str, value: str) -> None: self._client.delete( From eb06de0921938dfa7743f12b443246f8c657b6d5 Mon Sep 17 00:00:00 2001 From: yijq <163001319+yijiaquan@users.noreply.github.com> Date: Tue, 22 Jul 2025 08:24:54 +0800 Subject: [PATCH 10/14] refactor: Modify the triggering method of the variable selector in the modification object subtree panel(#22237) (#22238) --- .../variable/object-child-tree-panel/picker/field.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx index f90f30e7ce..ecc67885d1 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx @@ -41,7 +41,7 @@ const Field: FC = ({
!readonly && onSelect?.([...valueSelector, name])} + onMouseDown={() => !readonly && onSelect?.([...valueSelector, name])} >
From 2d8eace34bc49a4d089ca4e9706fff62540665b1 Mon Sep 17 00:00:00 2001 From: "Junyan Qin (Chin)" Date: Tue, 22 Jul 2025 10:27:35 +0800 Subject: [PATCH 11/14] feat: plugin deprecation notice (#22685) Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: twwu --- api/core/plugin/entities/marketplace.py | 7 ++ api/services/plugin/plugin_service.py | 6 + .../plugins/base/deprecation-notice.tsx | 104 ++++++++++++++++++ .../plugin-detail-panel/detail-header.tsx | 18 ++- .../components/plugins/plugin-item/index.tsx | 62 ++++++++--- .../plugins/plugin-page/plugins-panel.tsx | 34 +++--- web/app/components/plugins/types.ts | 6 + web/i18n/en-US/plugin.ts | 11 ++ web/i18n/ja-JP/plugin.ts | 11 ++ web/i18n/zh-Hans/plugin.ts | 11 ++ web/utils/format.ts | 4 + 11 files changed, 241 insertions(+), 33 deletions(-) create mode 100644 web/app/components/plugins/base/deprecation-notice.tsx diff --git a/api/core/plugin/entities/marketplace.py b/api/core/plugin/entities/marketplace.py index a19a44aa3c..1c13a621d4 100644 --- a/api/core/plugin/entities/marketplace.py +++ b/api/core/plugin/entities/marketplace.py @@ -32,6 +32,13 @@ class MarketplacePluginDeclaration(BaseModel): latest_package_identifier: str = Field( ..., description="Unique identifier for the latest package release of the plugin" ) + status: str = Field(..., description="Indicate the status of marketplace plugin, enum from `active` `deleted`") + deprecated_reason: str = Field( + ..., description="Not empty when status='deleted', indicates the reason why this plugin is deleted(deprecated)" + ) + alternative_plugin_id: str = Field( + ..., description="Optional, indicates the alternative plugin for user to switch to" + ) @model_validator(mode="before") @classmethod diff --git a/api/services/plugin/plugin_service.py b/api/services/plugin/plugin_service.py index 0a5bc44b64..9005f0669b 100644 --- a/api/services/plugin/plugin_service.py +++ b/api/services/plugin/plugin_service.py @@ -38,6 +38,9 @@ class PluginService: plugin_id: str version: str unique_identifier: str + status: str + deprecated_reason: str + alternative_plugin_id: str REDIS_KEY_PREFIX = "plugin_service:latest_plugin:" REDIS_TTL = 60 * 5 # 5 minutes @@ -71,6 +74,9 @@ class PluginService: plugin_id=plugin_id, version=manifest.latest_version, unique_identifier=manifest.latest_package_identifier, + status=manifest.status, + deprecated_reason=manifest.deprecated_reason, + alternative_plugin_id=manifest.alternative_plugin_id, ) # Store in Redis diff --git a/web/app/components/plugins/base/deprecation-notice.tsx b/web/app/components/plugins/base/deprecation-notice.tsx new file mode 100644 index 0000000000..36dc73fc20 --- /dev/null +++ b/web/app/components/plugins/base/deprecation-notice.tsx @@ -0,0 +1,104 @@ +import React, { useMemo } from 'react' +import type { FC } from 'react' +import Link from 'next/link' +import cn from '@/utils/classnames' +import { RiAlertFill } from '@remixicon/react' +import { Trans } from 'react-i18next' +import { snakeCase2CamelCase } from '@/utils/format' +import { useMixedTranslation } from '../marketplace/hooks' + +type DeprecationNoticeProps = { + status: 'deleted' | 'active' + deprecatedReason: string + alternativePluginId: string + alternativePluginURL: string + locale?: string + className?: string + innerWrapperClassName?: string + iconWrapperClassName?: string + textClassName?: string +} + +const i18nPrefix = 'plugin.detailPanel.deprecation' + +const DeprecationNotice: FC = ({ + status, + deprecatedReason, + alternativePluginId, + alternativePluginURL, + locale, + className, + innerWrapperClassName, + iconWrapperClassName, + textClassName, +}) => { + const { t } = useMixedTranslation(locale) + + const deprecatedReasonKey = useMemo(() => { + if (!deprecatedReason) return '' + return snakeCase2CamelCase(deprecatedReason) + }, [deprecatedReason]) + + // Check if the deprecatedReasonKey exists in i18n + const hasValidDeprecatedReason = useMemo(() => { + if (!deprecatedReason || !deprecatedReasonKey) return false + + // Define valid reason keys that exist in i18n + const validReasonKeys = ['businessAdjustments', 'ownershipTransferred', 'noMaintainer'] + return validReasonKeys.includes(deprecatedReasonKey) + }, [deprecatedReason, deprecatedReasonKey]) + + if (status !== 'deleted') + return null + + return ( +
+
+
+
+ +
+
+ { + hasValidDeprecatedReason && alternativePluginId && ( + + ), + }} + values={{ + deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`), + alternativePluginId, + }} + /> + ) + } + { + hasValidDeprecatedReason && !alternativePluginId && ( + + {t(`${i18nPrefix}.onlyReason`, { deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`) })} + + ) + } + { + !hasValidDeprecatedReason && ( + {t(`${i18nPrefix}.noReason`)} + ) + } +
+
+
+ ) +} + +export default React.memo(DeprecationNotice) diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 124e133c2b..9c31a0b8e1 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -29,7 +29,7 @@ import Toast from '@/app/components/base/toast' import { BoxSparkleFill } from '@/app/components/base/icons/src/vender/plugin' import { Github } from '@/app/components/base/icons/src/public/common' import { uninstallPlugin } from '@/service/plugins' -import { useGetLanguage } from '@/context/i18n' +import { useGetLanguage, useI18N } from '@/context/i18n' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import { useInvalidateAllToolProviders } from '@/service/use-tools' @@ -39,6 +39,7 @@ import { getMarketplaceUrl } from '@/utils/var' import { PluginAuth } from '@/app/components/plugins/plugin-auth' import { AuthCategory } from '@/app/components/plugins/plugin-auth' import { useAllToolProviders } from '@/service/use-tools' +import DeprecationNotice from '../base/deprecation-notice' const i18nPrefix = 'plugin.action' @@ -56,6 +57,7 @@ const DetailHeader = ({ const { t } = useTranslation() const { theme } = useTheme() const locale = useGetLanguage() + const { locale: currentLocale } = useI18N() const { checkForUpdates, fetchReleases } = useGitHubReleases() const { setShowUpdatePluginModal } = useModalContext() const { refreshModelProviders } = useProviderContext() @@ -70,6 +72,9 @@ const DetailHeader = ({ latest_version, meta, plugin_id, + status, + deprecated_reason, + alternative_plugin_id, } = detail const { author, category, name, label, description, icon, verified, tool } = detail.declaration const isTool = category === PluginType.tool @@ -98,7 +103,7 @@ const DetailHeader = ({ if (isFromGitHub) return `https://github.com/${meta!.repo}` if (isFromMarketplace) - return getMarketplaceUrl(`/plugins/${author}/${name}`, { theme }) + return getMarketplaceUrl(`/plugins/${author}/${name}`, { language: currentLocale, theme }) return '' }, [author, isFromGitHub, isFromMarketplace, meta, name, theme]) @@ -272,6 +277,15 @@ const DetailHeader = ({
+ {isFromMarketplace && ( + + )} { category === PluginType.tool && ( diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index 0ea8538692..c228ca4db4 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import { useTheme } from 'next-themes' import { RiArrowRightUpLine, @@ -55,6 +55,8 @@ const PluginItem: FC = ({ endpoints_active, meta, plugin_id, + status, + deprecated_reason, } = plugin const { category, author, name, label, description, icon, verified, meta: declarationMeta } = plugin.declaration @@ -70,9 +72,14 @@ const PluginItem: FC = ({ return gte(langGeniusVersionInfo.current_version, declarationMeta.minimum_dify_version ?? '0.0.0') }, [declarationMeta.minimum_dify_version, langGeniusVersionInfo.current_version]) - const handleDelete = () => { + const isDeprecated = useMemo(() => { + return status === 'deleted' && !!deprecated_reason + }, [status, deprecated_reason]) + + const handleDelete = useCallback(() => { refreshPluginList({ category } as any) - } + }, [category, refreshPluginList]) + const getValueFromI18nObject = useRenderI18nObject() const title = getValueFromI18nObject(label) const descriptionText = getValueFromI18nObject(description) @@ -81,7 +88,7 @@ const PluginItem: FC = ({ return (
= ({ setCurrentPluginID(plugin.plugin_id) }} > -
+
{/* Header */} -
+
= ({ alt={`plugin-${plugin_unique_identifier}-logo`} />
-
-
+
+
- {verified && <RiVerifiedBadgeLine className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" />} + {verified && <RiVerifiedBadgeLine className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' />} {!isDifyVersionCompatible && <Tooltip popupContent={ t('plugin.difyVersionNotCompatible', { minimalDifyVersion: declarationMeta.minimum_dify_version }) - }><RiErrorWarningLine color='red' className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" /></Tooltip>} + }><RiErrorWarningLine color='red' className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' /></Tooltip>} <Badge className='ml-1 shrink-0' text={source === PluginSource.github ? plugin.meta!.version : plugin.version} hasRedCornerMark={(source === PluginSource.marketplace) && !!plugin.latest_version && plugin.latest_version !== plugin.version} @@ -135,10 +142,11 @@ const PluginItem: FC<Props> = ({ </div> </div> </div> - <div className='mb-1 mt-1.5 flex h-4 items-center justify-between px-4'> - <div className='flex items-center'> + <div className='mb-1 mt-1.5 flex h-4 items-center gap-x-2 px-4'> + {/* Organization & Name */} + <div className='flex grow items-center overflow-hidden'> <OrgInfo - className="mt-0.5" + className='mt-0.5' orgName={orgName} packageName={name} packageNameClassName='w-auto max-w-[150px]' @@ -146,15 +154,20 @@ const PluginItem: FC<Props> = ({ {category === PluginType.extension && ( <> <div className='system-xs-regular mx-2 text-text-quaternary'>·</div> - <div className='system-xs-regular flex space-x-1 text-text-tertiary'> - <RiLoginCircleLine className='h-4 w-4' /> - <span>{t('plugin.endpointsEnabled', { num: endpoints_active })}</span> + <div className='system-xs-regular flex space-x-1 overflow-hidden text-text-tertiary'> + <RiLoginCircleLine className='h-4 w-4 shrink-0' /> + <span + className='truncate' + title={t('plugin.endpointsEnabled', { num: endpoints_active })} + > + {t('plugin.endpointsEnabled', { num: endpoints_active })} + </span> </div> </> )} </div> - - <div className='flex items-center'> + {/* Source */} + <div className='flex shrink-0 items-center'> {source === PluginSource.github && <> <a href={`https://github.com/${meta!.repo}`} target='_blank' className='flex items-center gap-1'> @@ -192,7 +205,20 @@ const PluginItem: FC<Props> = ({ </> } </div> + {/* Deprecated */} + {source === PluginSource.marketplace && enable_marketplace && isDeprecated && ( + <div className='system-2xs-medium-uppercase flex shrink-0 items-center gap-x-2'> + <span className='text-text-tertiary'>·</span> + <span className='text-text-warning'> + {t('plugin.deprecated')} + </span> + </div> + )} </div> + {/* BG Effect for Deprecated Plugin */} + {source === PluginSource.marketplace && enable_marketplace && isDeprecated && ( + <div className='absolute bottom-[-71px] right-[-45px] z-0 size-40 bg-components-badge-status-light-warning-halo opacity-60 blur-[120px]' /> + )} </div> ) } diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index a5f411c37e..ef4911e523 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -36,6 +36,9 @@ const PluginsPanel = () => { ...plugin, latest_version: installedLatestVersion?.versions[plugin.plugin_id]?.version ?? '', latest_unique_identifier: installedLatestVersion?.versions[plugin.plugin_id]?.unique_identifier ?? '', + status: installedLatestVersion?.versions[plugin.plugin_id]?.status ?? 'active', + deprecated_reason: installedLatestVersion?.versions[plugin.plugin_id]?.deprecated_reason ?? '', + alternative_plugin_id: installedLatestVersion?.versions[plugin.plugin_id]?.alternative_plugin_id ?? '', })) || [] }, [pluginList, installedLatestVersion]) @@ -66,20 +69,25 @@ const PluginsPanel = () => { onFilterChange={handleFilterChange} /> </div> - {isPluginListLoading ? <Loading type='app' /> : (filteredList?.length ?? 0) > 0 ? ( - <div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'> - <div className='w-full'> - <List pluginList={filteredList || []} /> - </div> - {!isLastPage && !isFetching && ( - <Button onClick={loadNextPage}> - {t('workflow.common.loadMore')} - </Button> + {isPluginListLoading && <Loading type='app' />} + {!isPluginListLoading && ( + <> + {(filteredList?.length ?? 0) > 0 ? ( + <div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'> + <div className='w-full'> + <List pluginList={filteredList || []} /> + </div> + {!isLastPage && !isFetching && ( + <Button onClick={loadNextPage}> + {t('workflow.common.loadMore')} + </Button> + )} + {isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>} + </div> + ) : ( + <Empty /> )} - {isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>} - </div> - ) : ( - <Empty /> + </> )} <PluginDetailPanel detail={currentPluginDetail} diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index cd4d5e9ece..1cd0409b1e 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -118,6 +118,9 @@ export type PluginDetail = { latest_unique_identifier: string source: PluginSource meta?: MetaData + status: 'active' | 'deleted' + deprecated_reason: string + alternative_plugin_id: string } export type PluginInfoFromMarketPlace = { @@ -343,6 +346,9 @@ export type InstalledLatestVersionResponse = { [plugin_id: string]: { unique_identifier: string version: string + status: 'active' | 'deleted' + deprecated_reason: string + alternative_plugin_id: string } | null } } diff --git a/web/i18n/en-US/plugin.ts b/web/i18n/en-US/plugin.ts index 2984f5f77c..3258144132 100644 --- a/web/i18n/en-US/plugin.ts +++ b/web/i18n/en-US/plugin.ts @@ -29,6 +29,7 @@ const translation = { searchTools: 'Search tools...', installPlugin: 'Install plugin', installFrom: 'INSTALL FROM', + deprecated: 'Deprecated', list: { noInstalled: 'No plugins installed', notFound: 'No plugins found', @@ -99,6 +100,16 @@ const translation = { configureApp: 'Configure App', configureModel: 'Configure model', configureTool: 'Configure tool', + deprecation: { + fullMessage: 'This plugin has been deprecated due to {{deprecatedReason}}, and will no longer be updated. Please use <CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink> instead.', + onlyReason: 'This plugin has been deprecated due to {{deprecatedReason}} and will no longer be updated.', + noReason: 'This plugin has been deprecated and will no longer be updated.', + reason: { + businessAdjustments: 'business adjustments', + ownershipTransferred: 'ownership transferred', + noMaintainer: 'no maintainer', + }, + }, }, install: '{{num}} installs', installAction: 'Install', diff --git a/web/i18n/ja-JP/plugin.ts b/web/i18n/ja-JP/plugin.ts index a12de17a16..a80cde7e38 100644 --- a/web/i18n/ja-JP/plugin.ts +++ b/web/i18n/ja-JP/plugin.ts @@ -84,6 +84,16 @@ const translation = { actionNum: '{{num}} {{action}} が含まれています', endpointsDocLink: 'ドキュメントを表示する', switchVersion: 'バージョンの切り替え', + deprecation: { + fullMessage: 'このプラグインは{{deprecatedReason}}のため非推奨となり、新しいバージョンはリリースされません。代わりに<CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>をご利用ください。', + onlyReason: 'このプラグインは{{deprecatedReason}}のため非推奨となり、新しいバージョンはリリースされません。', + noReason: 'このプラグインは廃止されており、今後更新されることはありません。', + reason: { + businessAdjustments: '事業調整', + ownershipTransferred: '所有権移転', + noMaintainer: 'メンテナーの不足', + }, + }, }, debugInfo: { title: 'デバッグ', @@ -198,6 +208,7 @@ const translation = { install: '{{num}} インストール', installAction: 'インストール', installFrom: 'インストール元', + deprecated: '非推奨', searchPlugins: '検索プラグイン', search: '検索', endpointsEnabled: '{{num}} セットのエンドポイントが有効になりました', diff --git a/web/i18n/zh-Hans/plugin.ts b/web/i18n/zh-Hans/plugin.ts index 5f8f641b72..4c95101592 100644 --- a/web/i18n/zh-Hans/plugin.ts +++ b/web/i18n/zh-Hans/plugin.ts @@ -29,6 +29,7 @@ const translation = { searchTools: '搜索工具...', installPlugin: '安装插件', installFrom: '安装源', + deprecated: '已弃用', list: { noInstalled: '无已安装的插件', notFound: '未找到插件', @@ -99,6 +100,16 @@ const translation = { configureApp: '应用设置', configureModel: '模型设置', configureTool: '工具设置', + deprecation: { + fullMessage: '由于{{deprecatedReason}},此插件已被弃用,将不再发布新版本。请使用<CustomLink href=\'https://example.com/\'>{{-alternativePluginId}}</CustomLink>替代。', + onlyReason: '由于{{deprecatedReason}},此插件已被弃用,将不再发布新版本。', + noReason: '此插件已被弃用,将不再发布新版本。', + reason: { + businessAdjustments: '业务调整', + ownershipTransferred: '所有权转移', + noMaintainer: '无人维护', + }, + }, }, install: '{{num}} 次安装', installAction: '安装', diff --git a/web/utils/format.ts b/web/utils/format.ts index 720c8f6762..781ec90815 100644 --- a/web/utils/format.ts +++ b/web/utils/format.ts @@ -56,3 +56,7 @@ export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string a.remove() window.URL.revokeObjectURL(url) } + +export const snakeCase2CamelCase = (input: string): string => { + return input.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase()) +} From c2c69ffb826fc28dbb825ed18010aba7372c8dbf Mon Sep 17 00:00:00 2001 From: KVOJJJin <jzongcode@gmail.com> Date: Tue, 22 Jul 2025 10:58:08 +0800 Subject: [PATCH 12/14] fix import error in marketplace (#22759) --- web/app/components/plugins/marketplace/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 7a29556bda..d6189a92a1 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -6,7 +6,7 @@ import PluginTypeSwitch from './plugin-type-switch' import ListWrapper from './list/list-wrapper' import type { SearchParams } from './types' import { getMarketplaceCollectionsAndPlugins } from './utils' -import { TanstackQueryIniter } from '@/context/query-client' +import { TanstackQueryInitializer } from '@/context/query-client' type MarketplaceProps = { locale: string @@ -39,7 +39,7 @@ const Marketplace = async ({ } return ( - <TanstackQueryIniter> + <TanstackQueryInitializer> <MarketplaceContextProvider searchParams={searchParams} shouldExclude={shouldExclude} @@ -65,7 +65,7 @@ const Marketplace = async ({ showInstallButton={showInstallButton} /> </MarketplaceContextProvider> - </TanstackQueryIniter> + </TanstackQueryInitializer> ) } From e9c9c5d8f1129301f808552230ad6253af44310b Mon Sep 17 00:00:00 2001 From: Novice <novice12185727@gmail.com> Date: Tue, 22 Jul 2025 13:00:24 +0800 Subject: [PATCH 13/14] fix: single step node execution init error (#22764) LGTM --- api/core/workflow/workflow_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/core/workflow/workflow_entry.py b/api/core/workflow/workflow_entry.py index d2375da39c..c8082ebf50 100644 --- a/api/core/workflow/workflow_entry.py +++ b/api/core/workflow/workflow_entry.py @@ -163,6 +163,7 @@ class WorkflowEntry: graph=graph, graph_runtime_state=GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()), ) + node.init_node_data(node_config_data) try: # variable selector to variable mapping @@ -273,6 +274,7 @@ class WorkflowEntry: graph=graph, graph_runtime_state=GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()), ) + node.init_node_data(node_data) try: # variable selector to variable mapping From c987001a1908bb837ddc75b5d3dbc670cd51d3f8 Mon Sep 17 00:00:00 2001 From: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Date: Tue, 22 Jul 2025 13:40:30 +0800 Subject: [PATCH 14/14] fix: add missing translation function to deprecation notice component (#22767) --- web/app/components/plugins/base/deprecation-notice.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/plugins/base/deprecation-notice.tsx b/web/app/components/plugins/base/deprecation-notice.tsx index 36dc73fc20..2366d31bac 100644 --- a/web/app/components/plugins/base/deprecation-notice.tsx +++ b/web/app/components/plugins/base/deprecation-notice.tsx @@ -65,6 +65,7 @@ const DeprecationNotice: FC<DeprecationNoticeProps> = ({ { hasValidDeprecatedReason && alternativePluginId && ( <Trans + t={t} i18nKey={`${i18nPrefix}.fullMessage`} components={{ CustomLink: (