From 9a8cf709baeb19d3197b51f01ac4a2f009728169 Mon Sep 17 00:00:00 2001 From: Nite Knite Date: Wed, 22 Oct 2025 11:05:27 +0800 Subject: [PATCH 01/14] chore: adjust the route scope for loading Zendesk scripts (#27244) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- web/app/(commonLayout)/layout.tsx | 2 ++ web/app/layout.tsx | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index ed1c995e25..be9c4fe49a 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -9,6 +9,7 @@ import { EventEmitterContextProvider } from '@/context/event-emitter' import { ProviderContextProvider } from '@/context/provider-context' import { ModalContextProvider } from '@/context/modal-context' import GotoAnything from '@/app/components/goto-anything' +import Zendesk from '@/app/components/base/zendesk' const Layout = ({ children }: { children: ReactNode }) => { return ( @@ -28,6 +29,7 @@ const Layout = ({ children }: { children: ReactNode }) => { + ) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index fde265cd22..1be802460b 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -3,7 +3,6 @@ import type { Viewport } from 'next' import I18nServer from './components/i18n-server' import BrowserInitializer from './components/browser-initializer' import SentryInitializer from './components/sentry-initializer' -import Zendesk from './components/base/zendesk' import { getLocaleOnServer } from '@/i18n-config/server' import { TanstackQueryInitializer } from '@/context/query-client' import { ThemeProvider } from 'next-themes' @@ -105,7 +104,6 @@ const LocaleLayout = async ({ - ) From e1ca7a9bdb0443b9f027b6de16c019fff434384c Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 22 Oct 2025 11:20:31 +0800 Subject: [PATCH 02/14] chore: hide useless error info in login page (#27245) --- web/service/use-common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/service/use-common.ts b/web/service/use-common.ts index 57b9c8b165..51b35c453b 100644 --- a/web/service/use-common.ts +++ b/web/service/use-common.ts @@ -118,7 +118,7 @@ export const useIsLogin = () => { gcTime: 0, queryFn: async (): Promise => { try { - await get('/account/profile', { + await get('/account/profile', {}, { silent: true, }) } From 523da66134e67345f818009827c37d268905ee88 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 11:41:49 +0800 Subject: [PATCH 03/14] chore: translate i18n files and update type definitions (#27243) Co-authored-by: WTW0313 <30284043+WTW0313@users.noreply.github.com> --- web/i18n/de-DE/workflow.ts | 2 ++ web/i18n/es-ES/workflow.ts | 2 ++ web/i18n/fa-IR/workflow.ts | 2 ++ web/i18n/fr-FR/workflow.ts | 2 ++ web/i18n/hi-IN/workflow.ts | 2 ++ web/i18n/id-ID/workflow.ts | 2 ++ web/i18n/it-IT/workflow.ts | 2 ++ web/i18n/ja-JP/workflow.ts | 2 ++ web/i18n/ko-KR/workflow.ts | 2 ++ web/i18n/pl-PL/workflow.ts | 2 ++ web/i18n/pt-BR/workflow.ts | 2 ++ web/i18n/ro-RO/workflow.ts | 2 ++ web/i18n/ru-RU/workflow.ts | 2 ++ web/i18n/sl-SI/workflow.ts | 2 ++ web/i18n/th-TH/workflow.ts | 2 ++ web/i18n/tr-TR/workflow.ts | 2 ++ web/i18n/uk-UA/workflow.ts | 2 ++ web/i18n/vi-VN/workflow.ts | 2 ++ web/i18n/zh-Hant/workflow.ts | 2 ++ 19 files changed, 38 insertions(+) diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts index 55bf4ddf72..4353e5e10c 100644 --- a/web/i18n/de-DE/workflow.ts +++ b/web/i18n/de-DE/workflow.ts @@ -949,6 +949,8 @@ const translation = { embeddingModelIsRequired: 'Ein Einbettungsmodell ist erforderlich', chunksVariableIsRequired: 'Die Variable \'Chunks\' ist erforderlich', rerankingModelIsRequired: 'Ein Reranking-Modell ist erforderlich', + embeddingModelIsInvalid: 'Einbettungsmodell ist ungültig', + rerankingModelIsInvalid: 'Das Reranking-Modell ist ungültig', }, }, tracing: { diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts index b4250284aa..d7a6bef9e7 100644 --- a/web/i18n/es-ES/workflow.ts +++ b/web/i18n/es-ES/workflow.ts @@ -949,6 +949,8 @@ const translation = { embeddingModelIsRequired: 'Se requiere un modelo de incrustación', rerankingModelIsRequired: 'Se requiere un modelo de reordenamiento', chunksVariableIsRequired: 'La variable Chunks es obligatoria', + rerankingModelIsInvalid: 'El modelo de reordenación no es válido', + embeddingModelIsInvalid: 'El modelo de incrustación no es válido', }, }, tracing: { diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts index 94d966f2a9..aba3a25010 100644 --- a/web/i18n/fa-IR/workflow.ts +++ b/web/i18n/fa-IR/workflow.ts @@ -949,6 +949,8 @@ const translation = { embeddingModelIsRequired: 'مدل جاسازی مورد نیاز است', chunksVariableIsRequired: 'متغیر تکه‌ها الزامی است', rerankingModelIsRequired: 'مدل رتبه‌بندی مجدد مورد نیاز است', + embeddingModelIsInvalid: 'مدل جاسازی نامعتبر است', + rerankingModelIsInvalid: 'مدل رتبه‌بندی مجدد نامعتبر است', }, }, tracing: { diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts index e7389b4b23..f6c1899cac 100644 --- a/web/i18n/fr-FR/workflow.ts +++ b/web/i18n/fr-FR/workflow.ts @@ -949,6 +949,8 @@ const translation = { rerankingModelIsRequired: 'Un modèle de rerankage est requis', embeddingModelIsRequired: 'Un modèle d\'intégration est requis', chunksVariableIsRequired: 'La variable Chunks est requise', + rerankingModelIsInvalid: 'Le modèle de rerank est invalide', + embeddingModelIsInvalid: 'Le modèle d\'intégration est invalide', }, }, tracing: { diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts index bf4c6cd8d3..224f3acaeb 100644 --- a/web/i18n/hi-IN/workflow.ts +++ b/web/i18n/hi-IN/workflow.ts @@ -969,6 +969,8 @@ const translation = { chunksVariableIsRequired: 'टुकड़े चर आवश्यक है', embeddingModelIsRequired: 'एम्बेडिंग मॉडल आवश्यक है', rerankingModelIsRequired: 'पुनः क्रमांकन मॉडल की आवश्यकता है', + rerankingModelIsInvalid: 'पुनः क्रमांकन मॉडल अमान्य है', + embeddingModelIsInvalid: 'एम्बेडिंग मॉडल अमान्य है', }, }, tracing: { diff --git a/web/i18n/id-ID/workflow.ts b/web/i18n/id-ID/workflow.ts index 2ec7ace93f..4ef6b2b832 100644 --- a/web/i18n/id-ID/workflow.ts +++ b/web/i18n/id-ID/workflow.ts @@ -924,6 +924,8 @@ const translation = { chunksVariableIsRequired: 'Variabel Chunks diperlukan', rerankingModelIsRequired: 'Model reranking diperlukan', embeddingModelIsRequired: 'Model embedding diperlukan', + rerankingModelIsInvalid: 'Model reranking tidak valid', + embeddingModelIsInvalid: 'Model embedding tidak valid', }, }, tracing: {}, diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts index a874c7a9b6..314b8e0c52 100644 --- a/web/i18n/it-IT/workflow.ts +++ b/web/i18n/it-IT/workflow.ts @@ -975,6 +975,8 @@ const translation = { chunksVariableIsRequired: 'La variabile Chunks è richiesta', rerankingModelIsRequired: 'È richiesto un modello di riordinamento', embeddingModelIsRequired: 'È necessario un modello di embedding', + embeddingModelIsInvalid: 'Il modello di embedding non è valido', + rerankingModelIsInvalid: 'Il modello di riorganizzazione è non valido', }, }, tracing: { diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index b3b61f974f..3320f5a89f 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -961,6 +961,8 @@ const translation = { chunksVariableIsRequired: 'Chunks変数は必須です', embeddingModelIsRequired: '埋め込みモデルが必要です', rerankingModelIsRequired: '再ランキングモデルが必要です', + embeddingModelIsInvalid: '埋め込みモデルが無効です', + rerankingModelIsInvalid: 'リランキングモデルは無効です', }, }, tracing: { diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts index 2199054f0e..427452943a 100644 --- a/web/i18n/ko-KR/workflow.ts +++ b/web/i18n/ko-KR/workflow.ts @@ -997,6 +997,8 @@ const translation = { chunksVariableIsRequired: 'Chunks 변수는 필수입니다', embeddingModelIsRequired: '임베딩 모델이 필요합니다', rerankingModelIsRequired: '재순위 모델이 필요합니다', + rerankingModelIsInvalid: '재정렬 모델이 유효하지 않습니다', + embeddingModelIsInvalid: '임베딩 모델이 유효하지 않습니다', }, }, tracing: { diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts index 7eac765449..7c4d85e3ec 100644 --- a/web/i18n/pl-PL/workflow.ts +++ b/web/i18n/pl-PL/workflow.ts @@ -949,6 +949,8 @@ const translation = { embeddingModelIsRequired: 'Wymagany jest model osadzania', chunksVariableIsRequired: 'Wymagana jest zmienna Chunks', rerankingModelIsRequired: 'Wymagany jest model ponownego rankingu', + embeddingModelIsInvalid: 'Model osadzania jest nieprawidłowy', + rerankingModelIsInvalid: 'Model ponownego rankingowania jest nieprawidłowy', }, }, tracing: { diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts index 9dc7439286..bd5cf49ed7 100644 --- a/web/i18n/pt-BR/workflow.ts +++ b/web/i18n/pt-BR/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'A variável \'chunks\' é obrigatória', embeddingModelIsRequired: 'Modelo de incorporação é necessário', rerankingModelIsRequired: 'Um modelo de reclassificação é necessário', + embeddingModelIsInvalid: 'O modelo de incorporação é inválido', + rerankingModelIsInvalid: 'O modelo de reclassificação é inválido', }, }, tracing: { diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts index b91cecac30..ffa1282380 100644 --- a/web/i18n/ro-RO/workflow.ts +++ b/web/i18n/ro-RO/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'Variabila Chunks este obligatorie', embeddingModelIsRequired: 'Este necesar un model de încorporare', rerankingModelIsRequired: 'Este necesar un model de reordonare', + rerankingModelIsInvalid: 'Modelul de reordonare este invalid', + embeddingModelIsInvalid: 'Modelul de încorporare este invalid', }, }, tracing: { diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts index f3be73ed99..78be03ba91 100644 --- a/web/i18n/ru-RU/workflow.ts +++ b/web/i18n/ru-RU/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'Переменная chunks обязательна', embeddingModelIsRequired: 'Требуется модель встраивания', rerankingModelIsRequired: 'Требуется модель перераспределения рангов', + rerankingModelIsInvalid: 'Модель повторной ранжировки недействительна', + embeddingModelIsInvalid: 'Модель встраивания недействительна', }, }, tracing: { diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts index 0aa445e820..dbc4a75c43 100644 --- a/web/i18n/sl-SI/workflow.ts +++ b/web/i18n/sl-SI/workflow.ts @@ -956,6 +956,8 @@ const translation = { chunksVariableIsRequired: 'Spremenljivka Chunks je obvezna', embeddingModelIsRequired: 'Zahteva se vgrajevalni model', rerankingModelIsRequired: 'Potreben je model za ponovno razvrščanje', + rerankingModelIsInvalid: 'Model prerazvrščanja ni veljaven', + embeddingModelIsInvalid: 'Vdelovalni model ni veljaven', }, }, tracing: { diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts index 3491814b73..419b577a02 100644 --- a/web/i18n/th-TH/workflow.ts +++ b/web/i18n/th-TH/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'ตัวแปร Chunks เป็นสิ่งจำเป็น', embeddingModelIsRequired: 'จำเป็นต้องใช้โมเดลฝัง', rerankingModelIsRequired: 'จำเป็นต้องมีโมเดลการจัดอันดับใหม่', + embeddingModelIsInvalid: 'แบบจำลองการฝังไม่ถูกต้อง', + rerankingModelIsInvalid: 'โมเดลการจัดอันดับใหม่ไม่ถูกต้อง', }, }, tracing: { diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts index bd853917b2..930664ce57 100644 --- a/web/i18n/tr-TR/workflow.ts +++ b/web/i18n/tr-TR/workflow.ts @@ -950,6 +950,8 @@ const translation = { embeddingModelIsRequired: 'Gömme modeli gereklidir', chunksVariableIsRequired: 'Chunks değişkeni gereklidir', rerankingModelIsRequired: 'Yeniden sıralama modeli gereklidir', + rerankingModelIsInvalid: 'Yeniden sıralama modeli geçersiz', + embeddingModelIsInvalid: 'Gömme modeli geçersiz', }, }, tracing: { diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts index 069cbcf74c..2f4f298204 100644 --- a/web/i18n/uk-UA/workflow.ts +++ b/web/i18n/uk-UA/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'Змінна chunks є обов\'язковою', embeddingModelIsRequired: 'Потрібна модель вбудовування', rerankingModelIsRequired: 'Потрібна модель повторного ранжування', + embeddingModelIsInvalid: 'Модель вбудовування недійсна', + rerankingModelIsInvalid: 'Модель переналаштування недійсна', }, }, tracing: { diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts index f7b62ee78d..4a3a720cb3 100644 --- a/web/i18n/vi-VN/workflow.ts +++ b/web/i18n/vi-VN/workflow.ts @@ -949,6 +949,8 @@ const translation = { chunksVariableIsRequired: 'Biến Chunks là bắt buộc', embeddingModelIsRequired: 'Cần có mô hình nhúng', rerankingModelIsRequired: 'Cần có mô hình sắp xếp lại', + embeddingModelIsInvalid: 'Mô hình nhúng không hợp lệ', + rerankingModelIsInvalid: 'Mô hình xếp hạng lại không hợp lệ', }, }, tracing: { diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts index 83464e5a7c..faa80b0fa4 100644 --- a/web/i18n/zh-Hant/workflow.ts +++ b/web/i18n/zh-Hant/workflow.ts @@ -949,6 +949,8 @@ const translation = { rerankingModelIsRequired: '需要重新排序模型', chunksVariableIsRequired: 'Chunks 變數是必需的', embeddingModelIsRequired: '需要嵌入模型', + rerankingModelIsInvalid: '重排序模型無效', + embeddingModelIsInvalid: '嵌入模型無效', }, }, tracing: { From bebb4ffbaadf3462ac78a1a3598e8498e8e5f7b5 Mon Sep 17 00:00:00 2001 From: GuanMu Date: Wed, 22 Oct 2025 11:43:37 +0800 Subject: [PATCH 04/14] Fix type error (#27217) --- .../chat/embedded-chatbot/chat-wrapper.tsx | 2 +- web/app/components/base/mermaid/index.tsx | 4 +-- web/app/components/billing/type.ts | 2 +- web/context/provider-context.tsx | 3 +- web/types/lamejs.d.ts | 36 +++++++++++++++++++ web/types/react-18-input-autosize.d.ts | 23 ++++++++++++ 6 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 web/types/lamejs.d.ts create mode 100644 web/types/react-18-input-autosize.d.ts diff --git a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx index 7502b5b767..1bb3dbf56f 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -237,7 +237,7 @@ const ChatWrapper = () => { return ( { const containerRef = useRef(null) const chartId = useRef(`mermaid-chart-${Math.random().toString(36).slice(2, 11)}`).current const [isLoading, setIsLoading] = useState(true) - const renderTimeoutRef = useRef() + const renderTimeoutRef = useRef(undefined) const [errMsg, setErrMsg] = useState('') const [imagePreviewUrl, setImagePreviewUrl] = useState('') @@ -187,7 +187,7 @@ const Flowchart = (props: FlowchartProps) => { }, []) // Update theme when prop changes, but allow internal override. - const prevThemeRef = useRef() + const prevThemeRef = useRef(undefined) useEffect(() => { // Only react if the theme prop from the outside has actually changed. if (props.theme && props.theme !== prevThemeRef.current) { diff --git a/web/app/components/billing/type.ts b/web/app/components/billing/type.ts index 72d46f1e70..7cc4d19755 100644 --- a/web/app/components/billing/type.ts +++ b/web/app/components/billing/type.ts @@ -10,7 +10,7 @@ export enum Priority { topPriority = 'top-priority', } -export type BasicPlan = Plan.sandbox | Plan.professional | Plan.team | Plan.enterprise +export type BasicPlan = Plan.sandbox | Plan.professional | Plan.team export type PlanInfo = { level: number diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index 6c558948d2..755131c859 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -17,7 +17,6 @@ import { } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' -import type { BasicPlan } from '@/app/components/billing/type' import { Plan, type UsagePlanInfo } from '@/app/components/billing/type' import { fetchCurrentPlanInfo } from '@/service/billing' import { parseCurrentPlan } from '@/app/components/billing/utils' @@ -37,7 +36,7 @@ type ProviderContextState = { supportRetrievalMethods: RETRIEVE_METHOD[] isAPIKeySet: boolean plan: { - type: BasicPlan + type: Plan usage: UsagePlanInfo total: UsagePlanInfo } diff --git a/web/types/lamejs.d.ts b/web/types/lamejs.d.ts new file mode 100644 index 0000000000..7ce3e6f89e --- /dev/null +++ b/web/types/lamejs.d.ts @@ -0,0 +1,36 @@ +declare module 'lamejs' { + export class Mp3Encoder { + constructor(channels: number, sampleRate: number, bitRate: number) + encodeBuffer(left: Int16Array, right?: Int16Array | null): Int8Array + flush(): Int8Array + } + + export class WavHeader { + static readHeader(data: DataView): { + channels: number + sampleRate: number + } + } + + const lamejs: { + Mp3Encoder: typeof Mp3Encoder + WavHeader: typeof WavHeader + } + + export default lamejs +} + +declare module 'lamejs/src/js/MPEGMode' { + const MPEGMode: any + export default MPEGMode +} + +declare module 'lamejs/src/js/Lame' { + const Lame: any + export default Lame +} + +declare module 'lamejs/src/js/BitStream' { + const BitStream: any + export default BitStream +} diff --git a/web/types/react-18-input-autosize.d.ts b/web/types/react-18-input-autosize.d.ts new file mode 100644 index 0000000000..0864b33e6a --- /dev/null +++ b/web/types/react-18-input-autosize.d.ts @@ -0,0 +1,23 @@ +declare module 'react-18-input-autosize' { + import type { CSSProperties, ChangeEvent, FocusEvent, KeyboardEvent } from 'react' + + export type AutosizeInputProps = { + value?: string | number + defaultValue?: string | number + onChange?: (event: ChangeEvent) => void + onFocus?: (event: FocusEvent) => void + onBlur?: (event: FocusEvent) => void + onKeyDown?: (event: KeyboardEvent) => void + placeholder?: string + className?: string + inputClassName?: string + style?: CSSProperties + inputStyle?: CSSProperties + minWidth?: number | string + maxWidth?: number | string + [key: string]: any + } + + const AutosizeInput: React.FC + export default AutosizeInput +} From 26ff59172eb2605a206997b7eb8b15f8b31c31a7 Mon Sep 17 00:00:00 2001 From: Alain Date: Wed, 22 Oct 2025 11:45:31 +0800 Subject: [PATCH 05/14] fix: fix OpenAPI Schema Import Pydantic Validation Errors for Complex Default Values (#27159) Co-authored-by: Alain Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/core/plugin/entities/parameters.py | 2 +- api/core/tools/utils/parser.py | 39 +++++++-- .../core/tools/utils/test_parser.py | 80 +++++++++++++++++++ 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/api/core/plugin/entities/parameters.py b/api/core/plugin/entities/parameters.py index 68b5c1084a..1e7f8e4c86 100644 --- a/api/core/plugin/entities/parameters.py +++ b/api/core/plugin/entities/parameters.py @@ -76,7 +76,7 @@ class PluginParameter(BaseModel): auto_generate: PluginParameterAutoGenerate | None = None template: PluginParameterTemplate | None = None required: bool = False - default: Union[float, int, str] | None = None + default: Union[float, int, str, bool] | None = None min: Union[float, int] | None = None max: Union[float, int] | None = None precision: int | None = None diff --git a/api/core/tools/utils/parser.py b/api/core/tools/utils/parser.py index c7ac3387e5..6eabde3991 100644 --- a/api/core/tools/utils/parser.py +++ b/api/core/tools/utils/parser.py @@ -62,6 +62,11 @@ class ApiBasedToolSchemaParser: root = root[ref] interface["operation"]["parameters"][i] = root for parameter in interface["operation"]["parameters"]: + # Handle complex type defaults that are not supported by PluginParameter + default_value = None + if "schema" in parameter and "default" in parameter["schema"]: + default_value = ApiBasedToolSchemaParser._sanitize_default_value(parameter["schema"]["default"]) + tool_parameter = ToolParameter( name=parameter["name"], label=I18nObject(en_US=parameter["name"], zh_Hans=parameter["name"]), @@ -72,9 +77,7 @@ class ApiBasedToolSchemaParser: required=parameter.get("required", False), form=ToolParameter.ToolParameterForm.LLM, llm_description=parameter.get("description"), - default=parameter["schema"]["default"] - if "schema" in parameter and "default" in parameter["schema"] - else None, + default=default_value, placeholder=I18nObject( en_US=parameter.get("description", ""), zh_Hans=parameter.get("description", "") ), @@ -134,6 +137,11 @@ class ApiBasedToolSchemaParser: required = body_schema.get("required", []) properties = body_schema.get("properties", {}) for name, property in properties.items(): + # Handle complex type defaults that are not supported by PluginParameter + default_value = ApiBasedToolSchemaParser._sanitize_default_value( + property.get("default", None) + ) + tool = ToolParameter( name=name, label=I18nObject(en_US=name, zh_Hans=name), @@ -144,12 +152,11 @@ class ApiBasedToolSchemaParser: required=name in required, form=ToolParameter.ToolParameterForm.LLM, llm_description=property.get("description", ""), - default=property.get("default", None), + default=default_value, placeholder=I18nObject( en_US=property.get("description", ""), zh_Hans=property.get("description", "") ), ) - # check if there is a type typ = ApiBasedToolSchemaParser._get_tool_parameter_type(property) if typ: @@ -197,6 +204,22 @@ class ApiBasedToolSchemaParser: return bundles + @staticmethod + def _sanitize_default_value(value): + """ + Sanitize default values for PluginParameter compatibility. + Complex types (list, dict) are converted to None to avoid validation errors. + + Args: + value: The default value from OpenAPI schema + + Returns: + None for complex types (list, dict), otherwise the original value + """ + if isinstance(value, (list, dict)): + return None + return value + @staticmethod def _get_tool_parameter_type(parameter: dict) -> ToolParameter.ToolParameterType | None: parameter = parameter or {} @@ -217,7 +240,11 @@ class ApiBasedToolSchemaParser: return ToolParameter.ToolParameterType.STRING elif typ == "array": items = parameter.get("items") or parameter.get("schema", {}).get("items") - return ToolParameter.ToolParameterType.FILES if items and items.get("format") == "binary" else None + if items and items.get("format") == "binary": + return ToolParameter.ToolParameterType.FILES + else: + # For regular arrays, return ARRAY type instead of None + return ToolParameter.ToolParameterType.ARRAY else: return None diff --git a/api/tests/unit_tests/core/tools/utils/test_parser.py b/api/tests/unit_tests/core/tools/utils/test_parser.py index e1eab21ca4..f39158aa59 100644 --- a/api/tests/unit_tests/core/tools/utils/test_parser.py +++ b/api/tests/unit_tests/core/tools/utils/test_parser.py @@ -109,3 +109,83 @@ def test_parse_openapi_to_tool_bundle_properties_all_of(app): assert tool_bundles[0].parameters[0].llm_description == "desc prop1" # TODO: support enum in OpenAPI # assert set(tool_bundles[0].parameters[0].options) == {"option1", "option2", "option3"} + + +def test_parse_openapi_to_tool_bundle_default_value_type_casting(app): + """ + Test that default values are properly cast to match parameter types. + This addresses the issue where array default values like [] cause validation errors + when parameter type is inferred as string/number/boolean. + """ + openapi = { + "openapi": "3.0.0", + "info": {"title": "Test API", "version": "1.0.0"}, + "servers": [{"url": "https://example.com"}], + "paths": { + "/product/create": { + "post": { + "operationId": "createProduct", + "summary": "Create a product", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "categories": { + "description": "List of category identifiers", + "default": [], + "type": "array", + "items": {"type": "string"}, + }, + "name": { + "description": "Product name", + "default": "Default Product", + "type": "string", + }, + "price": {"description": "Product price", "default": 0.0, "type": "number"}, + "available": { + "description": "Product availability", + "default": True, + "type": "boolean", + }, + }, + } + } + } + }, + "responses": {"200": {"description": "Default Response"}}, + } + } + }, + } + + with app.test_request_context(): + tool_bundles = ApiBasedToolSchemaParser.parse_openapi_to_tool_bundle(openapi) + + assert len(tool_bundles) == 1 + bundle = tool_bundles[0] + assert len(bundle.parameters) == 4 + + # Find parameters by name + params_by_name = {param.name: param for param in bundle.parameters} + + # Check categories parameter (array type with [] default) + categories_param = params_by_name["categories"] + assert categories_param.type == "array" # Will be detected by _get_tool_parameter_type + assert categories_param.default is None # Array default [] is converted to None + + # Check name parameter (string type with string default) + name_param = params_by_name["name"] + assert name_param.type == "string" + assert name_param.default == "Default Product" + + # Check price parameter (number type with number default) + price_param = params_by_name["price"] + assert price_param.type == "number" + assert price_param.default == 0.0 + + # Check available parameter (boolean type with boolean default) + available_param = params_by_name["available"] + assert available_param.type == "boolean" + assert available_param.default is True From 73e217ab0d79dde2243863aa073a5da65e894648 Mon Sep 17 00:00:00 2001 From: GuanMu Date: Wed, 22 Oct 2025 13:06:15 +0800 Subject: [PATCH 06/14] Fix type error (#27250) --- web/app/components/base/pagination/type.ts | 12 +++++++++--- web/app/components/tools/labels/constant.ts | 5 ++++- .../workflow/store/workflow/debug/mock-data.ts | 18 ++++++++++++++++++ web/app/components/workflow/utils/debug.ts | 3 +++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/pagination/type.ts b/web/app/components/base/pagination/type.ts index c3744c06c2..d8b7cf1614 100644 --- a/web/app/components/base/pagination/type.ts +++ b/web/app/components/base/pagination/type.ts @@ -1,5 +1,11 @@ import type { ButtonHTMLAttributes } from 'react' +type ElementProps = { + className?: string + children?: React.ReactNode + [key: string]: unknown +} + type IBasePaginationProps = { currentPage: number setCurrentPage: (page: number) => void @@ -31,7 +37,7 @@ type IPagination = IUsePagination & { } type ButtonProps = ButtonHTMLAttributes & { - as?: React.ReactNode + as?: React.ReactElement children?: string | React.ReactNode className?: string dataTestId?: string @@ -39,9 +45,9 @@ type ButtonProps = ButtonHTMLAttributes & { type PageButtonProps = ButtonProps & { /** - * Provide a custom ReactNode (e.g. Next/Link) + * Provide a custom ReactElement (e.g. Next/Link) */ - as?: React.ReactNode + as?: React.ReactElement activeClassName?: string inactiveClassName?: string dataTestIdActive?: string diff --git a/web/app/components/tools/labels/constant.ts b/web/app/components/tools/labels/constant.ts index ad4836e6a8..e7d3503e73 100644 --- a/web/app/components/tools/labels/constant.ts +++ b/web/app/components/tools/labels/constant.ts @@ -1,4 +1,7 @@ +import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' + export type Label = { name: string - label: string + label: TypeWithI18N + icon: string } diff --git a/web/app/components/workflow/store/workflow/debug/mock-data.ts b/web/app/components/workflow/store/workflow/debug/mock-data.ts index 9d1bf80076..0bc5555d8c 100644 --- a/web/app/components/workflow/store/workflow/debug/mock-data.ts +++ b/web/app/components/workflow/store/workflow/debug/mock-data.ts @@ -12,6 +12,9 @@ export const vars: VarInInspect[] = [ value_type: VarType.string, value: 'text value...', edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, { id: 'fdklajljgldjglkagjlk', @@ -22,6 +25,9 @@ export const vars: VarInInspect[] = [ value_type: VarType.string, value: 'made zhizhang', edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, ] @@ -35,6 +41,9 @@ export const conversationVars: VarInInspect[] = [ value_type: VarType.string, value: 'conversation var value...', edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, { id: 'con2', @@ -45,6 +54,9 @@ export const conversationVars: VarInInspect[] = [ value_type: VarType.number, value: 456, edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, ] @@ -58,6 +70,9 @@ export const systemVars: VarInInspect[] = [ value_type: VarType.string, value: 'Hello robot!', edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, { id: 'sys2', @@ -68,5 +83,8 @@ export const systemVars: VarInInspect[] = [ value_type: VarType.string, value: 'djflakjerlkjdlksfjslakjsdfl', edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, }, ] diff --git a/web/app/components/workflow/utils/debug.ts b/web/app/components/workflow/utils/debug.ts index 6dd111d714..4f47153111 100644 --- a/web/app/components/workflow/utils/debug.ts +++ b/web/app/components/workflow/utils/debug.ts @@ -21,5 +21,8 @@ export const outputToVarInInspect = ({ value_type: VarType.string, // TODO: wait for api or get from node value, edited: false, + visible: true, + is_truncated: false, + full_content: { size_bytes: 0, download_url: '' }, } } From 8e45753c6854b0c9822b2b7fddbfc87d3bce904e Mon Sep 17 00:00:00 2001 From: Cris <92035086+cuixiaojun001@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:36:29 +0800 Subject: [PATCH 07/14] fix:restore correct numeric values for ParamsAutoGenerated (#27252) --- api/core/workflow/nodes/agent/entities.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/workflow/nodes/agent/entities.py b/api/core/workflow/nodes/agent/entities.py index ce6eb33ecc..985ee5eef2 100644 --- a/api/core/workflow/nodes/agent/entities.py +++ b/api/core/workflow/nodes/agent/entities.py @@ -26,8 +26,8 @@ class AgentNodeData(BaseNodeData): class ParamsAutoGenerated(IntEnum): - CLOSE = auto() - OPEN = auto() + CLOSE = 0 + OPEN = 1 class AgentOldVersionModelFeatures(StrEnum): From 40d3332690ab3a10e937f8ef8472ba09f8276464 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Wed, 22 Oct 2025 14:59:08 +0800 Subject: [PATCH 08/14] fix: preserve share code headers after login redirect (#27225) Co-authored-by: yunlu.wen Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/controllers/web/passport.py | 33 ++++++++++++-------------------- web/service/fetch.ts | 34 ++++++++++++++++++++++++++++----- web/service/share.ts | 4 +++- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/api/controllers/web/passport.py b/api/controllers/web/passport.py index 776b743e92..a344777783 100644 --- a/api/controllers/web/passport.py +++ b/api/controllers/web/passport.py @@ -14,8 +14,6 @@ from extensions.ext_database import db from libs.passport import PassportService from libs.token import extract_access_token from models.model import App, EndUser, Site -from services.app_service import AppService -from services.enterprise.enterprise_service import EnterpriseService from services.feature_service import FeatureService from services.webapp_auth_service import WebAppAuthService, WebAppAuthType @@ -38,22 +36,17 @@ class PassportResource(Resource): app_code = request.headers.get(HEADER_NAME_APP_CODE) user_id = request.args.get("user_id") access_token = extract_access_token(request) - if app_code is None: raise Unauthorized("X-App-Code header is missing.") - app_id = AppService.get_app_id_by_code(app_code) - # exchange token for enterprise logined web user - enterprise_user_decoded = decode_enterprise_webapp_user_id(access_token) - if enterprise_user_decoded: - # a web user has already logged in, exchange a token for this app without redirecting to the login page - return exchange_token_for_existing_web_user( - app_code=app_code, enterprise_user_decoded=enterprise_user_decoded - ) - if system_features.webapp_auth.enabled: - app_settings = EnterpriseService.WebAppAuth.get_app_access_mode_by_id(app_id=app_id) - if not app_settings or not app_settings.access_mode == "public": - raise WebAppAuthRequiredError() + enterprise_user_decoded = decode_enterprise_webapp_user_id(access_token) + app_auth_type = WebAppAuthService.get_app_auth_type(app_code=app_code) + if app_auth_type != WebAppAuthType.PUBLIC: + if not enterprise_user_decoded: + raise WebAppAuthRequiredError() + return exchange_token_for_existing_web_user( + app_code=app_code, enterprise_user_decoded=enterprise_user_decoded, auth_type=app_auth_type + ) # get site from db and check if it is normal site = db.session.scalar(select(Site).where(Site.code == app_code, Site.status == "normal")) @@ -124,7 +117,7 @@ def decode_enterprise_webapp_user_id(jwt_token: str | None): return decoded -def exchange_token_for_existing_web_user(app_code: str, enterprise_user_decoded: dict): +def exchange_token_for_existing_web_user(app_code: str, enterprise_user_decoded: dict, auth_type: WebAppAuthType): """ Exchange a token for an existing web user session. """ @@ -145,13 +138,11 @@ def exchange_token_for_existing_web_user(app_code: str, enterprise_user_decoded: if not app_model or app_model.status != "normal" or not app_model.enable_site: raise NotFound() - app_auth_type = WebAppAuthService.get_app_auth_type(app_code=app_code) - - if app_auth_type == WebAppAuthType.PUBLIC: + if auth_type == WebAppAuthType.PUBLIC: return _exchange_for_public_app_token(app_model, site, enterprise_user_decoded) - elif app_auth_type == WebAppAuthType.EXTERNAL and user_auth_type != "external": + elif auth_type == WebAppAuthType.EXTERNAL and user_auth_type != "external": raise WebAppAuthRequiredError("Please login as external user.") - elif app_auth_type == WebAppAuthType.INTERNAL and user_auth_type != "internal": + elif auth_type == WebAppAuthType.INTERNAL and user_auth_type != "internal": raise WebAppAuthRequiredError("Please login as internal user.") end_user = None diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 541b1246d4..8d663c902b 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -69,12 +69,36 @@ const beforeErrorToast = (otherOptions: IOtherOptions): BeforeErrorHook => { } } +const SHARE_ROUTE_DENY_LIST = new Set(['webapp-signin', 'check-code', 'login']) + +const resolveShareCode = () => { + const pathnameSegments = globalThis.location.pathname.split('/').filter(Boolean) + const lastSegment = pathnameSegments.at(-1) || '' + if (lastSegment && !SHARE_ROUTE_DENY_LIST.has(lastSegment)) + return lastSegment + + const redirectParam = new URLSearchParams(globalThis.location.search).get('redirect_url') + if (!redirectParam) + return '' + try { + const redirectUrl = new URL(decodeURIComponent(redirectParam), globalThis.location.origin) + const redirectSegments = redirectUrl.pathname.split('/').filter(Boolean) + const redirectSegment = redirectSegments.at(-1) || '' + return SHARE_ROUTE_DENY_LIST.has(redirectSegment) ? '' : redirectSegment + } + catch { + return '' + } +} + const beforeRequestPublicWithCode = (request: Request) => { - request.headers.set('Authorization', `Bearer ${getWebAppAccessToken()}`) - const shareCode = globalThis.location.pathname.split('/').filter(Boolean).pop() || '' - // some pages does not end with share code, so we need to check it - // TODO: maybe find a better way to access app code? - if (shareCode === 'webapp-signin' || shareCode === 'check-code') + const accessToken = getWebAppAccessToken() + if (accessToken) + request.headers.set('Authorization', `Bearer ${accessToken}`) + else + request.headers.delete('Authorization') + const shareCode = resolveShareCode() + if (!shareCode) return request.headers.set(WEB_APP_SHARE_CODE_HEADER_NAME, shareCode) request.headers.set(PASSPORT_HEADER_NAME, getWebAppPassport(shareCode)) diff --git a/web/service/share.ts b/web/service/share.ts index ce03f508d1..b19dbc896d 100644 --- a/web/service/share.ts +++ b/web/service/share.ts @@ -291,7 +291,9 @@ export const textToAudioStream = (url: string, isPublicAPI: boolean, header: { c export const fetchAccessToken = async ({ userId, appCode }: { userId?: string, appCode: string }) => { const headers = new Headers() headers.append(WEB_APP_SHARE_CODE_HEADER_NAME, appCode) - headers.append('Authorization', `Bearer ${getWebAppAccessToken()}`) + const accessToken = getWebAppAccessToken() + if (accessToken) + headers.append('Authorization', `Bearer ${accessToken}`) const params = new URLSearchParams() userId && params.append('user_id', userId) const url = `/passport?${params.toString()}` From c61c2b0abdcec2f80db32aaf2cf73534cace6730 Mon Sep 17 00:00:00 2001 From: GuanMu Date: Wed, 22 Oct 2025 17:08:27 +0800 Subject: [PATCH 09/14] Fix type error (#27274) --- web/app/components/app/annotation/index.tsx | 8 --- web/app/components/app/annotation/list.tsx | 3 - .../config-prompt/advanced-prompt-input.tsx | 4 +- .../config-prompt/simple-prompt-input.tsx | 4 +- .../config-var/config-modal/index.tsx | 2 +- .../app/configuration/config-var/index.tsx | 4 +- .../config/agent/prompt-editor.tsx | 4 +- .../app/configuration/debug/index.tsx | 2 +- .../app/configuration/tools/index.tsx | 6 +- .../app/create-from-dsl-modal/index.tsx | 4 +- web/app/components/base/confirm/index.tsx | 2 +- web/app/components/tools/labels/constant.ts | 4 +- web/app/components/tools/types.ts | 1 + web/models/app.ts | 4 +- web/package.json | 2 + web/pnpm-lock.yaml | 55 +++++++++---------- 16 files changed, 55 insertions(+), 54 deletions(-) diff --git a/web/app/components/app/annotation/index.tsx b/web/app/components/app/annotation/index.tsx index 264b1ac727..bc63b85f6d 100644 --- a/web/app/components/app/annotation/index.tsx +++ b/web/app/components/app/annotation/index.tsx @@ -53,7 +53,6 @@ const Annotation: FC = (props) => { const [isShowViewModal, setIsShowViewModal] = useState(false) const [selectedIds, setSelectedIds] = useState([]) const debouncedQueryParams = useDebounce(queryParams, { wait: 500 }) - const [isBatchDeleting, setIsBatchDeleting] = useState(false) const fetchAnnotationConfig = async () => { const res = await doFetchAnnotationConfig(appDetail.id) @@ -108,9 +107,6 @@ const Annotation: FC = (props) => { } const handleBatchDelete = async () => { - if (isBatchDeleting) - return - setIsBatchDeleting(true) try { await delAnnotations(appDetail.id, selectedIds) Toast.notify({ message: t('common.api.actionSuccess'), type: 'success' }) @@ -121,9 +117,6 @@ const Annotation: FC = (props) => { catch (e: any) { Toast.notify({ type: 'error', message: e.message || t('common.api.actionFailed') }) } - finally { - setIsBatchDeleting(false) - } } const handleView = (item: AnnotationItem) => { @@ -213,7 +206,6 @@ const Annotation: FC = (props) => { onSelectedIdsChange={setSelectedIds} onBatchDelete={handleBatchDelete} onCancel={() => setSelectedIds([])} - isBatchDeleting={isBatchDeleting} /> :
} diff --git a/web/app/components/app/annotation/list.tsx b/web/app/components/app/annotation/list.tsx index 6705ac5768..70ecedb869 100644 --- a/web/app/components/app/annotation/list.tsx +++ b/web/app/components/app/annotation/list.tsx @@ -19,7 +19,6 @@ type Props = { onSelectedIdsChange: (selectedIds: string[]) => void onBatchDelete: () => Promise onCancel: () => void - isBatchDeleting?: boolean } const List: FC = ({ @@ -30,7 +29,6 @@ const List: FC = ({ onSelectedIdsChange, onBatchDelete, onCancel, - isBatchDeleting, }) => { const { t } = useTranslation() const { formatTime } = useTimestamp() @@ -142,7 +140,6 @@ const List: FC = ({ selectedIds={selectedIds} onBatchDelete={onBatchDelete} onCancel={onCancel} - isBatchDeleting={isBatchDeleting} /> )} diff --git a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx index 70e0334e98..aa8d0f65ca 100644 --- a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx @@ -78,7 +78,9 @@ const AdvancedPromptInput: FC = ({ const handleOpenExternalDataToolModal = () => { setShowExternalDataToolModal({ payload: {}, - onSaveCallback: (newExternalDataTool: ExternalDataTool) => { + onSaveCallback: (newExternalDataTool?: ExternalDataTool) => { + if (!newExternalDataTool) + return eventEmitter?.emit({ type: ADD_EXTERNAL_DATA_TOOL, payload: newExternalDataTool, diff --git a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx index 169e8a14a2..8634232b2b 100644 --- a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx @@ -76,7 +76,9 @@ const Prompt: FC = ({ const handleOpenExternalDataToolModal = () => { setShowExternalDataToolModal({ payload: {}, - onSaveCallback: (newExternalDataTool: ExternalDataTool) => { + onSaveCallback: (newExternalDataTool?: ExternalDataTool) => { + if (!newExternalDataTool) + return eventEmitter?.emit({ type: ADD_EXTERNAL_DATA_TOOL, payload: newExternalDataTool, diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index de7d2c9eac..3f32c9b0c7 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -320,7 +320,7 @@ const ConfigModal: FC = ({ {type === InputVarType.paragraph && (