feat: Support session variables (#3792)
This commit is contained in:
parent
0e78245bfb
commit
83a1ffb891
@ -23,6 +23,7 @@ def get_default_global_variable(input_field_list: List):
|
|||||||
if item.get('default_value', None) is not None
|
if item.get('default_value', None) is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_global_variable(node):
|
def get_global_variable(node):
|
||||||
body = node.workflow_manage.get_body()
|
body = node.workflow_manage.get_body()
|
||||||
history_chat_record = node.flow_params_serializer.data.get('history_chat_record', [])
|
history_chat_record = node.flow_params_serializer.data.get('history_chat_record', [])
|
||||||
@ -74,6 +75,7 @@ class BaseStartStepNode(IStarNode):
|
|||||||
'other': self.workflow_manage.other_list,
|
'other': self.workflow_manage.other_list,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
self.workflow_manage.chat_context = self.workflow_manage.get_chat_info().get_chat_variable()
|
||||||
return NodeResult(node_variable, workflow_variable)
|
return NodeResult(node_variable, workflow_variable)
|
||||||
|
|
||||||
def get_details(self, index: int, **kwargs):
|
def get_details(self, index: int, **kwargs):
|
||||||
|
|||||||
@ -2,8 +2,11 @@
|
|||||||
import json
|
import json
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
|
||||||
from application.flow.i_step_node import NodeResult
|
from application.flow.i_step_node import NodeResult
|
||||||
from application.flow.step_node.variable_assign_node.i_variable_assign_node import IVariableAssignNode
|
from application.flow.step_node.variable_assign_node.i_variable_assign_node import IVariableAssignNode
|
||||||
|
from application.models import Chat
|
||||||
|
|
||||||
|
|
||||||
class BaseVariableAssignNode(IVariableAssignNode):
|
class BaseVariableAssignNode(IVariableAssignNode):
|
||||||
@ -11,40 +14,56 @@ class BaseVariableAssignNode(IVariableAssignNode):
|
|||||||
self.context['variable_list'] = details.get('variable_list')
|
self.context['variable_list'] = details.get('variable_list')
|
||||||
self.context['result_list'] = details.get('result_list')
|
self.context['result_list'] = details.get('result_list')
|
||||||
|
|
||||||
|
def global_evaluation(self, variable, value):
|
||||||
|
self.workflow_manage.context[variable['fields'][1]] = value
|
||||||
|
|
||||||
|
def chat_evaluation(self, variable, value):
|
||||||
|
self.workflow_manage.chat_context[variable['fields'][1]] = value
|
||||||
|
|
||||||
|
def handle(self, variable, evaluation):
|
||||||
|
result = {
|
||||||
|
'name': variable['name'],
|
||||||
|
'input_value': self.get_reference_content(variable['fields']),
|
||||||
|
}
|
||||||
|
if variable['source'] == 'custom':
|
||||||
|
if variable['type'] == 'json':
|
||||||
|
if isinstance(variable['value'], dict) or isinstance(variable['value'], list):
|
||||||
|
val = variable['value']
|
||||||
|
else:
|
||||||
|
val = json.loads(variable['value'])
|
||||||
|
evaluation(variable, val)
|
||||||
|
result['output_value'] = variable['value'] = val
|
||||||
|
elif variable['type'] == 'string':
|
||||||
|
# 变量解析 例如:{{global.xxx}}
|
||||||
|
val = self.workflow_manage.generate_prompt(variable['value'])
|
||||||
|
evaluation(variable, val)
|
||||||
|
result['output_value'] = val
|
||||||
|
else:
|
||||||
|
val = variable['value']
|
||||||
|
evaluation(variable, val)
|
||||||
|
result['output_value'] = val
|
||||||
|
else:
|
||||||
|
reference = self.get_reference_content(variable['reference'])
|
||||||
|
evaluation(variable, reference)
|
||||||
|
result['output_value'] = reference
|
||||||
|
return result
|
||||||
|
|
||||||
def execute(self, variable_list, stream, **kwargs) -> NodeResult:
|
def execute(self, variable_list, stream, **kwargs) -> NodeResult:
|
||||||
#
|
#
|
||||||
result_list = []
|
result_list = []
|
||||||
|
is_chat = False
|
||||||
for variable in variable_list:
|
for variable in variable_list:
|
||||||
if 'fields' not in variable:
|
if 'fields' not in variable:
|
||||||
continue
|
continue
|
||||||
if 'global' == variable['fields'][0]:
|
if 'global' == variable['fields'][0]:
|
||||||
result = {
|
result = self.handle(variable, self.global_evaluation)
|
||||||
'name': variable['name'],
|
|
||||||
'input_value': self.get_reference_content(variable['fields']),
|
|
||||||
}
|
|
||||||
if variable['source'] == 'custom':
|
|
||||||
if variable['type'] == 'json':
|
|
||||||
if isinstance(variable['value'], dict) or isinstance(variable['value'], list):
|
|
||||||
val = variable['value']
|
|
||||||
else:
|
|
||||||
val = json.loads(variable['value'])
|
|
||||||
self.workflow_manage.context[variable['fields'][1]] = val
|
|
||||||
result['output_value'] = variable['value'] = val
|
|
||||||
elif variable['type'] == 'string':
|
|
||||||
# 变量解析 例如:{{global.xxx}}
|
|
||||||
val = self.workflow_manage.generate_prompt(variable['value'])
|
|
||||||
self.workflow_manage.context[variable['fields'][1]] = val
|
|
||||||
result['output_value'] = val
|
|
||||||
else:
|
|
||||||
val = variable['value']
|
|
||||||
self.workflow_manage.context[variable['fields'][1]] = val
|
|
||||||
result['output_value'] = val
|
|
||||||
else:
|
|
||||||
reference = self.get_reference_content(variable['reference'])
|
|
||||||
self.workflow_manage.context[variable['fields'][1]] = reference
|
|
||||||
result['output_value'] = reference
|
|
||||||
result_list.append(result)
|
result_list.append(result)
|
||||||
|
if 'chat' == variable['fields'][0]:
|
||||||
|
result = self.handle(variable, self.chat_evaluation)
|
||||||
|
result_list.append(result)
|
||||||
|
is_chat = True
|
||||||
|
if is_chat:
|
||||||
|
self.workflow_manage.get_chat_info().set_chat_variable(self.workflow_manage.chat_context)
|
||||||
return NodeResult({'variable_list': variable_list, 'result_list': result_list}, {})
|
return NodeResult({'variable_list': variable_list, 'result_list': result_list}, {})
|
||||||
|
|
||||||
def get_reference_content(self, fields: List[str]):
|
def get_reference_content(self, fields: List[str]):
|
||||||
|
|||||||
@ -117,6 +117,7 @@ class WorkflowManage:
|
|||||||
self.params = params
|
self.params = params
|
||||||
self.flow = flow
|
self.flow = flow
|
||||||
self.context = {}
|
self.context = {}
|
||||||
|
self.chat_context = {}
|
||||||
self.node_chunk_manage = NodeChunkManage(self)
|
self.node_chunk_manage = NodeChunkManage(self)
|
||||||
self.work_flow_post_handler = work_flow_post_handler
|
self.work_flow_post_handler = work_flow_post_handler
|
||||||
self.current_node = None
|
self.current_node = None
|
||||||
@ -131,6 +132,7 @@ class WorkflowManage:
|
|||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
self.field_list = []
|
self.field_list = []
|
||||||
self.global_field_list = []
|
self.global_field_list = []
|
||||||
|
self.chat_field_list = []
|
||||||
self.init_fields()
|
self.init_fields()
|
||||||
if start_node_id is not None:
|
if start_node_id is not None:
|
||||||
self.load_node(chat_record, start_node_id, start_node_data)
|
self.load_node(chat_record, start_node_id, start_node_data)
|
||||||
@ -140,6 +142,7 @@ class WorkflowManage:
|
|||||||
def init_fields(self):
|
def init_fields(self):
|
||||||
field_list = []
|
field_list = []
|
||||||
global_field_list = []
|
global_field_list = []
|
||||||
|
chat_field_list = []
|
||||||
for node in self.flow.nodes:
|
for node in self.flow.nodes:
|
||||||
properties = node.properties
|
properties = node.properties
|
||||||
node_name = properties.get('stepName')
|
node_name = properties.get('stepName')
|
||||||
@ -154,10 +157,16 @@ class WorkflowManage:
|
|||||||
if global_fields is not None:
|
if global_fields is not None:
|
||||||
for global_field in global_fields:
|
for global_field in global_fields:
|
||||||
global_field_list.append({**global_field, 'node_id': node_id, 'node_name': node_name})
|
global_field_list.append({**global_field, 'node_id': node_id, 'node_name': node_name})
|
||||||
|
chat_fields = node_config.get('chatFields')
|
||||||
|
if chat_fields is not None:
|
||||||
|
for chat_field in chat_fields:
|
||||||
|
chat_field_list.append({**chat_field, 'node_id': node_id, 'node_name': node_name})
|
||||||
field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
|
field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
|
||||||
global_field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
|
global_field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
|
||||||
|
chat_field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
|
||||||
self.field_list = field_list
|
self.field_list = field_list
|
||||||
self.global_field_list = global_field_list
|
self.global_field_list = global_field_list
|
||||||
|
self.chat_field_list = chat_field_list
|
||||||
|
|
||||||
def append_answer(self, content):
|
def append_answer(self, content):
|
||||||
self.answer += content
|
self.answer += content
|
||||||
@ -445,6 +454,9 @@ class WorkflowManage:
|
|||||||
return current_node.node_params.get('is_result', not self._has_next_node(
|
return current_node.node_params.get('is_result', not self._has_next_node(
|
||||||
current_node, current_node_result)) if current_node.node_params is not None else False
|
current_node, current_node_result)) if current_node.node_params is not None else False
|
||||||
|
|
||||||
|
def get_chat_info(self):
|
||||||
|
return self.work_flow_post_handler.chat_info
|
||||||
|
|
||||||
def get_chunk_content(self, chunk, is_end=False):
|
def get_chunk_content(self, chunk, is_end=False):
|
||||||
return 'data: ' + json.dumps(
|
return 'data: ' + json.dumps(
|
||||||
{'chat_id': self.params['chat_id'], 'id': self.params['chat_record_id'], 'operate': True,
|
{'chat_id': self.params['chat_id'], 'id': self.params['chat_record_id'], 'operate': True,
|
||||||
@ -587,12 +599,15 @@ class WorkflowManage:
|
|||||||
"""
|
"""
|
||||||
if node_id == 'global':
|
if node_id == 'global':
|
||||||
return INode.get_field(self.context, fields)
|
return INode.get_field(self.context, fields)
|
||||||
|
elif node_id == 'chat':
|
||||||
|
return INode.get_field(self.chat_context, fields)
|
||||||
else:
|
else:
|
||||||
return self.get_node_by_id(node_id).get_reference_field(fields)
|
return self.get_node_by_id(node_id).get_reference_field(fields)
|
||||||
|
|
||||||
def get_workflow_content(self):
|
def get_workflow_content(self):
|
||||||
context = {
|
context = {
|
||||||
'global': self.context,
|
'global': self.context,
|
||||||
|
'chat': self.chat_context
|
||||||
}
|
}
|
||||||
|
|
||||||
for node in self.node_context:
|
for node in self.node_context:
|
||||||
@ -610,6 +625,10 @@ class WorkflowManage:
|
|||||||
globeLabelNew = f"global.{field.get('value')}"
|
globeLabelNew = f"global.{field.get('value')}"
|
||||||
globeValue = f"context.get('global').get('{field.get('value', '')}','')"
|
globeValue = f"context.get('global').get('{field.get('value', '')}','')"
|
||||||
prompt = prompt.replace(globeLabel, globeValue).replace(globeLabelNew, globeValue)
|
prompt = prompt.replace(globeLabel, globeValue).replace(globeLabelNew, globeValue)
|
||||||
|
for field in self.chat_field_list:
|
||||||
|
chatLabel = f"chat.{field.get('value')}"
|
||||||
|
chatValue = f"context.get('chat').get('{field.get('value', '')}','')"
|
||||||
|
prompt = prompt.replace(chatLabel, chatValue)
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def generate_prompt(self, prompt: str):
|
def generate_prompt(self, prompt: str):
|
||||||
|
|||||||
@ -166,6 +166,34 @@ class ChatInfo:
|
|||||||
'exclude_paragraph_id_list': exclude_paragraph_id_list, 'stream': stream, 'chat_user_id': chat_user_id,
|
'exclude_paragraph_id_list': exclude_paragraph_id_list, 'stream': stream, 'chat_user_id': chat_user_id,
|
||||||
'chat_user_type': chat_user_type, 'form_data': form_data}
|
'chat_user_type': chat_user_type, 'form_data': form_data}
|
||||||
|
|
||||||
|
def set_chat(self, question):
|
||||||
|
if not self.debug:
|
||||||
|
if not QuerySet(Chat).filter(id=self.chat_id).exists():
|
||||||
|
Chat(id=self.chat_id, application_id=self.application_id, abstract=question[0:1024],
|
||||||
|
chat_user_id=self.chat_user_id, chat_user_type=self.chat_user_type,
|
||||||
|
asker=self.get_chat_user()).save()
|
||||||
|
|
||||||
|
def set_chat_variable(self, chat_context):
|
||||||
|
if not self.debug:
|
||||||
|
chat = QuerySet(Chat).filter(id=self.chat_id).first()
|
||||||
|
if chat:
|
||||||
|
chat.meta = {**(chat.meta if isinstance(chat.meta, dict) else {}), **chat_context}
|
||||||
|
chat.save()
|
||||||
|
else:
|
||||||
|
cache.set(Cache_Version.CHAT_VARIABLE.get_key(key=self.chat_id), chat_context,
|
||||||
|
version=Cache_Version.CHAT_VARIABLE.get_version(),
|
||||||
|
timeout=60 * 30)
|
||||||
|
|
||||||
|
def get_chat_variable(self):
|
||||||
|
if not self.debug:
|
||||||
|
chat = QuerySet(Chat).filter(id=self.chat_id).first()
|
||||||
|
if chat:
|
||||||
|
return chat.meta
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
return cache.get(Cache_Version.CHAT_VARIABLE.get_key(key=self.chat_id),
|
||||||
|
version=Cache_Version.CHAT_VARIABLE.get_version()) or {}
|
||||||
|
|
||||||
def append_chat_record(self, chat_record: ChatRecord):
|
def append_chat_record(self, chat_record: ChatRecord):
|
||||||
chat_record.problem_text = chat_record.problem_text[0:10240] if chat_record.problem_text is not None else ""
|
chat_record.problem_text = chat_record.problem_text[0:10240] if chat_record.problem_text is not None else ""
|
||||||
chat_record.answer_text = chat_record.answer_text[0:40960] if chat_record.problem_text is not None else ""
|
chat_record.answer_text = chat_record.answer_text[0:40960] if chat_record.problem_text is not None else ""
|
||||||
|
|||||||
@ -253,6 +253,7 @@ class ChatSerializers(serializers.Serializer):
|
|||||||
# 构建运行参数
|
# 构建运行参数
|
||||||
params = chat_info.to_pipeline_manage_params(message, get_post_handler(chat_info), exclude_paragraph_id_list,
|
params = chat_info.to_pipeline_manage_params(message, get_post_handler(chat_info), exclude_paragraph_id_list,
|
||||||
chat_user_id, chat_user_type, stream, form_data)
|
chat_user_id, chat_user_type, stream, form_data)
|
||||||
|
chat_info.set_chat(message)
|
||||||
# 运行流水线作业
|
# 运行流水线作业
|
||||||
pipeline_message.run(params)
|
pipeline_message.run(params)
|
||||||
return pipeline_message.context['chat_result']
|
return pipeline_message.context['chat_result']
|
||||||
@ -307,6 +308,7 @@ class ChatSerializers(serializers.Serializer):
|
|||||||
other_list,
|
other_list,
|
||||||
instance.get('runtime_node_id'),
|
instance.get('runtime_node_id'),
|
||||||
instance.get('node_data'), chat_record, instance.get('child_node'))
|
instance.get('node_data'), chat_record, instance.get('child_node'))
|
||||||
|
chat_info.set_chat(message)
|
||||||
r = work_flow_manage.run()
|
r = work_flow_manage.run()
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,9 @@ class Cache_Version(Enum):
|
|||||||
|
|
||||||
# 对话
|
# 对话
|
||||||
CHAT = "CHAT", lambda key: key
|
CHAT = "CHAT", lambda key: key
|
||||||
|
|
||||||
|
CHAT_VARIABLE = "CHAT_VARIABLE", lambda key: key
|
||||||
|
|
||||||
# 应用API KEY
|
# 应用API KEY
|
||||||
APPLICATION_API_KEY = "APPLICATION_API_KEY", lambda secret_key, use_get_data: secret_key
|
APPLICATION_API_KEY = "APPLICATION_API_KEY", lambda secret_key, use_get_data: secret_key
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ export default {
|
|||||||
variable: {
|
variable: {
|
||||||
label: '变量',
|
label: '变量',
|
||||||
global: '全局变量',
|
global: '全局变量',
|
||||||
|
chat: '会话变量',
|
||||||
Referencing: '引用变量',
|
Referencing: '引用变量',
|
||||||
ReferencingRequired: '引用变量必填',
|
ReferencingRequired: '引用变量必填',
|
||||||
ReferencingError: '引用变量错误',
|
ReferencingError: '引用变量错误',
|
||||||
|
|||||||
@ -51,7 +51,9 @@ const wheel = (e: any) => {
|
|||||||
function visibleChange(bool: boolean) {
|
function visibleChange(bool: boolean) {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
options.value = props.global
|
options.value = props.global
|
||||||
? props.nodeModel.get_up_node_field_list(false, true).filter((v: any) => v.value === 'global')
|
? props.nodeModel
|
||||||
|
.get_up_node_field_list(false, true)
|
||||||
|
.filter((v: any) => ['global', 'chat'].includes(v.value))
|
||||||
: props.nodeModel.get_up_node_field_list(false, true)
|
: props.nodeModel.get_up_node_field_list(false, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class AppNode extends HtmlResize.view {
|
|||||||
} else {
|
} else {
|
||||||
const filterNodes = props.graphModel.nodes.filter((v: any) => v.type === props.model.type)
|
const filterNodes = props.graphModel.nodes.filter((v: any) => v.type === props.model.type)
|
||||||
const filterNameSameNodes = filterNodes.filter(
|
const filterNameSameNodes = filterNodes.filter(
|
||||||
(v: any) => v.properties.stepName === props.model.properties.stepName
|
(v: any) => v.properties.stepName === props.model.properties.stepName,
|
||||||
)
|
)
|
||||||
if (filterNameSameNodes.length - 1 > 0) {
|
if (filterNameSameNodes.length - 1 > 0) {
|
||||||
getNodesName(filterNameSameNodes.length - 1)
|
getNodesName(filterNameSameNodes.length - 1)
|
||||||
@ -61,14 +61,20 @@ class AppNode extends HtmlResize.view {
|
|||||||
value: 'global',
|
value: 'global',
|
||||||
label: t('views.applicationWorkflow.variable.global'),
|
label: t('views.applicationWorkflow.variable.global'),
|
||||||
type: 'global',
|
type: 'global',
|
||||||
children: this.props.model.properties?.config?.globalFields || []
|
children: this.props.model.properties?.config?.globalFields || [],
|
||||||
|
})
|
||||||
|
result.push({
|
||||||
|
value: 'chat',
|
||||||
|
label: t('views.applicationWorkflow.variable.chat'),
|
||||||
|
type: 'chat',
|
||||||
|
children: this.props.model.properties?.config?.chatFields || [],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
result.push({
|
result.push({
|
||||||
value: this.props.model.id,
|
value: this.props.model.id,
|
||||||
label: this.props.model.properties.stepName,
|
label: this.props.model.properties.stepName,
|
||||||
type: this.props.model.type,
|
type: this.props.model.type,
|
||||||
children: this.props.model.properties?.config?.fields || []
|
children: this.props.model.properties?.config?.fields || [],
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -83,7 +89,7 @@ class AppNode extends HtmlResize.view {
|
|||||||
if (contain_self) {
|
if (contain_self) {
|
||||||
return {
|
return {
|
||||||
...this.up_node_field_dict,
|
...this.up_node_field_dict,
|
||||||
[this.props.model.id]: this.get_node_field_list()
|
[this.props.model.id]: this.get_node_field_list(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.up_node_field_dict ? this.up_node_field_dict : {}
|
return this.up_node_field_dict ? this.up_node_field_dict : {}
|
||||||
@ -92,7 +98,7 @@ class AppNode extends HtmlResize.view {
|
|||||||
get_up_node_field_list(contain_self: boolean, use_cache: boolean) {
|
get_up_node_field_list(contain_self: boolean, use_cache: boolean) {
|
||||||
const result = Object.values(this.get_up_node_field_dict(contain_self, use_cache)).reduce(
|
const result = Object.values(this.get_up_node_field_dict(contain_self, use_cache)).reduce(
|
||||||
(pre, next) => [...pre, ...next],
|
(pre, next) => [...pre, ...next],
|
||||||
[]
|
[],
|
||||||
)
|
)
|
||||||
const start_node_field_list = this.props.graphModel
|
const start_node_field_list = this.props.graphModel
|
||||||
.getNodeModelById('start-node')
|
.getNodeModelById('start-node')
|
||||||
@ -126,7 +132,7 @@ class AppNode extends HtmlResize.view {
|
|||||||
x: x - 10,
|
x: x - 10,
|
||||||
y: y - 12,
|
y: y - 12,
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30
|
height: 30,
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
lh('div', {
|
lh('div', {
|
||||||
@ -174,10 +180,10 @@ class AppNode extends HtmlResize.view {
|
|||||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5199_166905" result="shape"/>
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5199_166905" result="shape"/>
|
||||||
</filter>
|
</filter>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>`
|
</svg>`,
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +220,7 @@ class AppNode extends HtmlResize.view {
|
|||||||
} else {
|
} else {
|
||||||
this.r = h(this.component, {
|
this.r = h(this.component, {
|
||||||
properties: this.props.model.properties,
|
properties: this.props.model.properties,
|
||||||
nodeModel: this.props.model
|
nodeModel: this.props.model,
|
||||||
})
|
})
|
||||||
this.app = createApp({
|
this.app = createApp({
|
||||||
render() {
|
render() {
|
||||||
@ -223,13 +229,13 @@ class AppNode extends HtmlResize.view {
|
|||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
getNode: () => model,
|
getNode: () => model,
|
||||||
getGraph: () => graphModel
|
getGraph: () => graphModel,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
this.app.use(ElementPlus, {
|
this.app.use(ElementPlus, {
|
||||||
locale: zhCn
|
locale: zhCn,
|
||||||
})
|
})
|
||||||
this.app.use(Components)
|
this.app.use(Components)
|
||||||
this.app.use(directives)
|
this.app.use(directives)
|
||||||
@ -295,7 +301,7 @@ class AppNodeModel extends HtmlResize.model {
|
|||||||
}
|
}
|
||||||
getNodeStyle() {
|
getNodeStyle() {
|
||||||
return {
|
return {
|
||||||
overflow: 'visible'
|
overflow: 'visible',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getOutlineStyle() {
|
getOutlineStyle() {
|
||||||
@ -361,13 +367,13 @@ class AppNodeModel extends HtmlResize.model {
|
|||||||
message: t('views.applicationWorkflow.tip.onlyRight'),
|
message: t('views.applicationWorkflow.tip.onlyRight'),
|
||||||
validate: (sourceNode: any, targetNode: any, sourceAnchor: any) => {
|
validate: (sourceNode: any, targetNode: any, sourceAnchor: any) => {
|
||||||
return sourceAnchor.type === 'right'
|
return sourceAnchor.type === 'right'
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
this.sourceRules.push({
|
this.sourceRules.push({
|
||||||
message: t('views.applicationWorkflow.tip.notRecyclable'),
|
message: t('views.applicationWorkflow.tip.notRecyclable'),
|
||||||
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
|
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
|
||||||
return !isLoop(sourceNode.id, targetNode.id)
|
return !isLoop(sourceNode.id, targetNode.id)
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
this.sourceRules.push(circleOnlyAsTarget)
|
this.sourceRules.push(circleOnlyAsTarget)
|
||||||
@ -375,7 +381,7 @@ class AppNodeModel extends HtmlResize.model {
|
|||||||
message: t('views.applicationWorkflow.tip.onlyLeft'),
|
message: t('views.applicationWorkflow.tip.onlyLeft'),
|
||||||
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
|
validate: (sourceNode: any, targetNode: any, sourceAnchor: any, targetAnchor: any) => {
|
||||||
return targetAnchor.type === 'left'
|
return targetAnchor.type === 'left'
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
getDefaultAnchor() {
|
getDefaultAnchor() {
|
||||||
@ -390,14 +396,14 @@ class AppNodeModel extends HtmlResize.model {
|
|||||||
y: showNode ? y : y - 15,
|
y: showNode ? y : y - 15,
|
||||||
id: `${id}_left`,
|
id: `${id}_left`,
|
||||||
edgeAddable: false,
|
edgeAddable: false,
|
||||||
type: 'left'
|
type: 'left',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
anchors.push({
|
anchors.push({
|
||||||
x: x + width / 2 - 10,
|
x: x + width / 2 - 10,
|
||||||
y: showNode ? y : y - 15,
|
y: showNode ? y : y - 15,
|
||||||
id: `${id}_right`,
|
id: `${id}_right`,
|
||||||
type: 'right'
|
type: 'right',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
ui/src/workflow/icons/chat-icon.vue
Normal file
4
ui/src/workflow/icons/chat-icon.vue
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<template>
|
||||||
|
<img src="@/assets/workflow/icon_globe_color.svg" style="width: 18px" alt="" />
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts"></script>
|
||||||
118
ui/src/workflow/nodes/base-node/component/ChatFieldDialog.vue
Normal file
118
ui/src/workflow/nodes/base-node/component/ChatFieldDialog.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="isEdit ? $t('common.param.editParam') : $t('common.param.addParam')"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="close"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
label-position="top"
|
||||||
|
ref="fieldFormRef"
|
||||||
|
:rules="rules"
|
||||||
|
:model="form"
|
||||||
|
require-asterisk-position="right"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('dynamicsForm.paramForm.field.label')"
|
||||||
|
:required="true"
|
||||||
|
prop="field"
|
||||||
|
:rules="rules.field"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.field"
|
||||||
|
:maxlength="64"
|
||||||
|
:placeholder="$t('dynamicsForm.paramForm.field.placeholder')"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('dynamicsForm.paramForm.name.label')"
|
||||||
|
:required="true"
|
||||||
|
prop="label"
|
||||||
|
:rules="rules.label"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="form.label"
|
||||||
|
:maxlength="64"
|
||||||
|
show-word-limit
|
||||||
|
:placeholder="$t('dynamicsForm.paramForm.name.placeholder')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||||
|
<el-button type="primary" @click="submit(fieldFormRef)" :loading="loading">
|
||||||
|
{{ isEdit ? $t('common.save') : $t('common.add') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const fieldFormRef = ref()
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const currentIndex = ref(null)
|
||||||
|
const form = ref<any>({
|
||||||
|
field: '',
|
||||||
|
label: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
label: [
|
||||||
|
{ required: true, message: t('dynamicsForm.paramForm.name.requiredMessage'), trigger: 'blur' },
|
||||||
|
],
|
||||||
|
field: [
|
||||||
|
{ required: true, message: t('dynamicsForm.paramForm.field.requiredMessage'), trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_]+$/,
|
||||||
|
message: t('dynamicsForm.paramForm.field.requiredMessage2'),
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
|
||||||
|
const open = (row: any, index?: any) => {
|
||||||
|
if (row) {
|
||||||
|
form.value = cloneDeep(row)
|
||||||
|
isEdit.value = true
|
||||||
|
currentIndex.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
isEdit.value = false
|
||||||
|
currentIndex.value = null
|
||||||
|
form.value = {
|
||||||
|
field: '',
|
||||||
|
label: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return
|
||||||
|
await formEl.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
emit('refresh', form.value, currentIndex.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open, close })
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
110
ui/src/workflow/nodes/base-node/component/ChatFieldTable.vue
Normal file
110
ui/src/workflow/nodes/base-node/component/ChatFieldTable.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex-between mb-16">
|
||||||
|
<h5 class="break-all ellipsis lighter" style="max-width: 80%">
|
||||||
|
{{ $t('views.applicationWorkflow.variable.chat') }}
|
||||||
|
</h5>
|
||||||
|
<div>
|
||||||
|
<span class="ml-4">
|
||||||
|
<el-button link type="primary" @click="openAddDialog()">
|
||||||
|
<el-icon class="mr-4">
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
{{ $t('common.add') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
v-if="props.nodeModel.properties.chat_input_field_list?.length > 0"
|
||||||
|
:data="props.nodeModel.properties.chat_input_field_list"
|
||||||
|
class="mb-16"
|
||||||
|
ref="tableRef"
|
||||||
|
row-key="field"
|
||||||
|
>
|
||||||
|
<el-table-column prop="field" :label="$t('dynamicsForm.paramForm.field.label')" width="95">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="label" :label="$t('dynamicsForm.paramForm.name.label')">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span>
|
||||||
|
<span :title="row.label" class="ellipsis-1">
|
||||||
|
{{ row.label }}
|
||||||
|
</span></span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<span class="mr-4">
|
||||||
|
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||||
|
<el-button type="primary" text @click.stop="openAddDialog(row, $index)">
|
||||||
|
<el-icon><EditPen /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||||
|
<el-button type="primary" text @click="deleteField($index)">
|
||||||
|
<el-icon>
|
||||||
|
<Delete />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<ChatFieldDialog ref="ChatFieldDialogRef" @refresh="refreshFieldList"></ChatFieldDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { set, cloneDeep } from 'lodash'
|
||||||
|
import ChatFieldDialog from './ChatFieldDialog.vue'
|
||||||
|
import { MsgError } from '@/utils/message'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
|
||||||
|
const props = defineProps<{ nodeModel: any }>()
|
||||||
|
|
||||||
|
const tableRef = ref()
|
||||||
|
const ChatFieldDialogRef = ref()
|
||||||
|
|
||||||
|
const inputFieldList = ref<any[]>([])
|
||||||
|
|
||||||
|
function openAddDialog(data?: any, index?: any) {
|
||||||
|
ChatFieldDialogRef.value.open(data, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteField(index: any) {
|
||||||
|
inputFieldList.value.splice(index, 1)
|
||||||
|
props.nodeModel.graphModel.eventCenter.emit('chatFieldList')
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshFieldList(data: any, index: any) {
|
||||||
|
for (let i = 0; i < inputFieldList.value.length; i++) {
|
||||||
|
if (inputFieldList.value[i].field === data.field && index !== i) {
|
||||||
|
MsgError(t('views.applicationWorkflow.tip.paramErrorMessage') + data.field)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(index)
|
||||||
|
if (index) {
|
||||||
|
inputFieldList.value.splice(index, 1, data)
|
||||||
|
} else {
|
||||||
|
inputFieldList.value.push(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatFieldDialogRef.value.close()
|
||||||
|
props.nodeModel.graphModel.eventCenter.emit('chatFieldList')
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.nodeModel.properties.chat_input_field_list) {
|
||||||
|
inputFieldList.value = cloneDeep(props.nodeModel.properties.chat_input_field_list)
|
||||||
|
}
|
||||||
|
set(props.nodeModel.properties, 'chat_input_field_list', inputFieldList)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@ -83,6 +83,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<UserInputFieldTable ref="UserInputFieldTableFef" :node-model="nodeModel" />
|
<UserInputFieldTable ref="UserInputFieldTableFef" :node-model="nodeModel" />
|
||||||
<ApiInputFieldTable ref="ApiInputFieldTableFef" :node-model="nodeModel" />
|
<ApiInputFieldTable ref="ApiInputFieldTableFef" :node-model="nodeModel" />
|
||||||
|
<ChatFieldTable ref="ChatFieldTeble" :node-model="nodeModel"></ChatFieldTable>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<div class="flex-between">
|
<div class="flex-between">
|
||||||
@ -177,6 +178,7 @@ import TTSModeParamSettingDialog from '@/views/application/component/TTSModePara
|
|||||||
import ApiInputFieldTable from './component/ApiInputFieldTable.vue'
|
import ApiInputFieldTable from './component/ApiInputFieldTable.vue'
|
||||||
import UserInputFieldTable from './component/UserInputFieldTable.vue'
|
import UserInputFieldTable from './component/UserInputFieldTable.vue'
|
||||||
import FileUploadSettingDialog from '@/workflow/nodes/base-node/component/FileUploadSettingDialog.vue'
|
import FileUploadSettingDialog from '@/workflow/nodes/base-node/component/FileUploadSettingDialog.vue'
|
||||||
|
import ChatFieldTable from './component/ChatFieldTable.vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||||
const getApplicationDetail = inject('getApplicationDetail') as any
|
const getApplicationDetail = inject('getApplicationDetail') as any
|
||||||
|
|||||||
@ -20,6 +20,30 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="nodeModel.properties.config.chatFields">
|
||||||
|
<h5 class="title-decoration-1 mb-8">{{ $t('views.applicationWorkflow.variable.chat') }}</h5>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in nodeModel.properties.config.chatFields
|
||||||
|
? nodeModel.properties.config.chatFields
|
||||||
|
: []"
|
||||||
|
:key="index"
|
||||||
|
class="flex-between border-r-6 p-8-12 mb-8 layout-bg lighter"
|
||||||
|
@mouseenter="showicon = true"
|
||||||
|
@mouseleave="showicon = false"
|
||||||
|
>
|
||||||
|
<span class="break-all">{{ item.label }} {{ '{' + item.value + '}' }}</span>
|
||||||
|
<el-tooltip
|
||||||
|
effect="dark"
|
||||||
|
:content="$t('views.applicationWorkflow.setting.copyParam')"
|
||||||
|
placement="top"
|
||||||
|
v-if="showicon === true"
|
||||||
|
>
|
||||||
|
<el-button link @click="copyClick(`{{chat.${item.value}}}`)" style="padding: 0">
|
||||||
|
<AppIcon iconName="app-copy"></AppIcon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</NodeContainer>
|
</NodeContainer>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -75,8 +99,18 @@ const refreshFieldList = () => {
|
|||||||
const refreshFieldList = getRefreshFieldList()
|
const refreshFieldList = getRefreshFieldList()
|
||||||
set(props.nodeModel.properties.config, 'globalFields', [...globalFields, ...refreshFieldList])
|
set(props.nodeModel.properties.config, 'globalFields', [...globalFields, ...refreshFieldList])
|
||||||
}
|
}
|
||||||
props.nodeModel.graphModel.eventCenter.on('refreshFieldList', refreshFieldList)
|
|
||||||
|
|
||||||
|
const refreshChatFieldList = () => {
|
||||||
|
const chatFieldList = props.nodeModel.graphModel.nodes
|
||||||
|
.filter((v: any) => v.id === 'base-node')
|
||||||
|
.map((v: any) => cloneDeep(v.properties.chat_input_field_list))
|
||||||
|
.reduce((x: any, y: any) => [...x, ...y], [])
|
||||||
|
.map((i: any) => ({ label: i.label, value: i.field }))
|
||||||
|
|
||||||
|
set(props.nodeModel.properties.config, 'chatFields', chatFieldList)
|
||||||
|
}
|
||||||
|
props.nodeModel.graphModel.eventCenter.on('refreshFieldList', refreshFieldList)
|
||||||
|
props.nodeModel.graphModel.eventCenter.on('chatFieldList', refreshChatFieldList)
|
||||||
const refreshFileUploadConfig = () => {
|
const refreshFileUploadConfig = () => {
|
||||||
let fields = cloneDeep(props.nodeModel.properties.config.fields)
|
let fields = cloneDeep(props.nodeModel.properties.config.fields)
|
||||||
const form_data = props.nodeModel.graphModel.nodes
|
const form_data = props.nodeModel.graphModel.nodes
|
||||||
@ -120,6 +154,7 @@ const refreshFileUploadConfig = () => {
|
|||||||
props.nodeModel.graphModel.eventCenter.on('refreshFileUploadConfig', refreshFileUploadConfig)
|
props.nodeModel.graphModel.eventCenter.on('refreshFileUploadConfig', refreshFileUploadConfig)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
refreshChatFieldList()
|
||||||
refreshFieldList()
|
refreshFieldList()
|
||||||
refreshFileUploadConfig()
|
refreshFileUploadConfig()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -245,6 +245,11 @@ function variableChange(item: any) {
|
|||||||
item.name = field.label
|
item.name = field.label
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
node.properties.config.chatFields.forEach((field: any) => {
|
||||||
|
if (field.value === item.fields[1]) {
|
||||||
|
item.name = field.label
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user