diff --git a/.github/scripts/generate-i18n-changes.mjs b/.github/scripts/generate-i18n-changes.mjs new file mode 100644 index 0000000000..3d25115ac3 --- /dev/null +++ b/.github/scripts/generate-i18n-changes.mjs @@ -0,0 +1,82 @@ +import { execFileSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' + +const repoRoot = process.cwd() +const baseSha = process.env.BASE_SHA || '' +const headSha = process.env.HEAD_SHA || '' +const files = (process.env.CHANGED_FILES || '').split(/\s+/).filter(Boolean) +const outputPath = process.env.I18N_CHANGES_OUTPUT_PATH || '/tmp/i18n-changes.json' + +const englishPath = fileStem => path.join(repoRoot, 'web', 'i18n', 'en-US', `${fileStem}.json`) + +const readCurrentJson = (fileStem) => { + const filePath = englishPath(fileStem) + if (!fs.existsSync(filePath)) + return null + + return JSON.parse(fs.readFileSync(filePath, 'utf8')) +} + +const readBaseJson = (fileStem) => { + if (!baseSha) + return null + + try { + const relativePath = `web/i18n/en-US/${fileStem}.json` + const content = execFileSync('git', ['show', `${baseSha}:${relativePath}`], { encoding: 'utf8' }) + return JSON.parse(content) + } + catch { + return null + } +} + +const compareJson = (beforeValue, afterValue) => JSON.stringify(beforeValue) === JSON.stringify(afterValue) + +const changes = {} + +for (const fileStem of files) { + const currentJson = readCurrentJson(fileStem) + const beforeJson = readBaseJson(fileStem) || {} + const afterJson = currentJson || {} + const added = {} + const updated = {} + const deleted = [] + + for (const [key, value] of Object.entries(afterJson)) { + if (!(key in beforeJson)) { + added[key] = value + continue + } + + if (!compareJson(beforeJson[key], value)) { + updated[key] = { + before: beforeJson[key], + after: value, + } + } + } + + for (const key of Object.keys(beforeJson)) { + if (!(key in afterJson)) + deleted.push(key) + } + + changes[fileStem] = { + fileDeleted: currentJson === null, + added, + updated, + deleted, + } +} + +fs.writeFileSync( + outputPath, + JSON.stringify({ + baseSha, + headSha, + files, + changes, + }) +) diff --git a/.github/workflows/translate-i18n-claude.yml b/.github/workflows/translate-i18n-claude.yml index a813c87cec..e001f4d677 100644 --- a/.github/workflows/translate-i18n-claude.yml +++ b/.github/workflows/translate-i18n-claude.yml @@ -68,89 +68,7 @@ jobs: " web/i18n-config/languages.ts | sed 's/[[:space:]]*$//') generate_changes_json() { - node <<'NODE' - const { execFileSync } = require('node:child_process') - const fs = require('node:fs') - const path = require('node:path') - - const repoRoot = process.cwd() - const baseSha = process.env.BASE_SHA || '' - const headSha = process.env.HEAD_SHA || '' - const files = (process.env.CHANGED_FILES || '').split(/\s+/).filter(Boolean) - - const englishPath = fileStem => path.join(repoRoot, 'web', 'i18n', 'en-US', `${fileStem}.json`) - - const readCurrentJson = (fileStem) => { - const filePath = englishPath(fileStem) - if (!fs.existsSync(filePath)) - return null - - return JSON.parse(fs.readFileSync(filePath, 'utf8')) - } - - const readBaseJson = (fileStem) => { - if (!baseSha) - return null - - try { - const relativePath = `web/i18n/en-US/${fileStem}.json` - const content = execFileSync('git', ['show', `${baseSha}:${relativePath}`], { encoding: 'utf8' }) - return JSON.parse(content) - } - catch (error) { - return null - } - } - - const compareJson = (beforeValue, afterValue) => JSON.stringify(beforeValue) === JSON.stringify(afterValue) - - const changes = {} - - for (const fileStem of files) { - const currentJson = readCurrentJson(fileStem) - const beforeJson = readBaseJson(fileStem) || {} - const afterJson = currentJson || {} - const added = {} - const updated = {} - const deleted = [] - - for (const [key, value] of Object.entries(afterJson)) { - if (!(key in beforeJson)) { - added[key] = value - continue - } - - if (!compareJson(beforeJson[key], value)) { - updated[key] = { - before: beforeJson[key], - after: value, - } - } - } - - for (const key of Object.keys(beforeJson)) { - if (!(key in afterJson)) - deleted.push(key) - } - - changes[fileStem] = { - fileDeleted: currentJson === null, - added, - updated, - deleted, - } - } - - fs.writeFileSync( - '/tmp/i18n-changes.json', - JSON.stringify({ - baseSha, - headSha, - files, - changes, - }) - ) - NODE + node .github/scripts/generate-i18n-changes.mjs } if [ "${{ github.event_name }}" = "repository_dispatch" ]; then @@ -270,7 +188,7 @@ jobs: Tool rules: - Use Read for repository files. - Use Edit for JSON updates. - - Use Bash only for `pnpm`. + - Use Bash only for `vp`. - Do not use Bash for `git`, `gh`, or branch management. Required execution plan: @@ -292,7 +210,7 @@ jobs: - Read the current English JSON file for any file that still exists so wording, placeholders, and surrounding terminology stay accurate. - If `Structured change set available` is `false`, treat this as a scoped full sync and use the current English files plus scoped checks as the source of truth. 4. Run a scoped pre-check before editing: - - `pnpm --dir ${{ github.workspace }}/web run i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }}` + - `vp run dify-web#i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }}` - Use this command as the source of truth for missing and extra keys inside the current scope. 5. Apply translations. - For every target language and scoped file: @@ -300,19 +218,19 @@ jobs: - If the locale file does not exist yet, create it with `Write` and then continue with `Edit` as needed. - ADD missing keys. - UPDATE stale translations when the English value changed. - - DELETE removed keys. Prefer `pnpm --dir ${{ github.workspace }}/web run i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }} --auto-remove` for extra keys so deletions stay in scope. + - DELETE removed keys. Prefer `vp run dify-web#i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }} --auto-remove` for extra keys so deletions stay in scope. - Preserve placeholders exactly: `{{variable}}`, `${variable}`, HTML tags, component tags, and variable names. - Match the existing terminology and register used by each locale. - Prefer one Edit per file when stable, but prioritize correctness over batching. 6. Verify only the edited files. - - Run `pnpm --dir ${{ github.workspace }}/web lint:fix --quiet -- ` - - Run `pnpm --dir ${{ github.workspace }}/web run i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }}` + - Run `vp run dify-web#lint:fix --quiet -- ` + - Run `vp run dify-web#i18n:check ${{ steps.context.outputs.FILE_ARGS }} ${{ steps.context.outputs.LANG_ARGS }}` - If verification fails, fix the remaining problems before continuing. 7. Stop after the scoped locale files are updated and verification passes. - Do not create branches, commits, or pull requests. claude_args: | --max-turns 120 - --allowedTools "Read,Write,Edit,Bash(pnpm *),Bash(pnpm:*),Glob,Grep" + --allowedTools "Read,Write,Edit,Bash(vp *),Bash(vp:*),Glob,Grep" - name: Prepare branch metadata id: pr_meta @@ -354,6 +272,7 @@ jobs: - name: Create or update translation PR if: steps.pr_meta.outputs.has_changes == 'true' env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH_NAME: ${{ steps.pr_meta.outputs.branch_name }} FILES_IN_SCOPE: ${{ steps.context.outputs.CHANGED_FILES }} TARGET_LANGS: ${{ steps.context.outputs.TARGET_LANGS }} @@ -402,8 +321,8 @@ jobs: '', '## Verification', '', - `- \`pnpm --dir web run i18n:check --file ${process.env.FILES_IN_SCOPE} --lang ${process.env.TARGET_LANGS}\``, - `- \`pnpm --dir web lint:fix --quiet -- \``, + `- \`vp run dify-web#i18n:check --file ${process.env.FILES_IN_SCOPE} --lang ${process.env.TARGET_LANGS}\``, + `- \`vp run dify-web#lint:fix --quiet -- \``, '', '## Notes', '', diff --git a/.github/workflows/trigger-i18n-sync.yml b/.github/workflows/trigger-i18n-sync.yml index a1ca42b26e..9a11d3e8df 100644 --- a/.github/workflows/trigger-i18n-sync.yml +++ b/.github/workflows/trigger-i18n-sync.yml @@ -42,88 +42,7 @@ jobs: fi export BASE_SHA HEAD_SHA CHANGED_FILES - node <<'NODE' - const { execFileSync } = require('node:child_process') - const fs = require('node:fs') - const path = require('node:path') - - const repoRoot = process.cwd() - const baseSha = process.env.BASE_SHA || '' - const headSha = process.env.HEAD_SHA || '' - const files = (process.env.CHANGED_FILES || '').split(/\s+/).filter(Boolean) - - const englishPath = fileStem => path.join(repoRoot, 'web', 'i18n', 'en-US', `${fileStem}.json`) - - const readCurrentJson = (fileStem) => { - const filePath = englishPath(fileStem) - if (!fs.existsSync(filePath)) - return null - - return JSON.parse(fs.readFileSync(filePath, 'utf8')) - } - - const readBaseJson = (fileStem) => { - if (!baseSha) - return null - - try { - const relativePath = `web/i18n/en-US/${fileStem}.json` - const content = execFileSync('git', ['show', `${baseSha}:${relativePath}`], { encoding: 'utf8' }) - return JSON.parse(content) - } - catch (error) { - return null - } - } - - const compareJson = (beforeValue, afterValue) => JSON.stringify(beforeValue) === JSON.stringify(afterValue) - - const changes = {} - - for (const fileStem of files) { - const beforeJson = readBaseJson(fileStem) || {} - const afterJson = readCurrentJson(fileStem) || {} - const added = {} - const updated = {} - const deleted = [] - - for (const [key, value] of Object.entries(afterJson)) { - if (!(key in beforeJson)) { - added[key] = value - continue - } - - if (!compareJson(beforeJson[key], value)) { - updated[key] = { - before: beforeJson[key], - after: value, - } - } - } - - for (const key of Object.keys(beforeJson)) { - if (!(key in afterJson)) - deleted.push(key) - } - - changes[fileStem] = { - fileDeleted: readCurrentJson(fileStem) === null, - added, - updated, - deleted, - } - } - - fs.writeFileSync( - '/tmp/i18n-changes.json', - JSON.stringify({ - baseSha, - headSha, - files, - changes, - }) - ) - NODE + node .github/scripts/generate-i18n-changes.mjs if [ -n "$CHANGED_FILES" ]; then echo "has_changes=true" >> "$GITHUB_OUTPUT" diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit index db5c504606..13bbd81cf6 100755 --- a/.vite-hooks/pre-commit +++ b/.vite-hooks/pre-commit @@ -81,8 +81,8 @@ if $web_modified; then if $web_ts_modified; then echo "Running TypeScript type-check:tsgo" - if ! pnpm run type-check:tsgo; then - echo "Type check failed. Please run 'pnpm run type-check:tsgo' to fix the errors." + if ! npm run type-check:tsgo; then + echo "Type check failed. Please run 'npm run type-check:tsgo' to fix the errors." exit 1 fi else @@ -90,8 +90,8 @@ if $web_modified; then fi echo "Running knip" - if ! pnpm run knip; then - echo "Knip check failed. Please run 'pnpm run knip' to fix the errors." + if ! npm run knip; then + echo "Knip check failed. Please run 'npm run knip' to fix the errors." exit 1 fi diff --git a/api/core/plugin/impl/plugin.py b/api/core/plugin/impl/plugin.py index ec4858ae2e..c75c30a98a 100644 --- a/api/core/plugin/impl/plugin.py +++ b/api/core/plugin/impl/plugin.py @@ -209,7 +209,10 @@ class PluginInstaller(BasePluginClient): "GET", f"plugin/{tenant_id}/management/decode/from_identifier", PluginDecodeResponse, - params={"plugin_unique_identifier": plugin_unique_identifier}, + params={ + "plugin_unique_identifier": plugin_unique_identifier, + "PluginUniqueIdentifier": plugin_unique_identifier, # compat with daemon <= 0.5.4 + }, ) def fetch_plugin_installation_by_ids( diff --git a/api/core/workflow/nodes/knowledge_index/entities.py b/api/core/workflow/nodes/knowledge_index/entities.py index 6ff162973c..f8e239d250 100644 --- a/api/core/workflow/nodes/knowledge_index/entities.py +++ b/api/core/workflow/nodes/knowledge_index/entities.py @@ -4,7 +4,7 @@ from graphon.entities.base_node_data import BaseNodeData from graphon.enums import NodeType from pydantic import BaseModel -from core.rag.entities import WeightedScoreConfig +from core.rag.entities.retrieval_settings import WeightedScoreConfig from core.rag.index_processor.index_processor_base import SummaryIndexSettingDict from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/index.spec.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/index.spec.tsx index b5f38cdd1b..62b867d155 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/index.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/index.spec.tsx @@ -120,7 +120,10 @@ describe('HITLInputBlock', () => { }) await waitFor(() => { - expect(onWorkflowMapUpdate).toHaveBeenCalledWith(workflowNodesMap) + expect(onWorkflowMapUpdate).toHaveBeenCalledWith({ + workflowNodesMap, + availableVariables: [], + }) }) }) }) diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/variable-block.spec.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/variable-block.spec.tsx index c848d08c5c..db3e474b60 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/variable-block.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/__tests__/variable-block.spec.tsx @@ -148,7 +148,10 @@ describe('HITLInputVariableBlockComponent', () => { editor!.update(() => { $getRoot().selectEnd() }) - handled = editor!.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, createWorkflowNodesMap()) + handled = editor!.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, { + workflowNodesMap: createWorkflowNodesMap(), + availableVariables: [], + }) }) expect(handled).toBe(true) diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx index cd1515c57d..0da99b9155 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx @@ -22,7 +22,7 @@ const HITLInputReplacementBlock = ({ onFormInputsChange, onFormInputItemRename, onFormInputItemRemove, - workflowNodesMap, + workflowNodesMap = {}, getVarType, variables, readonly, diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx index 2c10fdbd5a..1b2af39ebe 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx @@ -14,6 +14,7 @@ import { useEffect, } from 'react' import { CustomTextNode } from '../custom-text/node' +import { UPDATE_WORKFLOW_NODES_MAP as WORKFLOW_UPDATE_WORKFLOW_NODES_MAP } from '../workflow-variable-block' import { $createHITLInputNode, HITLInputNode, @@ -21,11 +22,13 @@ import { export const INSERT_HITL_INPUT_BLOCK_COMMAND = createCommand('INSERT_HITL_INPUT_BLOCK_COMMAND') export const DELETE_HITL_INPUT_BLOCK_COMMAND = createCommand('DELETE_HITL_INPUT_BLOCK_COMMAND') -export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP') +export const UPDATE_WORKFLOW_NODES_MAP = WORKFLOW_UPDATE_WORKFLOW_NODES_MAP + const HITLInputBlock = memo(({ onInsert, onDelete, - workflowNodesMap, + workflowNodesMap = {}, + variables: workflowAvailableVariables, getVarType, readonly, }: HITLInputBlockType) => { @@ -33,9 +36,12 @@ const HITLInputBlock = memo(({ useEffect(() => { editor.update(() => { - editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap) + editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, { + workflowNodesMap: workflowNodesMap || {}, + availableVariables: workflowAvailableVariables || [], + }) }) - }, [editor, workflowNodesMap]) + }, [editor, workflowNodesMap, workflowAvailableVariables]) useEffect(() => { if (!editor.hasNodes([HITLInputNode])) diff --git a/web/app/components/base/prompt-editor/plugins/hitl-input-block/variable-block.tsx b/web/app/components/base/prompt-editor/plugins/hitl-input-block/variable-block.tsx index b1374b994f..a466d64eff 100644 --- a/web/app/components/base/prompt-editor/plugins/hitl-input-block/variable-block.tsx +++ b/web/app/components/base/prompt-editor/plugins/hitl-input-block/variable-block.tsx @@ -1,3 +1,4 @@ +import type { UpdateWorkflowNodesMapPayload } from '../workflow-variable-block' import type { WorkflowNodesMap } from '../workflow-variable-block/node' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' @@ -98,9 +99,8 @@ const HITLInputVariableBlockComponent = ({ return mergeRegister( editor.registerCommand( UPDATE_WORKFLOW_NODES_MAP, - (workflowNodesMap: WorkflowNodesMap) => { - setLocalWorkflowNodesMap(workflowNodesMap) - + (payload: UpdateWorkflowNodesMapPayload) => { + setLocalWorkflowNodesMap(payload.workflowNodesMap) return true }, COMMAND_PRIORITY_EDITOR, diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/component.spec.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/component.spec.tsx index ff064f2a99..a6cb70ddb6 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/component.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/component.spec.tsx @@ -1,4 +1,5 @@ import type { LexicalEditor } from 'lexical' +import type { UpdateWorkflowNodesMapPayload } from '../index' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import { mergeRegister } from '@lexical/utils' @@ -216,7 +217,7 @@ describe('WorkflowVariableBlockComponent', () => { }) }) - it('should mark env variable invalid when not found in environmentVariables', () => { + it('should treat env variable as valid regardless of environmentVariables contents', () => { const environmentVariables: Var[] = [{ variable: 'env.valid_key', type: VarType.string }] render( @@ -229,7 +230,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: expect.any(String), + errorMsg: undefined, })) }) @@ -281,7 +282,7 @@ describe('WorkflowVariableBlockComponent', () => { })) }) - it('should evaluate env fallback selector tokens when classifier is forced', () => { + it('should mark forced env branch invalid when selector prefix is missing', () => { mockForcedVariableKind.value = 'env' const environmentVariables: Var[] = [{ variable: '.', type: VarType.string }] @@ -295,7 +296,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: undefined, + errorMsg: expect.any(String), })) }) @@ -330,7 +331,7 @@ describe('WorkflowVariableBlockComponent', () => { })) }) - it('should mark conversation variable invalid when not found in conversationVariables', () => { + it('should treat conversation variable as valid regardless of conversationVariables contents', () => { const conversationVariables: Var[] = [{ variable: 'conversation.other', type: VarType.string }] render( @@ -343,7 +344,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: expect.any(String), + errorMsg: undefined, })) }) @@ -364,7 +365,7 @@ describe('WorkflowVariableBlockComponent', () => { })) }) - it('should evaluate conversation fallback selector tokens when classifier is forced', () => { + it('should mark forced conversation branch invalid when selector prefix is missing', () => { mockForcedVariableKind.value = 'conversation' const conversationVariables: Var[] = [{ variable: '.', type: VarType.string }] @@ -378,7 +379,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: undefined, + errorMsg: expect.any(String), })) }) @@ -427,7 +428,7 @@ describe('WorkflowVariableBlockComponent', () => { })) }) - it('should mark rag variable invalid when not found in ragVariables', () => { + it('should treat rag variable as valid regardless of ragVariables contents', () => { const ragVariables: Var[] = [{ variable: 'rag.shared.other', type: VarType.string }] render( @@ -440,7 +441,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: expect.any(String), + errorMsg: undefined, })) }) @@ -461,7 +462,7 @@ describe('WorkflowVariableBlockComponent', () => { })) }) - it('should evaluate rag fallback selector tokens when classifier is forced', () => { + it('should mark forced rag branch invalid when selector prefix is missing', () => { mockForcedVariableKind.value = 'rag' const ragVariables: Var[] = [{ variable: '..', type: VarType.string }] @@ -475,7 +476,7 @@ describe('WorkflowVariableBlockComponent', () => { ) expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ - errorMsg: undefined, + errorMsg: expect.any(String), })) }) @@ -488,20 +489,81 @@ describe('WorkflowVariableBlockComponent', () => { />, ) - const updateHandler = mockRegisterCommand.mock.calls[0][1] as (map: Record) => boolean + const updateHandler = mockRegisterCommand.mock.calls[0][1] as (payload: UpdateWorkflowNodesMapPayload) => boolean let result = false act(() => { result = updateHandler({ - 'node-1': { - title: 'Updated', - type: BlockEnum.LLM, - width: 100, - height: 50, - position: { x: 0, y: 0 }, + workflowNodesMap: { + 'node-1': { + title: 'Updated', + type: BlockEnum.LLM, + width: 100, + height: 50, + position: { x: 0, y: 0 }, + }, }, + availableVariables: [], }) }) expect(result).toBe(true) }) + + it('should mark non-special variable invalid when source key is missing in availableVariables', () => { + render( + , + ) + + expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ + errorMsg: expect.any(String), + })) + }) + + it('should keep non-special variable valid when source key exists in availableVariables', () => { + render( + , + ) + + expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({ + errorMsg: undefined, + })) + }) }) diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/index.spec.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/index.spec.tsx index 1591dc44f9..00b5b66660 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/index.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/index.spec.tsx @@ -105,7 +105,10 @@ describe('WorkflowVariableBlock', () => { ) expect(mockUpdate).toHaveBeenCalled() - expect(mockDispatchCommand).toHaveBeenCalledWith(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap) + expect(mockDispatchCommand).toHaveBeenCalledWith(UPDATE_WORKFLOW_NODES_MAP, { + workflowNodesMap, + availableVariables: [], + }) }) it('should throw when WorkflowVariableBlockNode is not registered', () => { @@ -137,6 +140,7 @@ describe('WorkflowVariableBlock', () => { ['node-1', 'answer'], workflowNodesMap, getVarType, + [], ) expect($insertNodes).toHaveBeenCalledWith([{ id: 'workflow-node' }]) expect(onInsert).toHaveBeenCalledTimes(1) diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/node.spec.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/node.spec.tsx index 8d7a1cc33d..4154cd2fd9 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/node.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/node.spec.tsx @@ -1,5 +1,5 @@ import type { Klass, LexicalEditor, LexicalNode } from 'lexical' -import type { Var } from '@/app/components/workflow/types' +import type { NodeOutPutVar } from '@/app/components/workflow/types' import { createEditor } from 'lexical' import { Type } from '@/app/components/workflow/nodes/llm/types' import { BlockEnum, VarType } from '@/app/components/workflow/types' @@ -57,45 +57,43 @@ describe('WorkflowVariableBlockNode', () => { it('should decorate with component props from node state', () => { runInEditor(() => { const getVarType = vi.fn(() => Type.number) - const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }] - const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }] - const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }] + const availableVariables: NodeOutPutVar[] = [{ + nodeId: 'node-1', + title: 'Node A', + vars: [{ variable: 'answer', type: VarType.string }], + }] const node = new WorkflowVariableBlockNode( ['node-1', 'answer'], { 'node-1': { title: 'A', type: BlockEnum.LLM } }, getVarType, 'decorator-key', - environmentVariables, - conversationVariables, - ragVariables, + availableVariables, ) const decorated = node.decorate() expect(decorated.props.nodeKey).toBe('decorator-key') expect(decorated.props.variables).toEqual(['node-1', 'answer']) expect(decorated.props.workflowNodesMap).toEqual({ 'node-1': { title: 'A', type: BlockEnum.LLM } }) - expect(decorated.props.environmentVariables).toEqual(environmentVariables) - expect(decorated.props.conversationVariables).toEqual(conversationVariables) - expect(decorated.props.ragVariables).toEqual(ragVariables) + expect(decorated.props.availableVariables).toEqual(availableVariables) }) }) - it('should export and import json with full payload', () => { + it('should export and import json with available variables payload', () => { runInEditor(() => { const getVarType = vi.fn(() => Type.string) - const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }] - const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }] - const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }] + const availableVariables: NodeOutPutVar[] = [{ + nodeId: 'node-1', + title: 'Node A', + vars: [{ variable: 'answer', type: VarType.string }], + }] const node = new WorkflowVariableBlockNode( ['node-1', 'answer'], { 'node-1': { title: 'A', type: BlockEnum.LLM } }, getVarType, undefined, - environmentVariables, - conversationVariables, - ragVariables, + availableVariables, ) expect(node.exportJSON()).toEqual({ @@ -104,9 +102,7 @@ describe('WorkflowVariableBlockNode', () => { variables: ['node-1', 'answer'], workflowNodesMap: { 'node-1': { title: 'A', type: BlockEnum.LLM } }, getVarType, - environmentVariables, - conversationVariables, - ragVariables, + availableVariables, }) const imported = WorkflowVariableBlockNode.importJSON({ @@ -115,48 +111,51 @@ describe('WorkflowVariableBlockNode', () => { variables: ['node-2', 'result'], workflowNodesMap: { 'node-2': { title: 'B', type: BlockEnum.Tool } }, getVarType, - environmentVariables, - conversationVariables, - ragVariables, + availableVariables, }) expect(imported).toBeInstanceOf(WorkflowVariableBlockNode) expect(imported.getVariables()).toEqual(['node-2', 'result']) expect(imported.getWorkflowNodesMap()).toEqual({ 'node-2': { title: 'B', type: BlockEnum.Tool } }) + expect(imported.getAvailableVariables()).toEqual(availableVariables) }) }) it('should return getters and text content in expected format', () => { runInEditor(() => { const getVarType = vi.fn(() => Type.string) - const environmentVariables: Var[] = [{ variable: 'env.key', type: VarType.string }] - const conversationVariables: Var[] = [{ variable: 'conversation.topic', type: VarType.string }] - const ragVariables: Var[] = [{ variable: 'rag.shared.answer', type: VarType.string }] + const availableVariables: NodeOutPutVar[] = [{ + nodeId: 'node-1', + title: 'Node A', + vars: [{ variable: 'answer', type: VarType.string }], + }] const node = new WorkflowVariableBlockNode( ['node-1', 'answer'], { 'node-1': { title: 'A', type: BlockEnum.LLM } }, getVarType, undefined, - environmentVariables, - conversationVariables, - ragVariables, + availableVariables, ) expect(node.getVariables()).toEqual(['node-1', 'answer']) expect(node.getWorkflowNodesMap()).toEqual({ 'node-1': { title: 'A', type: BlockEnum.LLM } }) expect(node.getVarType()).toBe(getVarType) - expect(node.getEnvironmentVariables()).toEqual(environmentVariables) - expect(node.getConversationVariables()).toEqual(conversationVariables) - expect(node.getRagVariables()).toEqual(ragVariables) + expect(node.getAvailableVariables()).toEqual(availableVariables) expect(node.getTextContent()).toBe('{{#node-1.answer#}}') }) }) it('should create node helper and type guard checks', () => { runInEditor(() => { - const node = $createWorkflowVariableBlockNode(['node-1', 'answer'], {}, undefined) + const availableVariables: NodeOutPutVar[] = [{ + nodeId: 'node-1', + title: 'Node A', + vars: [{ variable: 'answer', type: VarType.string }], + }] + const node = $createWorkflowVariableBlockNode(['node-1', 'answer'], {}, undefined, availableVariables) expect(node).toBeInstanceOf(WorkflowVariableBlockNode) + expect(node.getAvailableVariables()).toEqual(availableVariables) expect($isWorkflowVariableBlockNode(node)).toBe(true) expect($isWorkflowVariableBlockNode(null)).toBe(false) expect($isWorkflowVariableBlockNode(undefined)).toBe(false) diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/workflow-variable-block-replacement-block.spec.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/workflow-variable-block-replacement-block.spec.tsx index b9cb1faa37..9dcc37ec35 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/workflow-variable-block-replacement-block.spec.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/__tests__/workflow-variable-block-replacement-block.spec.tsx @@ -183,12 +183,7 @@ describe('WorkflowVariableBlockReplacementBlock', () => { ['node-1', 'output'], workflowNodesMap, getVarType, - variables[0].vars, - variables[1].vars, - [ - { variable: 'ragVarA', type: VarType.string, isRagVariable: true }, - { variable: 'rag.shared.answer', type: VarType.string, isRagVariable: true }, - ], + variables, ) expect($applyNodeReplacement).toHaveBeenCalledWith({ type: 'workflow-node' }) expect(created).toEqual({ type: 'workflow-node' }) @@ -214,8 +209,6 @@ describe('WorkflowVariableBlockReplacementBlock', () => { workflowNodesMap, undefined, [], - [], - undefined, ) }) }) diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx index 2b46d1a378..bf91d25834 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx @@ -1,5 +1,8 @@ +import type { + UpdateWorkflowNodesMapPayload, +} from './index' import type { WorkflowNodesMap } from './node' -import type { ValueSelector, Var } from '@/app/components/workflow/types' +import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' import { mergeRegister } from '@lexical/utils' import { @@ -15,7 +18,7 @@ import { import { useTranslation } from 'react-i18next' import { useReactFlow, useStoreApi } from 'reactflow' import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' -import { isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { isRagVariableVar, isSpecialVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel' import { VariableLabelInEditor, @@ -34,6 +37,7 @@ type WorkflowVariableBlockComponentProps = { nodeKey: string variables: string[] workflowNodesMap: WorkflowNodesMap + availableVariables?: NodeOutPutVar[] environmentVariables?: Var[] conversationVariables?: Var[] ragVariables?: Var[] @@ -47,10 +51,8 @@ const WorkflowVariableBlockComponent = ({ nodeKey, variables, workflowNodesMap = {}, + availableVariables, getVarType, - environmentVariables, - conversationVariables, - ragVariables, }: WorkflowVariableBlockComponentProps) => { const { t } = useTranslation() const [editor] = useLexicalComposerContext() @@ -66,36 +68,25 @@ const WorkflowVariableBlockComponent = ({ } )() const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState(workflowNodesMap) + const [localAvailableVariables, setLocalAvailableVariables] = useState(availableVariables || []) const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]] const isException = isExceptionVariable(varName, node?.type) const sourceNodeId = variables[isRagVar ? 1 : 0] const isLlmModelInstalled = useLlmModelPluginInstalled(sourceNodeId, localWorkflowNodesMap) const variableValid = useMemo(() => { - let variableValid = true - const isEnv = isENV(variables) - const isChatVar = isConversationVar(variables) - const isGlobal = isGlobalVar(variables) - if (isGlobal) + if (isSpecialVar(variables[0] ?? '')) return true - if (isEnv) { - if (environmentVariables) - variableValid = environmentVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) - } - else if (isChatVar) { - if (conversationVariables) - variableValid = conversationVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}`) - } - else if (isRagVar) { - if (ragVariables) - variableValid = ragVariables.some(v => v.variable === `${variables?.[0] ?? ''}.${variables?.[1] ?? ''}.${variables?.[2] ?? ''}`) - } - else { - variableValid = !!node - } - return variableValid - }, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables]) + if (!variables[1]) + return false + + const sourceNode = localAvailableVariables.find(v => v.nodeId === variables[0]) + if (!sourceNode) + return false + + return sourceNode.vars.some(v => v.variable === variables[1]) + }, [localAvailableVariables, variables]) const reactflow = useReactFlow() const store = useStoreApi() @@ -107,9 +98,9 @@ const WorkflowVariableBlockComponent = ({ return mergeRegister( editor.registerCommand( UPDATE_WORKFLOW_NODES_MAP, - (workflowNodesMap: WorkflowNodesMap) => { - setLocalWorkflowNodesMap(workflowNodesMap) - + (payload: UpdateWorkflowNodesMapPayload) => { + setLocalWorkflowNodesMap(payload.workflowNodesMap) + setLocalAvailableVariables(payload.availableVariables) return true }, COMMAND_PRIORITY_EDITOR, diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx index dfbd238dbf..ab79630f80 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx @@ -17,9 +17,14 @@ import { export const INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND') export const DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND = createCommand('DELETE_WORKFLOW_VARIABLE_BLOCK_COMMAND') -export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP') +export type UpdateWorkflowNodesMapPayload = { + workflowNodesMap: NonNullable + availableVariables: NonNullable +} +export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP') const WorkflowVariableBlock = memo(({ - workflowNodesMap, + workflowNodesMap = {}, + variables: workflowAvailableVariables, onInsert, onDelete, getVarType, @@ -28,9 +33,12 @@ const WorkflowVariableBlock = memo(({ useEffect(() => { editor.update(() => { - editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap) + editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, { + workflowNodesMap: workflowNodesMap || {}, + availableVariables: workflowAvailableVariables || [], + }) }) - }, [editor, workflowNodesMap]) + }, [editor, workflowNodesMap, workflowAvailableVariables]) useEffect(() => { if (!editor.hasNodes([WorkflowVariableBlockNode])) @@ -40,7 +48,12 @@ const WorkflowVariableBlock = memo(({ editor.registerCommand( INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND, (variables: string[]) => { - const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType) + const workflowVariableBlockNode = $createWorkflowVariableBlockNode( + variables, + workflowNodesMap, + getVarType, + workflowAvailableVariables || [], + ) $insertNodes([workflowVariableBlockNode]) if (onInsert) @@ -61,7 +74,7 @@ const WorkflowVariableBlock = memo(({ COMMAND_PRIORITY_EDITOR, ), ) - }, [editor, onInsert, onDelete, workflowNodesMap, getVarType]) + }, [editor, onInsert, onDelete, workflowNodesMap, getVarType, workflowAvailableVariables]) return null }) diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx index 743937d8a6..2d13627b20 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx @@ -1,49 +1,55 @@ import type { LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical' import type { GetVarType, WorkflowVariableBlockType } from '../../types' -import type { Var } from '@/app/components/workflow/types' +import type { NodeOutPutVar } from '@/app/components/workflow/types' import { DecoratorNode } from 'lexical' import WorkflowVariableBlockComponent from './component' -export type WorkflowNodesMap = WorkflowVariableBlockType['workflowNodesMap'] +export type WorkflowNodesMap = NonNullable type SerializedNode = SerializedLexicalNode & { variables: string[] workflowNodesMap: WorkflowNodesMap getVarType?: GetVarType - environmentVariables?: Var[] - conversationVariables?: Var[] - ragVariables?: Var[] + availableVariables?: NodeOutPutVar[] } export class WorkflowVariableBlockNode extends DecoratorNode { __variables: string[] __workflowNodesMap: WorkflowNodesMap __getVarType?: GetVarType - __environmentVariables?: Var[] - __conversationVariables?: Var[] - __ragVariables?: Var[] + __availableVariables?: NodeOutPutVar[] static getType(): string { return 'workflow-variable-block' } static clone(node: WorkflowVariableBlockNode): WorkflowVariableBlockNode { - return new WorkflowVariableBlockNode(node.__variables, node.__workflowNodesMap, node.__getVarType, node.__key, node.__environmentVariables, node.__conversationVariables, node.__ragVariables) + return new WorkflowVariableBlockNode( + node.__variables, + node.__workflowNodesMap, + node.__getVarType, + node.__key, + node.__availableVariables, + ) } isInline(): boolean { return true } - constructor(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType: any, key?: NodeKey, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]) { + constructor( + variables: string[], + workflowNodesMap: WorkflowNodesMap, + getVarType: any, + key?: NodeKey, + availableVariables?: NodeOutPutVar[], + ) { super(key) this.__variables = variables this.__workflowNodesMap = workflowNodesMap this.__getVarType = getVarType - this.__environmentVariables = environmentVariables - this.__conversationVariables = conversationVariables - this.__ragVariables = ragVariables + this.__availableVariables = availableVariables } createDOM(): HTMLElement { @@ -63,30 +69,34 @@ export class WorkflowVariableBlockNode extends DecoratorNode variables={this.__variables} workflowNodesMap={this.__workflowNodesMap} getVarType={this.__getVarType!} - environmentVariables={this.__environmentVariables} - conversationVariables={this.__conversationVariables} - ragVariables={this.__ragVariables} + availableVariables={this.__availableVariables} /> ) } static importJSON(serializedNode: SerializedNode): WorkflowVariableBlockNode { - const node = $createWorkflowVariableBlockNode(serializedNode.variables, serializedNode.workflowNodesMap, serializedNode.getVarType, serializedNode.environmentVariables, serializedNode.conversationVariables, serializedNode.ragVariables) + const node = $createWorkflowVariableBlockNode( + serializedNode.variables, + serializedNode.workflowNodesMap, + serializedNode.getVarType, + serializedNode.availableVariables, + ) return node } exportJSON(): SerializedNode { - return { + const json: SerializedNode = { type: 'workflow-variable-block', version: 1, variables: this.getVariables(), workflowNodesMap: this.getWorkflowNodesMap(), getVarType: this.getVarType(), - environmentVariables: this.getEnvironmentVariables(), - conversationVariables: this.getConversationVariables(), - ragVariables: this.getRagVariables(), } + if (this.getAvailableVariables()) + json.availableVariables = this.getAvailableVariables() + + return json } getVariables(): string[] { @@ -104,27 +114,28 @@ export class WorkflowVariableBlockNode extends DecoratorNode return self.__getVarType } - getEnvironmentVariables(): any { + getAvailableVariables(): NodeOutPutVar[] | undefined { const self = this.getLatest() - return self.__environmentVariables - } - - getConversationVariables(): any { - const self = this.getLatest() - return self.__conversationVariables - } - - getRagVariables(): any { - const self = this.getLatest() - return self.__ragVariables + return self.__availableVariables } getTextContent(): string { return `{{#${this.getVariables().join('.')}#}}` } } -export function $createWorkflowVariableBlockNode(variables: string[], workflowNodesMap: WorkflowNodesMap, getVarType?: GetVarType, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[]): WorkflowVariableBlockNode { - return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables) +export function $createWorkflowVariableBlockNode( + variables: string[], + workflowNodesMap: WorkflowNodesMap, + getVarType?: GetVarType, + availableVariables?: NodeOutPutVar[], +): WorkflowVariableBlockNode { + return new WorkflowVariableBlockNode( + variables, + workflowNodesMap, + getVarType, + undefined, + availableVariables, + ) } export function $isWorkflowVariableBlockNode( diff --git a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx index 573c97f465..e3c947d786 100644 --- a/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx +++ b/web/app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx @@ -15,19 +15,12 @@ import { WorkflowVariableBlockNode } from './index' import { $createWorkflowVariableBlockNode } from './node' const WorkflowVariableBlockReplacementBlock = ({ - workflowNodesMap, + workflowNodesMap = {}, getVarType, onInsert, variables, }: WorkflowVariableBlockType) => { const [editor] = useLexicalComposerContext() - const ragVariables = variables?.reduce((acc, curr) => { - if (curr.nodeId === 'rag') - acc.push(...curr.vars) - else - acc.push(...curr.vars.filter(v => v.isRagVariable)) - return acc - }, []) useEffect(() => { if (!editor.hasNodes([WorkflowVariableBlockNode])) @@ -39,8 +32,13 @@ const WorkflowVariableBlockReplacementBlock = ({ onInsert() const nodePathString = textNode.getTextContent().slice(3, -3) - return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables?.find(o => o.nodeId === 'env')?.vars || [], variables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables)) - }, [onInsert, workflowNodesMap, getVarType, variables, ragVariables]) + return $applyNodeReplacement($createWorkflowVariableBlockNode( + nodePathString.split('.'), + workflowNodesMap, + getVarType, + variables || [], + )) + }, [onInsert, workflowNodesMap, getVarType, variables]) const getMatch = useCallback((text: string) => { const matchArr = REGEX.exec(text) diff --git a/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx index 60b9d65260..490f981268 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx @@ -22,6 +22,9 @@ import Panel from '../panel' import { ParamType, ReasoningModeType } from '../types' import useConfig from '../use-config' +const reasoningModeFunctionToolCallingLabel = 'workflow.nodes.parameterExtractor.reasoningModeFunctionToolCalling' +const reasoningModePromptLabel = 'workflow.nodes.parameterExtractor.reasoningModePrompt' + type MockToolCollection = { id: string tools: Array<{ @@ -735,8 +738,8 @@ describe('parameter-extractor path', () => { />, ) - await user.click(screen.getByRole('button', { name: 'Function/Tool Calling' })) - await user.click(screen.getByRole('button', { name: 'Prompt' })) + await user.click(screen.getByRole('button', { name: reasoningModeFunctionToolCallingLabel })) + await user.click(screen.getByRole('button', { name: reasoningModePromptLabel })) expect(onChange).toHaveBeenNthCalledWith(1, ReasoningModeType.functionCall) expect(onChange).toHaveBeenNthCalledWith(2, ReasoningModeType.prompt) @@ -826,7 +829,7 @@ describe('parameter-extractor path', () => { target: { value: 'Extract city, budget, and due date' }, }) await user.click(screen.getByRole('button', { name: 'memory-config' })) - await user.click(screen.getByRole('button', { name: 'Function/Tool Calling' })) + await user.click(screen.getByRole('button', { name: reasoningModeFunctionToolCallingLabel })) expect(handleModelChanged).toHaveBeenCalledWith({ provider: 'anthropic', diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx index 68d050ff67..c4b8809ffe 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx @@ -33,12 +33,12 @@ const ReasoningModePicker: FC = ({ >
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 9828aa5858..722eb8a7e4 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -3385,7 +3385,7 @@ "count": 3 }, "react-refresh/only-export-components": { - "count": 3 + "count": 2 } }, "app/components/base/prompt-editor/plugins/hitl-input-block/input-field.tsx": { @@ -3485,12 +3485,7 @@ }, "app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx": { "ts/no-explicit-any": { - "count": 5 - } - }, - "app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx": { - "ts/no-explicit-any": { - "count": 1 + "count": 2 } }, "app/components/base/prompt-log-modal/card.tsx": { diff --git a/web/i18n/ar-TN/common.json b/web/i18n/ar-TN/common.json index 2d81e44a71..0ecaf44739 100644 --- a/web/i18n/ar-TN/common.json +++ b/web/i18n/ar-TN/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "الصفحات المصرح بها", "dataSource.notion.remove": "إزالة", "dataSource.notion.selector.addPages": "إضافة صفحات", + "dataSource.notion.selector.configure": "تكوين Notion", + "dataSource.notion.selector.docs": "وثائق Notion", + "dataSource.notion.selector.headerTitle": "اختر صفحات Notion", "dataSource.notion.selector.noSearchResult": "لا توجد نتائج بحث", "dataSource.notion.selector.pageSelected": "الصفحات المحددة", "dataSource.notion.selector.preview": "معاينة", diff --git a/web/i18n/ar-TN/workflow.json b/web/i18n/ar-TN/workflow.json index 9396649c69..e0e498e8d4 100644 --- a/web/i18n/ar-TN/workflow.json +++ b/web/i18n/ar-TN/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "هو نجاح. عند النجاح تكون القيمة 1، عند الفشل تكون القيمة 0.", "nodes.parameterExtractor.outputVars.usage": "معلومات استخدام النموذج", "nodes.parameterExtractor.reasoningMode": "وضع التفكير", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "استدعاء دالة/أداة", + "nodes.parameterExtractor.reasoningModePrompt": "موجّه", "nodes.parameterExtractor.reasoningModeTip": "يمكنك اختيار وضع التفكير المناسب بناءً على قدرة النموذج على الاستجابة للتعليمات لاستدعاء الوظيفة أو المطالبات.", "nodes.questionClassifiers.addClass": "إضافة فئة", "nodes.questionClassifiers.advancedSetting": "إعدادات متقدمة", diff --git a/web/i18n/de-DE/common.json b/web/i18n/de-DE/common.json index d33c1bcba1..5b15b78d76 100644 --- a/web/i18n/de-DE/common.json +++ b/web/i18n/de-DE/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Autorisierte Seiten", "dataSource.notion.remove": "Entfernen", "dataSource.notion.selector.addPages": "Seiten hinzufügen", + "dataSource.notion.selector.configure": "Notion konfigurieren", + "dataSource.notion.selector.docs": "Notion-Dokumentation", + "dataSource.notion.selector.headerTitle": "Notion-Seiten auswählen", "dataSource.notion.selector.noSearchResult": "Keine Suchergebnisse", "dataSource.notion.selector.pageSelected": "Ausgewählte Seiten", "dataSource.notion.selector.preview": "VORSCHAU", diff --git a/web/i18n/de-DE/workflow.json b/web/i18n/de-DE/workflow.json index 6648450686..7a1ddeeb1c 100644 --- a/web/i18n/de-DE/workflow.json +++ b/web/i18n/de-DE/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Ist Erfolg. Bei Erfolg beträgt der Wert 1, bei Misserfolg beträgt der Wert 0.", "nodes.parameterExtractor.outputVars.usage": "Nutzungsinformationen des Modells", "nodes.parameterExtractor.reasoningMode": "Schlussfolgerungsmodus", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Funktion/Tool-Aufruf", + "nodes.parameterExtractor.reasoningModePrompt": "Eingabeaufforderung", "nodes.parameterExtractor.reasoningModeTip": "Sie können den entsprechenden Schlussfolgerungsmodus basierend auf der Fähigkeit des Modells wählen, auf Anweisungen zur Funktionsaufruf- oder Eingabeaufforderungen zu reagieren.", "nodes.questionClassifiers.addClass": "Klasse hinzufügen", "nodes.questionClassifiers.advancedSetting": "Erweiterte Einstellung", diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index 5c7df02791..6f6e5de2cc 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Is Success.On success the value is 1, on failure the value is 0.", "nodes.parameterExtractor.outputVars.usage": "Model Usage Information", "nodes.parameterExtractor.reasoningMode": "Reasoning Mode", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Function/Tool Calling", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "You can choose the appropriate reasoning mode based on the model's ability to respond to instructions for function calling or prompts.", "nodes.questionClassifiers.addClass": "Add Class", "nodes.questionClassifiers.advancedSetting": "Advanced Setting", diff --git a/web/i18n/es-ES/common.json b/web/i18n/es-ES/common.json index 38f9d10396..135c7a5af4 100644 --- a/web/i18n/es-ES/common.json +++ b/web/i18n/es-ES/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Páginas autorizadas", "dataSource.notion.remove": "Eliminar", "dataSource.notion.selector.addPages": "Agregar páginas", + "dataSource.notion.selector.configure": "Configurar Notion", + "dataSource.notion.selector.docs": "Documentación de Notion", + "dataSource.notion.selector.headerTitle": "Elegir páginas de Notion", "dataSource.notion.selector.noSearchResult": "No hay resultados de búsqueda", "dataSource.notion.selector.pageSelected": "Páginas seleccionadas", "dataSource.notion.selector.preview": "VISTA PREVIA", diff --git a/web/i18n/es-ES/workflow.json b/web/i18n/es-ES/workflow.json index d23dd40a16..1955357c45 100644 --- a/web/i18n/es-ES/workflow.json +++ b/web/i18n/es-ES/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Es éxito. En caso de éxito el valor es 1, en caso de fallo el valor es 0.", "nodes.parameterExtractor.outputVars.usage": "Información de uso del modelo", "nodes.parameterExtractor.reasoningMode": "Modo de razonamiento", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Función/Llamada de herramienta", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Puede elegir el modo de razonamiento apropiado basado en la capacidad del modelo para responder a instrucciones para llamadas de funciones o indicaciones.", "nodes.questionClassifiers.addClass": "Agregar clase", "nodes.questionClassifiers.advancedSetting": "Configuración avanzada", diff --git a/web/i18n/fa-IR/common.json b/web/i18n/fa-IR/common.json index 686a6a5997..bbbe719a3d 100644 --- a/web/i18n/fa-IR/common.json +++ b/web/i18n/fa-IR/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "صفحات مجاز", "dataSource.notion.remove": "حذف", "dataSource.notion.selector.addPages": "افزودن صفحات", + "dataSource.notion.selector.configure": "پیکربندی Notion", + "dataSource.notion.selector.docs": "مستندات Notion", + "dataSource.notion.selector.headerTitle": "صفحات Notion را انتخاب کنید", "dataSource.notion.selector.noSearchResult": "نتیجه جستجویی یافت نشد", "dataSource.notion.selector.pageSelected": "صفحات انتخاب شده", "dataSource.notion.selector.preview": "پیش‌نمایش", diff --git a/web/i18n/fa-IR/workflow.json b/web/i18n/fa-IR/workflow.json index 1b1bf59d94..e957d45267 100644 --- a/web/i18n/fa-IR/workflow.json +++ b/web/i18n/fa-IR/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "موفقیت (۱=موفق، ۰=ناموفق)", "nodes.parameterExtractor.outputVars.usage": "اطلاعات مصرف مدل", "nodes.parameterExtractor.reasoningMode": "حالت استدلال", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "فراخوانی تابع/ابزار", + "nodes.parameterExtractor.reasoningModePrompt": "پیام", "nodes.parameterExtractor.reasoningModeTip": "حالت استدلال مناسب را بر اساس توانایی مدل (Function Call یا Prompt) انتخاب کنید.", "nodes.questionClassifiers.addClass": "افزودن کلاس", "nodes.questionClassifiers.advancedSetting": "تنظیمات پیشرفته", diff --git a/web/i18n/fr-FR/common.json b/web/i18n/fr-FR/common.json index a2bc856bf7..68f21b4304 100644 --- a/web/i18n/fr-FR/common.json +++ b/web/i18n/fr-FR/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Pages autorisées", "dataSource.notion.remove": "Supprimer", "dataSource.notion.selector.addPages": "Ajouter des pages", + "dataSource.notion.selector.configure": "Configurer Notion", + "dataSource.notion.selector.docs": "Documentation Notion", + "dataSource.notion.selector.headerTitle": "Choisir des pages Notion", "dataSource.notion.selector.noSearchResult": "Aucun résultat de recherche", "dataSource.notion.selector.pageSelected": "Pages Sélectionnées", "dataSource.notion.selector.preview": "APERÇU", diff --git a/web/i18n/fr-FR/workflow.json b/web/i18n/fr-FR/workflow.json index c172dbf41d..ec23ac5c98 100644 --- a/web/i18n/fr-FR/workflow.json +++ b/web/i18n/fr-FR/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Est réussi. En cas de succès, la valeur est 1, en cas d'échec, la valeur est 0.", "nodes.parameterExtractor.outputVars.usage": "Informations sur l'utilisation du modèle", "nodes.parameterExtractor.reasoningMode": "Mode de raisonnement", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Fonction/Appel d'outil", + "nodes.parameterExtractor.reasoningModePrompt": "Invite", "nodes.parameterExtractor.reasoningModeTip": "Vous pouvez choisir le mode de raisonnement approprié en fonction de la capacité du modèle à répondre aux instructions pour les appels de fonction ou les invites.", "nodes.questionClassifiers.addClass": "Ajouter une classe", "nodes.questionClassifiers.advancedSetting": "Paramètre avancé", diff --git a/web/i18n/hi-IN/common.json b/web/i18n/hi-IN/common.json index bbdd619b12..8f64de29b4 100644 --- a/web/i18n/hi-IN/common.json +++ b/web/i18n/hi-IN/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "अधिकृत पृष्ठ", "dataSource.notion.remove": "हटाएं", "dataSource.notion.selector.addPages": "पृष्ठ जोड़ें", + "dataSource.notion.selector.configure": "Notion कॉन्फ़िगर करें", + "dataSource.notion.selector.docs": "Notion दस्तावेज़", + "dataSource.notion.selector.headerTitle": "Notion पृष्ठ चुनें", "dataSource.notion.selector.noSearchResult": "कोई खोज परिणाम नहीं", "dataSource.notion.selector.pageSelected": "चयनित पृष्ठ", "dataSource.notion.selector.preview": "पूर्वावलोकन", diff --git a/web/i18n/hi-IN/workflow.json b/web/i18n/hi-IN/workflow.json index 2c14a31f55..f18de6aa3c 100644 --- a/web/i18n/hi-IN/workflow.json +++ b/web/i18n/hi-IN/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "सफलता है। सफलता पर मान 1 है, असफलता पर मान 0 है।", "nodes.parameterExtractor.outputVars.usage": "मॉडल उपयोग जानकारी", "nodes.parameterExtractor.reasoningMode": "रीज़निंग मोड", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "फ़ंक्शन/टूल कॉलिंग", + "nodes.parameterExtractor.reasoningModePrompt": "प्रॉम्प्ट", "nodes.parameterExtractor.reasoningModeTip": "फ़ंक्शन कॉलिंग या प्रॉम्प्ट्स के लिए निर्देशों का जवाब देने की मॉडल की क्षमता के आधार पर उपयुक्त रीज़निंग मोड चुन सकते हैं।", "nodes.questionClassifiers.addClass": "क्लास जोड़ें", "nodes.questionClassifiers.advancedSetting": "उन्नत सेटिंग", diff --git a/web/i18n/id-ID/common.json b/web/i18n/id-ID/common.json index 81245aec7b..2a2101f6e8 100644 --- a/web/i18n/id-ID/common.json +++ b/web/i18n/id-ID/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Halaman yang disahkan", "dataSource.notion.remove": "Hapus", "dataSource.notion.selector.addPages": "Tambahkan halaman", + "dataSource.notion.selector.configure": "Konfigurasi Notion", + "dataSource.notion.selector.docs": "Dokumentasi Notion", + "dataSource.notion.selector.headerTitle": "Pilih halaman Notion", "dataSource.notion.selector.noSearchResult": "Tidak ada hasil pencarian", "dataSource.notion.selector.pageSelected": "Halaman yang Dipilih", "dataSource.notion.selector.preview": "PRATAYANG", diff --git a/web/i18n/id-ID/workflow.json b/web/i18n/id-ID/workflow.json index 87d0415793..5030489cb1 100644 --- a/web/i18n/id-ID/workflow.json +++ b/web/i18n/id-ID/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Apakah Success.Pada keberhasilan nilainya adalah 1, pada kegagalan nilainya adalah 0.", "nodes.parameterExtractor.outputVars.usage": "Informasi Penggunaan Model", "nodes.parameterExtractor.reasoningMode": "Mode Penalaran", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Fungsi/Pemanggilan Alat", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Anda dapat memilih mode penalaran yang sesuai berdasarkan kemampuan model untuk menanggapi instruksi untuk pemanggilan fungsi atau perintah.", "nodes.questionClassifiers.addClass": "Tambahkan Kelas", "nodes.questionClassifiers.advancedSetting": "Pengaturan Lanjutan", diff --git a/web/i18n/it-IT/common.json b/web/i18n/it-IT/common.json index b389ed09ef..e2cb004c2a 100644 --- a/web/i18n/it-IT/common.json +++ b/web/i18n/it-IT/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Pagine autorizzate", "dataSource.notion.remove": "Rimuovi", "dataSource.notion.selector.addPages": "Aggiungi pagine", + "dataSource.notion.selector.configure": "Configura Notion", + "dataSource.notion.selector.docs": "Documentazione Notion", + "dataSource.notion.selector.headerTitle": "Scegli le pagine Notion", "dataSource.notion.selector.noSearchResult": "Nessun risultato di ricerca", "dataSource.notion.selector.pageSelected": "Pagine selezionate", "dataSource.notion.selector.preview": "ANTEPRIMA", diff --git a/web/i18n/it-IT/workflow.json b/web/i18n/it-IT/workflow.json index d9e802c2b6..a39e00c5d8 100644 --- a/web/i18n/it-IT/workflow.json +++ b/web/i18n/it-IT/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "È successo. In caso di successo il valore è 1, in caso di fallimento il valore è 0.", "nodes.parameterExtractor.outputVars.usage": "Informazioni sull'utilizzo del modello", "nodes.parameterExtractor.reasoningMode": "Modalità di ragionamento", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Funzione/Chiamata strumento", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Puoi scegliere la modalità di ragionamento appropriata in base alla capacità del modello di rispondere alle istruzioni per la chiamata delle funzioni o i prompt.", "nodes.questionClassifiers.addClass": "Aggiungi Classe", "nodes.questionClassifiers.advancedSetting": "Impostazione Avanzata", diff --git a/web/i18n/ja-JP/common.json b/web/i18n/ja-JP/common.json index 7b2e34e757..9c27e43d6e 100644 --- a/web/i18n/ja-JP/common.json +++ b/web/i18n/ja-JP/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "認証済みページ", "dataSource.notion.remove": "削除", "dataSource.notion.selector.addPages": "ページの追加", + "dataSource.notion.selector.configure": "Notionを設定する", + "dataSource.notion.selector.docs": "Notionドキュメント", + "dataSource.notion.selector.headerTitle": "Notionページを選択", "dataSource.notion.selector.noSearchResult": "検索結果なし", "dataSource.notion.selector.pageSelected": "選択済みページ", "dataSource.notion.selector.preview": "プレビュー", diff --git a/web/i18n/ja-JP/workflow.json b/web/i18n/ja-JP/workflow.json index 0242249d30..089468053a 100644 --- a/web/i18n/ja-JP/workflow.json +++ b/web/i18n/ja-JP/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "成功。成功した場合の値は 1、失敗した場合の値は 0 です。", "nodes.parameterExtractor.outputVars.usage": "モデル使用量", "nodes.parameterExtractor.reasoningMode": "推論モード", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "関数/ツール呼び出し", + "nodes.parameterExtractor.reasoningModePrompt": "プロンプト", "nodes.parameterExtractor.reasoningModeTip": "関数呼び出しやプロンプトの指示に応答するモデルの能力に基づいて、適切な推論モードを選択できます。", "nodes.questionClassifiers.addClass": "クラスを追加", "nodes.questionClassifiers.advancedSetting": "高度な設定", diff --git a/web/i18n/ko-KR/common.json b/web/i18n/ko-KR/common.json index bdd08a7715..d0ae0be2ee 100644 --- a/web/i18n/ko-KR/common.json +++ b/web/i18n/ko-KR/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "페이지가 허가됨", "dataSource.notion.remove": "제거하기", "dataSource.notion.selector.addPages": "페이지 추가하기", + "dataSource.notion.selector.configure": "Notion 구성", + "dataSource.notion.selector.docs": "Notion 문서", + "dataSource.notion.selector.headerTitle": "Notion 페이지 선택", "dataSource.notion.selector.noSearchResult": "검색 결과 없음", "dataSource.notion.selector.pageSelected": "페이지 선택됨", "dataSource.notion.selector.preview": "미리보기", diff --git a/web/i18n/ko-KR/workflow.json b/web/i18n/ko-KR/workflow.json index 2709fa1917..ab602c391c 100644 --- a/web/i18n/ko-KR/workflow.json +++ b/web/i18n/ko-KR/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "성공 여부. 성공 시 값은 1 이고, 실패 시 값은 0 입니다.", "nodes.parameterExtractor.outputVars.usage": "모델 사용 정보", "nodes.parameterExtractor.reasoningMode": "추론 모드", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "함수/도구 호출", + "nodes.parameterExtractor.reasoningModePrompt": "프롬프트", "nodes.parameterExtractor.reasoningModeTip": "모델의 함수 호출 또는 프롬프트에 대한 지시 응답 능력을 기반으로 적절한 추론 모드를 선택할 수 있습니다.", "nodes.questionClassifiers.addClass": "클래스 추가", "nodes.questionClassifiers.advancedSetting": "고급 설정", diff --git a/web/i18n/nl-NL/common.json b/web/i18n/nl-NL/common.json index 592b85dc63..18261ae545 100644 --- a/web/i18n/nl-NL/common.json +++ b/web/i18n/nl-NL/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Pages authorized", "dataSource.notion.remove": "Remove", "dataSource.notion.selector.addPages": "Add pages", + "dataSource.notion.selector.configure": "Notion configureren", + "dataSource.notion.selector.docs": "Notion-documentatie", + "dataSource.notion.selector.headerTitle": "Notion-pagina's kiezen", "dataSource.notion.selector.noSearchResult": "No search results", "dataSource.notion.selector.pageSelected": "Pages Selected", "dataSource.notion.selector.preview": "PREVIEW", diff --git a/web/i18n/nl-NL/workflow.json b/web/i18n/nl-NL/workflow.json index eb18daf9f7..b706c42962 100644 --- a/web/i18n/nl-NL/workflow.json +++ b/web/i18n/nl-NL/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Is Success.On success the value is 1, on failure the value is 0.", "nodes.parameterExtractor.outputVars.usage": "Model Usage Information", "nodes.parameterExtractor.reasoningMode": "Reasoning Mode", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Functie/Tool-aanroep", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "You can choose the appropriate reasoning mode based on the model's ability to respond to instructions for function calling or prompts.", "nodes.questionClassifiers.addClass": "Add Class", "nodes.questionClassifiers.advancedSetting": "Advanced Setting", diff --git a/web/i18n/pl-PL/common.json b/web/i18n/pl-PL/common.json index 5e693dd2f5..cf7413cf51 100644 --- a/web/i18n/pl-PL/common.json +++ b/web/i18n/pl-PL/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Strony autoryzowane", "dataSource.notion.remove": "Usuń", "dataSource.notion.selector.addPages": "Dodaj strony", + "dataSource.notion.selector.configure": "Skonfiguruj Notion", + "dataSource.notion.selector.docs": "Dokumentacja Notion", + "dataSource.notion.selector.headerTitle": "Wybierz strony Notion", "dataSource.notion.selector.noSearchResult": "Brak wyników wyszukiwania", "dataSource.notion.selector.pageSelected": "Zaznaczone strony", "dataSource.notion.selector.preview": "PODGLĄD", diff --git a/web/i18n/pl-PL/workflow.json b/web/i18n/pl-PL/workflow.json index adb639f295..1f54fe6437 100644 --- a/web/i18n/pl-PL/workflow.json +++ b/web/i18n/pl-PL/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Czy się udało. W przypadku sukcesu wartość wynosi 1, w przypadku niepowodzenia wartość wynosi 0.", "nodes.parameterExtractor.outputVars.usage": "Informacje o użyciu modelu", "nodes.parameterExtractor.reasoningMode": "Tryb wnioskowania", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Funkcja/Wywołanie narzędzia", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Możesz wybrać odpowiedni tryb wnioskowania w zależności od zdolności modelu do reagowania na instrukcje dotyczące wywoływania funkcji lub zapytań.", "nodes.questionClassifiers.addClass": "Dodaj klasę", "nodes.questionClassifiers.advancedSetting": "Zaawansowane ustawienia", diff --git a/web/i18n/pt-BR/common.json b/web/i18n/pt-BR/common.json index 2d40cf1ee7..74b2a74f87 100644 --- a/web/i18n/pt-BR/common.json +++ b/web/i18n/pt-BR/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Páginas autorizadas", "dataSource.notion.remove": "Remover", "dataSource.notion.selector.addPages": "Adicionar páginas", + "dataSource.notion.selector.configure": "Configurar Notion", + "dataSource.notion.selector.docs": "Documentação do Notion", + "dataSource.notion.selector.headerTitle": "Escolher páginas do Notion", "dataSource.notion.selector.noSearchResult": "Nenhum resultado de pesquisa", "dataSource.notion.selector.pageSelected": "Páginas Selecionadas", "dataSource.notion.selector.preview": "PRÉ-VISUALIZAÇÃO", diff --git a/web/i18n/pt-BR/workflow.json b/web/i18n/pt-BR/workflow.json index aebf281b34..ab1fea2990 100644 --- a/web/i18n/pt-BR/workflow.json +++ b/web/i18n/pt-BR/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "É sucesso. Em caso de sucesso, o valor é 1, em caso de falha, o valor é 0.", "nodes.parameterExtractor.outputVars.usage": "Informações de uso do modelo", "nodes.parameterExtractor.reasoningMode": "Modo de raciocínio", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Função/Chamada de Ferramenta", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Você pode escolher o modo de raciocínio apropriado com base na capacidade do modelo de responder a instruções para chamadas de função ou prompts.", "nodes.questionClassifiers.addClass": "Adicionar classe", "nodes.questionClassifiers.advancedSetting": "Configuração avançada", diff --git a/web/i18n/ro-RO/common.json b/web/i18n/ro-RO/common.json index 84ae4cdce0..cbe7396a04 100644 --- a/web/i18n/ro-RO/common.json +++ b/web/i18n/ro-RO/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Pagini autorizate", "dataSource.notion.remove": "Elimină", "dataSource.notion.selector.addPages": "Adăugați pagini", + "dataSource.notion.selector.configure": "Configurare Notion", + "dataSource.notion.selector.docs": "Documentație Notion", + "dataSource.notion.selector.headerTitle": "Alegeți paginile Notion", "dataSource.notion.selector.noSearchResult": "Niciun rezultat la căutare", "dataSource.notion.selector.pageSelected": "Pagini selectate", "dataSource.notion.selector.preview": "PREVIZUALIZARE", diff --git a/web/i18n/ro-RO/workflow.json b/web/i18n/ro-RO/workflow.json index ff21dbb9fc..dbe331e316 100644 --- a/web/i18n/ro-RO/workflow.json +++ b/web/i18n/ro-RO/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Este succes. În caz de succes valoarea este 1, în caz de eșec valoarea este 0.", "nodes.parameterExtractor.outputVars.usage": "Informații de utilizare a modelului", "nodes.parameterExtractor.reasoningMode": "Mod de raționament", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Funcție/Apel instrument", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Puteți alege modul de raționament potrivit în funcție de capacitatea modelului de a răspunde la instrucțiuni pentru apelarea funcțiilor sau prompturi.", "nodes.questionClassifiers.addClass": "Adăugați clasă", "nodes.questionClassifiers.advancedSetting": "Setare avansată", diff --git a/web/i18n/ru-RU/common.json b/web/i18n/ru-RU/common.json index ba6a3f6078..ccaf5efe60 100644 --- a/web/i18n/ru-RU/common.json +++ b/web/i18n/ru-RU/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Авторизованные страницы", "dataSource.notion.remove": "Удалить", "dataSource.notion.selector.addPages": "Добавить страницы", + "dataSource.notion.selector.configure": "Настроить Notion", + "dataSource.notion.selector.docs": "Документация Notion", + "dataSource.notion.selector.headerTitle": "Выберите страницы Notion", "dataSource.notion.selector.noSearchResult": "Нет результатов поиска", "dataSource.notion.selector.pageSelected": "Выбранные страницы", "dataSource.notion.selector.preview": "ПРЕДПРОСМОТР", diff --git a/web/i18n/ru-RU/workflow.json b/web/i18n/ru-RU/workflow.json index 9b302c19f6..48a253b31b 100644 --- a/web/i18n/ru-RU/workflow.json +++ b/web/i18n/ru-RU/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Успешно. В случае успеха значение равно 1, в случае сбоя - 0.", "nodes.parameterExtractor.outputVars.usage": "Информация об использовании модели", "nodes.parameterExtractor.reasoningMode": "Режим рассуждения", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Вызов функции/инструмента", + "nodes.parameterExtractor.reasoningModePrompt": "Подсказка", "nodes.parameterExtractor.reasoningModeTip": "Вы можете выбрать соответствующий режим рассуждения, основываясь на способности модели реагировать на инструкции для вызова функций или подсказки.", "nodes.questionClassifiers.addClass": "Добавить класс", "nodes.questionClassifiers.advancedSetting": "Расширенные настройки", diff --git a/web/i18n/sl-SI/common.json b/web/i18n/sl-SI/common.json index a0e7130176..95739a5d24 100644 --- a/web/i18n/sl-SI/common.json +++ b/web/i18n/sl-SI/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Dovoljene strani", "dataSource.notion.remove": "Odstrani", "dataSource.notion.selector.addPages": "Dodajanje strani", + "dataSource.notion.selector.configure": "Konfiguriraj Notion", + "dataSource.notion.selector.docs": "Dokumentacija Notion", + "dataSource.notion.selector.headerTitle": "Izberite strani Notion", "dataSource.notion.selector.noSearchResult": "Ni rezultatov iskanja", "dataSource.notion.selector.pageSelected": "Izbrane strani", "dataSource.notion.selector.preview": "PREDOGLED", diff --git a/web/i18n/sl-SI/workflow.json b/web/i18n/sl-SI/workflow.json index 814fe5b117..f2b032cfaa 100644 --- a/web/i18n/sl-SI/workflow.json +++ b/web/i18n/sl-SI/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Ali je uspeh. Na uspehu je vrednost 1, na neuspehu je vrednost 0.", "nodes.parameterExtractor.outputVars.usage": "Informacije o uporabi modela", "nodes.parameterExtractor.reasoningMode": "Način razmišljanja", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Klic funkcije/orodja", + "nodes.parameterExtractor.reasoningModePrompt": "Poziv", "nodes.parameterExtractor.reasoningModeTip": "Lahko izberete ustrezen način razmišljanja glede na sposobnost modela, da se odzove na navodila za klic funkcij ali pozive.", "nodes.questionClassifiers.addClass": "Dodaj razred", "nodes.questionClassifiers.advancedSetting": "Napredno nastavitev", diff --git a/web/i18n/th-TH/common.json b/web/i18n/th-TH/common.json index eb45b7e796..ca2b2acca0 100644 --- a/web/i18n/th-TH/common.json +++ b/web/i18n/th-TH/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "เพจที่ได้รับอนุญาต", "dataSource.notion.remove": "ถอด", "dataSource.notion.selector.addPages": "เพิ่มหน้า", + "dataSource.notion.selector.configure": "กำหนดค่า Notion", + "dataSource.notion.selector.docs": "เอกสาร Notion", + "dataSource.notion.selector.headerTitle": "เลือกหน้า Notion", "dataSource.notion.selector.noSearchResult": "ไม่มีผลการค้นหา", "dataSource.notion.selector.pageSelected": "หน้าที่เลือก", "dataSource.notion.selector.preview": "ดูตัวอย่าง", diff --git a/web/i18n/th-TH/workflow.json b/web/i18n/th-TH/workflow.json index df656580ea..fb49264d71 100644 --- a/web/i18n/th-TH/workflow.json +++ b/web/i18n/th-TH/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "คือ Success เมื่อสําเร็จค่าคือ 1 เมื่อล้มเหลวค่าเป็น 0", "nodes.parameterExtractor.outputVars.usage": "ข้อมูลการใช้งานรุ่น", "nodes.parameterExtractor.reasoningMode": "โหมดการให้เหตุผล", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "ฟังก์ชัน/การเรียกใช้เครื่องมือ", + "nodes.parameterExtractor.reasoningModePrompt": "พรอมต์", "nodes.parameterExtractor.reasoningModeTip": "คุณสามารถเลือกโหมดการให้เหตุผลที่เหมาะสมตามความสามารถของโมเดลในการตอบสนองต่อคําแนะนําสําหรับการเรียกใช้ฟังก์ชันหรือข้อความแจ้ง", "nodes.questionClassifiers.addClass": "เพิ่มชั้นเรียน", "nodes.questionClassifiers.advancedSetting": "การตั้งค่าขั้นสูง", diff --git a/web/i18n/tr-TR/common.json b/web/i18n/tr-TR/common.json index f8877e75ca..8193b1141d 100644 --- a/web/i18n/tr-TR/common.json +++ b/web/i18n/tr-TR/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Yetkilendirilen sayfalar", "dataSource.notion.remove": "Kaldır", "dataSource.notion.selector.addPages": "Sayfa ekle", + "dataSource.notion.selector.configure": "Notion'ı Yapılandır", + "dataSource.notion.selector.docs": "Notion belgeleri", + "dataSource.notion.selector.headerTitle": "Notion sayfalarını seçin", "dataSource.notion.selector.noSearchResult": "Arama sonucu yok", "dataSource.notion.selector.pageSelected": "Seçilen Sayfalar", "dataSource.notion.selector.preview": "ÖNİZLEME", diff --git a/web/i18n/tr-TR/workflow.json b/web/i18n/tr-TR/workflow.json index 847f3a61f4..6b87b4e1e8 100644 --- a/web/i18n/tr-TR/workflow.json +++ b/web/i18n/tr-TR/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Başarılı mı. Başarılı olduğunda değer 1, başarısız olduğunda değer 0'dır.", "nodes.parameterExtractor.outputVars.usage": "Model Kullanım Bilgileri", "nodes.parameterExtractor.reasoningMode": "Akıl Yürütme Modu", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Fonksiyon/Araç Çağrısı", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Modelin fonksiyon çağırma veya istemler için talimatlara yanıt verme yeteneğine bağlı olarak uygun akıl yürütme modunu seçebilirsiniz.", "nodes.questionClassifiers.addClass": "Sınıf Ekle", "nodes.questionClassifiers.advancedSetting": "Gelişmiş Ayarlar", diff --git a/web/i18n/uk-UA/common.json b/web/i18n/uk-UA/common.json index 2eb457c835..5ad4723ff7 100644 --- a/web/i18n/uk-UA/common.json +++ b/web/i18n/uk-UA/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Авторизовані сторінки", "dataSource.notion.remove": "Видалити", "dataSource.notion.selector.addPages": "Додати сторінки", + "dataSource.notion.selector.configure": "Налаштувати Notion", + "dataSource.notion.selector.docs": "Документація Notion", + "dataSource.notion.selector.headerTitle": "Виберіть сторінки Notion", "dataSource.notion.selector.noSearchResult": "Результатів пошуку немає", "dataSource.notion.selector.pageSelected": "Сторінки вибрано", "dataSource.notion.selector.preview": "ПЕРЕДПЕРЕГЛЯД", diff --git a/web/i18n/uk-UA/workflow.json b/web/i18n/uk-UA/workflow.json index eaf7d551a7..70d5378d22 100644 --- a/web/i18n/uk-UA/workflow.json +++ b/web/i18n/uk-UA/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Є успіх. У разі успіху значення 1, у разі невдачі значення 0.", "nodes.parameterExtractor.outputVars.usage": "Інформація про використання моделі", "nodes.parameterExtractor.reasoningMode": "Режим інференції", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Виклик функції/інструменту", + "nodes.parameterExtractor.reasoningModePrompt": "Підказка", "nodes.parameterExtractor.reasoningModeTip": "Ви можете вибрати відповідний режим інференції залежно від здатності моделі реагувати на інструкції щодо викликів функцій або запитів.", "nodes.questionClassifiers.addClass": "Додати клас", "nodes.questionClassifiers.advancedSetting": "Розширене налаштування", diff --git a/web/i18n/vi-VN/common.json b/web/i18n/vi-VN/common.json index 13e74daccf..f12d735a4f 100644 --- a/web/i18n/vi-VN/common.json +++ b/web/i18n/vi-VN/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "Các trang được ủy quyền", "dataSource.notion.remove": "Xóa", "dataSource.notion.selector.addPages": "Thêm trang", + "dataSource.notion.selector.configure": "Cấu hình Notion", + "dataSource.notion.selector.docs": "Tài liệu Notion", + "dataSource.notion.selector.headerTitle": "Chọn các trang Notion", "dataSource.notion.selector.noSearchResult": "Không có kết quả tìm kiếm", "dataSource.notion.selector.pageSelected": "Các trang đã chọn", "dataSource.notion.selector.preview": "Xem trước", diff --git a/web/i18n/vi-VN/workflow.json b/web/i18n/vi-VN/workflow.json index 94a4dfd848..9bf9b4d61c 100644 --- a/web/i18n/vi-VN/workflow.json +++ b/web/i18n/vi-VN/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "Thành công. Khi thành công giá trị là 1, khi thất bại giá trị là 0.", "nodes.parameterExtractor.outputVars.usage": "Thông tin sử dụng mô hình", "nodes.parameterExtractor.reasoningMode": "Chế độ suy luận", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "Hàm/Gọi công cụ", + "nodes.parameterExtractor.reasoningModePrompt": "Prompt", "nodes.parameterExtractor.reasoningModeTip": "Bạn có thể chọn chế độ suy luận phù hợp dựa trên khả năng của mô hình để phản hồi các hướng dẫn về việc gọi hàm hoặc prompt.", "nodes.questionClassifiers.addClass": "Thêm lớp", "nodes.questionClassifiers.advancedSetting": "Cài đặt nâng cao", diff --git a/web/i18n/zh-Hans/common.json b/web/i18n/zh-Hans/common.json index 3c406e8938..b3f8178888 100644 --- a/web/i18n/zh-Hans/common.json +++ b/web/i18n/zh-Hans/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "已授权页面", "dataSource.notion.remove": "删除", "dataSource.notion.selector.addPages": "添加页面", + "dataSource.notion.selector.configure": "配置 Notion", + "dataSource.notion.selector.docs": "Notion 文档", + "dataSource.notion.selector.headerTitle": "选择 Notion 页面", "dataSource.notion.selector.noSearchResult": "无搜索结果", "dataSource.notion.selector.pageSelected": "已选页面", "dataSource.notion.selector.preview": "预览", diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index e6fc7d9ba9..6bb832f925 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "是否成功。成功时值为 1,失败时值为 0。", "nodes.parameterExtractor.outputVars.usage": "模型用量信息", "nodes.parameterExtractor.reasoningMode": "推理模式", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "函数/工具调用", + "nodes.parameterExtractor.reasoningModePrompt": "提示词", "nodes.parameterExtractor.reasoningModeTip": "你可以根据模型对于 Function calling 或 Prompt 的指令响应能力选择合适的推理模式", "nodes.questionClassifiers.addClass": "添加分类", "nodes.questionClassifiers.advancedSetting": "高级设置", diff --git a/web/i18n/zh-Hant/common.json b/web/i18n/zh-Hant/common.json index 6cabc3638f..c2cbc40d2e 100644 --- a/web/i18n/zh-Hant/common.json +++ b/web/i18n/zh-Hant/common.json @@ -135,6 +135,9 @@ "dataSource.notion.pagesAuthorized": "已授權頁面", "dataSource.notion.remove": "刪除", "dataSource.notion.selector.addPages": "新增頁面", + "dataSource.notion.selector.configure": "配置 Notion", + "dataSource.notion.selector.docs": "Notion 文件", + "dataSource.notion.selector.headerTitle": "選擇 Notion 頁面", "dataSource.notion.selector.noSearchResult": "無搜尋結果", "dataSource.notion.selector.pageSelected": "已選頁面", "dataSource.notion.selector.preview": "預覽", diff --git a/web/i18n/zh-Hant/workflow.json b/web/i18n/zh-Hant/workflow.json index b7e34018d4..865d8b66ae 100644 --- a/web/i18n/zh-Hant/workflow.json +++ b/web/i18n/zh-Hant/workflow.json @@ -856,6 +856,8 @@ "nodes.parameterExtractor.outputVars.isSuccess": "是否成功。成功時值為 1,失敗時值為 0。", "nodes.parameterExtractor.outputVars.usage": "模型用量信息", "nodes.parameterExtractor.reasoningMode": "推理模式", + "nodes.parameterExtractor.reasoningModeFunctionToolCalling": "函數/工具調用", + "nodes.parameterExtractor.reasoningModePrompt": "提示詞", "nodes.parameterExtractor.reasoningModeTip": "你可以根據模型對於 Function calling 或 Prompt 的指令響應能力選擇合適的推理模式", "nodes.questionClassifiers.addClass": "新增分類", "nodes.questionClassifiers.advancedSetting": "高級設置",