mirror of https://github.com/langgenius/dify.git
Merge remote-tracking branch 'origin/feat/workflow' into feat/workflow
This commit is contained in:
commit
bab88efda9
|
|
@ -8,7 +8,7 @@ api = ExternalApi(bp)
|
|||
from . import admin, apikey, extension, feature, setup, version, ping
|
||||
# Import app controllers
|
||||
from .app import (advanced_prompt_template, annotation, app, audio, completion, conversation, generator, message,
|
||||
model_config, site, statistic, workflow, workflow_run, workflow_app_log, workflow_statistic)
|
||||
model_config, site, statistic, workflow, workflow_run, workflow_app_log, workflow_statistic, agent)
|
||||
# Import auth controllers
|
||||
from .auth import activate, data_source_oauth, login, oauth
|
||||
# Import billing controllers
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
from flask_restful import Resource, reqparse
|
||||
|
||||
from controllers.console import api
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.setup import setup_required
|
||||
from controllers.console.wraps import account_initialization_required
|
||||
from libs.helper import uuid_value
|
||||
from libs.login import login_required
|
||||
from models.model import AppMode
|
||||
from services.agent_service import AgentService
|
||||
|
||||
|
||||
class AgentLogApi(Resource):
|
||||
@setup_required
|
||||
@login_required
|
||||
@account_initialization_required
|
||||
@get_app_model(mode=[AppMode.AGENT_CHAT])
|
||||
def get(self, app_model):
|
||||
"""Get agent logs"""
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument('message_id', type=uuid_value, required=True, location='args')
|
||||
parser.add_argument('conversation_id', type=uuid_value, required=True, location='args')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return AgentService.get_agent_logs(
|
||||
app_model,
|
||||
args['conversation_id'],
|
||||
args['message_id']
|
||||
)
|
||||
|
||||
api.add_resource(AgentLogApi, '/apps/<uuid:app_id>/agent/logs')
|
||||
|
|
@ -11,6 +11,7 @@ class ToolProviderType(Enum):
|
|||
Enum class for tool provider
|
||||
"""
|
||||
BUILT_IN = "built-in"
|
||||
DATASET_RETRIEVAL = "dataset-retrieval"
|
||||
APP_BASED = "app-based"
|
||||
API_BASED = "api-based"
|
||||
|
||||
|
|
@ -161,6 +162,8 @@ class ToolIdentity(BaseModel):
|
|||
author: str = Field(..., description="The author of the tool")
|
||||
name: str = Field(..., description="The name of the tool")
|
||||
label: I18nObject = Field(..., description="The label of the tool")
|
||||
provider: str = Field(..., description="The provider of the tool")
|
||||
icon: Optional[str] = None
|
||||
|
||||
class ToolCredentialsOption(BaseModel):
|
||||
value: str = Field(..., description="The value of the option")
|
||||
|
|
@ -334,23 +337,25 @@ class ToolInvokeMeta(BaseModel):
|
|||
"""
|
||||
time_cost: float = Field(..., description="The time cost of the tool invoke")
|
||||
error: Optional[str] = None
|
||||
tool_config: Optional[dict] = None
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> 'ToolInvokeMeta':
|
||||
"""
|
||||
Get an empty instance of ToolInvokeMeta
|
||||
"""
|
||||
return cls(time_cost=0.0, error=None)
|
||||
return cls(time_cost=0.0, error=None, tool_config={})
|
||||
|
||||
@classmethod
|
||||
def error_instance(cls, error: str) -> 'ToolInvokeMeta':
|
||||
"""
|
||||
Get an instance of ToolInvokeMeta with error
|
||||
"""
|
||||
return cls(time_cost=0.0, error=error)
|
||||
return cls(time_cost=0.0, error=error, tool_config={})
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
'time_cost': self.time_cost,
|
||||
'error': self.error,
|
||||
'tool_config': self.tool_config,
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ from models.tools import ApiToolProvider
|
|||
|
||||
|
||||
class ApiBasedToolProviderController(ToolProviderController):
|
||||
provider_id: str
|
||||
|
||||
@staticmethod
|
||||
def from_db(db_provider: ApiToolProvider, auth_type: ApiProviderAuthType) -> 'ApiBasedToolProviderController':
|
||||
credentials_schema = {
|
||||
|
|
@ -89,9 +91,10 @@ class ApiBasedToolProviderController(ToolProviderController):
|
|||
'en_US': db_provider.description,
|
||||
'zh_Hans': db_provider.description
|
||||
},
|
||||
'icon': db_provider.icon
|
||||
'icon': db_provider.icon,
|
||||
},
|
||||
'credentials_schema': credentials_schema
|
||||
'credentials_schema': credentials_schema,
|
||||
'provider_id': db_provider.id,
|
||||
})
|
||||
|
||||
@property
|
||||
|
|
@ -120,7 +123,8 @@ class ApiBasedToolProviderController(ToolProviderController):
|
|||
'en_US': tool_bundle.operation_id,
|
||||
'zh_Hans': tool_bundle.operation_id
|
||||
},
|
||||
'icon': tool_bundle.icon if tool_bundle.icon else ''
|
||||
'icon': self.identity.icon,
|
||||
'provider': self.provider_id,
|
||||
},
|
||||
'description': {
|
||||
'human': {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ class BuiltinToolProviderController(ToolProviderController):
|
|||
script_path=path.join(path.dirname(path.realpath(__file__)),
|
||||
'builtin', provider, 'tools', f'{tool_name}.py'),
|
||||
parent_type=BuiltinTool)
|
||||
tool["identity"]["provider"] = provider
|
||||
tools.append(assistant_tool_class(**tool))
|
||||
|
||||
self.tools = tools
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import requests
|
|||
|
||||
import core.helper.ssrf_proxy as ssrf_proxy
|
||||
from core.tools.entities.tool_bundle import ApiBasedToolBundle
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage
|
||||
from core.tools.entities.tool_entities import ToolInvokeMessage, ToolProviderType
|
||||
from core.tools.entities.user_entities import UserToolProvider
|
||||
from core.tools.errors import ToolInvokeError, ToolParameterValidationError, ToolProviderCredentialValidationError
|
||||
from core.tools.tool.tool import Tool
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ class ApiTool(Tool):
|
|||
api_bundle=self.api_bundle.copy() if self.api_bundle else None,
|
||||
runtime=Tool.Runtime(**meta)
|
||||
)
|
||||
|
||||
|
||||
def validate_credentials(self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False) -> str:
|
||||
"""
|
||||
validate the credentials for Api tool
|
||||
|
|
@ -49,6 +50,9 @@ class ApiTool(Tool):
|
|||
# validate response
|
||||
return self.validate_and_parse_response(response)
|
||||
|
||||
def tool_provider_type(self) -> ToolProviderType:
|
||||
return UserToolProvider.ProviderType.API
|
||||
|
||||
def assembling_request(self, parameters: dict[str, Any]) -> dict[str, Any]:
|
||||
headers = {}
|
||||
credentials = self.runtime.credentials or {}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
from core.model_runtime.entities.llm_entities import LLMResult
|
||||
from core.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage
|
||||
from core.tools.entities.tool_entities import ToolProviderType
|
||||
from core.tools.entities.user_entities import UserToolProvider
|
||||
from core.tools.model.tool_model_manager import ToolModelManager
|
||||
from core.tools.tool.tool import Tool
|
||||
from core.tools.utils.web_reader_tool import get_url
|
||||
|
|
@ -40,6 +42,9 @@ class BuiltinTool(Tool):
|
|||
prompt_messages=prompt_messages,
|
||||
)
|
||||
|
||||
def tool_provider_type(self) -> ToolProviderType:
|
||||
return UserToolProvider.ProviderType.BUILTIN
|
||||
|
||||
def get_max_tokens(self) -> int:
|
||||
"""
|
||||
get max tokens
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@ from core.app.entities.app_invoke_entities import InvokeFrom
|
|||
from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCallbackHandler
|
||||
from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
|
||||
from core.tools.entities.common_entities import I18nObject
|
||||
from core.tools.entities.tool_entities import ToolDescription, ToolIdentity, ToolInvokeMessage, ToolParameter
|
||||
from core.tools.entities.tool_entities import (
|
||||
ToolDescription,
|
||||
ToolIdentity,
|
||||
ToolInvokeMessage,
|
||||
ToolParameter,
|
||||
ToolProviderType,
|
||||
)
|
||||
from core.tools.tool.tool import Tool
|
||||
|
||||
|
||||
|
|
@ -53,7 +59,7 @@ class DatasetRetrieverTool(Tool):
|
|||
for langchain_tool in langchain_tools:
|
||||
tool = DatasetRetrieverTool(
|
||||
langchain_tool=langchain_tool,
|
||||
identity=ToolIdentity(author='', name=langchain_tool.name, label=I18nObject(en_US='', zh_Hans='')),
|
||||
identity=ToolIdentity(provider='', author='', name=langchain_tool.name, label=I18nObject(en_US='', zh_Hans='')),
|
||||
parameters=[],
|
||||
is_team_authorization=True,
|
||||
description=ToolDescription(
|
||||
|
|
@ -77,6 +83,9 @@ class DatasetRetrieverTool(Tool):
|
|||
required=True,
|
||||
default=''),
|
||||
]
|
||||
|
||||
def tool_provider_type(self) -> ToolProviderType:
|
||||
return ToolProviderType.DATASET_RETRIEVAL
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from core.model_runtime.entities.message_entities import (
|
|||
UserPromptMessage,
|
||||
)
|
||||
from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
|
||||
from core.tools.entities.tool_entities import ModelToolPropertyKey, ToolInvokeMessage
|
||||
from core.tools.entities.tool_entities import ModelToolPropertyKey, ToolInvokeMessage, ToolProviderType
|
||||
from core.tools.tool.tool import Tool
|
||||
|
||||
VISION_PROMPT = """## Image Recognition Task
|
||||
|
|
@ -79,6 +79,9 @@ class ModelTool(Tool):
|
|||
"""
|
||||
pass
|
||||
|
||||
def tool_provider_type(self) -> ToolProviderType:
|
||||
return ToolProviderType.BUILT_IN
|
||||
|
||||
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
|
||||
"""
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from core.tools.entities.tool_entities import (
|
|||
ToolIdentity,
|
||||
ToolInvokeMessage,
|
||||
ToolParameter,
|
||||
ToolProviderType,
|
||||
ToolRuntimeImageVariable,
|
||||
ToolRuntimeVariable,
|
||||
ToolRuntimeVariablePool,
|
||||
|
|
@ -59,6 +60,14 @@ class Tool(BaseModel, ABC):
|
|||
runtime=Tool.Runtime(**meta),
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def tool_provider_type(self) -> ToolProviderType:
|
||||
"""
|
||||
get the tool provider type
|
||||
|
||||
:return: the tool provider type
|
||||
"""
|
||||
|
||||
def load_variables(self, variables: ToolRuntimeVariablePool):
|
||||
"""
|
||||
load variables from database
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from copy import deepcopy
|
||||
from datetime import datetime, timezone
|
||||
from typing import Union
|
||||
|
||||
|
|
@ -55,11 +56,7 @@ class ToolEngine:
|
|||
tool_inputs=tool_parameters
|
||||
)
|
||||
|
||||
try:
|
||||
meta, response = ToolEngine._invoke(tool, tool_parameters, user_id)
|
||||
except ToolEngineInvokeError as e:
|
||||
meta = e.meta
|
||||
|
||||
meta, response = ToolEngine._invoke(tool, tool_parameters, user_id)
|
||||
response = ToolFileMessageTransformer.transform_tool_invoke_messages(
|
||||
messages=response,
|
||||
user_id=user_id,
|
||||
|
|
@ -104,11 +101,16 @@ class ToolEngine:
|
|||
except ToolInvokeError as e:
|
||||
error_response = f"tool invoke error: {e}"
|
||||
agent_tool_callback.on_tool_error(e)
|
||||
except ToolEngineInvokeError as e:
|
||||
meta = e.args[0]
|
||||
error_response = f"tool invoke error: {meta.error}"
|
||||
agent_tool_callback.on_tool_error(e)
|
||||
return error_response, [], meta
|
||||
except Exception as e:
|
||||
error_response = f"unknown error: {e}"
|
||||
agent_tool_callback.on_tool_error(e)
|
||||
|
||||
return error_response, [], meta
|
||||
return error_response, [], ToolInvokeMeta.error_instance(error_response)
|
||||
|
||||
@staticmethod
|
||||
def workflow_invoke(tool: Tool, tool_parameters: dict,
|
||||
|
|
@ -146,12 +148,18 @@ class ToolEngine:
|
|||
Invoke the tool with the given arguments.
|
||||
"""
|
||||
started_at = datetime.now(timezone.utc)
|
||||
meta = ToolInvokeMeta(time_cost=0.0, error=None)
|
||||
meta = ToolInvokeMeta(time_cost=0.0, error=None, tool_config={
|
||||
'tool_name': tool.identity.name,
|
||||
'tool_provider': tool.identity.provider,
|
||||
'tool_provider_type': tool.tool_provider_type().value,
|
||||
'tool_parameters': deepcopy(tool.runtime.runtime_parameters),
|
||||
'tool_icon': tool.identity.icon
|
||||
})
|
||||
try:
|
||||
response = tool.invoke(user_id, tool_parameters)
|
||||
except Exception as e:
|
||||
meta.error = str(e)
|
||||
raise ToolEngineInvokeError(meta=meta)
|
||||
raise ToolEngineInvokeError(meta)
|
||||
finally:
|
||||
ended_at = datetime.now(timezone.utc)
|
||||
meta.time_cost = (ended_at - started_at).total_seconds()
|
||||
|
|
|
|||
|
|
@ -1163,6 +1163,10 @@ class MessageAgentThought(db.Model):
|
|||
else:
|
||||
return []
|
||||
|
||||
@property
|
||||
def tools(self) -> list[str]:
|
||||
return self.tool.split(";") if self.tool else []
|
||||
|
||||
@property
|
||||
def tool_labels(self) -> dict:
|
||||
try:
|
||||
|
|
@ -1182,6 +1186,55 @@ class MessageAgentThought(db.Model):
|
|||
return {}
|
||||
except Exception as e:
|
||||
return {}
|
||||
|
||||
@property
|
||||
def tool_inputs_dict(self) -> dict:
|
||||
tools = self.tools
|
||||
try:
|
||||
if self.tool_input:
|
||||
data = json.loads(self.tool_input)
|
||||
result = {}
|
||||
for tool in tools:
|
||||
if tool in data:
|
||||
result[tool] = data[tool]
|
||||
else:
|
||||
if len(tools) == 1:
|
||||
result[tool] = data
|
||||
else:
|
||||
result[tool] = {}
|
||||
return result
|
||||
else:
|
||||
return {
|
||||
tool: {} for tool in tools
|
||||
}
|
||||
except Exception as e:
|
||||
return {}
|
||||
|
||||
@property
|
||||
def tool_outputs_dict(self) -> dict:
|
||||
tools = self.tools
|
||||
try:
|
||||
if self.observation:
|
||||
data = json.loads(self.observation)
|
||||
result = {}
|
||||
for tool in tools:
|
||||
if tool in data:
|
||||
result[tool] = data[tool]
|
||||
else:
|
||||
if len(tools) == 1:
|
||||
result[tool] = data
|
||||
else:
|
||||
result[tool] = {}
|
||||
return result
|
||||
else:
|
||||
return {
|
||||
tool: {} for tool in tools
|
||||
}
|
||||
except Exception as e:
|
||||
if self.observation:
|
||||
return {
|
||||
tool: self.observation for tool in tools
|
||||
}
|
||||
|
||||
class DatasetRetrieverResource(db.Model):
|
||||
__tablename__ = 'dataset_retriever_resources'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
from core.app.app_config.easy_ui_based_app.agent.manager import AgentConfigManager
|
||||
from extensions.ext_database import db
|
||||
from models.account import Account
|
||||
from models.model import App, Conversation, EndUser, Message, MessageAgentThought
|
||||
from services.tools_transform_service import ToolTransformService
|
||||
|
||||
|
||||
class AgentService:
|
||||
@classmethod
|
||||
def get_agent_logs(cls, app_model: App,
|
||||
conversation_id: str,
|
||||
message_id: str) -> dict:
|
||||
"""
|
||||
Service to get agent logs
|
||||
"""
|
||||
conversation: Conversation = db.session.query(Conversation).filter(
|
||||
Conversation.id == conversation_id,
|
||||
Conversation.app_id == app_model.id,
|
||||
).first()
|
||||
|
||||
if not conversation:
|
||||
raise ValueError(f"Conversation not found: {conversation_id}")
|
||||
|
||||
message: Message = db.session.query(Message).filter(
|
||||
Message.id == message_id,
|
||||
Message.conversation_id == conversation_id,
|
||||
).first()
|
||||
|
||||
if not message:
|
||||
raise ValueError(f"Message not found: {message_id}")
|
||||
|
||||
agent_thoughts: list[MessageAgentThought] = message.agent_thoughts
|
||||
|
||||
if conversation.from_end_user_id:
|
||||
# only select name field
|
||||
executor = db.session.query(EndUser, EndUser.name).filter(
|
||||
EndUser.id == conversation.from_end_user_id
|
||||
).first()
|
||||
else:
|
||||
executor = db.session.query(Account, Account.name).filter(
|
||||
Account.id == conversation.from_account_id
|
||||
).first()
|
||||
|
||||
if executor:
|
||||
executor = executor.name
|
||||
else:
|
||||
executor = 'Unknown'
|
||||
|
||||
result = {
|
||||
'meta': {
|
||||
'status': 'success',
|
||||
'executor': executor,
|
||||
'start_time': message.created_at.isoformat(),
|
||||
'elapsed_time': message.provider_response_latency,
|
||||
'total_tokens': message.answer_tokens + message.message_tokens,
|
||||
'agent_mode': app_model.app_model_config.agent_mode_dict.get('strategy', 'react'),
|
||||
'iterations': len(agent_thoughts),
|
||||
},
|
||||
'iterations': [],
|
||||
'files': message.files,
|
||||
}
|
||||
|
||||
agent_config = AgentConfigManager.convert(app_model.app_model_config.to_dict())
|
||||
agent_tools = agent_config.tools
|
||||
|
||||
def find_agent_tool(tool_name: str):
|
||||
for agent_tool in agent_tools:
|
||||
if agent_tool.tool_name == tool_name:
|
||||
return agent_tool
|
||||
|
||||
for agent_thought in agent_thoughts:
|
||||
tools = agent_thought.tools
|
||||
tool_labels = agent_thought.tool_labels
|
||||
tool_meta = agent_thought.tool_meta
|
||||
tool_inputs = agent_thought.tool_inputs_dict
|
||||
tool_outputs = agent_thought.tool_outputs_dict
|
||||
tool_calls = []
|
||||
for tool in tools:
|
||||
tool_name = tool
|
||||
tool_label = tool_labels.get(tool_name, tool_name)
|
||||
tool_input = tool_inputs.get(tool_name, {})
|
||||
tool_output = tool_outputs.get(tool_name, {})
|
||||
tool_meta_data = tool_meta.get(tool_name, {})
|
||||
tool_config = tool_meta_data.get('tool_config', {})
|
||||
tool_icon = ToolTransformService.get_tool_provider_icon_url(
|
||||
provider_type=tool_config.get('tool_provider_type', ''),
|
||||
provider_name=tool_config.get('tool_provider', ''),
|
||||
icon=tool_config.get('tool_icon', '')
|
||||
)
|
||||
if not tool_icon:
|
||||
tool_entity = find_agent_tool(tool_name)
|
||||
if tool_entity:
|
||||
tool_icon = ToolTransformService.get_tool_provider_icon_url(
|
||||
provider_type=tool_entity.provider_type,
|
||||
provider_name=tool_entity.provider_id,
|
||||
icon=''
|
||||
)
|
||||
|
||||
tool_calls.append({
|
||||
'status': 'success' if not tool_meta_data.get('error') else 'error',
|
||||
'error': tool_meta_data.get('error'),
|
||||
'time_cost': tool_meta_data.get('time_cost', 0),
|
||||
'tool_name': tool_name,
|
||||
'tool_label': tool_label,
|
||||
'tool_input': tool_input,
|
||||
'tool_output': tool_output,
|
||||
'tool_parameters': tool_meta_data.get('tool_parameters', {}),
|
||||
'tool_icon': tool_icon,
|
||||
})
|
||||
|
||||
result['iterations'].append({
|
||||
'tokens': agent_thought.tokens,
|
||||
'tool_calls': tool_calls,
|
||||
'tool_raw': {
|
||||
'inputs': agent_thought.tool_input,
|
||||
'outputs': agent_thought.observation,
|
||||
},
|
||||
'thought': agent_thought.thought,
|
||||
'created_at': agent_thought.created_at.isoformat(),
|
||||
'files': agent_thought.files,
|
||||
})
|
||||
|
||||
return result
|
||||
|
|
@ -7,7 +7,6 @@ import type { Body } from '../../types'
|
|||
import { BodyType } from '../../types'
|
||||
import useKeyValueList from '../../hooks/use-key-value-list'
|
||||
import KeyValue from '../key-value'
|
||||
import TextEditor from '../../../_base/components/editor/text-editor'
|
||||
import useAvailableVarList from '../../../_base/hooks/use-available-var-list'
|
||||
import InputWithVar from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import type { Var } from '@/app/components/workflow/types'
|
||||
|
|
@ -127,22 +126,17 @@ const EditBody: FC<Props> = ({
|
|||
)}
|
||||
|
||||
{type === BodyType.rawText && (
|
||||
<TextEditor
|
||||
<InputWithVar
|
||||
title={<div className='uppercase'>Raw text</div>}
|
||||
onChange={handleBodyValueChange}
|
||||
value={payload.data}
|
||||
minHeight={150}
|
||||
readonly={readonly}
|
||||
justVar
|
||||
nodesOutputVars={availableVarList}
|
||||
readOnly={readonly}
|
||||
/>
|
||||
)}
|
||||
|
||||
{type === BodyType.json && (
|
||||
// <CodeEditor
|
||||
// readOnly={readonly}
|
||||
// title={<div className='uppercase'>JSON</div>}
|
||||
// value={payload.data} onChange={handleBodyValueChange}
|
||||
// language={CodeLanguage.json}
|
||||
// />
|
||||
<InputWithVar
|
||||
title='JSON'
|
||||
value={payload.data}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,39 @@
|
|||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import type { HttpNodeType } from './types'
|
||||
import type { NodeProps } from '@/app/components/workflow/types'
|
||||
import SupportVarInput from '@/app/components/workflow/nodes/_base/components/support-var-input'
|
||||
import type { NodeProps, Var } from '@/app/components/workflow/types'
|
||||
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
|
||||
import { VarType } from '@/app/components/workflow/types'
|
||||
import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var'
|
||||
|
||||
const Node: FC<NodeProps<HttpNodeType>> = ({
|
||||
id,
|
||||
data,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const { method, url } = data
|
||||
const availableVarList = useAvailableVarList(id, {
|
||||
onlyLeafNodeVar: false,
|
||||
filterVar: (varPayload: Var) => {
|
||||
return [VarType.string, VarType.number].includes(varPayload.type)
|
||||
},
|
||||
})
|
||||
return (
|
||||
<div className='mb-1 px-3 py-1'>
|
||||
<div className='flex items-center p-1 rounded-md bg-gray-100'>
|
||||
<div className='shrink-0 px-1 h-7 leading-7 rounded bg-gray-25 text-xs font-semibold text-gray-700 uppercase'>{method}</div>
|
||||
<SupportVarInput
|
||||
wrapClassName='w-0 grow truncate flex items-center'
|
||||
textClassName='ml-1 text-xs font-normal text-gray-700'
|
||||
<Input
|
||||
className={cn('bg-gray-100 border-gray-100', 'w-0 grow rounded-lg px-3 py-[6px] border')}
|
||||
value={url}
|
||||
readonly
|
||||
onChange={() => { }}
|
||||
readOnly
|
||||
nodesOutputVars={availableVarList}
|
||||
onFocusChange={() => { }}
|
||||
placeholder={t('workflow.nodes.http.apiPlaceholder')!}
|
||||
placeholderClassName='!leading-[21px]'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,32 +15,39 @@ const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => {
|
|||
|
||||
return (
|
||||
<div className='px-3'>
|
||||
<div className='flex items-center h-6 relative px-1'>
|
||||
<div className='w-full text-right text-gray-700 text-xs font-semibold'>IF</div>
|
||||
<div className='relative flex items-center h-6 px-1'>
|
||||
<div className='w-full text-xs font-semibold text-right text-gray-700'>IF</div>
|
||||
<NodeSourceHandle
|
||||
{...props}
|
||||
handleId='true'
|
||||
handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-0.5 leading-4 text-[10px] font-medium text-gray-500 uppercase'>{t(`${i18nPrefix}.conditions`)}</div>
|
||||
<div className='space-y-0.5'>
|
||||
{conditions.filter(item => (item.variable_selector && item.variable_selector.length > 0 && item.comparison_operator && (isEmptyRelatedOperator(item.comparison_operator!) ? true : !!item.value))).map((condition, i) => (
|
||||
{conditions.map((condition, i) => (
|
||||
<div key={condition.id} className='relative'>
|
||||
<div className='flex items-center h-6 bg-gray-100 rounded-md px-1 space-x-1 text-xs font-normal text-gray-700'>
|
||||
<Variable02 className='w-3.5 h-3.5 text-primary-500' />
|
||||
<span>{condition.variable_selector.slice(-1)[0]}</span>
|
||||
<span className='text-gray-500'>{isComparisonOperatorNeedTranslate(condition.comparison_operator) ? t(`${i18nPrefix}.comparisonOperator.${condition.comparison_operator}`) : condition.comparison_operator}</span>
|
||||
{!isEmptyRelatedOperator(condition.comparison_operator!) && <span>{condition.value}</span>}
|
||||
</div>
|
||||
{(condition.variable_selector?.length > 0 && condition.comparison_operator && (isEmptyRelatedOperator(condition.comparison_operator!) ? true : !!condition.value))
|
||||
? (
|
||||
<div className='flex items-center h-6 px-1 space-x-1 text-xs font-normal text-gray-700 bg-gray-100 rounded-md'>
|
||||
<Variable02 className='w-3.5 h-3.5 text-primary-500' />
|
||||
<span>{condition.variable_selector.slice(-1)[0]}</span>
|
||||
<span className='text-gray-500'>{isComparisonOperatorNeedTranslate(condition.comparison_operator) ? t(`${i18nPrefix}.comparisonOperator.${condition.comparison_operator}`) : condition.comparison_operator}</span>
|
||||
{!isEmptyRelatedOperator(condition.comparison_operator!) && <span>{condition.value}</span>}
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className='flex items-center h-6 px-1 space-x-1 text-xs font-normal text-gray-500 bg-gray-100 rounded-md'>
|
||||
{t(`${i18nPrefix}.conditionNotSetup`)}
|
||||
</div>
|
||||
)}
|
||||
{i !== conditions.length - 1 && (
|
||||
<div className='absolute z-10 right-0 bottom-[-10px] leading-4 text-[10px] font-medium text-primary-600 uppercase'>{t(`${i18nPrefix}.${logical_operator}`)}</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className='flex items-center h-6 relative px-1'>
|
||||
<div className='w-full text-right text-gray-700 text-xs font-semibold'>ELSE</div>
|
||||
<div className='relative flex items-center h-6 px-1'>
|
||||
<div className='w-full text-xs font-semibold text-right text-gray-700'>ELSE</div>
|
||||
<NodeSourceHandle
|
||||
{...props}
|
||||
handleId='false'
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ const ConfigPrompt: FC<Props> = ({
|
|||
isChatModel,
|
||||
isChatApp,
|
||||
payload,
|
||||
variables,
|
||||
onChange,
|
||||
isShowContext,
|
||||
hasSetBlockStatus,
|
||||
|
|
@ -137,7 +136,6 @@ const ConfigPrompt: FC<Props> = ({
|
|||
}
|
||||
value={item.text}
|
||||
onChange={handleChatModePromptChange(index)}
|
||||
variables={variables}
|
||||
readOnly={readOnly}
|
||||
showRemove={(payload as PromptItem[]).length > 1}
|
||||
onRemove={handleRemove(index)}
|
||||
|
|
@ -165,7 +163,6 @@ const ConfigPrompt: FC<Props> = ({
|
|||
title={<span className='capitalize'>{t(`${i18nPrefix}.prompt`)}</span>}
|
||||
value={(payload as PromptItem).text}
|
||||
onChange={handleCompletionPromptChange}
|
||||
variables={variables}
|
||||
readOnly={readOnly}
|
||||
isChatModel={isChatModel}
|
||||
isChatApp={isChatApp}
|
||||
|
|
|
|||
|
|
@ -193,7 +193,6 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||
</div>}
|
||||
value={QUERY_PLACEHOLDER_TEXT}
|
||||
onChange={() => { }}
|
||||
variables={[]}
|
||||
readOnly
|
||||
isShowContext={false}
|
||||
isChatApp
|
||||
|
|
@ -243,7 +242,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
|||
<OutputVars>
|
||||
<>
|
||||
<VarItem
|
||||
name='output'
|
||||
name='text'
|
||||
type='string'
|
||||
description={t(`${i18nPrefix}.outputVars.output`)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -247,7 +247,6 @@ const translation = {
|
|||
},
|
||||
},
|
||||
ifElse: {
|
||||
conditions: 'Conditions',
|
||||
if: 'If',
|
||||
else: 'Else',
|
||||
elseDescription: 'Used to define the logic that should be executed when the if condition is not met.',
|
||||
|
|
@ -269,6 +268,7 @@ const translation = {
|
|||
},
|
||||
enterValue: 'Enter value',
|
||||
addCondition: 'Add Condition',
|
||||
conditionNotSetup: 'Condition NOT setup',
|
||||
},
|
||||
variableAssigner: {
|
||||
title: 'Assign variables',
|
||||
|
|
|
|||
|
|
@ -247,7 +247,6 @@ const translation = {
|
|||
},
|
||||
},
|
||||
ifElse: {
|
||||
conditions: '条件',
|
||||
if: 'If',
|
||||
else: 'Else',
|
||||
elseDescription: '用于定义当 if 条件不满足时应执行的逻辑。',
|
||||
|
|
@ -269,6 +268,7 @@ const translation = {
|
|||
},
|
||||
enterValue: '输入值',
|
||||
addCondition: '添加条件',
|
||||
conditionNotSetup: '条件未设置',
|
||||
},
|
||||
variableAssigner: {
|
||||
title: '变量赋值',
|
||||
|
|
|
|||
826
web/yarn.lock
826
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue