mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
Compare commits
8 Commits
chore/i18n
...
feat/per-d
| Author | SHA1 | Date | |
|---|---|---|---|
| e93fdddb20 | |||
| ccfc8c6f15 | |||
| 4fb3fab82d | |||
| 3cea0dfb07 | |||
| 0d6db3a3f3 | |||
| 3d5a81bd30 | |||
| 208604a3a8 | |||
| 63bfba0bdb |
@ -134,6 +134,26 @@ class DatabaseConfig(BaseSettings):
|
||||
default="",
|
||||
)
|
||||
|
||||
DIFY_DB_USER: str | None = Field(
|
||||
description="Override for DB_USERNAME. Takes precedence when set.",
|
||||
default=None,
|
||||
)
|
||||
|
||||
DIFY_DB_PASS: str | None = Field(
|
||||
description="Override for DB_PASSWORD. Takes precedence when set.",
|
||||
default=None,
|
||||
)
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def effective_db_username(self) -> str:
|
||||
return self.DIFY_DB_USER if self.DIFY_DB_USER is not None else self.DB_USERNAME
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def effective_db_password(self) -> str:
|
||||
return self.DIFY_DB_PASS if self.DIFY_DB_PASS is not None else self.DB_PASSWORD
|
||||
|
||||
DB_DATABASE: str = Field(
|
||||
description="Name of the database to connect to.",
|
||||
default="dify",
|
||||
@ -163,7 +183,7 @@ class DatabaseConfig(BaseSettings):
|
||||
db_extras = f"?{db_extras}" if db_extras else ""
|
||||
return (
|
||||
f"{self.SQLALCHEMY_DATABASE_URI_SCHEME}://"
|
||||
f"{quote_plus(self.DB_USERNAME)}:{quote_plus(self.DB_PASSWORD)}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_DATABASE}"
|
||||
f"{quote_plus(self.effective_db_username)}:{quote_plus(self.effective_db_password)}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_DATABASE}"
|
||||
f"{db_extras}"
|
||||
)
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$ROOT/docker"
|
||||
docker compose --env-file middleware.env -f docker-compose.middleware.yaml -p dify up -d
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
|
||||
ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
cd "$ROOT/docker"
|
||||
docker compose --env-file middleware.env -f docker-compose.middleware.yaml -p dify up -d
|
||||
|
||||
7
e2e/features/smoke/unauthenticated-entry.feature
Normal file
7
e2e/features/smoke/unauthenticated-entry.feature
Normal file
@ -0,0 +1,7 @@
|
||||
@smoke @unauthenticated
|
||||
Feature: Unauthenticated app console entry
|
||||
Scenario: Redirect to the sign-in page when opening the apps console without logging in
|
||||
Given I am not signed in
|
||||
When I open the apps console
|
||||
Then I should be redirected to the signin page
|
||||
And I should see the "Sign in" button
|
||||
@ -9,3 +9,10 @@ Given('I am signed in as the default E2E admin', async function (this: DifyWorld
|
||||
'text/plain',
|
||||
)
|
||||
})
|
||||
|
||||
Given('I am not signed in', async function (this: DifyWorld) {
|
||||
this.attach(
|
||||
'Using a clean browser context without the shared authenticated storage state.',
|
||||
'text/plain',
|
||||
)
|
||||
})
|
||||
|
||||
@ -10,6 +10,10 @@ Then('I should stay on the apps console', async function (this: DifyWorld) {
|
||||
await expect(this.getPage()).toHaveURL(/\/apps(?:\?.*)?$/)
|
||||
})
|
||||
|
||||
Then('I should be redirected to the signin page', async function (this: DifyWorld) {
|
||||
await expect(this.getPage()).toHaveURL(/\/signin(?:\?.*)?$/)
|
||||
})
|
||||
|
||||
Then('I should see the {string} button', async function (this: DifyWorld, label: string) {
|
||||
await expect(this.getPage().getByRole('button', { name: label })).toBeVisible()
|
||||
})
|
||||
|
||||
@ -46,7 +46,11 @@ BeforeAll(async () => {
|
||||
Before(async function (this: DifyWorld, { pickle }) {
|
||||
if (!browser) throw new Error('Shared Playwright browser is not available.')
|
||||
|
||||
await this.startAuthenticatedSession(browser)
|
||||
const isUnauthenticatedScenario = pickle.tags.some((tag) => tag.name === '@unauthenticated')
|
||||
|
||||
if (isUnauthenticatedScenario) await this.startUnauthenticatedSession(browser)
|
||||
else await this.startAuthenticatedSession(browser)
|
||||
|
||||
this.scenarioStartedAt = Date.now()
|
||||
|
||||
const tags = pickle.tags.map((tag) => tag.name).join(' ')
|
||||
|
||||
@ -25,12 +25,12 @@ export class DifyWorld extends World {
|
||||
this.pageErrors = []
|
||||
}
|
||||
|
||||
async startAuthenticatedSession(browser: Browser) {
|
||||
async startSession(browser: Browser, authenticated: boolean) {
|
||||
this.resetScenarioState()
|
||||
this.context = await browser.newContext({
|
||||
baseURL,
|
||||
locale: defaultLocale,
|
||||
storageState: authStatePath,
|
||||
...(authenticated ? { storageState: authStatePath } : {}),
|
||||
})
|
||||
this.context.setDefaultTimeout(30_000)
|
||||
this.page = await this.context.newPage()
|
||||
@ -44,6 +44,14 @@ export class DifyWorld extends World {
|
||||
})
|
||||
}
|
||||
|
||||
async startAuthenticatedSession(browser: Browser) {
|
||||
await this.startSession(browser, true)
|
||||
}
|
||||
|
||||
async startUnauthenticatedSession(browser: Browser) {
|
||||
await this.startSession(browser, false)
|
||||
}
|
||||
|
||||
getPage() {
|
||||
if (!this.page) throw new Error('Playwright page has not been initialized for this scenario.')
|
||||
|
||||
|
||||
@ -120,7 +120,10 @@ describe('HITLInputBlock', () => {
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onWorkflowMapUpdate).toHaveBeenCalledWith(workflowNodesMap)
|
||||
expect(onWorkflowMapUpdate).toHaveBeenCalledWith({
|
||||
workflowNodesMap,
|
||||
availableVariables: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -22,7 +22,7 @@ const HITLInputReplacementBlock = ({
|
||||
onFormInputsChange,
|
||||
onFormInputItemRename,
|
||||
onFormInputItemRemove,
|
||||
workflowNodesMap,
|
||||
workflowNodesMap = {},
|
||||
getVarType,
|
||||
variables,
|
||||
readonly,
|
||||
|
||||
@ -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]))
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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<string, unknown>) => 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(
|
||||
<WorkflowVariableBlockComponent
|
||||
nodeKey="k"
|
||||
variables={['node-1', 'missing_key']}
|
||||
workflowNodesMap={{
|
||||
'node-1': {
|
||||
title: 'Node A',
|
||||
type: BlockEnum.LLM,
|
||||
width: 200,
|
||||
height: 100,
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
}}
|
||||
availableVariables={[
|
||||
{
|
||||
nodeId: 'node-1',
|
||||
title: 'Node A',
|
||||
vars: [{ variable: 'existing_key', type: VarType.string }],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
|
||||
errorMsg: expect.any(String),
|
||||
}))
|
||||
})
|
||||
|
||||
it('should keep non-special variable valid when source key exists in availableVariables', () => {
|
||||
render(
|
||||
<WorkflowVariableBlockComponent
|
||||
nodeKey="k"
|
||||
variables={['node-1', 'existing_key']}
|
||||
workflowNodesMap={{
|
||||
'node-1': {
|
||||
title: 'Node A',
|
||||
type: BlockEnum.LLM,
|
||||
width: 200,
|
||||
height: 100,
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
}}
|
||||
availableVariables={[
|
||||
{
|
||||
nodeId: 'node-1',
|
||||
title: 'Node A',
|
||||
vars: [{ variable: 'existing_key', type: VarType.string }],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(mockVarLabel).toHaveBeenCalledWith(expect.objectContaining({
|
||||
errorMsg: undefined,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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>(workflowNodesMap)
|
||||
const [localAvailableVariables, setLocalAvailableVariables] = useState<NodeOutPutVar[]>(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,
|
||||
|
||||
@ -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<WorkflowVariableBlockType['workflowNodesMap']>
|
||||
availableVariables: NonNullable<WorkflowVariableBlockType['variables']>
|
||||
}
|
||||
export const UPDATE_WORKFLOW_NODES_MAP = createCommand<UpdateWorkflowNodesMapPayload>('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
|
||||
})
|
||||
|
||||
@ -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<WorkflowVariableBlockType['workflowNodesMap']>
|
||||
|
||||
type SerializedNode = SerializedLexicalNode & {
|
||||
variables: string[]
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
getVarType?: GetVarType
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
availableVariables?: NodeOutPutVar[]
|
||||
}
|
||||
|
||||
export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element> {
|
||||
__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<React.JSX.Element>
|
||||
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<React.JSX.Element>
|
||||
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(
|
||||
|
||||
@ -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<any[]>((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)
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -33,12 +33,12 @@ const ReasoningModePicker: FC<Props> = ({
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-x-1">
|
||||
<OptionCard
|
||||
title="Function/Tool Calling"
|
||||
title={t(`${i18nPrefix}.reasoningModeFunctionToolCalling`, { ns: 'workflow' })}
|
||||
onSelect={handleChange(ReasoningModeType.functionCall)}
|
||||
selected={type === ReasoningModeType.functionCall}
|
||||
/>
|
||||
<OptionCard
|
||||
title="Prompt"
|
||||
title={t(`${i18nPrefix}.reasoningModePrompt`, { ns: 'workflow' })}
|
||||
selected={type === ReasoningModeType.prompt}
|
||||
onSelect={handleChange(ReasoningModeType.prompt)}
|
||||
/>
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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": "معاينة",
|
||||
|
||||
@ -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": "إعدادات متقدمة",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "پیشنمایش",
|
||||
|
||||
@ -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": "تنظیمات پیشرفته",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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é",
|
||||
|
||||
@ -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": "पूर्वावलोकन",
|
||||
|
||||
@ -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": "उन्नत सेटिंग",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "プレビュー",
|
||||
|
||||
@ -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": "高度な設定",
|
||||
|
||||
@ -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": "미리보기",
|
||||
|
||||
@ -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": "고급 설정",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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ă",
|
||||
|
||||
@ -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": "ПРЕДПРОСМОТР",
|
||||
|
||||
@ -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": "Расширенные настройки",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "ดูตัวอย่าง",
|
||||
|
||||
@ -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": "การตั้งค่าขั้นสูง",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "ПЕРЕДПЕРЕГЛЯД",
|
||||
|
||||
@ -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": "Розширене налаштування",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "高级设置",
|
||||
|
||||
@ -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": "預覽",
|
||||
|
||||
@ -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": "高級設置",
|
||||
|
||||
@ -38,6 +38,26 @@ export type ResponseError = {
|
||||
status: number
|
||||
}
|
||||
|
||||
const createResponseFromHTTPError = (error: HTTPError): Response => {
|
||||
const headers = new Headers(error.response.headers)
|
||||
headers.delete('content-length')
|
||||
|
||||
let body: BodyInit | null = null
|
||||
if (typeof error.data === 'string')
|
||||
body = error.data
|
||||
else if (error.data !== undefined)
|
||||
body = JSON.stringify(error.data)
|
||||
|
||||
if (body !== null && !headers.has('content-type'))
|
||||
headers.set('content-type', ContentType.json)
|
||||
|
||||
return new Response(body, {
|
||||
status: error.response.status,
|
||||
statusText: error.response.statusText,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
|
||||
const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook => {
|
||||
return async ({ response }) => {
|
||||
if (!/^([23])\d{2}$/.test(String(response.status))) {
|
||||
@ -209,7 +229,7 @@ async function base<T>(url: string, options: FetchOptionType = {}, otherOptions:
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof HTTPError)
|
||||
throw error.response.clone()
|
||||
throw createResponseFromHTTPError(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user