mirror of
https://github.com/langgenius/dify.git
synced 2026-02-09 15:10:13 -05:00
Compare commits
16 Commits
29a6dd5648
...
d8765c8c16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8765c8c16 | ||
|
|
f6b0fda9f7 | ||
|
|
f359bbc5de | ||
|
|
7067b5f3cb | ||
|
|
d47bc3abc4 | ||
|
|
fff2c11d9c | ||
|
|
001950d9f8 | ||
|
|
7fb6e0cdfe | ||
|
|
41b218f427 | ||
|
|
a71f336ee0 | ||
|
|
cce7970f77 | ||
|
|
9614fe8e6e | ||
|
|
5eeb6c56f0 | ||
|
|
fcf2a334d2 | ||
|
|
f80b5a9537 | ||
|
|
defa99e6cd |
@@ -15,6 +15,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
import contexts
|
||||
from configs import dify_config
|
||||
from constants import UUID_NIL
|
||||
from core.app.layers.sandbox_layer import SandboxLayer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from controllers.console.app.workflow import LoopNodeRunPayload
|
||||
@@ -30,7 +31,6 @@ from core.app.apps.message_based_app_queue_manager import MessageBasedAppQueueMa
|
||||
from core.app.entities.app_invoke_entities import AdvancedChatAppGenerateEntity, InvokeFrom
|
||||
from core.app.entities.task_entities import ChatbotAppBlockingResponse, ChatbotAppStreamResponse
|
||||
from core.app.layers.pause_state_persist_layer import PauseStateLayerConfig, PauseStatePersistenceLayer
|
||||
from core.app.layers.sandbox_layer import SandboxLayer
|
||||
from core.helper.trace_id_helper import extract_external_trace_id_from_args
|
||||
from core.model_runtime.errors.invoke import InvokeAuthorizationError
|
||||
from core.ops.ops_trace_manager import TraceQueueManager
|
||||
@@ -500,6 +500,27 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
|
||||
state_owner_user_id=pause_state_config.state_owner_user_id,
|
||||
)
|
||||
)
|
||||
sandbox: Sandbox | None = None
|
||||
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
|
||||
sandbox_provider = SandboxProviderService.get_sandbox_provider(
|
||||
application_generate_entity.app_config.tenant_id
|
||||
)
|
||||
if workflow.version == Workflow.VERSION_DRAFT:
|
||||
sandbox = SandboxService.create_draft(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
else:
|
||||
sandbox = SandboxService.create(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_id=conversation.id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
graph_layers.append(SandboxLayer(sandbox))
|
||||
|
||||
# new thread with request context and contextvars
|
||||
context = contextvars.copy_context()
|
||||
@@ -518,6 +539,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
|
||||
"workflow_node_execution_repository": workflow_node_execution_repository,
|
||||
"graph_engine_layers": tuple(graph_layers),
|
||||
"graph_runtime_state": graph_runtime_state,
|
||||
"sandbox": sandbox,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -565,6 +587,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
|
||||
workflow_node_execution_repository: WorkflowNodeExecutionRepository,
|
||||
graph_engine_layers: Sequence[GraphEngineLayer] = (),
|
||||
graph_runtime_state: GraphRuntimeState | None = None,
|
||||
sandbox: Sandbox | None = None,
|
||||
):
|
||||
"""
|
||||
Generate worker in a new thread.
|
||||
@@ -592,29 +615,6 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator):
|
||||
if workflow is None:
|
||||
raise ValueError("Workflow not found")
|
||||
|
||||
sandbox: Sandbox | None = None
|
||||
graph_engine_layers: tuple = ()
|
||||
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
|
||||
sandbox_provider = SandboxProviderService.get_sandbox_provider(
|
||||
application_generate_entity.app_config.tenant_id
|
||||
)
|
||||
if workflow.version == Workflow.VERSION_DRAFT:
|
||||
sandbox = SandboxService.create_draft(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
else:
|
||||
sandbox = SandboxService.create(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_id=conversation_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
graph_engine_layers = (SandboxLayer(sandbox=sandbox),)
|
||||
|
||||
# Determine system_user_id based on invocation source
|
||||
is_external_api_call = application_generate_entity.invoke_from in {
|
||||
InvokeFrom.WEB_APP,
|
||||
|
||||
@@ -119,6 +119,7 @@ class AdvancedChatAppRunner(WorkflowBasedAppRunner):
|
||||
|
||||
if resume_state is not None:
|
||||
graph_runtime_state = resume_state
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
variable_pool = graph_runtime_state.variable_pool
|
||||
graph = self._init_graph(
|
||||
graph_config=self._workflow.graph_dict,
|
||||
@@ -175,9 +176,7 @@ class AdvancedChatAppRunner(WorkflowBasedAppRunner):
|
||||
|
||||
# init graph
|
||||
graph_runtime_state = GraphRuntimeState(variable_pool=variable_pool, start_at=time.time())
|
||||
|
||||
if self._sandbox:
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
|
||||
graph = self._init_graph(
|
||||
graph_config=self._workflow.graph_dict,
|
||||
|
||||
@@ -32,7 +32,7 @@ from core.helper.trace_id_helper import extract_external_trace_id_from_args
|
||||
from core.model_runtime.errors.invoke import InvokeAuthorizationError
|
||||
from core.ops.ops_trace_manager import TraceQueueManager
|
||||
from core.repositories import DifyCoreRepositoryFactory
|
||||
from core.sandbox import Sandbox
|
||||
from core.sandbox.sandbox import Sandbox
|
||||
from core.workflow.graph_engine.layers.base import GraphEngineLayer
|
||||
from core.workflow.repositories.draft_variable_repository import DraftVariableSaverFactory
|
||||
from core.workflow.repositories.workflow_execution_repository import WorkflowExecutionRepository
|
||||
@@ -316,6 +316,28 @@ class WorkflowAppGenerator(BaseAppGenerator):
|
||||
)
|
||||
)
|
||||
|
||||
sandbox: Sandbox | None = None
|
||||
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
|
||||
sandbox_provider = SandboxProviderService.get_sandbox_provider(
|
||||
application_generate_entity.app_config.tenant_id
|
||||
)
|
||||
if workflow.version == Workflow.VERSION_DRAFT:
|
||||
sandbox = SandboxService.create_draft(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
else:
|
||||
sandbox = SandboxService.create(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_id=application_generate_entity.workflow_execution_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
graph_layers.append(SandboxLayer(sandbox=sandbox))
|
||||
|
||||
# new thread with request context and contextvars
|
||||
context = contextvars.copy_context()
|
||||
|
||||
@@ -335,6 +357,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
|
||||
"workflow_node_execution_repository": workflow_node_execution_repository,
|
||||
"graph_engine_layers": tuple(graph_layers),
|
||||
"graph_runtime_state": graph_runtime_state,
|
||||
"sandbox": sandbox,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -533,6 +556,7 @@ class WorkflowAppGenerator(BaseAppGenerator):
|
||||
root_node_id: str | None = None,
|
||||
graph_engine_layers: Sequence[GraphEngineLayer] = (),
|
||||
graph_runtime_state: GraphRuntimeState | None = None,
|
||||
sandbox: Sandbox | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Generate worker in a new thread.
|
||||
@@ -554,31 +578,6 @@ class WorkflowAppGenerator(BaseAppGenerator):
|
||||
if workflow is None:
|
||||
raise ValueError("Workflow not found")
|
||||
|
||||
sandbox: Sandbox | None = None
|
||||
if workflow.get_feature(WorkflowFeatures.SANDBOX).enabled:
|
||||
sandbox_provider = SandboxProviderService.get_sandbox_provider(
|
||||
application_generate_entity.app_config.tenant_id
|
||||
)
|
||||
if workflow.version == Workflow.VERSION_DRAFT:
|
||||
sandbox = SandboxService.create_draft(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
else:
|
||||
sandbox = SandboxService.create(
|
||||
tenant_id=application_generate_entity.app_config.tenant_id,
|
||||
app_id=application_generate_entity.app_config.app_id,
|
||||
user_id=application_generate_entity.user_id,
|
||||
sandbox_id=application_generate_entity.workflow_execution_id,
|
||||
sandbox_provider=sandbox_provider,
|
||||
)
|
||||
graph_engine_layers = (
|
||||
*graph_engine_layers,
|
||||
SandboxLayer(sandbox=sandbox),
|
||||
)
|
||||
|
||||
# Determine system_user_id based on invocation source
|
||||
is_external_api_call = application_generate_entity.invoke_from in {
|
||||
InvokeFrom.WEB_APP,
|
||||
|
||||
@@ -78,6 +78,7 @@ class WorkflowAppRunner(WorkflowBasedAppRunner):
|
||||
|
||||
if resume_state is not None:
|
||||
graph_runtime_state = resume_state
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
variable_pool = graph_runtime_state.variable_pool
|
||||
graph = self._init_graph(
|
||||
graph_config=self._workflow.graph_dict,
|
||||
@@ -115,9 +116,7 @@ class WorkflowAppRunner(WorkflowBasedAppRunner):
|
||||
)
|
||||
|
||||
graph_runtime_state = GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter())
|
||||
|
||||
if self._sandbox:
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
graph_runtime_state.set_sandbox(self._sandbox)
|
||||
|
||||
# init graph
|
||||
graph = self._init_graph(
|
||||
|
||||
@@ -355,7 +355,7 @@ class GraphRuntimeState:
|
||||
def sandbox(self) -> Sandbox | None:
|
||||
return self._sandbox
|
||||
|
||||
def set_sandbox(self, sandbox: Sandbox) -> None:
|
||||
def set_sandbox(self, sandbox: Sandbox | None) -> None:
|
||||
self._sandbox = sandbox
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import functools
|
||||
from collections.abc import Callable
|
||||
from typing import Any, TypeVar, cast
|
||||
from typing import ParamSpec, TypeVar, cast
|
||||
|
||||
from opentelemetry.trace import get_tracer
|
||||
|
||||
@@ -8,7 +8,8 @@ from configs import dify_config
|
||||
from extensions.otel.decorators.handler import SpanHandler
|
||||
from extensions.otel.runtime import is_instrument_flag_enabled
|
||||
|
||||
T = TypeVar("T", bound=Callable[..., Any])
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
|
||||
_HANDLER_INSTANCES: dict[type[SpanHandler], SpanHandler] = {SpanHandler: SpanHandler()}
|
||||
|
||||
@@ -20,7 +21,7 @@ def _get_handler_instance(handler_class: type[SpanHandler]) -> SpanHandler:
|
||||
return _HANDLER_INSTANCES[handler_class]
|
||||
|
||||
|
||||
def trace_span(handler_class: type[SpanHandler] | None = None) -> Callable[[T], T]:
|
||||
def trace_span(handler_class: type[SpanHandler] | None = None) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
||||
"""
|
||||
Decorator that traces a function with an OpenTelemetry span.
|
||||
|
||||
@@ -30,9 +31,9 @@ def trace_span(handler_class: type[SpanHandler] | None = None) -> Callable[[T],
|
||||
:param handler_class: Optional handler class to use for this span. If None, uses the default SpanHandler.
|
||||
"""
|
||||
|
||||
def decorator(func: T) -> T:
|
||||
def decorator(func: Callable[P, R]) -> Callable[P, R]:
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
if not (dify_config.ENABLE_OTEL or is_instrument_flag_enabled()):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
@@ -46,6 +47,6 @@ def trace_span(handler_class: type[SpanHandler] | None = None) -> Callable[[T],
|
||||
kwargs=kwargs,
|
||||
)
|
||||
|
||||
return cast(T, wrapper)
|
||||
return cast(Callable[P, R], wrapper)
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import inspect
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from opentelemetry.trace import SpanKind, Status, StatusCode
|
||||
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
class SpanHandler:
|
||||
"""
|
||||
@@ -31,9 +33,9 @@ class SpanHandler:
|
||||
|
||||
def _extract_arguments(
|
||||
self,
|
||||
wrapped: Callable[..., Any],
|
||||
args: tuple[Any, ...],
|
||||
kwargs: Mapping[str, Any],
|
||||
wrapped: Callable[..., R],
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> dict[str, Any] | None:
|
||||
"""
|
||||
Extract function arguments using inspect.signature.
|
||||
@@ -62,10 +64,10 @@ class SpanHandler:
|
||||
def wrapper(
|
||||
self,
|
||||
tracer: Any,
|
||||
wrapped: Callable[..., Any],
|
||||
args: tuple[Any, ...],
|
||||
kwargs: Mapping[str, Any],
|
||||
) -> Any:
|
||||
wrapped: Callable[..., R],
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> R:
|
||||
"""
|
||||
Fully control the wrapper behavior.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any
|
||||
from typing import Any, TypeVar
|
||||
|
||||
from opentelemetry.trace import SpanKind, Status, StatusCode
|
||||
from opentelemetry.util.types import AttributeValue
|
||||
@@ -12,16 +12,19 @@ from models.model import Account
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
R = TypeVar("R")
|
||||
|
||||
|
||||
class AppGenerateHandler(SpanHandler):
|
||||
"""Span handler for ``AppGenerateService.generate``."""
|
||||
|
||||
def wrapper(
|
||||
self,
|
||||
tracer: Any,
|
||||
wrapped: Callable[..., Any],
|
||||
args: tuple[Any, ...],
|
||||
kwargs: Mapping[str, Any],
|
||||
) -> Any:
|
||||
wrapped: Callable[..., R],
|
||||
args: tuple[object, ...],
|
||||
kwargs: Mapping[str, object],
|
||||
) -> R:
|
||||
try:
|
||||
arguments = self._extract_arguments(wrapped, args, kwargs)
|
||||
if not arguments:
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { FC } from 'react'
|
||||
import type { WorkflowNodesMap } from '../workflow-variable-block/node'
|
||||
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { Type } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import { RiDeleteBinLine, RiEditLine } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
@@ -23,6 +23,7 @@ type HITLInputComponentUIProps = {
|
||||
onRename: (payload: FormInputItem, oldName: string) => void
|
||||
onRemove: (varName: string) => void
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -49,6 +50,7 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
|
||||
onRename,
|
||||
onRemove,
|
||||
workflowNodesMap = {},
|
||||
nodeOutputVars,
|
||||
getVarType,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
@@ -118,6 +120,7 @@ const HITLInputComponentUI: FC<HITLInputComponentUIProps> = ({
|
||||
<VariableBlock
|
||||
variables={formInput.default?.selector}
|
||||
workflowNodesMap={workflowNodesMap}
|
||||
nodeOutputVars={nodeOutputVars}
|
||||
getVarType={getVarType}
|
||||
environmentVariables={environmentVariables}
|
||||
conversationVariables={conversationVariables}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { FC } from 'react'
|
||||
import type { WorkflowNodesMap } from '../workflow-variable-block/node'
|
||||
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { Type } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import { produce } from 'immer'
|
||||
import { useCallback } from 'react'
|
||||
import { useSelectOrDelete } from '../../hooks'
|
||||
@@ -18,6 +18,7 @@ type HITLInputComponentProps = {
|
||||
onRename: (payload: FormInputItem, oldName: string) => void
|
||||
onRemove: (varName: string) => void
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -37,6 +38,7 @@ const HITLInputComponent: FC<HITLInputComponentProps> = ({
|
||||
onRename,
|
||||
onRemove,
|
||||
workflowNodesMap = {},
|
||||
nodeOutputVars,
|
||||
getVarType,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
@@ -73,6 +75,7 @@ const HITLInputComponent: FC<HITLInputComponentProps> = ({
|
||||
onRename={onRename}
|
||||
onRemove={onRemove}
|
||||
workflowNodesMap={workflowNodesMap}
|
||||
nodeOutputVars={nodeOutputVars}
|
||||
getVarType={getVarType}
|
||||
environmentVariables={environmentVariables}
|
||||
conversationVariables={conversationVariables}
|
||||
|
||||
@@ -55,6 +55,7 @@ const HITLInputReplacementBlock = ({
|
||||
onFormInputItemRemove!,
|
||||
workflowNodesMap,
|
||||
getVarType,
|
||||
variables,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
ragVariables,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { HITLInputBlockType } from '../../types'
|
||||
import type { UpdateWorkflowNodesMapPayload } from '../workflow-variable-block'
|
||||
import type {
|
||||
HITLNodeProps,
|
||||
} from './node'
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
useEffect,
|
||||
} from 'react'
|
||||
import { CustomTextNode } from '../custom-text/node'
|
||||
import { UPDATE_WORKFLOW_NODES_MAP } from '../workflow-variable-block'
|
||||
import {
|
||||
$createHITLInputNode,
|
||||
HITLInputNode,
|
||||
@@ -21,7 +23,6 @@ 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 type HITLInputProps = {
|
||||
onInsert?: () => void
|
||||
@@ -31,6 +32,7 @@ const HITLInputBlock = memo(({
|
||||
onInsert,
|
||||
onDelete,
|
||||
workflowNodesMap,
|
||||
variables,
|
||||
getVarType,
|
||||
readonly,
|
||||
}: HITLInputBlockType) => {
|
||||
@@ -38,9 +40,13 @@ const HITLInputBlock = memo(({
|
||||
|
||||
useEffect(() => {
|
||||
editor.update(() => {
|
||||
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
|
||||
const payload: UpdateWorkflowNodesMapPayload = {
|
||||
workflowNodesMap: workflowNodesMap || {},
|
||||
nodeOutputVars: variables || [],
|
||||
}
|
||||
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, payload)
|
||||
})
|
||||
}, [editor, workflowNodesMap])
|
||||
}, [editor, workflowNodesMap, variables])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([HITLInputNode]))
|
||||
@@ -66,6 +72,7 @@ const HITLInputBlock = memo(({
|
||||
onFormInputItemRemove,
|
||||
workflowNodesMap,
|
||||
getVarType,
|
||||
variables,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
@@ -94,7 +101,7 @@ const HITLInputBlock = memo(({
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
}, [editor, onInsert, onDelete])
|
||||
}, [editor, onInsert, onDelete, workflowNodesMap, getVarType, variables, readonly])
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical'
|
||||
import type { GetVarType } from '../../types'
|
||||
import type { WorkflowNodesMap } from '../workflow-variable-block/node'
|
||||
import type { FormInputItem } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { Var } from '@/app/components/workflow/types'
|
||||
import type { NodeOutPutVar, Var } from '@/app/components/workflow/types'
|
||||
import { DecoratorNode } from 'lexical'
|
||||
import HILTInputBlockComponent from './component'
|
||||
|
||||
@@ -15,6 +15,7 @@ export type HITLNodeProps = {
|
||||
onFormInputItemRemove: (varName: string) => void
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
getVarType?: GetVarType
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -32,6 +33,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
__onFormInputItemRemove: (varName: string) => void
|
||||
__workflowNodesMap: WorkflowNodesMap
|
||||
__getVarType?: GetVarType
|
||||
__nodeOutputVars?: NodeOutPutVar[]
|
||||
__environmentVariables?: Var[]
|
||||
__conversationVariables?: Var[]
|
||||
__ragVariables?: Var[]
|
||||
@@ -89,6 +91,11 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
return self.__getVarType
|
||||
}
|
||||
|
||||
getNodeOutputVars(): NodeOutPutVar[] {
|
||||
const self = this.getLatest()
|
||||
return self.__nodeOutputVars || []
|
||||
}
|
||||
|
||||
getEnvironmentVariables(): Var[] {
|
||||
const self = this.getLatest()
|
||||
return self.__environmentVariables || []
|
||||
@@ -119,6 +126,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
node.__onFormInputItemRemove,
|
||||
node.__workflowNodesMap,
|
||||
node.__getVarType,
|
||||
node.__nodeOutputVars,
|
||||
node.__environmentVariables,
|
||||
node.__conversationVariables,
|
||||
node.__ragVariables,
|
||||
@@ -140,6 +148,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
onFormInputItemRemove: (varName: string) => void,
|
||||
workflowNodesMap: WorkflowNodesMap,
|
||||
getVarType?: GetVarType,
|
||||
nodeOutputVars?: NodeOutPutVar[],
|
||||
environmentVariables?: Var[],
|
||||
conversationVariables?: Var[],
|
||||
ragVariables?: Var[],
|
||||
@@ -156,6 +165,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
this.__onFormInputItemRemove = onFormInputItemRemove
|
||||
this.__workflowNodesMap = workflowNodesMap
|
||||
this.__getVarType = getVarType
|
||||
this.__nodeOutputVars = nodeOutputVars
|
||||
this.__environmentVariables = environmentVariables
|
||||
this.__conversationVariables = conversationVariables
|
||||
this.__ragVariables = ragVariables
|
||||
@@ -184,6 +194,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
onRemove={this.getOnFormInputItemRemove()}
|
||||
workflowNodesMap={this.getWorkflowNodesMap()}
|
||||
getVarType={this.getGetVarType()}
|
||||
nodeOutputVars={this.getNodeOutputVars()}
|
||||
environmentVariables={this.getEnvironmentVariables()}
|
||||
conversationVariables={this.getConversationVariables()}
|
||||
ragVariables={this.getRagVariables()}
|
||||
@@ -202,6 +213,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
serializedNode.onFormInputItemRemove,
|
||||
serializedNode.workflowNodesMap,
|
||||
serializedNode.getVarType,
|
||||
serializedNode.nodeOutputVars,
|
||||
serializedNode.environmentVariables,
|
||||
serializedNode.conversationVariables,
|
||||
serializedNode.ragVariables,
|
||||
@@ -223,6 +235,7 @@ export class HITLInputNode extends DecoratorNode<React.JSX.Element> {
|
||||
onFormInputItemRemove: this.getOnFormInputItemRemove(),
|
||||
workflowNodesMap: this.getWorkflowNodesMap(),
|
||||
getVarType: this.getGetVarType(),
|
||||
nodeOutputVars: this.getNodeOutputVars(),
|
||||
environmentVariables: this.getEnvironmentVariables(),
|
||||
conversationVariables: this.getConversationVariables(),
|
||||
ragVariables: this.getRagVariables(),
|
||||
@@ -244,6 +257,7 @@ export function $createHITLInputNode(
|
||||
onFormInputItemRemove: (varName: string) => void,
|
||||
workflowNodesMap: WorkflowNodesMap,
|
||||
getVarType?: GetVarType,
|
||||
nodeOutputVars?: NodeOutPutVar[],
|
||||
environmentVariables?: Var[],
|
||||
conversationVariables?: Var[],
|
||||
ragVariables?: Var[],
|
||||
@@ -258,6 +272,7 @@ export function $createHITLInputNode(
|
||||
onFormInputItemRemove,
|
||||
workflowNodesMap,
|
||||
getVarType,
|
||||
nodeOutputVars,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
ragVariables,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { mergeRegister } from '@lexical/utils'
|
||||
import {
|
||||
@@ -19,6 +20,7 @@ import {
|
||||
isGlobalVar,
|
||||
isRagVariableVar,
|
||||
isSystemVar,
|
||||
isValueSelectorInNodeOutputVars,
|
||||
} from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import VarFullPathPanel from '@/app/components/workflow/nodes/_base/components/variable/var-full-path-panel'
|
||||
import {
|
||||
@@ -32,6 +34,7 @@ import { HITLInputNode } from './node'
|
||||
type HITLInputVariableBlockComponentProps = {
|
||||
variables: string[]
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -44,6 +47,7 @@ type HITLInputVariableBlockComponentProps = {
|
||||
const HITLInputVariableBlockComponent = ({
|
||||
variables,
|
||||
workflowNodesMap = {},
|
||||
nodeOutputVars,
|
||||
getVarType,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
@@ -62,10 +66,14 @@ const HITLInputVariableBlockComponent = ({
|
||||
}
|
||||
)()
|
||||
const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState<WorkflowNodesMap>(workflowNodesMap)
|
||||
const [localNodeOutputVars, setLocalNodeOutputVars] = useState<NodeOutPutVar[]>(nodeOutputVars || [])
|
||||
const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]]
|
||||
|
||||
const isException = isExceptionVariable(varName, node?.type)
|
||||
const variableValid = useMemo(() => {
|
||||
if (localNodeOutputVars.length)
|
||||
return isValueSelectorInNodeOutputVars(variables, localNodeOutputVars)
|
||||
|
||||
let variableValid = true
|
||||
const isEnv = isENV(variables)
|
||||
const isChatVar = isConversationVar(variables)
|
||||
@@ -89,7 +97,7 @@ const HITLInputVariableBlockComponent = ({
|
||||
variableValid = !!node
|
||||
}
|
||||
return variableValid
|
||||
}, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables])
|
||||
}, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables, localNodeOutputVars])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([HITLInputNode]))
|
||||
@@ -98,8 +106,9 @@ const HITLInputVariableBlockComponent = ({
|
||||
return mergeRegister(
|
||||
editor.registerCommand(
|
||||
UPDATE_WORKFLOW_NODES_MAP,
|
||||
(workflowNodesMap: WorkflowNodesMap) => {
|
||||
setLocalWorkflowNodesMap(workflowNodesMap)
|
||||
(payload: UpdateWorkflowNodesMapPayload) => {
|
||||
setLocalWorkflowNodesMap(payload.workflowNodesMap)
|
||||
setLocalNodeOutputVars(payload.nodeOutputVars)
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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 +16,7 @@ import {
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useReactFlow, useStoreApi } from 'reactflow'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, isValueSelectorInNodeOutputVars } 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 +35,7 @@ type WorkflowVariableBlockComponentProps = {
|
||||
nodeKey: string
|
||||
variables: string[]
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -47,6 +49,7 @@ const WorkflowVariableBlockComponent = ({
|
||||
nodeKey,
|
||||
variables,
|
||||
workflowNodesMap = {},
|
||||
nodeOutputVars,
|
||||
getVarType,
|
||||
environmentVariables,
|
||||
conversationVariables,
|
||||
@@ -66,12 +69,16 @@ const WorkflowVariableBlockComponent = ({
|
||||
}
|
||||
)()
|
||||
const [localWorkflowNodesMap, setLocalWorkflowNodesMap] = useState<WorkflowNodesMap>(workflowNodesMap)
|
||||
const [localNodeOutputVars, setLocalNodeOutputVars] = useState<NodeOutPutVar[]>(nodeOutputVars || [])
|
||||
const node = localWorkflowNodesMap![variables[isRagVar ? 1 : 0]]
|
||||
const isContextVariable = (node?.type === BlockEnum.Agent || node?.type === BlockEnum.LLM)
|
||||
&& variables[variablesLength - 1] === 'context'
|
||||
|
||||
const isException = isExceptionVariable(varName, node?.type)
|
||||
const variableValid = useMemo(() => {
|
||||
if (localNodeOutputVars.length)
|
||||
return isValueSelectorInNodeOutputVars(variables, localNodeOutputVars)
|
||||
|
||||
let variableValid = true
|
||||
const isEnv = isENV(variables)
|
||||
const isChatVar = isConversationVar(variables)
|
||||
@@ -95,7 +102,7 @@ const WorkflowVariableBlockComponent = ({
|
||||
variableValid = !!node
|
||||
}
|
||||
return variableValid
|
||||
}, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables])
|
||||
}, [variables, node, environmentVariables, conversationVariables, isRagVar, ragVariables, localNodeOutputVars])
|
||||
|
||||
const reactflow = useReactFlow()
|
||||
const store = useStoreApi()
|
||||
@@ -107,8 +114,9 @@ const WorkflowVariableBlockComponent = ({
|
||||
return mergeRegister(
|
||||
editor.registerCommand(
|
||||
UPDATE_WORKFLOW_NODES_MAP,
|
||||
(workflowNodesMap: WorkflowNodesMap) => {
|
||||
setLocalWorkflowNodesMap(workflowNodesMap)
|
||||
(payload: UpdateWorkflowNodesMapPayload) => {
|
||||
setLocalWorkflowNodesMap(payload.workflowNodesMap)
|
||||
setLocalNodeOutputVars(payload.nodeOutputVars)
|
||||
|
||||
return true
|
||||
},
|
||||
|
||||
@@ -19,7 +19,13 @@ 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 CLEAR_HIDE_MENU_TIMEOUT = createCommand('CLEAR_HIDE_MENU_TIMEOUT')
|
||||
export const UPDATE_WORKFLOW_NODES_MAP = createCommand('UPDATE_WORKFLOW_NODES_MAP')
|
||||
|
||||
export type UpdateWorkflowNodesMapPayload = {
|
||||
workflowNodesMap: NonNullable<WorkflowVariableBlockType['workflowNodesMap']>
|
||||
nodeOutputVars: NonNullable<WorkflowVariableBlockType['variables']>
|
||||
}
|
||||
|
||||
export const UPDATE_WORKFLOW_NODES_MAP = createCommand<UpdateWorkflowNodesMapPayload>('UPDATE_WORKFLOW_NODES_MAP')
|
||||
|
||||
export type WorkflowVariableBlockProps = {
|
||||
getWorkflowNode: (nodeId: string) => Node
|
||||
@@ -29,6 +35,7 @@ export type WorkflowVariableBlockProps = {
|
||||
}
|
||||
const WorkflowVariableBlock = memo(({
|
||||
workflowNodesMap,
|
||||
variables,
|
||||
onInsert,
|
||||
onDelete,
|
||||
getVarType,
|
||||
@@ -37,9 +44,12 @@ const WorkflowVariableBlock = memo(({
|
||||
|
||||
useEffect(() => {
|
||||
editor.update(() => {
|
||||
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, workflowNodesMap)
|
||||
editor.dispatchCommand(UPDATE_WORKFLOW_NODES_MAP, {
|
||||
workflowNodesMap: workflowNodesMap || {},
|
||||
nodeOutputVars: variables || [],
|
||||
})
|
||||
})
|
||||
}, [editor, workflowNodesMap])
|
||||
}, [editor, workflowNodesMap, variables])
|
||||
|
||||
useEffect(() => {
|
||||
if (!editor.hasNodes([WorkflowVariableBlockNode]))
|
||||
@@ -48,9 +58,9 @@ const WorkflowVariableBlock = memo(({
|
||||
return mergeRegister(
|
||||
editor.registerCommand(
|
||||
INSERT_WORKFLOW_VARIABLE_BLOCK_COMMAND,
|
||||
(variables: string[]) => {
|
||||
(insertedVariables: string[]) => {
|
||||
editor.dispatchCommand(CLEAR_HIDE_MENU_TIMEOUT, undefined)
|
||||
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(variables, workflowNodesMap, getVarType)
|
||||
const workflowVariableBlockNode = $createWorkflowVariableBlockNode(insertedVariables, workflowNodesMap, getVarType, undefined, undefined, undefined, variables)
|
||||
|
||||
$insertNodes([workflowVariableBlockNode])
|
||||
if (onInsert)
|
||||
@@ -71,7 +81,7 @@ const WorkflowVariableBlock = memo(({
|
||||
COMMAND_PRIORITY_EDITOR,
|
||||
),
|
||||
)
|
||||
}, [editor, onInsert, onDelete, workflowNodesMap, getVarType])
|
||||
}, [editor, onInsert, onDelete, workflowNodesMap, getVarType, variables])
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical'
|
||||
import type { GetVarType, WorkflowVariableBlockType } from '../../types'
|
||||
import type { Var } from '@/app/components/workflow/types'
|
||||
import type { NodeOutPutVar, Var } from '@/app/components/workflow/types'
|
||||
import { DecoratorNode } from 'lexical'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import WorkflowVariableBlockComponent from './component'
|
||||
@@ -11,6 +11,7 @@ export type SerializedNode = SerializedLexicalNode & {
|
||||
variables: string[]
|
||||
workflowNodesMap: WorkflowNodesMap
|
||||
getVarType?: GetVarType
|
||||
nodeOutputVars?: NodeOutPutVar[]
|
||||
environmentVariables?: Var[]
|
||||
conversationVariables?: Var[]
|
||||
ragVariables?: Var[]
|
||||
@@ -20,6 +21,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
__variables: string[]
|
||||
__workflowNodesMap: WorkflowNodesMap
|
||||
__getVarType?: GetVarType
|
||||
__nodeOutputVars?: NodeOutPutVar[]
|
||||
__environmentVariables?: Var[]
|
||||
__conversationVariables?: Var[]
|
||||
__ragVariables?: Var[]
|
||||
@@ -29,14 +31,14 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
}
|
||||
|
||||
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.__environmentVariables, node.__conversationVariables, node.__ragVariables, node.__nodeOutputVars)
|
||||
}
|
||||
|
||||
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, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[], nodeOutputVars?: NodeOutPutVar[]) {
|
||||
super(key)
|
||||
|
||||
this.__variables = variables
|
||||
@@ -45,6 +47,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
this.__environmentVariables = environmentVariables
|
||||
this.__conversationVariables = conversationVariables
|
||||
this.__ragVariables = ragVariables
|
||||
this.__nodeOutputVars = nodeOutputVars
|
||||
}
|
||||
|
||||
createDOM(): HTMLElement {
|
||||
@@ -63,6 +66,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
nodeKey={this.getKey()}
|
||||
variables={this.__variables}
|
||||
workflowNodesMap={this.__workflowNodesMap}
|
||||
nodeOutputVars={this.__nodeOutputVars}
|
||||
getVarType={this.__getVarType!}
|
||||
environmentVariables={this.__environmentVariables}
|
||||
conversationVariables={this.__conversationVariables}
|
||||
@@ -72,7 +76,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
}
|
||||
|
||||
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.environmentVariables, serializedNode.conversationVariables, serializedNode.ragVariables, serializedNode.nodeOutputVars)
|
||||
|
||||
return node
|
||||
}
|
||||
@@ -84,6 +88,7 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
variables: this.getVariables(),
|
||||
workflowNodesMap: this.getWorkflowNodesMap(),
|
||||
getVarType: this.getVarType(),
|
||||
nodeOutputVars: this.getNodeOutputVars(),
|
||||
environmentVariables: this.getEnvironmentVariables(),
|
||||
conversationVariables: this.getConversationVariables(),
|
||||
ragVariables: this.getRagVariables(),
|
||||
@@ -105,6 +110,11 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
return self.__getVarType
|
||||
}
|
||||
|
||||
getNodeOutputVars(): NodeOutPutVar[] {
|
||||
const self = this.getLatest()
|
||||
return self.__nodeOutputVars || []
|
||||
}
|
||||
|
||||
getEnvironmentVariables(): any {
|
||||
const self = this.getLatest()
|
||||
return self.__environmentVariables
|
||||
@@ -129,8 +139,8 @@ export class WorkflowVariableBlockNode extends DecoratorNode<React.JSX.Element>
|
||||
return `{{${marker}${variables.join('.')}${marker}}}`
|
||||
}
|
||||
}
|
||||
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, environmentVariables?: Var[], conversationVariables?: Var[], ragVariables?: Var[], nodeOutputVars?: NodeOutPutVar[]): WorkflowVariableBlockNode {
|
||||
return new WorkflowVariableBlockNode(variables, workflowNodesMap, getVarType, undefined, environmentVariables, conversationVariables, ragVariables, nodeOutputVars)
|
||||
}
|
||||
|
||||
export function $isWorkflowVariableBlockNode(
|
||||
|
||||
@@ -39,7 +39,7 @@ 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))
|
||||
return $applyNodeReplacement($createWorkflowVariableBlockNode(nodePathString.split('.'), workflowNodesMap, getVarType, variables?.find(o => o.nodeId === 'env')?.vars || [], variables?.find(o => o.nodeId === 'conversation')?.vars || [], ragVariables, variables))
|
||||
}, [onInsert, workflowNodesMap, getVarType, variables, ragVariables])
|
||||
|
||||
const getMatch = useCallback((text: string) => {
|
||||
|
||||
@@ -86,6 +86,7 @@ export const getGlobalVars = (isChatMode: boolean): Var[] => {
|
||||
export const VAR_SHOW_NAME_MAP: Record<string, string> = {
|
||||
'sys.query': 'query',
|
||||
'sys.files': 'files',
|
||||
'context': 'chat history',
|
||||
}
|
||||
|
||||
export const RETRIEVAL_OUTPUT_STRUCT = `{
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
useGetToolIcon,
|
||||
useNodesMetaData,
|
||||
} from '../hooks'
|
||||
import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variable/utils'
|
||||
import { getNodeUsedVars, isValueSelectorInNodeOutputVars } from '../nodes/_base/components/variable/utils'
|
||||
import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
@@ -186,18 +186,8 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
|
||||
const availableVars = map[node.id].availableVars
|
||||
|
||||
for (const variable of usedVars) {
|
||||
const isSpecialVars = isSpecialVar(variable[0])
|
||||
if (!isSpecialVars) {
|
||||
const usedNode = availableVars.find(v => v.nodeId === variable?.[0])
|
||||
if (usedNode) {
|
||||
const usedVar = usedNode.vars.find(v => v.variable === variable?.[1])
|
||||
if (!usedVar)
|
||||
errorMessage = t('errorMsg.invalidVariable', { ns: 'workflow' })
|
||||
}
|
||||
else {
|
||||
errorMessage = t('errorMsg.invalidVariable', { ns: 'workflow' })
|
||||
}
|
||||
}
|
||||
if (!isValueSelectorInNodeOutputVars(variable, availableVars))
|
||||
errorMessage = t('errorMsg.invalidVariable', { ns: 'workflow' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,20 +373,9 @@ export const useChecklistBeforePublish = () => {
|
||||
const availableVars = map[node.id].availableVars
|
||||
|
||||
for (const variable of usedVars) {
|
||||
const isSpecialVars = isSpecialVar(variable[0])
|
||||
if (!isSpecialVars) {
|
||||
const usedNode = availableVars.find(v => v.nodeId === variable?.[0])
|
||||
if (usedNode) {
|
||||
const usedVar = usedNode.vars.find(v => v.variable === variable?.[1])
|
||||
if (!usedVar) {
|
||||
notify({ type: 'error', message: `[${node.data.title}] ${t('errorMsg.invalidVariable', { ns: 'workflow' })}` })
|
||||
return false
|
||||
}
|
||||
}
|
||||
else {
|
||||
notify({ type: 'error', message: `[${node.data.title}] ${t('errorMsg.invalidVariable', { ns: 'workflow' })}` })
|
||||
return false
|
||||
}
|
||||
if (!isValueSelectorInNodeOutputVars(variable, availableVars)) {
|
||||
notify({ type: 'error', message: `[${node.data.title}] ${t('errorMsg.invalidVariable', { ns: 'workflow' })}` })
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow'
|
||||
import { fetchAllInspectVars } from '@/service/workflow'
|
||||
import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type'
|
||||
import { toNodeOutputVars } from '../nodes/_base/components/variable/utils'
|
||||
import { isValueSelectorInNodeOutputVars, toNodeOutputVars } from '../nodes/_base/components/variable/utils'
|
||||
import { applyAgentSubgraphInspectVars } from './inspect-vars-agent-alias'
|
||||
|
||||
type Params = {
|
||||
@@ -90,10 +90,14 @@ export const useSetWorkflowVarsWithValue = ({
|
||||
const nodesWithVars: NodeWithVar[] = withValueNodes.map((node) => {
|
||||
const nodeId = node.id
|
||||
const isParentNode = resolvedInteractionMode === InteractionMode.Subgraph && parentNodeIds.has(nodeId)
|
||||
const varsUnderTheNode = inspectVars.filter((varItem) => {
|
||||
return varItem.selector[0] === nodeId
|
||||
})
|
||||
const nodeVar = allNodesOutputVars.find(item => item.nodeId === nodeId)
|
||||
const varsUnderTheNode = inspectVars.filter((varItem) => {
|
||||
if (varItem.selector[0] !== nodeId)
|
||||
return false
|
||||
if (!nodeVar)
|
||||
return false
|
||||
return isValueSelectorInNodeOutputVars(varItem.selector, [nodeVar])
|
||||
})
|
||||
|
||||
return {
|
||||
nodeId,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
isConversationVar,
|
||||
isENV,
|
||||
isSystemVar,
|
||||
isValueSelectorInNodeOutputVars,
|
||||
toNodeOutputVars,
|
||||
} from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
@@ -140,13 +141,15 @@ export const useInspectVarsCrudCommon = ({
|
||||
}
|
||||
const currentNodeOutputVars = toNodeOutputVars([currentNode], false, () => true, [], [], [], allPluginInfoList, schemaTypeDefinitions)
|
||||
const vars = await fetchNodeInspectVars(flowType, flowId, nodeId)
|
||||
const varsWithSchemaType = vars.map((varItem) => {
|
||||
const schemaType = currentNodeOutputVars[0]?.vars.find(v => v.variable === varItem.name)?.schemaType || ''
|
||||
return {
|
||||
...varItem,
|
||||
schemaType,
|
||||
}
|
||||
})
|
||||
const varsWithSchemaType = vars
|
||||
.filter(varItem => isValueSelectorInNodeOutputVars(varItem.selector, currentNodeOutputVars))
|
||||
.map((varItem) => {
|
||||
const schemaType = currentNodeOutputVars[0]?.vars.find(v => v.variable === varItem.name)?.schemaType || ''
|
||||
return {
|
||||
...varItem,
|
||||
schemaType,
|
||||
}
|
||||
})
|
||||
setNodeInspectVars(nodeId, varsWithSchemaType)
|
||||
const resolvedInteractionMode = interactionMode ?? InteractionMode.Default
|
||||
if (resolvedInteractionMode !== InteractionMode.Subgraph) {
|
||||
@@ -154,16 +157,29 @@ export const useInspectVarsCrudCommon = ({
|
||||
const nextNodes = applyAgentSubgraphInspectVars(nodesWithInspectVars, nodeArr)
|
||||
setNodesWithInspectVars(nextNodes)
|
||||
}
|
||||
}, [workflowStore, flowType, flowId, invalidateSysVarValues, invalidateConversationVarValues, buildInTools, customTools, workflowTools, mcpTools, interactionMode])
|
||||
}, [workflowStore, flowType, flowId, invalidateSysVarValues, invalidateConversationVarValues, buildInTools, customTools, workflowTools, mcpTools, interactionMode, store])
|
||||
|
||||
// after last run would call this
|
||||
const appendNodeInspectVars = useCallback((nodeId: string, payload: VarInInspect[], allNodes: Node[]) => {
|
||||
const { dataSourceList } = workflowStore.getState()
|
||||
const nodeInfo = allNodes.find(node => node.id === nodeId)
|
||||
const allPluginInfoList = {
|
||||
buildInTools: buildInTools || [],
|
||||
customTools: customTools || [],
|
||||
workflowTools: workflowTools || [],
|
||||
mcpTools: mcpTools || [],
|
||||
dataSourceList: dataSourceList || [],
|
||||
}
|
||||
const currentNodeOutputVars = nodeInfo
|
||||
? toNodeOutputVars([nodeInfo], false, () => true, [], [], [], allPluginInfoList)
|
||||
: []
|
||||
const validPayload = payload.filter(varItem => isValueSelectorInNodeOutputVars(varItem.selector, currentNodeOutputVars))
|
||||
|
||||
const {
|
||||
nodesWithInspectVars,
|
||||
setNodesWithInspectVars,
|
||||
} = workflowStore.getState()
|
||||
const nodes = produce(nodesWithInspectVars, (draft) => {
|
||||
const nodeInfo = allNodes.find(node => node.id === nodeId)
|
||||
if (nodeInfo) {
|
||||
const index = draft.findIndex(node => node.nodeId === nodeId)
|
||||
if (index === -1) {
|
||||
@@ -171,12 +187,12 @@ export const useInspectVarsCrudCommon = ({
|
||||
nodeId,
|
||||
nodeType: nodeInfo.data.type,
|
||||
title: nodeInfo.data.title,
|
||||
vars: payload,
|
||||
vars: validPayload,
|
||||
nodePayload: nodeInfo.data,
|
||||
})
|
||||
}
|
||||
else {
|
||||
draft[index].vars = payload
|
||||
draft[index].vars = validPayload
|
||||
// put the node to the topAdd commentMore actions
|
||||
draft.unshift(draft.splice(index, 1)[0])
|
||||
}
|
||||
@@ -187,7 +203,7 @@ export const useInspectVarsCrudCommon = ({
|
||||
const nextNodes = shouldApplyAlias ? applyAgentSubgraphInspectVars(nodes, allNodes) : nodes
|
||||
setNodesWithInspectVars(nextNodes)
|
||||
handleCancelNodeSuccessStatus(nodeId)
|
||||
}, [workflowStore, handleCancelNodeSuccessStatus, interactionMode])
|
||||
}, [workflowStore, handleCancelNodeSuccessStatus, interactionMode, buildInTools, customTools, workflowTools, mcpTools])
|
||||
|
||||
const hasNodeInspectVar = useCallback((nodeId: string, varId: string) => {
|
||||
const { nodesWithInspectVars } = workflowStore.getState()
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import type {
|
||||
CommonNodeType,
|
||||
Node,
|
||||
NodeOutPutVar,
|
||||
ValueSelector,
|
||||
VarType,
|
||||
} from '@/app/components/workflow/types'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNodes, useReactFlow, useStoreApi } from 'reactflow'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, isValueSelectorInNodeOutputVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
import {
|
||||
VariableLabelInSelect,
|
||||
} from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
|
||||
@@ -19,12 +20,14 @@ type VariableTagProps = {
|
||||
varType: VarType
|
||||
isShort?: boolean
|
||||
availableNodes?: Node[]
|
||||
availableVars?: NodeOutPutVar[]
|
||||
}
|
||||
const VariableTag = ({
|
||||
valueSelector,
|
||||
varType,
|
||||
isShort,
|
||||
availableNodes,
|
||||
availableVars,
|
||||
}: VariableTagProps) => {
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const isRagVar = isRagVariableVar(valueSelector)
|
||||
@@ -40,7 +43,12 @@ const VariableTag = ({
|
||||
const isEnv = isENV(valueSelector)
|
||||
const isChatVar = isConversationVar(valueSelector)
|
||||
const isGlobal = isGlobalVar(valueSelector)
|
||||
const isValid = Boolean(node) || isEnv || isChatVar || isRagVar || isGlobal
|
||||
const isValid = useMemo(() => {
|
||||
if (availableVars)
|
||||
return isValueSelectorInNodeOutputVars(valueSelector, availableVars)
|
||||
|
||||
return Boolean(node) || isEnv || isChatVar || isRagVar || isGlobal
|
||||
}, [availableVars, valueSelector, node, isEnv, isChatVar, isRagVar, isGlobal])
|
||||
|
||||
const variableName = isSystemVar(valueSelector) ? valueSelector.slice(0).join('.') : valueSelector.slice(1).join('.')
|
||||
const isException = isExceptionVariable(variableName, node?.data.type)
|
||||
|
||||
@@ -340,6 +340,29 @@ const findExceptVarInObject = (
|
||||
return res
|
||||
}
|
||||
|
||||
const getLLMNodeOutputVars = (llmNodeData: LLMNodeType): Var[] => {
|
||||
const isComputerUseEnabled = !!llmNodeData.computer_use
|
||||
const vars = [...LLM_OUTPUT_STRUCT].filter((item) => {
|
||||
if (isComputerUseEnabled)
|
||||
return true
|
||||
return item.variable !== 'generation'
|
||||
})
|
||||
|
||||
if (
|
||||
llmNodeData.structured_output_enabled
|
||||
&& llmNodeData.structured_output?.schema?.properties
|
||||
&& Object.keys(llmNodeData.structured_output.schema.properties).length > 0
|
||||
) {
|
||||
vars.push({
|
||||
variable: 'structured_output',
|
||||
type: VarType.object,
|
||||
children: llmNodeData.structured_output,
|
||||
})
|
||||
}
|
||||
|
||||
return vars
|
||||
}
|
||||
|
||||
const formatItem = (
|
||||
item: any,
|
||||
isChatMode: boolean,
|
||||
@@ -415,18 +438,8 @@ const formatItem = (
|
||||
}
|
||||
|
||||
case BlockEnum.LLM: {
|
||||
res.vars = [...LLM_OUTPUT_STRUCT]
|
||||
if (
|
||||
data.structured_output_enabled
|
||||
&& data.structured_output?.schema?.properties
|
||||
&& Object.keys(data.structured_output.schema.properties).length > 0
|
||||
) {
|
||||
res.vars.push({
|
||||
variable: 'structured_output',
|
||||
type: VarType.object,
|
||||
children: data.structured_output,
|
||||
})
|
||||
}
|
||||
const llmNodeData = data as LLMNodeType
|
||||
res.vars = getLLMNodeOutputVars(llmNodeData)
|
||||
|
||||
break
|
||||
}
|
||||
@@ -1304,6 +1317,104 @@ export const getNodeInfoById = (nodes: any, id: string) => {
|
||||
return nodes.find((node: any) => node.id === id)
|
||||
}
|
||||
|
||||
const normalizeSpecialValueSelector = (valueSelector: ValueSelector): ValueSelector => {
|
||||
if (valueSelector.length > 1 && isSpecialVar(valueSelector[1]))
|
||||
return valueSelector.slice(1)
|
||||
return valueSelector
|
||||
}
|
||||
|
||||
const getVarRootSelector = (nodeId: string, variable: string): ValueSelector => {
|
||||
const path = variable.split('.')
|
||||
if (path.length > 0 && isSpecialVar(path[0]))
|
||||
return path
|
||||
return [nodeId, ...path]
|
||||
}
|
||||
|
||||
const isSelectorPathValidInStructuredProperties = (
|
||||
properties: Record<string, StructField> | undefined,
|
||||
selectorTail: ValueSelector,
|
||||
): boolean => {
|
||||
if (!properties)
|
||||
return false
|
||||
if (selectorTail.length === 0)
|
||||
return true
|
||||
|
||||
const [currentKey, ...rest] = selectorTail
|
||||
const property = properties[currentKey]
|
||||
if (!property)
|
||||
return false
|
||||
if (rest.length === 0)
|
||||
return true
|
||||
|
||||
if (property.type === Type.object)
|
||||
return isSelectorPathValidInStructuredProperties(property.properties, rest)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const isSelectorPathValidInVar = (
|
||||
variable: Var,
|
||||
selectorTail: ValueSelector,
|
||||
): boolean => {
|
||||
if (selectorTail.length === 0)
|
||||
return true
|
||||
if (!variable.children)
|
||||
return false
|
||||
|
||||
const structuredProperties = (variable.children as StructuredOutput)?.schema?.properties
|
||||
if (structuredProperties)
|
||||
return isSelectorPathValidInStructuredProperties(structuredProperties, selectorTail)
|
||||
|
||||
if (!Array.isArray(variable.children))
|
||||
return false
|
||||
|
||||
const [currentKey, ...rest] = selectorTail
|
||||
const child = variable.children.find(item => item.variable === currentKey)
|
||||
if (!child)
|
||||
return false
|
||||
return isSelectorPathValidInVar(child, rest)
|
||||
}
|
||||
|
||||
const isValueSelectorMatchVar = (
|
||||
valueSelector: ValueSelector,
|
||||
nodeId: string,
|
||||
variable: Var,
|
||||
): boolean => {
|
||||
const rootSelector = getVarRootSelector(nodeId, variable.variable)
|
||||
if (valueSelector.length < rootSelector.length)
|
||||
return false
|
||||
|
||||
const isRootMatched = rootSelector.every((segment, index) => {
|
||||
return valueSelector[index] === segment
|
||||
})
|
||||
if (!isRootMatched)
|
||||
return false
|
||||
|
||||
const selectorTail = valueSelector.slice(rootSelector.length)
|
||||
return isSelectorPathValidInVar(variable, selectorTail)
|
||||
}
|
||||
|
||||
export const isValueSelectorInNodeOutputVars = (
|
||||
valueSelector: ValueSelector,
|
||||
nodeOutputVars: NodeOutPutVar[],
|
||||
): boolean => {
|
||||
if (!Array.isArray(valueSelector) || valueSelector.length === 0)
|
||||
return false
|
||||
|
||||
const normalizedSelector = normalizeSpecialValueSelector(valueSelector)
|
||||
const selectorsToCheck = [valueSelector]
|
||||
if (normalizedSelector.join('.') !== valueSelector.join('.'))
|
||||
selectorsToCheck.push(normalizedSelector)
|
||||
|
||||
return selectorsToCheck.some((selector) => {
|
||||
return nodeOutputVars.some((nodeOutputVar) => {
|
||||
return nodeOutputVar.vars.some((variable) => {
|
||||
return isValueSelectorMatchVar(selector, nodeOutputVar.nodeId, variable)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const matchNotSystemVars = (prompts: string[]) => {
|
||||
if (!prompts)
|
||||
return []
|
||||
@@ -1371,7 +1482,10 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
||||
const contextVar = (data as LLMNodeType).context?.variable_selector
|
||||
? [(data as LLMNodeType).context?.variable_selector]
|
||||
: []
|
||||
res = [...inputVars, ...contextVar]
|
||||
const jinja2VarSelectors = payload.prompt_config?.jinja2_variables
|
||||
?.map(item => item.value_selector)
|
||||
.filter(selector => Array.isArray(selector) && selector.length > 0) || []
|
||||
res = [...inputVars, ...contextVar, ...jinja2VarSelectors]
|
||||
break
|
||||
}
|
||||
case BlockEnum.KnowledgeRetrieval: {
|
||||
@@ -1416,6 +1530,14 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
||||
})
|
||||
break
|
||||
}
|
||||
case BlockEnum.Command: {
|
||||
const payload = data as CommandNodeType
|
||||
res = matchNotSystemVars([
|
||||
payload.command,
|
||||
payload.working_directory,
|
||||
])
|
||||
break
|
||||
}
|
||||
case BlockEnum.QuestionClassifier: {
|
||||
const payload = data as QuestionClassifierNodeType
|
||||
res = [payload.query_variable_selector]
|
||||
@@ -2081,19 +2203,8 @@ export const getNodeOutputVars = (
|
||||
}
|
||||
|
||||
case BlockEnum.LLM: {
|
||||
const vars = [...LLM_OUTPUT_STRUCT]
|
||||
const llmNodeData = data as LLMNodeType
|
||||
if (
|
||||
llmNodeData.structured_output_enabled
|
||||
&& llmNodeData.structured_output?.schema?.properties
|
||||
&& Object.keys(llmNodeData.structured_output.schema.properties).length > 0
|
||||
) {
|
||||
vars.push({
|
||||
variable: 'structured_output',
|
||||
type: VarType.object,
|
||||
children: llmNodeData.structured_output,
|
||||
})
|
||||
}
|
||||
const vars = getLLMNodeOutputVars(llmNodeData)
|
||||
varsToValueSelectorList(vars, [id], res)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ import { cn } from '@/utils/classnames'
|
||||
import useAvailableVarList from '../../hooks/use-available-var-list'
|
||||
import RemoveButton from '../remove-button'
|
||||
import ConstantField from './constant-field'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, removeFileVars, varTypeToStructType } from './utils'
|
||||
import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, isValueSelectorInNodeOutputVars, removeFileVars, varTypeToStructType } from './utils'
|
||||
import VarFullPathPanel from './var-full-path-panel'
|
||||
import VarReferencePopup from './var-reference-popup'
|
||||
|
||||
@@ -312,7 +312,9 @@ const VarReferencePicker: FC<Props> = ({
|
||||
const isChatVar = isConversationVar(value as ValueSelector)
|
||||
const isGlobal = isGlobalVar(value as ValueSelector)
|
||||
const isRagVar = isRagVariableVar(value as ValueSelector)
|
||||
const isValidVar = Boolean(outputVarNode) || isEnv || isChatVar || isGlobal || isRagVar
|
||||
const isValidVar = !hasValue || !Array.isArray(value)
|
||||
? true
|
||||
: isValueSelectorInNodeOutputVars(value, outputVars)
|
||||
const isException = isExceptionVariable(varName, outputVarNode?.type)
|
||||
return {
|
||||
isEnv,
|
||||
@@ -322,7 +324,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
isValidVar,
|
||||
isException,
|
||||
}
|
||||
}, [value, outputVarNode, varName])
|
||||
}, [value, hasValue, outputVarNode, outputVars, varName])
|
||||
|
||||
// 8(left/right-padding) + 14(icon) + 4 + 14 + 2 = 42 + 17 buff
|
||||
const availableWidth = triggerWidth - 56
|
||||
|
||||
@@ -27,6 +27,77 @@ import { Type } from '../../../llm/types'
|
||||
import ManageInputField from './manage-input-field'
|
||||
import { isSpecialVar, varTypeToStructType } from './utils'
|
||||
|
||||
const isStructuredOutputChildren = (children?: Var['children']): children is StructuredOutput => {
|
||||
return !!(children as StructuredOutput | undefined)?.schema?.properties
|
||||
}
|
||||
|
||||
const matchesPath = (segments: string[], query: string) => {
|
||||
return segments.join('.').toLowerCase().includes(query)
|
||||
}
|
||||
|
||||
const matchesStructuredProperties = (
|
||||
properties: Record<string, Field>,
|
||||
query: string,
|
||||
prefix: string[],
|
||||
): boolean => {
|
||||
return Object.keys(properties).some((key) => {
|
||||
const field = properties[key]
|
||||
const nextPath = [...prefix, key]
|
||||
if (matchesPath(nextPath, query))
|
||||
return true
|
||||
|
||||
if (field.type === Type.object && field.properties)
|
||||
return matchesStructuredProperties(field.properties, query, nextPath)
|
||||
|
||||
if (field.type === Type.array && field.items?.type === Type.object && field.items?.properties)
|
||||
return matchesStructuredProperties(field.items.properties, query, nextPath)
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
const matchesStructuredOutput = (
|
||||
structuredOutput: StructuredOutput,
|
||||
query: string,
|
||||
prefix: string[],
|
||||
): boolean => {
|
||||
return matchesStructuredProperties(structuredOutput.schema.properties, query, prefix)
|
||||
}
|
||||
|
||||
const matchesVarChildren = (children: Var[], query: string, prefix: string[]): boolean => {
|
||||
return children.some((child) => {
|
||||
const nextPath = [...prefix, child.variable]
|
||||
if (matchesPath(nextPath, query))
|
||||
return true
|
||||
|
||||
const childChildren = child.children
|
||||
if (!childChildren)
|
||||
return false
|
||||
|
||||
if (Array.isArray(childChildren))
|
||||
return matchesVarChildren(childChildren, query, nextPath)
|
||||
|
||||
if (isStructuredOutputChildren(childChildren))
|
||||
return matchesStructuredOutput(childChildren, query, nextPath)
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
const matchesNestedVar = (itemData: Var, query: string): boolean => {
|
||||
const children = itemData.children
|
||||
if (!children)
|
||||
return false
|
||||
|
||||
if (Array.isArray(children))
|
||||
return matchesVarChildren(children, query, [itemData.variable])
|
||||
|
||||
if (isStructuredOutputChildren(children))
|
||||
return matchesStructuredOutput(children, query, [itemData.variable])
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type ItemProps = {
|
||||
nodeId: string
|
||||
title: string
|
||||
@@ -362,7 +433,11 @@ const VarReferenceVars: FC<Props> = ({
|
||||
const matchedByTitle = titleLower.includes(normalizedSearchTextLower)
|
||||
const nodeVars = matchedByTitle
|
||||
? node.vars
|
||||
: node.vars.filter(v => v.variable.toLowerCase().includes(normalizedSearchTextLower))
|
||||
: node.vars.filter((v) => {
|
||||
if (v.variable.toLowerCase().includes(normalizedSearchTextLower))
|
||||
return true
|
||||
return matchesNestedVar(v, normalizedSearchTextLower)
|
||||
})
|
||||
if (nodeVars.length === 0)
|
||||
return
|
||||
res.push({
|
||||
|
||||
@@ -54,8 +54,10 @@ export const useVarColor = (variables: string[], isExceptionVariable?: boolean,
|
||||
}
|
||||
|
||||
export const useVarName = (variables: string[], notShowFullPath?: boolean) => {
|
||||
const showName = VAR_SHOW_NAME_MAP[variables.join('.')]
|
||||
let variableFullPathName = variables.slice(1).join('.')
|
||||
const fullPathKey = variables.join('.')
|
||||
const keyWithoutNodePrefix = variables.slice(1).join('.')
|
||||
const showName = VAR_SHOW_NAME_MAP[fullPathKey] ?? VAR_SHOW_NAME_MAP[keyWithoutNodePrefix]
|
||||
let variableFullPathName = keyWithoutNodePrefix
|
||||
|
||||
if (isRagVariableVar(variables))
|
||||
variableFullPathName = variables.slice(2).join('.')
|
||||
|
||||
@@ -38,6 +38,7 @@ const ConditionVarSelector = ({
|
||||
valueSelector={valueSelector}
|
||||
varType={varType}
|
||||
availableNodes={availableNodes}
|
||||
availableVars={nodesOutputVars}
|
||||
isShort
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -121,6 +121,7 @@ const ConditionNumberInput = ({
|
||||
<VariableTag
|
||||
valueSelector={variableTransformer(value) as string[]}
|
||||
varType={VarType.number}
|
||||
availableVars={variables}
|
||||
isShort={isShort}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -57,6 +57,7 @@ const ConditionVariableSelector = ({
|
||||
valueSelector={valueSelector}
|
||||
varType={varType}
|
||||
availableNodes={availableNodes}
|
||||
availableVars={nodesOutputVars}
|
||||
isShort
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -75,7 +75,7 @@ const ConfigContextItem: FC<Props> = ({
|
||||
)}
|
||||
>
|
||||
<div className="text-text-secondary system-xs-semibold-uppercase">
|
||||
{t('nodes.llm.context', { ns: 'workflow' })}
|
||||
{t('nodes.llm.chatHistorry', { ns: 'workflow' })}
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<VariableLabelInSelect
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
|
||||
type ReasoningFormatConfigProps = {
|
||||
value?: 'tagged' | 'separated'
|
||||
onChange: (value: 'tagged' | 'separated') => void
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
const ReasoningFormatConfig: FC<ReasoningFormatConfigProps> = ({
|
||||
value = 'tagged',
|
||||
onChange,
|
||||
readonly = false,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Field
|
||||
title={t('nodes.llm.reasoningFormat.title', { ns: 'workflow' })}
|
||||
tooltip={t('nodes.llm.reasoningFormat.tooltip', { ns: 'workflow' })}
|
||||
operations={(
|
||||
// ON = separated, OFF = tagged
|
||||
<Switch
|
||||
defaultValue={value === 'separated'}
|
||||
onChange={enabled => onChange(enabled ? 'separated' : 'tagged')}
|
||||
size="md"
|
||||
disabled={readonly}
|
||||
key={value}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<div />
|
||||
</Field>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReasoningFormatConfig
|
||||
@@ -23,7 +23,6 @@ import MemoryConfig from '../_base/components/memory-config'
|
||||
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
|
||||
import ComputerUseConfig from './components/computer-use-config'
|
||||
import ConfigPrompt from './components/config-prompt'
|
||||
import ReasoningFormatConfig from './components/reasoning-format-config'
|
||||
import StructureOutput from './components/structure-output'
|
||||
import Tools from './components/tools'
|
||||
import MaxIterations from './components/tools/max-iterations'
|
||||
@@ -72,7 +71,6 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
handleStructureOutputEnableChange,
|
||||
handleStructureOutputChange,
|
||||
filterJinja2InputVar,
|
||||
handleReasoningFormatChange,
|
||||
isSupportSandbox,
|
||||
handleComputerUseChange,
|
||||
} = useConfig(id, data)
|
||||
@@ -335,13 +333,6 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
value={inputs.max_iterations}
|
||||
onChange={handleMaxIterationsChange}
|
||||
/>
|
||||
|
||||
{/* Reasoning Format */}
|
||||
<ReasoningFormatConfig
|
||||
value={inputs.reasoning_format || 'tagged'}
|
||||
onChange={handleReasoningFormatChange}
|
||||
readonly={readOnly}
|
||||
/>
|
||||
</div>
|
||||
</FieldCollapse>
|
||||
|
||||
@@ -410,28 +401,30 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
)}
|
||||
>
|
||||
<>
|
||||
<VarItem
|
||||
name="generation"
|
||||
type="object"
|
||||
description={t(`${i18nPrefix}.outputVars.generation`, { ns: 'workflow' })}
|
||||
subItems={[
|
||||
{
|
||||
name: 'content',
|
||||
type: 'string',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'reasoning_content',
|
||||
type: 'array[string]',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'tool_calls',
|
||||
type: 'array[object]',
|
||||
description: '',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
{!!inputs.computer_use && (
|
||||
<VarItem
|
||||
name="generation"
|
||||
type="object"
|
||||
description={t(`${i18nPrefix}.outputVars.generation`, { ns: 'workflow' })}
|
||||
subItems={[
|
||||
{
|
||||
name: 'content',
|
||||
type: 'string',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'reasoning_content',
|
||||
type: 'array[string]',
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
name: 'tool_calls',
|
||||
type: 'array[object]',
|
||||
description: '',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<VarItem
|
||||
name="text"
|
||||
type="string"
|
||||
|
||||
@@ -38,7 +38,6 @@ export type LLMNodeType = CommonNodeType & {
|
||||
}
|
||||
structured_output_enabled?: boolean
|
||||
structured_output?: StructuredOutput
|
||||
reasoning_format?: 'tagged' | 'separated'
|
||||
tools?: ToolValue[]
|
||||
tool_settings?: ToolSetting[]
|
||||
max_iterations?: number
|
||||
|
||||
@@ -362,14 +362,6 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
// reasoning format
|
||||
const handleReasoningFormatChange = useCallback((reasoningFormat: 'tagged' | 'separated') => {
|
||||
const newInputs = produce(inputRef.current, (draft) => {
|
||||
draft.reasoning_format = reasoningFormat
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [setInputs])
|
||||
|
||||
const {
|
||||
availableVars,
|
||||
availableNodesWithParent,
|
||||
@@ -411,7 +403,6 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
setStructuredOutputCollapsed,
|
||||
handleStructureOutputEnableChange,
|
||||
filterJinja2InputVar,
|
||||
handleReasoningFormatChange,
|
||||
isSupportSandbox,
|
||||
handleComputerUseChange,
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ const ConditionVarSelector = ({
|
||||
valueSelector={valueSelector}
|
||||
varType={varType}
|
||||
availableNodes={availableNodes}
|
||||
availableVars={nodesOutputVars}
|
||||
isShort
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -121,6 +121,7 @@ const ConditionNumberInput = ({
|
||||
<VariableTag
|
||||
valueSelector={variableTransformer(value) as string[]}
|
||||
varType={VarType.number}
|
||||
availableVars={variables}
|
||||
isShort={isShort}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1482,7 +1482,7 @@
|
||||
},
|
||||
"app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx": {
|
||||
"react-refresh/only-export-components": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/base/prompt-editor/plugins/last-run-block/index.tsx": {
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "محتوى التفكير",
|
||||
"nodes.llm.outputVars.usage": "معلومات استخدام النموذج",
|
||||
"nodes.llm.prompt": "المطالبة",
|
||||
"nodes.llm.reasoningFormat.separated": "فصل علامات التفكير",
|
||||
"nodes.llm.reasoningFormat.tagged": "الاحتفاظ بعلامات التفكير",
|
||||
"nodes.llm.reasoningFormat.title": "تمكين فصل علامة التفكير",
|
||||
"nodes.llm.reasoningFormat.tooltip": "استخراج المحتوى من علامات التفكير وتخزينه في حقل content_reasoning.",
|
||||
"nodes.llm.resolution.high": "عالية",
|
||||
"nodes.llm.resolution.low": "منخفضة",
|
||||
"nodes.llm.resolution.name": "الدقة",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Reasoning-Inhalt",
|
||||
"nodes.llm.outputVars.usage": "Nutzungsinformationen des Modells",
|
||||
"nodes.llm.prompt": "Prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Separate Denk tags",
|
||||
"nodes.llm.reasoningFormat.tagged": "Behalte die Denk-Tags",
|
||||
"nodes.llm.reasoningFormat.title": "Aktivieren Sie die Trennung von Argumentations-Tags",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Inhalte aus Denk-Tags extrahieren und im Feld reasoning_content speichern.",
|
||||
"nodes.llm.resolution.high": "Hoch",
|
||||
"nodes.llm.resolution.low": "Niedrig",
|
||||
"nodes.llm.resolution.name": "Auflösung",
|
||||
|
||||
@@ -210,7 +210,7 @@
|
||||
"showMyCreatedAppsOnly": "Created by me",
|
||||
"structOutput.LLMResponse": "LLM Response",
|
||||
"structOutput.configure": "Configure",
|
||||
"structOutput.disabledByComputerUse": "Structured Output is disabled when Computer Use is enabled.",
|
||||
"structOutput.disabledByComputerUse": "Structured Output is disabled when Agent Mode is enabled.",
|
||||
"structOutput.disabledByTools": "Structured Output is disabled when tools are enabled.",
|
||||
"structOutput.modelNotSupported": "Model not supported",
|
||||
"structOutput.modelNotSupportedTip": "The current model does not support this feature and is automatically downgraded to prompt injection.",
|
||||
|
||||
@@ -497,8 +497,8 @@
|
||||
"plugin.serpapi.apiKeyPlaceholder": "Enter your API key",
|
||||
"plugin.serpapi.keyFrom": "Get your SerpAPI key from SerpAPI Account Page",
|
||||
"promptEditor.context.item.desc": "Insert context template",
|
||||
"promptEditor.context.item.title": "Chat History",
|
||||
"promptEditor.context.modal.add": "Add Chat History ",
|
||||
"promptEditor.context.item.title": "Context",
|
||||
"promptEditor.context.modal.add": "Add Context ",
|
||||
"promptEditor.context.modal.footer": "You can manage contexts in the Context section below.",
|
||||
"promptEditor.context.modal.title": "{{num}} Knowledge in Context",
|
||||
"promptEditor.existed": "Already exists in the prompt",
|
||||
@@ -616,8 +616,8 @@
|
||||
"sandboxProvider.notConfigured": "Not Configured",
|
||||
"sandboxProvider.otherProvider": "OTHER PROVIDERS",
|
||||
"sandboxProvider.setAsActive": "Set as Active",
|
||||
"sandboxProvider.ssh.description": "Run agent workloads in a remote SSH VM with file transfer support.",
|
||||
"sandboxProvider.ssh.label": "SSH VM",
|
||||
"sandboxProvider.ssh.description": "Run agent workloads in a remote SSH with file transfer support.",
|
||||
"sandboxProvider.ssh.label": "SSH",
|
||||
"sandboxProvider.switchModal.cancel": "Cancel",
|
||||
"sandboxProvider.switchModal.confirm": "Switch",
|
||||
"sandboxProvider.switchModal.confirmText": "You are about to switch the active sandbox provider to <bold>{{provider}}</bold>.",
|
||||
|
||||
@@ -776,6 +776,7 @@
|
||||
"nodes.llm.addContext": "Add Context",
|
||||
"nodes.llm.addMessage": "Add Message",
|
||||
"nodes.llm.advancedSettings": "Advanced Settings",
|
||||
"nodes.llm.chatHistorry": "Chat History",
|
||||
"nodes.llm.computerUse.disabledByStructuredOutput": "Agent mode is disabled when Structured Output is enabled.",
|
||||
"nodes.llm.computerUse.referenceTools": "Reference Tools",
|
||||
"nodes.llm.computerUse.referenceToolsEmpty": "Tools referenced in the prompt will appear here",
|
||||
@@ -818,10 +819,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Reasoning Content",
|
||||
"nodes.llm.outputVars.usage": "Model Usage Information",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Separate think tags",
|
||||
"nodes.llm.reasoningFormat.tagged": "Keep think tags",
|
||||
"nodes.llm.reasoningFormat.title": "Enable reasoning tag separation",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Extract content from think tags and store it in the reasoning_content field.",
|
||||
"nodes.llm.removeContext": "Remove context",
|
||||
"nodes.llm.resolution.high": "High",
|
||||
"nodes.llm.resolution.low": "Low",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Contenido de razonamiento",
|
||||
"nodes.llm.outputVars.usage": "Información de uso del modelo",
|
||||
"nodes.llm.prompt": "indicación",
|
||||
"nodes.llm.reasoningFormat.separated": "Separar etiquetas de pensamiento",
|
||||
"nodes.llm.reasoningFormat.tagged": "Mantén las etiquetas de pensamiento",
|
||||
"nodes.llm.reasoningFormat.title": "Habilitar la separación de etiquetas de razonamiento",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Extraer contenido de las etiquetas de pensamiento y almacenarlo en el campo reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Alta",
|
||||
"nodes.llm.resolution.low": "Baja",
|
||||
"nodes.llm.resolution.name": "Resolución",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "محتوای استدلال",
|
||||
"nodes.llm.outputVars.usage": "اطلاعات استفاده از مدل",
|
||||
"nodes.llm.prompt": "پیشنهاد",
|
||||
"nodes.llm.reasoningFormat.separated": "تگهای تفکر جداگانه",
|
||||
"nodes.llm.reasoningFormat.tagged": "به فکر برچسبها باشید",
|
||||
"nodes.llm.reasoningFormat.title": "فعالسازی جداسازی برچسبهای استدلال",
|
||||
"nodes.llm.reasoningFormat.tooltip": "محتوا را از تگهای تفکر استخراج کرده و در فیلد reasoning_content ذخیره کنید.",
|
||||
"nodes.llm.resolution.high": "بالا",
|
||||
"nodes.llm.resolution.low": "پایین",
|
||||
"nodes.llm.resolution.name": "وضوح",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Contenu de raisonnement",
|
||||
"nodes.llm.outputVars.usage": "Informations sur l'utilisation du modèle",
|
||||
"nodes.llm.prompt": "invite",
|
||||
"nodes.llm.reasoningFormat.separated": "Séparer les balises de réflexion",
|
||||
"nodes.llm.reasoningFormat.tagged": "Gardez les étiquettes de pensée",
|
||||
"nodes.llm.reasoningFormat.title": "Activer la séparation des balises de raisonnement",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Extraire le contenu des balises think et le stocker dans le champ reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Haute",
|
||||
"nodes.llm.resolution.low": "Basse",
|
||||
"nodes.llm.resolution.name": "Résolution",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "तर्क सामग्री",
|
||||
"nodes.llm.outputVars.usage": "मॉडल उपयोग जानकारी",
|
||||
"nodes.llm.prompt": "प्रॉम्प्ट",
|
||||
"nodes.llm.reasoningFormat.separated": "अलग सोच टैग",
|
||||
"nodes.llm.reasoningFormat.tagged": "टैग्स के बारे में सोचते रहें",
|
||||
"nodes.llm.reasoningFormat.title": "कारण संबंध टैग विभाजन सक्षम करें",
|
||||
"nodes.llm.reasoningFormat.tooltip": "थिंक टैग से सामग्री निकाले और इसे reasoning_content क्षेत्र में संग्रहित करें।",
|
||||
"nodes.llm.resolution.high": "उच्च",
|
||||
"nodes.llm.resolution.low": "निम्न",
|
||||
"nodes.llm.resolution.name": "रेजोल्यूशन",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Konten penalaran",
|
||||
"nodes.llm.outputVars.usage": "Informasi Penggunaan Model",
|
||||
"nodes.llm.prompt": "cepat",
|
||||
"nodes.llm.reasoningFormat.separated": "Pisahkan tag pemikiran",
|
||||
"nodes.llm.reasoningFormat.tagged": "Tetap pikirkan tag",
|
||||
"nodes.llm.reasoningFormat.title": "Aktifkan pemisahan tag penalaran",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Ekstrak konten dari tag pikir dan simpan di field reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Tinggi",
|
||||
"nodes.llm.resolution.low": "Rendah",
|
||||
"nodes.llm.resolution.name": "Resolusi",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Contenuto del ragionamento",
|
||||
"nodes.llm.outputVars.usage": "Informazioni sull'utilizzo del modello",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Tag di pensiero separati",
|
||||
"nodes.llm.reasoningFormat.tagged": "Continua a pensare ai tag",
|
||||
"nodes.llm.reasoningFormat.title": "Abilita la separazione dei tag di ragionamento",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Estrai il contenuto dai tag think e conservalo nel campo reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Alta",
|
||||
"nodes.llm.resolution.low": "Bassa",
|
||||
"nodes.llm.resolution.name": "Risoluzione",
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"showMyCreatedAppsOnly": "自分が作成したアプリ",
|
||||
"structOutput.LLMResponse": "LLM のレスポンス",
|
||||
"structOutput.configure": "設定",
|
||||
"structOutput.disabledByComputerUse": "Computer Use を有効にすると、構造化出力は無効になります。",
|
||||
"structOutput.disabledByComputerUse": "Agent Mode を有効にすると、構造化出力は無効になります。",
|
||||
"structOutput.disabledByTools": "ツールを有効にすると、構造化出力は無効になります。",
|
||||
"structOutput.modelNotSupported": "モデルが対応していません",
|
||||
"structOutput.modelNotSupportedTip": "現在のモデルはこの機能に対応しておらず、自動的にプロンプトインジェクションに切り替わります。",
|
||||
|
||||
@@ -497,8 +497,8 @@
|
||||
"plugin.serpapi.apiKeyPlaceholder": "API キーを入力してください",
|
||||
"plugin.serpapi.keyFrom": "SerpAPI アカウントページから SerpAPI キーを取得してください",
|
||||
"promptEditor.context.item.desc": "コンテキストテンプレートを挿入",
|
||||
"promptEditor.context.item.title": "チャット履歴",
|
||||
"promptEditor.context.modal.add": "チャット履歴を追加",
|
||||
"promptEditor.context.item.title": "コンテキスト",
|
||||
"promptEditor.context.modal.add": "コンテキストを追加",
|
||||
"promptEditor.context.modal.footer": "以下のコンテキストセクションでコンテキストを管理できます。",
|
||||
"promptEditor.context.modal.title": "{{num}} 番目のコンテキスト",
|
||||
"promptEditor.existed": "プロンプトにすでに存在します",
|
||||
|
||||
@@ -751,6 +751,7 @@
|
||||
"nodes.listFilter.selectVariableKeyPlaceholder": "サブ変数キーを選択する",
|
||||
"nodes.llm.addContext": "コンテキスト追加",
|
||||
"nodes.llm.addMessage": "メッセージ追加",
|
||||
"nodes.llm.chatHistorry": "チャット履歴",
|
||||
"nodes.llm.computerUse.disabledByStructuredOutput": "構造化出力を有効にすると、Agent mode は無効になります。",
|
||||
"nodes.llm.computerUse.title": "Agent mode",
|
||||
"nodes.llm.computerUse.tooltip": "Agent mode で実行時ファイルシステムとツールアクセスを管理します。",
|
||||
@@ -790,10 +791,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "推論内容",
|
||||
"nodes.llm.outputVars.usage": "モデル使用量",
|
||||
"nodes.llm.prompt": "プロンプト",
|
||||
"nodes.llm.reasoningFormat.separated": "思考タグを分ける",
|
||||
"nodes.llm.reasoningFormat.tagged": "タグを考え続けてください",
|
||||
"nodes.llm.reasoningFormat.title": "推論タグの分離を有効にする",
|
||||
"nodes.llm.reasoningFormat.tooltip": "thinkタグから内容を抽出し、それをreasoning_contentフィールドに保存します。",
|
||||
"nodes.llm.removeContext": "コンテキストを削除",
|
||||
"nodes.llm.resolution.high": "高",
|
||||
"nodes.llm.resolution.low": "低",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "추론 내용",
|
||||
"nodes.llm.outputVars.usage": "모델 사용 정보",
|
||||
"nodes.llm.prompt": "프롬프트",
|
||||
"nodes.llm.reasoningFormat.separated": "추론 태그 분리",
|
||||
"nodes.llm.reasoningFormat.tagged": "추론 태그 유지",
|
||||
"nodes.llm.reasoningFormat.title": "추론 태그 분리 활성화",
|
||||
"nodes.llm.reasoningFormat.tooltip": "추론 태그에서 내용을 추출하고 이를 reasoning_content 필드에 저장합니다",
|
||||
"nodes.llm.resolution.high": "높음",
|
||||
"nodes.llm.resolution.low": "낮음",
|
||||
"nodes.llm.resolution.name": "해상도",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Treść rozumowania",
|
||||
"nodes.llm.outputVars.usage": "Informacje o użyciu modelu",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Oddziel tagi myślenia",
|
||||
"nodes.llm.reasoningFormat.tagged": "Zachowaj myśl tagi",
|
||||
"nodes.llm.reasoningFormat.title": "Włącz separację tagów uzasadnienia",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Wyodrębnij treść z tagów think i przechowaj ją w polu reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Wysoka",
|
||||
"nodes.llm.resolution.low": "Niska",
|
||||
"nodes.llm.resolution.name": "Rozdzielczość",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Conteúdo de raciocínio",
|
||||
"nodes.llm.outputVars.usage": "Informações de uso do modelo",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Separe as tags de pensamento",
|
||||
"nodes.llm.reasoningFormat.tagged": "Mantenha as tags de pensamento",
|
||||
"nodes.llm.reasoningFormat.title": "Ativar separação de tags de raciocínio",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Extraia o conteúdo das tags de pensamento e armazene-o no campo reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Alta",
|
||||
"nodes.llm.resolution.low": "Baixa",
|
||||
"nodes.llm.resolution.name": "Resolução",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Conținut de raționament",
|
||||
"nodes.llm.outputVars.usage": "Informații de utilizare a modelului",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Etichete de gândire separate",
|
||||
"nodes.llm.reasoningFormat.tagged": "Ține minte etichetele",
|
||||
"nodes.llm.reasoningFormat.title": "Activează separarea etichetelor de raționare",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Extrage conținutul din etichetele think și stochează-l în câmpul reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Înaltă",
|
||||
"nodes.llm.resolution.low": "Joasă",
|
||||
"nodes.llm.resolution.name": "Rezoluție",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Содержимое рассуждений",
|
||||
"nodes.llm.outputVars.usage": "Информация об использовании модели",
|
||||
"nodes.llm.prompt": "подсказка",
|
||||
"nodes.llm.reasoningFormat.separated": "Отдельные теги для мышления",
|
||||
"nodes.llm.reasoningFormat.tagged": "Продолжайте думать о тегах",
|
||||
"nodes.llm.reasoningFormat.title": "Включите разделение тегов на основе логики",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Извлечь содержимое из тегов think и сохранить его в поле reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Высокое",
|
||||
"nodes.llm.resolution.low": "Низкое",
|
||||
"nodes.llm.resolution.name": "Разрешение",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Vsebina razmišljanja",
|
||||
"nodes.llm.outputVars.usage": "Informacije o uporabi modela",
|
||||
"nodes.llm.prompt": "ukaz",
|
||||
"nodes.llm.reasoningFormat.separated": "Ločite oznake za razmišljanje",
|
||||
"nodes.llm.reasoningFormat.tagged": "Ohranite oznake za razmišljanje",
|
||||
"nodes.llm.reasoningFormat.title": "Omogoči ločevanje oznak za razsojanje",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Izvleći vsebino iz miselnih oznak in jo shraniti v polje reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Visoko",
|
||||
"nodes.llm.resolution.low": "Nizko",
|
||||
"nodes.llm.resolution.name": "Resolucija",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "เนื้อหาการให้เหตุผล",
|
||||
"nodes.llm.outputVars.usage": "ข้อมูลการใช้งานรุ่น",
|
||||
"nodes.llm.prompt": "พร้อมท์",
|
||||
"nodes.llm.reasoningFormat.separated": "แยกแท็กความคิดเห็น",
|
||||
"nodes.llm.reasoningFormat.tagged": "รักษาความคิดเกี่ยวกับแท็ก",
|
||||
"nodes.llm.reasoningFormat.title": "เปิดใช้งานการแยกแท็กการเหตุผล",
|
||||
"nodes.llm.reasoningFormat.tooltip": "ดึงเนื้อหาจากแท็กคิดและเก็บไว้ในฟิลด์ reasoning_content.",
|
||||
"nodes.llm.resolution.high": "สูง",
|
||||
"nodes.llm.resolution.low": "ต่ํา",
|
||||
"nodes.llm.resolution.name": "มติ",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Akıl yürütme içeriği",
|
||||
"nodes.llm.outputVars.usage": "Model Kullanım Bilgileri",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Ayrı düşünce etiketleri",
|
||||
"nodes.llm.reasoningFormat.tagged": "Etiketleri düşünmeye devam et",
|
||||
"nodes.llm.reasoningFormat.title": "Akıl yürütme etiket ayrımını etkinleştir",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Düşünce etiketlerinden içeriği çıkarın ve bunu reasoning_content alanında saklayın.",
|
||||
"nodes.llm.resolution.high": "Yüksek",
|
||||
"nodes.llm.resolution.low": "Düşük",
|
||||
"nodes.llm.resolution.name": "Çözünürlük",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Зміст міркування",
|
||||
"nodes.llm.outputVars.usage": "Інформація про використання моделі",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Окремі теги для думок",
|
||||
"nodes.llm.reasoningFormat.tagged": "Продовжуйте думати про мітки",
|
||||
"nodes.llm.reasoningFormat.title": "Увімкніть розділення тегів для міркування",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Витягніть вміст з тегів think і зберігайте його в полі reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Висока",
|
||||
"nodes.llm.resolution.low": "Низька",
|
||||
"nodes.llm.resolution.name": "Роздільна здатність",
|
||||
|
||||
@@ -780,10 +780,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "Nội dung lập luận",
|
||||
"nodes.llm.outputVars.usage": "Thông tin sử dụng mô hình",
|
||||
"nodes.llm.prompt": "prompt",
|
||||
"nodes.llm.reasoningFormat.separated": "Tách biệt các thẻ suy nghĩ",
|
||||
"nodes.llm.reasoningFormat.tagged": "Giữ lại thẻ suy nghĩ",
|
||||
"nodes.llm.reasoningFormat.title": "Bật chế độ phân tách nhãn lý luận",
|
||||
"nodes.llm.reasoningFormat.tooltip": "Trích xuất nội dung từ các thẻ think và lưu nó vào trường reasoning_content.",
|
||||
"nodes.llm.resolution.high": "Cao",
|
||||
"nodes.llm.resolution.low": "Thấp",
|
||||
"nodes.llm.resolution.name": "Độ phân giải",
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
"showMyCreatedAppsOnly": "我创建的",
|
||||
"structOutput.LLMResponse": "LLM 的响应",
|
||||
"structOutput.configure": "配置",
|
||||
"structOutput.disabledByComputerUse": "启用计算机使用时,将禁用结构化输出。",
|
||||
"structOutput.disabledByComputerUse": "启用 Agent 模式时,将禁用结构化输出。",
|
||||
"structOutput.disabledByTools": "启用工具时,将禁用结构化输出。",
|
||||
"structOutput.modelNotSupported": "模型不支持",
|
||||
"structOutput.modelNotSupportedTip": "当前模型不支持此功能,将自动降级为提示注入。",
|
||||
|
||||
@@ -497,8 +497,8 @@
|
||||
"plugin.serpapi.apiKeyPlaceholder": "输入你的 API 密钥",
|
||||
"plugin.serpapi.keyFrom": "从 SerpAPI 帐户页面获取您的 SerpAPI 密钥",
|
||||
"promptEditor.context.item.desc": "插入上下文模板",
|
||||
"promptEditor.context.item.title": "对话历史",
|
||||
"promptEditor.context.modal.add": "添加对话历史",
|
||||
"promptEditor.context.item.title": "上下文",
|
||||
"promptEditor.context.modal.add": "添加上下文",
|
||||
"promptEditor.context.modal.footer": "您可以在下面的“上下文”部分中管理上下文。",
|
||||
"promptEditor.context.modal.title": "有 {{num}} 个知识库在上下文中",
|
||||
"promptEditor.existed": "Prompt 中已存在",
|
||||
|
||||
@@ -769,6 +769,7 @@
|
||||
"nodes.listFilter.selectVariableKeyPlaceholder": "选择子变量的 Key",
|
||||
"nodes.llm.addContext": "添加上下文",
|
||||
"nodes.llm.addMessage": "添加消息",
|
||||
"nodes.llm.chatHistorry": "对话历史",
|
||||
"nodes.llm.computerUse.disabledByStructuredOutput": "启用结构化输出时,将禁用 Agent 模式。",
|
||||
"nodes.llm.computerUse.referenceTools": "引用工具",
|
||||
"nodes.llm.computerUse.referenceToolsEmpty": "提示词中引用的工具会显示在这里",
|
||||
@@ -811,10 +812,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "推理内容",
|
||||
"nodes.llm.outputVars.usage": "模型用量信息",
|
||||
"nodes.llm.prompt": "提示词",
|
||||
"nodes.llm.reasoningFormat.separated": "分开思考标签",
|
||||
"nodes.llm.reasoningFormat.tagged": "保持思考标签",
|
||||
"nodes.llm.reasoningFormat.title": "启用推理标签分离",
|
||||
"nodes.llm.reasoningFormat.tooltip": "从think标签中提取内容,并将其存储在reasoning_content字段中。",
|
||||
"nodes.llm.removeContext": "删除上下文",
|
||||
"nodes.llm.resolution.high": "高",
|
||||
"nodes.llm.resolution.low": "低",
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
"showMyCreatedAppsOnly": "我建立的",
|
||||
"structOutput.LLMResponse": "LLM 回應",
|
||||
"structOutput.configure": "配置",
|
||||
"structOutput.disabledByComputerUse": "啟用電腦使用時,將停用結構化輸出。",
|
||||
"structOutput.disabledByComputerUse": "啟用 Agent 模式時,將停用結構化輸出。",
|
||||
"structOutput.disabledByTools": "啟用工具時,將停用結構化輸出。",
|
||||
"structOutput.modelNotSupported": "模型不支持",
|
||||
"structOutput.modelNotSupportedTip": "當前模型不支持此功能,並自動降級為提示注入。",
|
||||
|
||||
@@ -497,8 +497,8 @@
|
||||
"plugin.serpapi.apiKeyPlaceholder": "輸入你的 API 金鑰",
|
||||
"plugin.serpapi.keyFrom": "從 SerpAPI 帳戶頁面獲取您的 SerpAPI 金鑰",
|
||||
"promptEditor.context.item.desc": "插入上下文模板",
|
||||
"promptEditor.context.item.title": "對話歷史",
|
||||
"promptEditor.context.modal.add": "新增對話歷史",
|
||||
"promptEditor.context.item.title": "上下文",
|
||||
"promptEditor.context.modal.add": "新增上下文",
|
||||
"promptEditor.context.modal.footer": "您可以在下面的“上下文”部分中管理上下文。",
|
||||
"promptEditor.context.modal.title": "有 {{num}} 個知識庫在上下文中",
|
||||
"promptEditor.existed": "Prompt 中已存在",
|
||||
|
||||
@@ -751,6 +751,7 @@
|
||||
"nodes.listFilter.selectVariableKeyPlaceholder": "Select sub variable key(選擇子變數鍵)",
|
||||
"nodes.llm.addContext": "新增上下文",
|
||||
"nodes.llm.addMessage": "新增消息",
|
||||
"nodes.llm.chatHistorry": "對話歷史",
|
||||
"nodes.llm.computerUse.disabledByStructuredOutput": "啟用結構化輸出時,將停用 Agent 模式。",
|
||||
"nodes.llm.computerUse.referenceToolsEmpty": "提示詞中引用的工具會顯示在這裡",
|
||||
"nodes.llm.computerUse.title": "Agent 模式",
|
||||
@@ -791,10 +792,6 @@
|
||||
"nodes.llm.outputVars.reasoning_content": "推理內容",
|
||||
"nodes.llm.outputVars.usage": "模型用量信息",
|
||||
"nodes.llm.prompt": "提示詞",
|
||||
"nodes.llm.reasoningFormat.separated": "分開思考標籤",
|
||||
"nodes.llm.reasoningFormat.tagged": "保持思考標籤",
|
||||
"nodes.llm.reasoningFormat.title": "啟用推理標籤分離",
|
||||
"nodes.llm.reasoningFormat.tooltip": "從 think 標籤中提取內容並將其存儲在 reasoning_content 欄位中。",
|
||||
"nodes.llm.removeContext": "刪除上下文",
|
||||
"nodes.llm.resolution.high": "高",
|
||||
"nodes.llm.resolution.low": "低",
|
||||
|
||||
Reference in New Issue
Block a user