Merge branch 'main' of https://github.com/maxkb-dev/maxkb
This commit is contained in:
commit
1e1e98e155
@ -40,6 +40,10 @@ def write_context(step_variable: Dict, global_variable: Dict, node, workflow):
|
|||||||
node.context['run_time'] = time.time() - node.context['start_time']
|
node.context['run_time'] = time.time() - node.context['start_time']
|
||||||
|
|
||||||
|
|
||||||
|
def is_interrupt(node, step_variable: Dict, global_variable: Dict):
|
||||||
|
return node.type == 'form-node' and not node.context.get('is_submit', False)
|
||||||
|
|
||||||
|
|
||||||
class WorkFlowPostHandler:
|
class WorkFlowPostHandler:
|
||||||
def __init__(self, chat_info, client_id, client_type):
|
def __init__(self, chat_info, client_id, client_type):
|
||||||
self.chat_info = chat_info
|
self.chat_info = chat_info
|
||||||
@ -57,7 +61,7 @@ class WorkFlowPostHandler:
|
|||||||
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
|
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
|
||||||
'answer_tokens' in row and row.get('answer_tokens') is not None])
|
'answer_tokens' in row and row.get('answer_tokens') is not None])
|
||||||
answer_text_list = workflow.get_answer_text_list()
|
answer_text_list = workflow.get_answer_text_list()
|
||||||
answer_text = '\n\n'.join(answer_text_list)
|
answer_text = '\n\n'.join(answer['content'] for answer in answer_text_list)
|
||||||
if workflow.chat_record is not None:
|
if workflow.chat_record is not None:
|
||||||
chat_record = workflow.chat_record
|
chat_record = workflow.chat_record
|
||||||
chat_record.answer_text = answer_text
|
chat_record.answer_text = answer_text
|
||||||
@ -91,10 +95,11 @@ class WorkFlowPostHandler:
|
|||||||
|
|
||||||
class NodeResult:
|
class NodeResult:
|
||||||
def __init__(self, node_variable: Dict, workflow_variable: Dict,
|
def __init__(self, node_variable: Dict, workflow_variable: Dict,
|
||||||
_write_context=write_context):
|
_write_context=write_context, _is_interrupt=is_interrupt):
|
||||||
self._write_context = _write_context
|
self._write_context = _write_context
|
||||||
self.node_variable = node_variable
|
self.node_variable = node_variable
|
||||||
self.workflow_variable = workflow_variable
|
self.workflow_variable = workflow_variable
|
||||||
|
self._is_interrupt = _is_interrupt
|
||||||
|
|
||||||
def write_context(self, node, workflow):
|
def write_context(self, node, workflow):
|
||||||
return self._write_context(self.node_variable, self.workflow_variable, node, workflow)
|
return self._write_context(self.node_variable, self.workflow_variable, node, workflow)
|
||||||
@ -102,6 +107,14 @@ class NodeResult:
|
|||||||
def is_assertion_result(self):
|
def is_assertion_result(self):
|
||||||
return 'branch_id' in self.node_variable
|
return 'branch_id' in self.node_variable
|
||||||
|
|
||||||
|
def is_interrupt_exec(self, current_node):
|
||||||
|
"""
|
||||||
|
是否中断执行
|
||||||
|
@param current_node:
|
||||||
|
@return:
|
||||||
|
"""
|
||||||
|
return self._is_interrupt(current_node, self.node_variable, self.workflow_variable)
|
||||||
|
|
||||||
|
|
||||||
class ReferenceAddressSerializer(serializers.Serializer):
|
class ReferenceAddressSerializer(serializers.Serializer):
|
||||||
node_id = serializers.CharField(required=True, error_messages=ErrMessage.char("节点id"))
|
node_id = serializers.CharField(required=True, error_messages=ErrMessage.char("节点id"))
|
||||||
@ -139,14 +152,18 @@ class INode:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def get_answer_text(self):
|
def get_answer_text(self):
|
||||||
return self.answer_text
|
if self.answer_text is None:
|
||||||
|
return None
|
||||||
|
return {'content': self.answer_text, 'runtime_node_id': self.runtime_node_id,
|
||||||
|
'chat_record_id': self.workflow_params['chat_record_id']}
|
||||||
|
|
||||||
def __init__(self, node, workflow_params, workflow_manage, up_node_id_list=None):
|
def __init__(self, node, workflow_params, workflow_manage, up_node_id_list=None,
|
||||||
|
get_node_params=lambda node: node.properties.get('node_data')):
|
||||||
# 当前步骤上下文,用于存储当前步骤信息
|
# 当前步骤上下文,用于存储当前步骤信息
|
||||||
self.status = 200
|
self.status = 200
|
||||||
self.err_message = ''
|
self.err_message = ''
|
||||||
self.node = node
|
self.node = node
|
||||||
self.node_params = node.properties.get('node_data')
|
self.node_params = get_node_params(node)
|
||||||
self.workflow_params = workflow_params
|
self.workflow_params = workflow_params
|
||||||
self.workflow_manage = workflow_manage
|
self.workflow_manage = workflow_manage
|
||||||
self.node_params_serializer = None
|
self.node_params_serializer = None
|
||||||
|
|||||||
@ -14,6 +14,8 @@ class ApplicationNodeSerializer(serializers.Serializer):
|
|||||||
user_input_field_list = serializers.ListField(required=False, error_messages=ErrMessage.uuid("用户输入字段"))
|
user_input_field_list = serializers.ListField(required=False, error_messages=ErrMessage.uuid("用户输入字段"))
|
||||||
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片"))
|
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片"))
|
||||||
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
||||||
|
child_node = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("子节点"))
|
||||||
|
node_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("表单数据"))
|
||||||
|
|
||||||
|
|
||||||
class IApplicationNode(INode):
|
class IApplicationNode(INode):
|
||||||
@ -55,5 +57,5 @@ class IApplicationNode(INode):
|
|||||||
message=str(question), **kwargs)
|
message=str(question), **kwargs)
|
||||||
|
|
||||||
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
||||||
app_document_list=None, app_image_list=None, **kwargs) -> NodeResult:
|
app_document_list=None, app_image_list=None, child_node=None, node_data=None, **kwargs) -> NodeResult:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -2,19 +2,25 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from typing import List, Dict
|
from typing import Dict
|
||||||
|
|
||||||
from application.flow.i_step_node import NodeResult, INode
|
from application.flow.i_step_node import NodeResult, INode
|
||||||
from application.flow.step_node.application_node.i_application_node import IApplicationNode
|
from application.flow.step_node.application_node.i_application_node import IApplicationNode
|
||||||
from application.models import Chat
|
from application.models import Chat
|
||||||
from common.handle.impl.response.openai_to_response import OpenaiToResponse
|
|
||||||
|
|
||||||
|
|
||||||
def string_to_uuid(input_str):
|
def string_to_uuid(input_str):
|
||||||
return str(uuid.uuid5(uuid.NAMESPACE_DNS, input_str))
|
return str(uuid.uuid5(uuid.NAMESPACE_DNS, input_str))
|
||||||
|
|
||||||
|
|
||||||
|
def _is_interrupt_exec(node, node_variable: Dict, workflow_variable: Dict):
|
||||||
|
return node_variable.get('is_interrupt_exec', False)
|
||||||
|
|
||||||
|
|
||||||
def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow, answer: str):
|
def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow, answer: str):
|
||||||
result = node_variable.get('result')
|
result = node_variable.get('result')
|
||||||
|
node.context['child_node'] = node_variable['child_node']
|
||||||
|
node.context['is_interrupt_exec'] = node_variable['is_interrupt_exec']
|
||||||
node.context['message_tokens'] = result.get('usage', {}).get('prompt_tokens', 0)
|
node.context['message_tokens'] = result.get('usage', {}).get('prompt_tokens', 0)
|
||||||
node.context['answer_tokens'] = result.get('usage', {}).get('completion_tokens', 0)
|
node.context['answer_tokens'] = result.get('usage', {}).get('completion_tokens', 0)
|
||||||
node.context['answer'] = answer
|
node.context['answer'] = answer
|
||||||
@ -36,17 +42,34 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
|
|||||||
response = node_variable.get('result')
|
response = node_variable.get('result')
|
||||||
answer = ''
|
answer = ''
|
||||||
usage = {}
|
usage = {}
|
||||||
|
node_child_node = {}
|
||||||
|
is_interrupt_exec = False
|
||||||
for chunk in response:
|
for chunk in response:
|
||||||
# 先把流转成字符串
|
# 先把流转成字符串
|
||||||
response_content = chunk.decode('utf-8')[6:]
|
response_content = chunk.decode('utf-8')[6:]
|
||||||
response_content = json.loads(response_content)
|
response_content = json.loads(response_content)
|
||||||
choices = response_content.get('choices')
|
content = response_content.get('content', '')
|
||||||
if choices and isinstance(choices, list) and len(choices) > 0:
|
runtime_node_id = response_content.get('runtime_node_id', '')
|
||||||
content = choices[0].get('delta', {}).get('content', '')
|
chat_record_id = response_content.get('chat_record_id', '')
|
||||||
answer += content
|
child_node = response_content.get('child_node')
|
||||||
yield content
|
node_type = response_content.get('node_type')
|
||||||
|
real_node_id = response_content.get('real_node_id')
|
||||||
|
node_is_end = response_content.get('node_is_end', False)
|
||||||
|
if node_type == 'form-node':
|
||||||
|
is_interrupt_exec = True
|
||||||
|
answer += content
|
||||||
|
node_child_node = {'runtime_node_id': runtime_node_id, 'chat_record_id': chat_record_id,
|
||||||
|
'child_node': child_node}
|
||||||
|
yield {'content': content,
|
||||||
|
'node_type': node_type,
|
||||||
|
'runtime_node_id': runtime_node_id, 'chat_record_id': chat_record_id,
|
||||||
|
'child_node': child_node,
|
||||||
|
'real_node_id': real_node_id,
|
||||||
|
'node_is_end': node_is_end}
|
||||||
usage = response_content.get('usage', {})
|
usage = response_content.get('usage', {})
|
||||||
node_variable['result'] = {'usage': usage}
|
node_variable['result'] = {'usage': usage}
|
||||||
|
node_variable['is_interrupt_exec'] = is_interrupt_exec
|
||||||
|
node_variable['child_node'] = node_child_node
|
||||||
_write_context(node_variable, workflow_variable, node, workflow, answer)
|
_write_context(node_variable, workflow_variable, node, workflow, answer)
|
||||||
|
|
||||||
|
|
||||||
@ -64,6 +87,11 @@ def write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wor
|
|||||||
|
|
||||||
|
|
||||||
class BaseApplicationNode(IApplicationNode):
|
class BaseApplicationNode(IApplicationNode):
|
||||||
|
def get_answer_text(self):
|
||||||
|
if self.answer_text is None:
|
||||||
|
return None
|
||||||
|
return {'content': self.answer_text, 'runtime_node_id': self.runtime_node_id,
|
||||||
|
'chat_record_id': self.workflow_params['chat_record_id'], 'child_node': self.context.get('child_node')}
|
||||||
|
|
||||||
def save_context(self, details, workflow_manage):
|
def save_context(self, details, workflow_manage):
|
||||||
self.context['answer'] = details.get('answer')
|
self.context['answer'] = details.get('answer')
|
||||||
@ -72,7 +100,7 @@ class BaseApplicationNode(IApplicationNode):
|
|||||||
self.answer_text = details.get('answer')
|
self.answer_text = details.get('answer')
|
||||||
|
|
||||||
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
||||||
app_document_list=None, app_image_list=None,
|
app_document_list=None, app_image_list=None, child_node=None, node_data=None,
|
||||||
**kwargs) -> NodeResult:
|
**kwargs) -> NodeResult:
|
||||||
from application.serializers.chat_message_serializers import ChatMessageSerializer
|
from application.serializers.chat_message_serializers import ChatMessageSerializer
|
||||||
# 生成嵌入应用的chat_id
|
# 生成嵌入应用的chat_id
|
||||||
@ -85,6 +113,14 @@ class BaseApplicationNode(IApplicationNode):
|
|||||||
app_document_list = []
|
app_document_list = []
|
||||||
if app_image_list is None:
|
if app_image_list is None:
|
||||||
app_image_list = []
|
app_image_list = []
|
||||||
|
runtime_node_id = None
|
||||||
|
record_id = None
|
||||||
|
child_node_value = None
|
||||||
|
if child_node is not None:
|
||||||
|
runtime_node_id = child_node.get('runtime_node_id')
|
||||||
|
record_id = child_node.get('chat_record_id')
|
||||||
|
child_node_value = child_node.get('child_node')
|
||||||
|
|
||||||
response = ChatMessageSerializer(
|
response = ChatMessageSerializer(
|
||||||
data={'chat_id': current_chat_id, 'message': message,
|
data={'chat_id': current_chat_id, 'message': message,
|
||||||
're_chat': re_chat,
|
're_chat': re_chat,
|
||||||
@ -94,16 +130,20 @@ class BaseApplicationNode(IApplicationNode):
|
|||||||
'client_type': client_type,
|
'client_type': client_type,
|
||||||
'document_list': app_document_list,
|
'document_list': app_document_list,
|
||||||
'image_list': app_image_list,
|
'image_list': app_image_list,
|
||||||
'form_data': kwargs}).chat(base_to_response=OpenaiToResponse())
|
'runtime_node_id': runtime_node_id,
|
||||||
|
'chat_record_id': record_id,
|
||||||
|
'child_node': child_node_value,
|
||||||
|
'node_data': node_data,
|
||||||
|
'form_data': kwargs}).chat()
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
if stream:
|
if stream:
|
||||||
content_generator = response.streaming_content
|
content_generator = response.streaming_content
|
||||||
return NodeResult({'result': content_generator, 'question': message}, {},
|
return NodeResult({'result': content_generator, 'question': message}, {},
|
||||||
_write_context=write_context_stream)
|
_write_context=write_context_stream, _is_interrupt=_is_interrupt_exec)
|
||||||
else:
|
else:
|
||||||
data = json.loads(response.content)
|
data = json.loads(response.content)
|
||||||
return NodeResult({'result': data, 'question': message}, {},
|
return NodeResult({'result': data, 'question': message}, {},
|
||||||
_write_context=write_context)
|
_write_context=write_context, _is_interrupt=_is_interrupt_exec)
|
||||||
|
|
||||||
def get_details(self, index: int, **kwargs):
|
def get_details(self, index: int, **kwargs):
|
||||||
global_fields = []
|
global_fields = []
|
||||||
|
|||||||
@ -17,6 +17,7 @@ from common.util.field_message import ErrMessage
|
|||||||
class FormNodeParamsSerializer(serializers.Serializer):
|
class FormNodeParamsSerializer(serializers.Serializer):
|
||||||
form_field_list = serializers.ListField(required=True, error_messages=ErrMessage.list("表单配置"))
|
form_field_list = serializers.ListField(required=True, error_messages=ErrMessage.list("表单配置"))
|
||||||
form_content_format = serializers.CharField(required=True, error_messages=ErrMessage.char('表单输出内容'))
|
form_content_format = serializers.CharField(required=True, error_messages=ErrMessage.char('表单输出内容'))
|
||||||
|
form_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("表单数据"))
|
||||||
|
|
||||||
|
|
||||||
class IFormNode(INode):
|
class IFormNode(INode):
|
||||||
@ -29,5 +30,5 @@ class IFormNode(INode):
|
|||||||
def _run(self):
|
def _run(self):
|
||||||
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
|
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
|
||||||
|
|
||||||
def execute(self, form_field_list, form_content_format, **kwargs) -> NodeResult:
|
def execute(self, form_field_list, form_content_format, form_data, **kwargs) -> NodeResult:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -42,7 +42,12 @@ class BaseFormNode(IFormNode):
|
|||||||
for key in form_data:
|
for key in form_data:
|
||||||
self.context[key] = form_data[key]
|
self.context[key] = form_data[key]
|
||||||
|
|
||||||
def execute(self, form_field_list, form_content_format, **kwargs) -> NodeResult:
|
def execute(self, form_field_list, form_content_format, form_data, **kwargs) -> NodeResult:
|
||||||
|
if form_data is not None:
|
||||||
|
self.context['is_submit'] = True
|
||||||
|
self.context['form_data'] = form_data
|
||||||
|
else:
|
||||||
|
self.context['is_submit'] = False
|
||||||
form_setting = {"form_field_list": form_field_list, "runtime_node_id": self.runtime_node_id,
|
form_setting = {"form_field_list": form_field_list, "runtime_node_id": self.runtime_node_id,
|
||||||
"chat_record_id": self.flow_params_serializer.data.get("chat_record_id"),
|
"chat_record_id": self.flow_params_serializer.data.get("chat_record_id"),
|
||||||
"is_submit": self.context.get("is_submit", False)}
|
"is_submit": self.context.get("is_submit", False)}
|
||||||
@ -63,7 +68,8 @@ class BaseFormNode(IFormNode):
|
|||||||
form = f'<form_rander>{json.dumps(form_setting)}</form_rander>'
|
form = f'<form_rander>{json.dumps(form_setting)}</form_rander>'
|
||||||
prompt_template = PromptTemplate.from_template(form_content_format, template_format='jinja2')
|
prompt_template = PromptTemplate.from_template(form_content_format, template_format='jinja2')
|
||||||
value = prompt_template.format(form=form)
|
value = prompt_template.format(form=form)
|
||||||
return value
|
return {'content': value, 'runtime_node_id': self.runtime_node_id,
|
||||||
|
'chat_record_id': self.workflow_params['chat_record_id']}
|
||||||
|
|
||||||
def get_details(self, index: int, **kwargs):
|
def get_details(self, index: int, **kwargs):
|
||||||
form_content_format = self.context.get('form_content_format')
|
form_content_format = self.context.get('form_content_format')
|
||||||
|
|||||||
@ -244,15 +244,15 @@ class WorkflowManage:
|
|||||||
base_to_response: BaseToResponse = SystemToResponse(), form_data=None, image_list=None,
|
base_to_response: BaseToResponse = SystemToResponse(), form_data=None, image_list=None,
|
||||||
document_list=None,
|
document_list=None,
|
||||||
start_node_id=None,
|
start_node_id=None,
|
||||||
start_node_data=None, chat_record=None):
|
start_node_data=None, chat_record=None, child_node=None):
|
||||||
if form_data is None:
|
if form_data is None:
|
||||||
form_data = {}
|
form_data = {}
|
||||||
if image_list is None:
|
if image_list is None:
|
||||||
image_list = []
|
image_list = []
|
||||||
if document_list is None:
|
if document_list is None:
|
||||||
document_list = []
|
document_list = []
|
||||||
|
self.start_node_id = start_node_id
|
||||||
self.start_node = None
|
self.start_node = None
|
||||||
self.start_node_result_future = None
|
|
||||||
self.form_data = form_data
|
self.form_data = form_data
|
||||||
self.image_list = image_list
|
self.image_list = image_list
|
||||||
self.document_list = document_list
|
self.document_list = document_list
|
||||||
@ -270,6 +270,7 @@ class WorkflowManage:
|
|||||||
self.base_to_response = base_to_response
|
self.base_to_response = base_to_response
|
||||||
self.chat_record = chat_record
|
self.chat_record = chat_record
|
||||||
self.await_future_map = {}
|
self.await_future_map = {}
|
||||||
|
self.child_node = child_node
|
||||||
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)
|
||||||
else:
|
else:
|
||||||
@ -290,11 +291,17 @@ class WorkflowManage:
|
|||||||
for node_details in sorted(chat_record.details.values(), key=lambda d: d.get('index')):
|
for node_details in sorted(chat_record.details.values(), key=lambda d: d.get('index')):
|
||||||
node_id = node_details.get('node_id')
|
node_id = node_details.get('node_id')
|
||||||
if node_details.get('runtime_node_id') == start_node_id:
|
if node_details.get('runtime_node_id') == start_node_id:
|
||||||
self.start_node = self.get_node_cls_by_id(node_id, node_details.get('up_node_id_list'))
|
def get_node_params(n):
|
||||||
self.start_node.valid_args(self.start_node.node_params, self.start_node.workflow_params)
|
is_result = False
|
||||||
self.start_node.save_context(node_details, self)
|
if n.type == 'application-node':
|
||||||
node_result = NodeResult({**start_node_data, 'form_data': start_node_data, 'is_submit': True}, {})
|
is_result = True
|
||||||
self.start_node_result_future = NodeResultFuture(node_result, None)
|
return {**n.properties.get('node_data'), 'form_data': start_node_data, 'node_data': start_node_data,
|
||||||
|
'child_node': self.child_node, 'is_result': is_result}
|
||||||
|
|
||||||
|
self.start_node = self.get_node_cls_by_id(node_id, node_details.get('up_node_id_list'),
|
||||||
|
get_node_params=get_node_params)
|
||||||
|
self.start_node.valid_args(
|
||||||
|
{**self.start_node.node_params, 'form_data': start_node_data}, self.start_node.workflow_params)
|
||||||
self.node_context.append(self.start_node)
|
self.node_context.append(self.start_node)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -306,7 +313,7 @@ class WorkflowManage:
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.params.get('stream'):
|
if self.params.get('stream'):
|
||||||
return self.run_stream(self.start_node, self.start_node_result_future)
|
return self.run_stream(self.start_node, None)
|
||||||
return self.run_block()
|
return self.run_block()
|
||||||
|
|
||||||
def run_block(self):
|
def run_block(self):
|
||||||
@ -352,9 +359,19 @@ class WorkflowManage:
|
|||||||
break
|
break
|
||||||
yield chunk
|
yield chunk
|
||||||
finally:
|
finally:
|
||||||
|
details = self.get_runtime_details()
|
||||||
|
message_tokens = sum([row.get('message_tokens') for row in details.values() if
|
||||||
|
'message_tokens' in row and row.get('message_tokens') is not None])
|
||||||
|
answer_tokens = sum([row.get('answer_tokens') for row in details.values() if
|
||||||
|
'answer_tokens' in row and row.get('answer_tokens') is not None])
|
||||||
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
|
self.work_flow_post_handler.handler(self.params['chat_id'], self.params['chat_record_id'],
|
||||||
self.answer,
|
self.answer,
|
||||||
self)
|
self)
|
||||||
|
yield self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||||
|
self.params['chat_record_id'],
|
||||||
|
'',
|
||||||
|
[],
|
||||||
|
'', True, message_tokens, answer_tokens, {})
|
||||||
|
|
||||||
def run_chain_async(self, current_node, node_result_future):
|
def run_chain_async(self, current_node, node_result_future):
|
||||||
future = executor.submit(self.run_chain, current_node, node_result_future)
|
future = executor.submit(self.run_chain, current_node, node_result_future)
|
||||||
@ -423,6 +440,8 @@ class WorkflowManage:
|
|||||||
|
|
||||||
def hand_event_node_result(self, current_node, node_result_future):
|
def hand_event_node_result(self, current_node, node_result_future):
|
||||||
node_chunk = NodeChunk()
|
node_chunk = NodeChunk()
|
||||||
|
real_node_id = current_node.runtime_node_id
|
||||||
|
child_node = {}
|
||||||
try:
|
try:
|
||||||
current_result = node_result_future.result()
|
current_result = node_result_future.result()
|
||||||
result = current_result.write_context(current_node, self)
|
result = current_result.write_context(current_node, self)
|
||||||
@ -430,21 +449,38 @@ class WorkflowManage:
|
|||||||
if self.is_result(current_node, current_result):
|
if self.is_result(current_node, current_result):
|
||||||
self.node_chunk_manage.add_node_chunk(node_chunk)
|
self.node_chunk_manage.add_node_chunk(node_chunk)
|
||||||
for r in result:
|
for r in result:
|
||||||
|
content = r
|
||||||
|
child_node = {}
|
||||||
|
node_is_end = False
|
||||||
|
if isinstance(r, dict):
|
||||||
|
content = r.get('content')
|
||||||
|
child_node = {'runtime_node_id': r.get('runtime_node_id'),
|
||||||
|
'chat_record_id': r.get('chat_record_id')
|
||||||
|
, 'child_node': r.get('child_node')}
|
||||||
|
real_node_id = r.get('real_node_id')
|
||||||
|
node_is_end = r.get('node_is_end')
|
||||||
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||||
self.params['chat_record_id'],
|
self.params['chat_record_id'],
|
||||||
current_node.id,
|
current_node.id,
|
||||||
current_node.up_node_id_list,
|
current_node.up_node_id_list,
|
||||||
r, False, 0, 0,
|
content, False, 0, 0,
|
||||||
{'node_type': current_node.type,
|
{'node_type': current_node.type,
|
||||||
'view_type': current_node.view_type})
|
'runtime_node_id': current_node.runtime_node_id,
|
||||||
|
'view_type': current_node.view_type,
|
||||||
|
'child_node': child_node,
|
||||||
|
'node_is_end': node_is_end,
|
||||||
|
'real_node_id': real_node_id})
|
||||||
node_chunk.add_chunk(chunk)
|
node_chunk.add_chunk(chunk)
|
||||||
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
chunk = self.base_to_response.to_stream_chunk_response(self.params['chat_id'],
|
||||||
self.params['chat_record_id'],
|
self.params['chat_record_id'],
|
||||||
current_node.id,
|
current_node.id,
|
||||||
current_node.up_node_id_list,
|
current_node.up_node_id_list,
|
||||||
'', False, 0, 0, {'node_is_end': True,
|
'', False, 0, 0, {'node_is_end': True,
|
||||||
|
'runtime_node_id': current_node.runtime_node_id,
|
||||||
'node_type': current_node.type,
|
'node_type': current_node.type,
|
||||||
'view_type': current_node.view_type})
|
'view_type': current_node.view_type,
|
||||||
|
'child_node': child_node,
|
||||||
|
'real_node_id': real_node_id})
|
||||||
node_chunk.end(chunk)
|
node_chunk.end(chunk)
|
||||||
else:
|
else:
|
||||||
list(result)
|
list(result)
|
||||||
@ -461,8 +497,12 @@ class WorkflowManage:
|
|||||||
current_node.id,
|
current_node.id,
|
||||||
current_node.up_node_id_list,
|
current_node.up_node_id_list,
|
||||||
str(e), False, 0, 0,
|
str(e), False, 0, 0,
|
||||||
{'node_is_end': True, 'node_type': current_node.type,
|
{'node_is_end': True,
|
||||||
'view_type': current_node.view_type})
|
'runtime_node_id': current_node.runtime_node_id,
|
||||||
|
'node_type': current_node.type,
|
||||||
|
'view_type': current_node.view_type,
|
||||||
|
'child_node': {},
|
||||||
|
'real_node_id': real_node_id})
|
||||||
if not self.node_chunk_manage.contains(node_chunk):
|
if not self.node_chunk_manage.contains(node_chunk):
|
||||||
self.node_chunk_manage.add_node_chunk(node_chunk)
|
self.node_chunk_manage.add_node_chunk(node_chunk)
|
||||||
node_chunk.end(chunk)
|
node_chunk.end(chunk)
|
||||||
@ -554,9 +594,9 @@ class WorkflowManage:
|
|||||||
else:
|
else:
|
||||||
if len(result) > 0:
|
if len(result) > 0:
|
||||||
exec_index = len(result) - 1
|
exec_index = len(result) - 1
|
||||||
content = result[exec_index]
|
content = result[exec_index]['content']
|
||||||
result[exec_index] += answer_text if len(
|
result[exec_index]['content'] += answer_text['content'] if len(
|
||||||
content) == 0 else ('\n\n' + answer_text)
|
content) == 0 else ('\n\n' + answer_text['content'])
|
||||||
else:
|
else:
|
||||||
answer_text = node.get_answer_text()
|
answer_text = node.get_answer_text()
|
||||||
result.insert(0, answer_text)
|
result.insert(0, answer_text)
|
||||||
@ -613,8 +653,8 @@ class WorkflowManage:
|
|||||||
@param current_node_result: 当前可执行节点结果
|
@param current_node_result: 当前可执行节点结果
|
||||||
@return: 可执行节点列表
|
@return: 可执行节点列表
|
||||||
"""
|
"""
|
||||||
|
# 判断是否中断执行
|
||||||
if current_node.type == 'form-node' and 'form_data' not in current_node_result.node_variable:
|
if current_node_result.is_interrupt_exec(current_node):
|
||||||
return []
|
return []
|
||||||
node_list = []
|
node_list = []
|
||||||
if current_node_result is not None and current_node_result.is_assertion_result():
|
if current_node_result is not None and current_node_result.is_assertion_result():
|
||||||
@ -689,11 +729,12 @@ class WorkflowManage:
|
|||||||
base_node_list = [node for node in self.flow.nodes if node.type == 'base-node']
|
base_node_list = [node for node in self.flow.nodes if node.type == 'base-node']
|
||||||
return base_node_list[0]
|
return base_node_list[0]
|
||||||
|
|
||||||
def get_node_cls_by_id(self, node_id, up_node_id_list=None):
|
def get_node_cls_by_id(self, node_id, up_node_id_list=None,
|
||||||
|
get_node_params=lambda node: node.properties.get('node_data')):
|
||||||
for node in self.flow.nodes:
|
for node in self.flow.nodes:
|
||||||
if node.id == node_id:
|
if node.id == node_id:
|
||||||
node_instance = get_node(node.type)(node,
|
node_instance = get_node(node.type)(node,
|
||||||
self.params, self, up_node_id_list)
|
self.params, self, up_node_id_list, get_node_params)
|
||||||
return node_instance
|
return node_instance
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import django.contrib.postgres.fields
|
|||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
sql = """
|
sql = """
|
||||||
UPDATE "public".application_chat_record
|
UPDATE application_chat_record
|
||||||
SET "answer_text_list" = ARRAY[answer_text];
|
SET answer_text_list=ARRAY[jsonb_build_object('content',answer_text)]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -28,8 +28,7 @@ class Migration(migrations.Migration):
|
|||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='chatrecord',
|
model_name='chatrecord',
|
||||||
name='answer_text_list',
|
name='answer_text_list',
|
||||||
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=40960), default=list,
|
field=django.contrib.postgres.fields.ArrayField(base_field=models.JSONField(), default=list, size=None, verbose_name='改进标注列表')
|
||||||
size=None, verbose_name='改进标注列表'),
|
|
||||||
),
|
),
|
||||||
migrations.RunSQL(sql)
|
migrations.RunSQL(sql)
|
||||||
]
|
]
|
||||||
|
|||||||
@ -69,7 +69,6 @@ class Application(AppModelMixin):
|
|||||||
file_upload_enable = models.BooleanField(verbose_name="文件上传是否启用", default=False)
|
file_upload_enable = models.BooleanField(verbose_name="文件上传是否启用", default=False)
|
||||||
file_upload_setting = models.JSONField(verbose_name="文件上传相关设置", default=dict)
|
file_upload_setting = models.JSONField(verbose_name="文件上传相关设置", default=dict)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_model_prompt():
|
def get_default_model_prompt():
|
||||||
return ('已知信息:'
|
return ('已知信息:'
|
||||||
@ -148,7 +147,7 @@ class ChatRecord(AppModelMixin):
|
|||||||
problem_text = models.CharField(max_length=10240, verbose_name="问题")
|
problem_text = models.CharField(max_length=10240, verbose_name="问题")
|
||||||
answer_text = models.CharField(max_length=40960, verbose_name="答案")
|
answer_text = models.CharField(max_length=40960, verbose_name="答案")
|
||||||
answer_text_list = ArrayField(verbose_name="改进标注列表",
|
answer_text_list = ArrayField(verbose_name="改进标注列表",
|
||||||
base_field=models.CharField(max_length=40960)
|
base_field=models.JSONField()
|
||||||
, default=list)
|
, default=list)
|
||||||
message_tokens = models.IntegerField(verbose_name="请求token数量", default=0)
|
message_tokens = models.IntegerField(verbose_name="请求token数量", default=0)
|
||||||
answer_tokens = models.IntegerField(verbose_name="响应token数量", default=0)
|
answer_tokens = models.IntegerField(verbose_name="响应token数量", default=0)
|
||||||
|
|||||||
@ -238,13 +238,14 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||||||
runtime_node_id = serializers.CharField(required=False, allow_null=True, allow_blank=True,
|
runtime_node_id = serializers.CharField(required=False, allow_null=True, allow_blank=True,
|
||||||
error_messages=ErrMessage.char("运行时节点id"))
|
error_messages=ErrMessage.char("运行时节点id"))
|
||||||
|
|
||||||
node_data = serializers.DictField(required=False, error_messages=ErrMessage.char("节点参数"))
|
node_data = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.char("节点参数"))
|
||||||
application_id = serializers.UUIDField(required=False, allow_null=True, error_messages=ErrMessage.uuid("应用id"))
|
application_id = serializers.UUIDField(required=False, allow_null=True, error_messages=ErrMessage.uuid("应用id"))
|
||||||
client_id = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端id"))
|
client_id = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端id"))
|
||||||
client_type = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端类型"))
|
client_type = serializers.CharField(required=True, error_messages=ErrMessage.char("客户端类型"))
|
||||||
form_data = serializers.DictField(required=False, error_messages=ErrMessage.char("全局变量"))
|
form_data = serializers.DictField(required=False, error_messages=ErrMessage.char("全局变量"))
|
||||||
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片"))
|
image_list = serializers.ListField(required=False, error_messages=ErrMessage.list("图片"))
|
||||||
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
document_list = serializers.ListField(required=False, error_messages=ErrMessage.list("文档"))
|
||||||
|
child_node = serializers.DictField(required=False, allow_null=True, error_messages=ErrMessage.dict("子节点"))
|
||||||
|
|
||||||
def is_valid_application_workflow(self, *, raise_exception=False):
|
def is_valid_application_workflow(self, *, raise_exception=False):
|
||||||
self.is_valid_intraday_access_num()
|
self.is_valid_intraday_access_num()
|
||||||
@ -353,7 +354,7 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||||||
'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type),
|
'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type),
|
||||||
base_to_response, form_data, image_list, document_list,
|
base_to_response, form_data, image_list, document_list,
|
||||||
self.data.get('runtime_node_id'),
|
self.data.get('runtime_node_id'),
|
||||||
self.data.get('node_data'), chat_record)
|
self.data.get('node_data'), chat_record, self.data.get('child_node'))
|
||||||
r = work_flow_manage.run()
|
r = work_flow_manage.run()
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|||||||
@ -169,7 +169,7 @@ function initMaxkbStyle(root){
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
{{x_type}}: {{x_value}}px;
|
{{x_type}}: {{x_value}}px;
|
||||||
{{y_type}}: {{y_value}}px;
|
{{y_type}}: {{y_value}}px;
|
||||||
z-index: 1000;
|
z-index: 10001;
|
||||||
}
|
}
|
||||||
#maxkb .maxkb-tips {
|
#maxkb .maxkb-tips {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -180,7 +180,7 @@ function initMaxkbStyle(root){
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: #3370FF;
|
background: #3370FF;
|
||||||
z-index: 1000;
|
z-index: 10001;
|
||||||
}
|
}
|
||||||
#maxkb .maxkb-tips .maxkb-arrow {
|
#maxkb .maxkb-tips .maxkb-arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@ -138,7 +138,8 @@ class ChatView(APIView):
|
|||||||
'node_id': request.data.get('node_id', None),
|
'node_id': request.data.get('node_id', None),
|
||||||
'runtime_node_id': request.data.get('runtime_node_id', None),
|
'runtime_node_id': request.data.get('runtime_node_id', None),
|
||||||
'node_data': request.data.get('node_data', {}),
|
'node_data': request.data.get('node_data', {}),
|
||||||
'chat_record_id': request.data.get('chat_record_id')}
|
'chat_record_id': request.data.get('chat_record_id'),
|
||||||
|
'child_node': request.data.get('child_node')}
|
||||||
).chat()
|
).chat()
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False)
|
@action(methods=['GET'], detail=False)
|
||||||
|
|||||||
@ -10,8 +10,14 @@ import setting.models
|
|||||||
from setting.models import Model
|
from setting.models import Model
|
||||||
from .listener_manage import *
|
from .listener_manage import *
|
||||||
|
|
||||||
|
update_document_status_sql = """
|
||||||
|
UPDATE "public"."document"
|
||||||
|
SET status ="replace"("replace"("replace"(status, '1', '3'), '0', '3'), '4', '3')
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
# QuerySet(Document).filter(status__in=[Status.embedding, Status.queue_up]).update(**{'status': Status.error})
|
# QuerySet(Document).filter(status__in=[Status.embedding, Status.queue_up]).update(**{'status': Status.error})
|
||||||
QuerySet(Model).filter(status=setting.models.Status.DOWNLOAD).update(status=setting.models.Status.ERROR,
|
QuerySet(Model).filter(status=setting.models.Status.DOWNLOAD).update(status=setting.models.Status.ERROR,
|
||||||
meta={'message': "下载程序被中断,请重试"})
|
meta={'message': "下载程序被中断,请重试"})
|
||||||
|
update_execute(update_document_status_sql, [])
|
||||||
|
|||||||
@ -35,7 +35,7 @@ class OpenaiToResponse(BaseToResponse):
|
|||||||
def to_stream_chunk_response(self, chat_id, chat_record_id, node_id, up_node_id_list, content, is_end, completion_tokens,
|
def to_stream_chunk_response(self, chat_id, chat_record_id, node_id, up_node_id_list, content, is_end, completion_tokens,
|
||||||
prompt_tokens, other_params: dict = None):
|
prompt_tokens, other_params: dict = None):
|
||||||
chunk = ChatCompletionChunk(id=chat_record_id, model='', object='chat.completion.chunk',
|
chunk = ChatCompletionChunk(id=chat_record_id, model='', object='chat.completion.chunk',
|
||||||
created=datetime.datetime.now().second, choices=[
|
created=datetime.datetime.now().second,choices=[
|
||||||
Choice(delta=ChoiceDelta(content=content, chat_id=chat_id), finish_reason='stop' if is_end else None,
|
Choice(delta=ChoiceDelta(content=content, chat_id=chat_id), finish_reason='stop' if is_end else None,
|
||||||
index=0)],
|
index=0)],
|
||||||
usage=CompletionUsage(completion_tokens=completion_tokens,
|
usage=CompletionUsage(completion_tokens=completion_tokens,
|
||||||
|
|||||||
@ -28,7 +28,7 @@ class SystemToResponse(BaseToResponse):
|
|||||||
prompt_tokens, other_params: dict = None):
|
prompt_tokens, other_params: dict = None):
|
||||||
if other_params is None:
|
if other_params is None:
|
||||||
other_params = {}
|
other_params = {}
|
||||||
chunk = json.dumps({'chat_id': str(chat_id), 'id': str(chat_record_id), 'operate': True,
|
chunk = json.dumps({'chat_id': str(chat_id), 'chat_record_id': str(chat_record_id), 'operate': True,
|
||||||
'content': content, 'node_id': node_id, 'up_node_id_list': up_node_id_list, 'is_end': is_end,
|
'content': content, 'node_id': node_id, 'up_node_id_list': up_node_id_list, 'is_end': is_end,
|
||||||
'usage': {'completion_tokens': completion_tokens,
|
'usage': {'completion_tokens': completion_tokens,
|
||||||
'prompt_tokens': prompt_tokens,
|
'prompt_tokens': prompt_tokens,
|
||||||
|
|||||||
@ -142,7 +142,10 @@ class Fork:
|
|||||||
if len(charset_list) > 0:
|
if len(charset_list) > 0:
|
||||||
charset = charset_list[0]
|
charset = charset_list[0]
|
||||||
if charset != encoding:
|
if charset != encoding:
|
||||||
html_content = response.content.decode(charset)
|
try:
|
||||||
|
html_content = response.content.decode(charset)
|
||||||
|
except Exception as e:
|
||||||
|
logging.getLogger("max_kb").error(f'{e}')
|
||||||
return BeautifulSoup(html_content, "html.parser")
|
return BeautifulSoup(html_content, "html.parser")
|
||||||
return beautiful_soup
|
return beautiful_soup
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import openpyxl
|
|||||||
from celery_once import AlreadyQueued
|
from celery_once import AlreadyQueued
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet, Count
|
||||||
from django.db.models.functions import Substr, Reverse
|
from django.db.models.functions import Substr, Reverse
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
@ -56,6 +56,7 @@ from embedding.task.embedding import embedding_by_document, delete_embedding_by_
|
|||||||
delete_embedding_by_document, update_embedding_dataset_id, delete_embedding_by_paragraph_ids, \
|
delete_embedding_by_document, update_embedding_dataset_id, delete_embedding_by_paragraph_ids, \
|
||||||
embedding_by_document_list
|
embedding_by_document_list
|
||||||
from smartdoc.conf import PROJECT_DIR
|
from smartdoc.conf import PROJECT_DIR
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
parse_qa_handle_list = [XlsParseQAHandle(), CsvParseQAHandle(), XlsxParseQAHandle()]
|
parse_qa_handle_list = [XlsParseQAHandle(), CsvParseQAHandle(), XlsxParseQAHandle()]
|
||||||
parse_table_handle_list = [CsvSplitHandle(), XlsSplitHandle(), XlsxSplitHandle()]
|
parse_table_handle_list = [CsvSplitHandle(), XlsSplitHandle(), XlsxSplitHandle()]
|
||||||
@ -442,6 +443,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||||||
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
||||||
# 删除问题
|
# 删除问题
|
||||||
QuerySet(model=ProblemParagraphMapping).filter(document_id=document_id).delete()
|
QuerySet(model=ProblemParagraphMapping).filter(document_id=document_id).delete()
|
||||||
|
delete_problems_and_mappings([document_id])
|
||||||
# 删除向量库
|
# 删除向量库
|
||||||
delete_embedding_by_document(document_id)
|
delete_embedding_by_document(document_id)
|
||||||
paragraphs = get_split_model('web.md').parse(result.content)
|
paragraphs = get_split_model('web.md').parse(result.content)
|
||||||
@ -660,7 +662,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||||||
# 删除段落
|
# 删除段落
|
||||||
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
QuerySet(model=Paragraph).filter(document_id=document_id).delete()
|
||||||
# 删除问题
|
# 删除问题
|
||||||
QuerySet(model=ProblemParagraphMapping).filter(document_id=document_id).delete()
|
delete_problems_and_mappings([document_id])
|
||||||
# 删除向量库
|
# 删除向量库
|
||||||
delete_embedding_by_document(document_id)
|
delete_embedding_by_document(document_id)
|
||||||
return True
|
return True
|
||||||
@ -987,7 +989,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||||||
document_id_list = instance.get("id_list")
|
document_id_list = instance.get("id_list")
|
||||||
QuerySet(Document).filter(id__in=document_id_list).delete()
|
QuerySet(Document).filter(id__in=document_id_list).delete()
|
||||||
QuerySet(Paragraph).filter(document_id__in=document_id_list).delete()
|
QuerySet(Paragraph).filter(document_id__in=document_id_list).delete()
|
||||||
QuerySet(ProblemParagraphMapping).filter(document_id__in=document_id_list).delete()
|
delete_problems_and_mappings(document_id_list)
|
||||||
# 删除向量库
|
# 删除向量库
|
||||||
delete_embedding_by_document_list(document_id_list)
|
delete_embedding_by_document_list(document_id_list)
|
||||||
return True
|
return True
|
||||||
@ -1086,3 +1088,18 @@ def file_to_paragraph(file, pattern_list: List, with_filter: bool, limit: int):
|
|||||||
if split_handle.support(file, get_buffer):
|
if split_handle.support(file, get_buffer):
|
||||||
return split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
return split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
||||||
return default_split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
return default_split_handle.handle(file, pattern_list, with_filter, limit, get_buffer, save_image)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_problems_and_mappings(document_ids):
|
||||||
|
problem_paragraph_mappings = ProblemParagraphMapping.objects.filter(document_id__in=document_ids)
|
||||||
|
problem_ids = set(problem_paragraph_mappings.values_list('problem_id', flat=True))
|
||||||
|
|
||||||
|
if problem_ids:
|
||||||
|
problem_paragraph_mappings.delete()
|
||||||
|
remaining_problem_counts = ProblemParagraphMapping.objects.filter(problem_id__in=problem_ids).values(
|
||||||
|
'problem_id').annotate(count=Count('problem_id'))
|
||||||
|
remaining_problem_ids = {pc['problem_id'] for pc in remaining_problem_counts}
|
||||||
|
problem_ids_to_delete = problem_ids - remaining_problem_ids
|
||||||
|
Problem.objects.filter(id__in=problem_ids_to_delete).delete()
|
||||||
|
else:
|
||||||
|
problem_paragraph_mappings.delete()
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from typing import Dict
|
|||||||
|
|
||||||
from celery_once import AlreadyQueued
|
from celery_once import AlreadyQueued
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet, Count
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||||||
self.is_valid(raise_exception=True)
|
self.is_valid(raise_exception=True)
|
||||||
paragraph_id_list = instance.get("id_list")
|
paragraph_id_list = instance.get("id_list")
|
||||||
QuerySet(Paragraph).filter(id__in=paragraph_id_list).delete()
|
QuerySet(Paragraph).filter(id__in=paragraph_id_list).delete()
|
||||||
QuerySet(ProblemParagraphMapping).filter(paragraph_id__in=paragraph_id_list).delete()
|
delete_problems_and_mappings(paragraph_id_list)
|
||||||
update_document_char_length(self.data.get('document_id'))
|
update_document_char_length(self.data.get('document_id'))
|
||||||
# 删除向量库
|
# 删除向量库
|
||||||
delete_embedding_by_paragraph_ids(paragraph_id_list)
|
delete_embedding_by_paragraph_ids(paragraph_id_list)
|
||||||
@ -541,14 +541,7 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||||||
self.is_valid(raise_exception=True)
|
self.is_valid(raise_exception=True)
|
||||||
paragraph_id = self.data.get('paragraph_id')
|
paragraph_id = self.data.get('paragraph_id')
|
||||||
Paragraph.objects.filter(id=paragraph_id).delete()
|
Paragraph.objects.filter(id=paragraph_id).delete()
|
||||||
|
delete_problems_and_mappings([paragraph_id])
|
||||||
problem_id = ProblemParagraphMapping.objects.filter(paragraph_id=paragraph_id).values_list('problem_id',
|
|
||||||
flat=True).first()
|
|
||||||
|
|
||||||
if problem_id is not None:
|
|
||||||
if ProblemParagraphMapping.objects.filter(problem_id=problem_id).count() == 1:
|
|
||||||
Problem.objects.filter(id=problem_id).delete()
|
|
||||||
ProblemParagraphMapping.objects.filter(paragraph_id=paragraph_id).delete()
|
|
||||||
|
|
||||||
update_document_char_length(self.data.get('document_id'))
|
update_document_char_length(self.data.get('document_id'))
|
||||||
delete_embedding_by_paragraph(paragraph_id)
|
delete_embedding_by_paragraph(paragraph_id)
|
||||||
@ -755,3 +748,18 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||||||
prompt)
|
prompt)
|
||||||
except AlreadyQueued as e:
|
except AlreadyQueued as e:
|
||||||
raise AppApiException(500, "任务正在执行中,请勿重复下发")
|
raise AppApiException(500, "任务正在执行中,请勿重复下发")
|
||||||
|
|
||||||
|
|
||||||
|
def delete_problems_and_mappings(paragraph_ids):
|
||||||
|
problem_paragraph_mappings = ProblemParagraphMapping.objects.filter(paragraph_id__in=paragraph_ids)
|
||||||
|
problem_ids = set(problem_paragraph_mappings.values_list('problem_id', flat=True))
|
||||||
|
|
||||||
|
if problem_ids:
|
||||||
|
problem_paragraph_mappings.delete()
|
||||||
|
remaining_problem_counts = ProblemParagraphMapping.objects.filter(problem_id__in=problem_ids).values(
|
||||||
|
'problem_id').annotate(count=Count('problem_id'))
|
||||||
|
remaining_problem_ids = {pc['problem_id'] for pc in remaining_problem_counts}
|
||||||
|
problem_ids_to_delete = problem_ids - remaining_problem_ids
|
||||||
|
Problem.objects.filter(id__in=problem_ids_to_delete).delete()
|
||||||
|
else:
|
||||||
|
problem_paragraph_mappings.delete()
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
@date:2024/8/19 14:13
|
@date:2024/8/19 14:13
|
||||||
@desc:
|
@desc:
|
||||||
"""
|
"""
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
from typing import List
|
from typing import List
|
||||||
@ -17,7 +16,7 @@ from django.db.models import QuerySet
|
|||||||
from common.config.embedding_config import ModelManage
|
from common.config.embedding_config import ModelManage
|
||||||
from common.event import ListenerManagement, UpdateProblemArgs, UpdateEmbeddingDatasetIdArgs, \
|
from common.event import ListenerManagement, UpdateProblemArgs, UpdateEmbeddingDatasetIdArgs, \
|
||||||
UpdateEmbeddingDocumentIdArgs
|
UpdateEmbeddingDocumentIdArgs
|
||||||
from dataset.models import Document, Status, TaskType, State
|
from dataset.models import Document, TaskType, State
|
||||||
from ops import celery_app
|
from ops import celery_app
|
||||||
from setting.models import Model
|
from setting.models import Model
|
||||||
from setting.models_provider import get_model
|
from setting.models_provider import get_model
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.15 on 2024-10-15 14:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
UPDATE "public"."model"
|
||||||
|
SET "model_params_form" = '[{"attrs": {"max": 1, "min": 0.1, "step": 0.01, "precision": 2, "show-input": true, "show-input-controls": false}, "field": "temperature", "label": {"attrs": {"tooltip": "较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定"}, "label": "温度", "input_type": "TooltipLabel", "props_info": {}}, "required": true, "input_type": "Slider", "props_info": {}, "trigger_type": "OPTION_LIST", "default_value": 0.5, "relation_show_field_dict": {}, "relation_trigger_field_dict": {}}, {"attrs": {"max": 100000, "min": 1, "step": 1, "precision": 0, "show-input": true, "show-input-controls": false}, "field": "max_tokens", "label": {"attrs": {"tooltip": "指定模型可生成的最大token个数"}, "label": "输出最大Tokens", "input_type": "TooltipLabel", "props_info": {}}, "required": true, "input_type": "Slider", "props_info": {}, "trigger_type": "OPTION_LIST", "default_value": 4096, "relation_show_field_dict": {}, "relation_trigger_field_dict": {}}]'
|
||||||
|
WHERE jsonb_array_length(model_params_form)=0
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('setting', '0008_modelparam'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunSQL(sql)
|
||||||
|
]
|
||||||
@ -79,7 +79,6 @@ class ModelSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
create_user = serializers.CharField(required=False, error_messages=ErrMessage.char("创建者"))
|
create_user = serializers.CharField(required=False, error_messages=ErrMessage.char("创建者"))
|
||||||
|
|
||||||
|
|
||||||
def list(self, with_valid):
|
def list(self, with_valid):
|
||||||
if with_valid:
|
if with_valid:
|
||||||
self.is_valid(raise_exception=True)
|
self.is_valid(raise_exception=True)
|
||||||
@ -92,7 +91,8 @@ class ModelSerializer(serializers.Serializer):
|
|||||||
model_query_set = QuerySet(Model).filter(Q(user_id=create_user))
|
model_query_set = QuerySet(Model).filter(Q(user_id=create_user))
|
||||||
# 当前用户能查看其他人的模型,只能查看公开的
|
# 当前用户能查看其他人的模型,只能查看公开的
|
||||||
else:
|
else:
|
||||||
model_query_set = QuerySet(Model).filter((Q(user_id=self.data.get('create_user')) & Q(permission_type='PUBLIC')))
|
model_query_set = QuerySet(Model).filter(
|
||||||
|
(Q(user_id=self.data.get('create_user')) & Q(permission_type='PUBLIC')))
|
||||||
else:
|
else:
|
||||||
model_query_set = QuerySet(Model).filter((Q(user_id=user_id) | Q(permission_type='PUBLIC')))
|
model_query_set = QuerySet(Model).filter((Q(user_id=user_id) | Q(permission_type='PUBLIC')))
|
||||||
query_params = {}
|
query_params = {}
|
||||||
@ -107,11 +107,11 @@ class ModelSerializer(serializers.Serializer):
|
|||||||
if self.data.get('permission_type') is not None:
|
if self.data.get('permission_type') is not None:
|
||||||
query_params['permission_type'] = self.data.get('permission_type')
|
query_params['permission_type'] = self.data.get('permission_type')
|
||||||
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{'id': str(model.id), 'provider': model.provider, 'name': model.name, 'model_type': model.model_type,
|
{'id': str(model.id), 'provider': model.provider, 'name': model.name, 'model_type': model.model_type,
|
||||||
'model_name': model.model_name, 'status': model.status, 'meta': model.meta,
|
'model_name': model.model_name, 'status': model.status, 'meta': model.meta,
|
||||||
'permission_type': model.permission_type, 'user_id': model.user_id, 'username': model.user.username} for model in
|
'permission_type': model.permission_type, 'user_id': model.user_id, 'username': model.user.username}
|
||||||
|
for model in
|
||||||
model_query_set.filter(**query_params).order_by("-create_time")]
|
model_query_set.filter(**query_params).order_by("-create_time")]
|
||||||
|
|
||||||
class Edit(serializers.Serializer):
|
class Edit(serializers.Serializer):
|
||||||
@ -243,14 +243,7 @@ class ModelSerializer(serializers.Serializer):
|
|||||||
self.is_valid(raise_exception=True)
|
self.is_valid(raise_exception=True)
|
||||||
model_id = self.data.get('id')
|
model_id = self.data.get('id')
|
||||||
model = QuerySet(Model).filter(id=model_id).first()
|
model = QuerySet(Model).filter(id=model_id).first()
|
||||||
credential = get_model_credential(model.provider, model.model_type, model.model_name)
|
|
||||||
# 已经保存过的模型参数表单
|
# 已经保存过的模型参数表单
|
||||||
if model.model_params_form is not None and len(model.model_params_form) > 0:
|
|
||||||
return model.model_params_form
|
|
||||||
# 没有保存过的LLM类型的
|
|
||||||
if credential.get_model_params_setting_form(model.model_name) is not None:
|
|
||||||
return credential.get_model_params_setting_form(model.model_name).to_form_list()
|
|
||||||
# 其他的
|
|
||||||
return model.model_params_form
|
return model.model_params_form
|
||||||
|
|
||||||
class ModelParamsForm(serializers.Serializer):
|
class ModelParamsForm(serializers.Serializer):
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
@desc:
|
@desc:
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from smartdoc.const import CONFIG, PROJECT_DIR
|
from smartdoc.const import CONFIG, PROJECT_DIR
|
||||||
|
|
||||||
@ -34,6 +35,11 @@ CELERY_TASK_SOFT_TIME_LIMIT = 3600
|
|||||||
CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS = True
|
CELERY_WORKER_CANCEL_LONG_RUNNING_TASKS_ON_CONNECTION_LOSS = True
|
||||||
CELERY_ACKS_LATE = True
|
CELERY_ACKS_LATE = True
|
||||||
celery_once_path = os.path.join(celery_data_dir, "celery_once")
|
celery_once_path = os.path.join(celery_data_dir, "celery_once")
|
||||||
|
try:
|
||||||
|
if os.path.exists(celery_once_path) and os.path.isdir(celery_once_path):
|
||||||
|
shutil.rmtree(celery_once_path)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
CELERY_ONCE = {
|
CELERY_ONCE = {
|
||||||
'backend': 'celery_once.backends.File',
|
'backend': 'celery_once.backends.File',
|
||||||
'settings': {'location': celery_once_path}
|
'settings': {'location': celery_once_path}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "run-p --max_old_space_size=4096 type-check build-only",
|
"build": "set NODE_OPTIONS=--max_old_space_size=4096 && run-p type-check build-only",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 52 KiB |
@ -23,8 +23,9 @@ interface ApplicationFormType {
|
|||||||
tts_type?: string
|
tts_type?: string
|
||||||
}
|
}
|
||||||
interface Chunk {
|
interface Chunk {
|
||||||
|
real_node_id: string
|
||||||
chat_id: string
|
chat_id: string
|
||||||
id: string
|
chat_record_id: string
|
||||||
content: string
|
content: string
|
||||||
node_id: string
|
node_id: string
|
||||||
up_node_id: string
|
up_node_id: string
|
||||||
@ -32,13 +33,20 @@ interface Chunk {
|
|||||||
node_is_end: boolean
|
node_is_end: boolean
|
||||||
node_type: string
|
node_type: string
|
||||||
view_type: string
|
view_type: string
|
||||||
|
runtime_node_id: string
|
||||||
|
child_node: any
|
||||||
}
|
}
|
||||||
interface chatType {
|
interface chatType {
|
||||||
id: string
|
id: string
|
||||||
problem_text: string
|
problem_text: string
|
||||||
answer_text: string
|
answer_text: string
|
||||||
buffer: Array<String>
|
buffer: Array<String>
|
||||||
answer_text_list: Array<string>
|
answer_text_list: Array<{
|
||||||
|
content: string
|
||||||
|
chat_record_id?: string
|
||||||
|
runtime_node_id?: string
|
||||||
|
child_node?: any
|
||||||
|
}>
|
||||||
/**
|
/**
|
||||||
* 是否写入结束
|
* 是否写入结束
|
||||||
*/
|
*/
|
||||||
@ -92,15 +100,24 @@ export class ChatRecordManage {
|
|||||||
this.write_ed = false
|
this.write_ed = false
|
||||||
this.node_list = []
|
this.node_list = []
|
||||||
}
|
}
|
||||||
append_answer(chunk_answer: string, index?: number) {
|
append_answer(
|
||||||
this.chat.answer_text_list[index != undefined ? index : this.chat.answer_text_list.length - 1] =
|
chunk_answer: string,
|
||||||
this.chat.answer_text_list[
|
index?: number,
|
||||||
index !== undefined ? index : this.chat.answer_text_list.length - 1
|
chat_record_id?: string,
|
||||||
]
|
runtime_node_id?: string,
|
||||||
? this.chat.answer_text_list[
|
child_node?: any
|
||||||
index !== undefined ? index : this.chat.answer_text_list.length - 1
|
) {
|
||||||
] + chunk_answer
|
const set_index = index != undefined ? index : this.chat.answer_text_list.length - 1
|
||||||
: chunk_answer
|
const content = this.chat.answer_text_list[set_index]
|
||||||
|
? this.chat.answer_text_list[set_index].content + chunk_answer
|
||||||
|
: chunk_answer
|
||||||
|
this.chat.answer_text_list[set_index] = {
|
||||||
|
content: content,
|
||||||
|
chat_record_id,
|
||||||
|
runtime_node_id,
|
||||||
|
child_node
|
||||||
|
}
|
||||||
|
|
||||||
this.chat.answer_text = this.chat.answer_text + chunk_answer
|
this.chat.answer_text = this.chat.answer_text + chunk_answer
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,14 +144,22 @@ export class ChatRecordManage {
|
|||||||
run_node.view_type == 'single_view' ||
|
run_node.view_type == 'single_view' ||
|
||||||
(run_node.view_type == 'many_view' && current_up_node.view_type == 'single_view')
|
(run_node.view_type == 'many_view' && current_up_node.view_type == 'single_view')
|
||||||
) {
|
) {
|
||||||
const none_index = this.chat.answer_text_list.indexOf('')
|
const none_index = this.findIndex(
|
||||||
|
this.chat.answer_text_list,
|
||||||
|
(item) => item.content == '',
|
||||||
|
'index'
|
||||||
|
)
|
||||||
if (none_index > -1) {
|
if (none_index > -1) {
|
||||||
answer_text_list_index = none_index
|
answer_text_list_index = none_index
|
||||||
} else {
|
} else {
|
||||||
answer_text_list_index = this.chat.answer_text_list.length
|
answer_text_list_index = this.chat.answer_text_list.length
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const none_index = this.chat.answer_text_list.indexOf('')
|
const none_index = this.findIndex(
|
||||||
|
this.chat.answer_text_list,
|
||||||
|
(item) => item.content === '',
|
||||||
|
'index'
|
||||||
|
)
|
||||||
if (none_index > -1) {
|
if (none_index > -1) {
|
||||||
answer_text_list_index = none_index
|
answer_text_list_index = none_index
|
||||||
} else {
|
} else {
|
||||||
@ -152,6 +177,19 @@ export class ChatRecordManage {
|
|||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
findIndex<T>(array: Array<T>, find: (item: T) => boolean, type: 'last' | 'index') {
|
||||||
|
let set_index = -1
|
||||||
|
for (let index = 0; index < array.length; index++) {
|
||||||
|
const element = array[index]
|
||||||
|
if (find(element)) {
|
||||||
|
set_index = index
|
||||||
|
if (type == 'index') {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set_index
|
||||||
|
}
|
||||||
closeInterval() {
|
closeInterval() {
|
||||||
this.chat.write_ed = true
|
this.chat.write_ed = true
|
||||||
this.write_ed = true
|
this.write_ed = true
|
||||||
@ -161,7 +199,11 @@ export class ChatRecordManage {
|
|||||||
if (this.id) {
|
if (this.id) {
|
||||||
clearInterval(this.id)
|
clearInterval(this.id)
|
||||||
}
|
}
|
||||||
const last_index = this.chat.answer_text_list.lastIndexOf('')
|
const last_index = this.findIndex(
|
||||||
|
this.chat.answer_text_list,
|
||||||
|
(item) => item.content == '',
|
||||||
|
'last'
|
||||||
|
)
|
||||||
if (last_index > 0) {
|
if (last_index > 0) {
|
||||||
this.chat.answer_text_list.splice(last_index, 1)
|
this.chat.answer_text_list.splice(last_index, 1)
|
||||||
}
|
}
|
||||||
@ -193,19 +235,29 @@ export class ChatRecordManage {
|
|||||||
)
|
)
|
||||||
this.append_answer(
|
this.append_answer(
|
||||||
(divider_content ? divider_content.splice(0).join('') : '') + context.join(''),
|
(divider_content ? divider_content.splice(0).join('') : '') + context.join(''),
|
||||||
answer_text_list_index
|
answer_text_list_index,
|
||||||
|
current_node.chat_record_id,
|
||||||
|
current_node.runtime_node_id,
|
||||||
|
current_node.child_node
|
||||||
)
|
)
|
||||||
} else if (this.is_close) {
|
} else if (this.is_close) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const node_info = this.get_run_node()
|
const node_info = this.get_run_node()
|
||||||
|
|
||||||
if (node_info == undefined) {
|
if (node_info == undefined) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.append_answer(
|
this.append_answer(
|
||||||
(node_info.divider_content ? node_info.divider_content.splice(0).join('') : '') +
|
(node_info.divider_content ? node_info.divider_content.splice(0).join('') : '') +
|
||||||
node_info.current_node.buffer.splice(0).join(''),
|
node_info.current_node.buffer.splice(0).join(''),
|
||||||
node_info.answer_text_list_index
|
node_info.answer_text_list_index,
|
||||||
|
current_node.chat_record_id,
|
||||||
|
current_node.runtime_node_id,
|
||||||
|
current_node.child_node
|
||||||
)
|
)
|
||||||
|
if (node_info.current_node.buffer.length == 0) {
|
||||||
|
node_info.current_node.is_end = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.closeInterval()
|
this.closeInterval()
|
||||||
} else {
|
} else {
|
||||||
@ -213,7 +265,10 @@ export class ChatRecordManage {
|
|||||||
if (s !== undefined) {
|
if (s !== undefined) {
|
||||||
this.append_answer(
|
this.append_answer(
|
||||||
(divider_content ? divider_content.splice(0).join('') : '') + s,
|
(divider_content ? divider_content.splice(0).join('') : '') + s,
|
||||||
answer_text_list_index
|
answer_text_list_index,
|
||||||
|
current_node.chat_record_id,
|
||||||
|
current_node.runtime_node_id,
|
||||||
|
current_node.child_node
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,16 +290,18 @@ export class ChatRecordManage {
|
|||||||
this.is_stop = false
|
this.is_stop = false
|
||||||
}
|
}
|
||||||
appendChunk(chunk: Chunk) {
|
appendChunk(chunk: Chunk) {
|
||||||
let n = this.node_list.find(
|
let n = this.node_list.find((item) => item.real_node_id == chunk.real_node_id)
|
||||||
(item) => item.node_id == chunk.node_id && item.up_node_id === chunk.up_node_id
|
|
||||||
)
|
|
||||||
if (n) {
|
if (n) {
|
||||||
n.buffer.push(...chunk.content)
|
n.buffer.push(...chunk.content)
|
||||||
} else {
|
} else {
|
||||||
n = {
|
n = {
|
||||||
buffer: [...chunk.content],
|
buffer: [...chunk.content],
|
||||||
|
real_node_id: chunk.real_node_id,
|
||||||
node_id: chunk.node_id,
|
node_id: chunk.node_id,
|
||||||
|
chat_record_id: chunk.chat_record_id,
|
||||||
up_node_id: chunk.up_node_id,
|
up_node_id: chunk.up_node_id,
|
||||||
|
runtime_node_id: chunk.runtime_node_id,
|
||||||
|
child_node: chunk.child_node,
|
||||||
node_type: chunk.node_type,
|
node_type: chunk.node_type,
|
||||||
index: this.node_list.length,
|
index: this.node_list.length,
|
||||||
view_type: chunk.view_type,
|
view_type: chunk.view_type,
|
||||||
@ -257,9 +314,12 @@ export class ChatRecordManage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
append(answer_text_block: string) {
|
append(answer_text_block: string) {
|
||||||
const index =this.chat.answer_text_list.indexOf("")
|
let set_index = this.findIndex(
|
||||||
this.chat.answer_text_list[index]=answer_text_block
|
this.chat.answer_text_list,
|
||||||
|
(item) => item.content == '',
|
||||||
|
'index'
|
||||||
|
)
|
||||||
|
this.chat.answer_text_list[set_index] = { content: answer_text_block }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -342,7 +342,7 @@
|
|||||||
<template v-if="item.type === WorkflowType.FormNode">
|
<template v-if="item.type === WorkflowType.FormNode">
|
||||||
<div class="card-never border-r-4">
|
<div class="card-never border-r-4">
|
||||||
<h5 class="p-8-12">
|
<h5 class="p-8-12">
|
||||||
参数输入<span style="color: #f54a45">{{
|
参数输出<span style="color: #f54a45">{{
|
||||||
item.is_submit ? '' : '(用户未提交)'
|
item.is_submit ? '' : '(用户未提交)'
|
||||||
}}</span>
|
}}</span>
|
||||||
</h5>
|
</h5>
|
||||||
|
|||||||
@ -20,9 +20,12 @@
|
|||||||
<el-input v-model="detail.padding_problem_text" disabled />
|
<el-input v-model="detail.padding_problem_text" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="引用分段">
|
<el-form-item label="引用分段">
|
||||||
<template v-for="(item, index) in detail.paragraph_list" :key="index">
|
<div v-if="detail.paragraph_list.length > 0">
|
||||||
<ParagraphCard :data="item" :index="index" />
|
<template v-for="(item, index) in detail.paragraph_list" :key="index">
|
||||||
</template>
|
<ParagraphCard :data="item" :index="index" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<span v-else> - </span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="footer-content flex-between">
|
<div class="footer-content flex-between">
|
||||||
<el-text class="flex align-center" style="width: 70%">
|
<el-text class="flex align-center" style="width: 50%">
|
||||||
<img :src="getImgUrl(data?.document_name?.trim())" alt="" width="20" class="mr-4" />
|
<img :src="getImgUrl(data?.document_name?.trim())" alt="" width="20" class="mr-4" />
|
||||||
|
|
||||||
<template v-if="meta?.source_url">
|
<template v-if="meta?.source_url">
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="item-content mb-16 lighter">
|
<div class="item-content mb-16 lighter">
|
||||||
<template v-for="(answer_text, index) in chatRecord.answer_text_list" :key="index">
|
<template v-for="(answer_text, index) in answer_text_list" :key="index">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
|
<img v-if="application.avatar" :src="application.avatar" height="32px" width="32px" />
|
||||||
<LogoIcon v-else height="32px" width="32px" />
|
<LogoIcon v-else height="32px" width="32px" />
|
||||||
@ -9,14 +9,18 @@
|
|||||||
<el-card shadow="always" class="dialog-card mb-8">
|
<el-card shadow="always" class="dialog-card mb-8">
|
||||||
<MdRenderer
|
<MdRenderer
|
||||||
v-if="
|
v-if="
|
||||||
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) && !answer_text
|
(chatRecord.write_ed === undefined || chatRecord.write_ed === true) &&
|
||||||
|
!answer_text.content
|
||||||
"
|
"
|
||||||
source=" 抱歉,没有查找到相关内容,请重新描述您的问题或提供更多信息。"
|
source=" 抱歉,没有查找到相关内容,请重新描述您的问题或提供更多信息。"
|
||||||
></MdRenderer>
|
></MdRenderer>
|
||||||
<MdRenderer
|
<MdRenderer
|
||||||
|
:chat_record_id="answer_text.chat_record_id"
|
||||||
|
:child_node="answer_text.child_node"
|
||||||
|
:runtime_node_id="answer_text.runtime_node_id"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
v-else-if="answer_text"
|
v-else-if="answer_text.content"
|
||||||
:source="answer_text"
|
:source="answer_text.content"
|
||||||
:send-message="chatMessage"
|
:send-message="chatMessage"
|
||||||
></MdRenderer>
|
></MdRenderer>
|
||||||
<span v-else-if="chatRecord.is_stop" shadow="always" class="dialog-card">
|
<span v-else-if="chatRecord.is_stop" shadow="always" class="dialog-card">
|
||||||
@ -36,7 +40,8 @@
|
|||||||
<OperationButton
|
<OperationButton
|
||||||
:type="type"
|
:type="type"
|
||||||
:application="application"
|
:application="application"
|
||||||
:chat-record="chatRecord"
|
:chatRecord="chatRecord"
|
||||||
|
@update:chatRecord="(event: any) => emit('update:chatRecord', event)"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:start-chat="startChat"
|
:start-chat="startChat"
|
||||||
:stop-chat="stopChat"
|
:stop-chat="stopChat"
|
||||||
@ -50,6 +55,7 @@ import KnowledgeSource from '@/components/ai-chat/KnowledgeSource.vue'
|
|||||||
import MdRenderer from '@/components/markdown/MdRenderer.vue'
|
import MdRenderer from '@/components/markdown/MdRenderer.vue'
|
||||||
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
|
import OperationButton from '@/components/ai-chat/component/operation-button/index.vue'
|
||||||
import { type chatType } from '@/api/type/application'
|
import { type chatType } from '@/api/type/application'
|
||||||
|
import { computed } from 'vue'
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
chatRecord: chatType
|
chatRecord: chatType
|
||||||
application: any
|
application: any
|
||||||
@ -59,6 +65,8 @@ const props = defineProps<{
|
|||||||
type: 'log' | 'ai-chat' | 'debug-ai-chat'
|
type: 'log' | 'ai-chat' | 'debug-ai-chat'
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:chatRecord'])
|
||||||
|
|
||||||
const chatMessage = (question: string, type: 'old' | 'new', other_params_data?: any) => {
|
const chatMessage = (question: string, type: 'old' | 'new', other_params_data?: any) => {
|
||||||
if (type === 'old') {
|
if (type === 'old') {
|
||||||
add_answer_text_list(props.chatRecord.answer_text_list)
|
add_answer_text_list(props.chatRecord.answer_text_list)
|
||||||
@ -68,9 +76,17 @@ const chatMessage = (question: string, type: 'old' | 'new', other_params_data?:
|
|||||||
props.sendMessage(question, other_params_data)
|
props.sendMessage(question, other_params_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const add_answer_text_list = (answer_text_list: Array<string>) => {
|
const add_answer_text_list = (answer_text_list: Array<any>) => {
|
||||||
answer_text_list.push('')
|
answer_text_list.push({ content: '' })
|
||||||
}
|
}
|
||||||
|
const answer_text_list = computed(() => {
|
||||||
|
return props.chatRecord.answer_text_list.map((item) => {
|
||||||
|
if (typeof item == 'string') {
|
||||||
|
return { content: item }
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
function showSource(row: any) {
|
function showSource(row: any) {
|
||||||
if (props.type === 'log') {
|
if (props.type === 'log') {
|
||||||
|
|||||||
@ -85,7 +85,8 @@
|
|||||||
>
|
>
|
||||||
<el-tooltip effect="dark" placement="top" popper-class="upload-tooltip-width">
|
<el-tooltip effect="dark" placement="top" popper-class="upload-tooltip-width">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="break-all pre-wrap">上传文件:最多{{
|
<div class="break-all pre-wrap">
|
||||||
|
上传文件:最多{{
|
||||||
props.applicationDetails.file_upload_setting.maxFiles
|
props.applicationDetails.file_upload_setting.maxFiles
|
||||||
}}个,每个文件限制
|
}}个,每个文件限制
|
||||||
{{ props.applicationDetails.file_upload_setting.fileLimit }}MB<br />文件类型:{{
|
{{ props.applicationDetails.file_upload_setting.fileLimit }}MB<br />文件类型:{{
|
||||||
@ -93,7 +94,7 @@
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-button text>
|
<el-button text :disabled="checkMaxFilesLimit()" class="mt-4">
|
||||||
<el-icon><Paperclip /></el-icon>
|
<el-icon><Paperclip /></el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
@ -223,6 +224,13 @@ const getAcceptList = () => {
|
|||||||
return accepts.map((ext: any) => '.' + ext).join(',')
|
return accepts.map((ext: any) => '.' + ext).join(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkMaxFilesLimit = () => {
|
||||||
|
return (
|
||||||
|
props.applicationDetails.file_upload_setting.maxFiles <=
|
||||||
|
uploadImageList.value.length + uploadDocumentList.value.length
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const uploadFile = async (file: any, fileList: any) => {
|
const uploadFile = async (file: any, fileList: any) => {
|
||||||
const { maxFiles, fileLimit } = props.applicationDetails.file_upload_setting
|
const { maxFiles, fileLimit } = props.applicationDetails.file_upload_setting
|
||||||
// 单次上传文件数量限制
|
// 单次上传文件数量限制
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
v-if="data.improve_paragraph_id_list.length === 0"
|
v-if="buttonData.improve_paragraph_id_list.length === 0"
|
||||||
effect="dark"
|
effect="dark"
|
||||||
content="修改内容"
|
content="修改内容"
|
||||||
placement="top"
|
placement="top"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { copyClick } from '@/utils/clipboard'
|
import { copyClick } from '@/utils/clipboard'
|
||||||
import EditContentDialog from '@/views/log/component/EditContentDialog.vue'
|
import EditContentDialog from '@/views/log/component/EditContentDialog.vue'
|
||||||
import EditMarkDialog from '@/views/log/component/EditMarkDialog.vue'
|
import EditMarkDialog from '@/views/log/component/EditMarkDialog.vue'
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<LogOperationButton
|
<LogOperationButton
|
||||||
v-if="type === 'log'"
|
v-if="type === 'log'"
|
||||||
v-bind:data="chatRecord"
|
v-bind:data="chatRecord"
|
||||||
|
@update:data="(event: any) => emit('update:chatRecord', event)"
|
||||||
:applicationId="application.id"
|
:applicationId="application.id"
|
||||||
:tts="application.tts_model_enable"
|
:tts="application.tts_model_enable"
|
||||||
:tts_type="application.tts_type"
|
:tts_type="application.tts_type"
|
||||||
@ -54,5 +55,6 @@ defineProps<{
|
|||||||
stopChat: (chat_record: any) => void
|
stopChat: (chat_record: any) => void
|
||||||
regenerationChart: (chat_record: any) => void
|
regenerationChart: (chat_record: any) => void
|
||||||
}>()
|
}>()
|
||||||
|
const emit = defineEmits(['update:chatRecord'])
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
<AnswerContent
|
<AnswerContent
|
||||||
:application="applicationDetails"
|
:application="applicationDetails"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:chat-record="item"
|
v-model:chat-record="chatList[index]"
|
||||||
:type="type"
|
:type="type"
|
||||||
:send-message="sendMessage"
|
:send-message="sendMessage"
|
||||||
:chat-management="ChatManagement"
|
:chat-management="ChatManagement"
|
||||||
@ -222,9 +222,10 @@ const getWrite = (chat: any, reader: any, stream: boolean) => {
|
|||||||
for (const index in split) {
|
for (const index in split) {
|
||||||
const chunk = JSON?.parse(split[index].replace('data:', ''))
|
const chunk = JSON?.parse(split[index].replace('data:', ''))
|
||||||
chat.chat_id = chunk.chat_id
|
chat.chat_id = chunk.chat_id
|
||||||
chat.record_id = chunk.id
|
chat.record_id = chunk.chat_record_id
|
||||||
ChatManagement.appendChunk(chat.id, chunk)
|
if (!chunk.is_end) {
|
||||||
|
ChatManagement.appendChunk(chat.id, chunk)
|
||||||
|
}
|
||||||
if (chunk.is_end) {
|
if (chunk.is_end) {
|
||||||
// 流处理成功 返回成功回调
|
// 流处理成功 返回成功回调
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
@ -278,7 +279,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
|
|||||||
id: randomId(),
|
id: randomId(),
|
||||||
problem_text: problem ? problem : inputValue.value.trim(),
|
problem_text: problem ? problem : inputValue.value.trim(),
|
||||||
answer_text: '',
|
answer_text: '',
|
||||||
answer_text_list: [''],
|
answer_text_list: [{ content: '' }],
|
||||||
buffer: [],
|
buffer: [],
|
||||||
write_ed: false,
|
write_ed: false,
|
||||||
is_stop: false,
|
is_stop: false,
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
v-for="(option, $index) in formValue.option_list"
|
v-for="(option, $index) in formValue.option_list"
|
||||||
:key="$index"
|
:key="$index"
|
||||||
:gutter="10"
|
:gutter="10"
|
||||||
|
class="mb-8"
|
||||||
>
|
>
|
||||||
<el-col :span="10"
|
<el-col :span="10"
|
||||||
><div class="grid-content ep-bg-purple" />
|
><div class="grid-content ep-bg-purple" />
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
v-for="(option, $index) in formValue.option_list"
|
v-for="(option, $index) in formValue.option_list"
|
||||||
:key="$index"
|
:key="$index"
|
||||||
:gutter="10"
|
:gutter="10"
|
||||||
|
class="mb-8"
|
||||||
>
|
>
|
||||||
<el-col :span="10"
|
<el-col :span="10"
|
||||||
><div class="grid-content ep-bg-purple" />
|
><div class="grid-content ep-bg-purple" />
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
v-for="(option, $index) in formValue.option_list"
|
v-for="(option, $index) in formValue.option_list"
|
||||||
:key="$index"
|
:key="$index"
|
||||||
:gutter="10"
|
:gutter="10"
|
||||||
|
class="mb-8"
|
||||||
>
|
>
|
||||||
<el-col :span="10"
|
<el-col :span="10"
|
||||||
><div class="grid-content ep-bg-purple" />
|
><div class="grid-content ep-bg-purple" />
|
||||||
@ -93,7 +94,7 @@ const formField = computed<FormField>(() => {
|
|||||||
})
|
})
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
return {
|
return {
|
||||||
input_type: 'RadioCard',
|
input_type: 'RadioRow',
|
||||||
attrs: {},
|
attrs: {},
|
||||||
default_value: formValue.value.default_value,
|
default_value: formValue.value.default_value,
|
||||||
text_field: 'label',
|
text_field: 'label',
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
v-for="(option, $index) in formValue.option_list"
|
v-for="(option, $index) in formValue.option_list"
|
||||||
:key="$index"
|
:key="$index"
|
||||||
:gutter="10"
|
:gutter="10"
|
||||||
|
class="mb-8"
|
||||||
>
|
>
|
||||||
<el-col :span="10"
|
<el-col :span="10"
|
||||||
><div class="grid-content ep-bg-purple" />
|
><div class="grid-content ep-bg-purple" />
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
:key="item.value"
|
:key="item.value"
|
||||||
class="item"
|
class="item"
|
||||||
shadow="never"
|
shadow="never"
|
||||||
:class="[modelValue == item[valueField] ? 'active' : '']"
|
:class="[inputDisabled ? 'is-disabled' : '', modelValue == item[valueField] ? 'active' : '']"
|
||||||
@click="selected(item[valueField])"
|
@click="inputDisabled ? () => {} : selected(item[valueField])"
|
||||||
>
|
>
|
||||||
{{ item[textField] }}
|
{{ item[textField] }}
|
||||||
</el-card>
|
</el-card>
|
||||||
@ -15,6 +15,9 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import type { FormField } from '@/components/dynamics-form/type'
|
import type { FormField } from '@/components/dynamics-form/type'
|
||||||
|
import { useFormDisabled } from 'element-plus'
|
||||||
|
const inputDisabled = useFormDisabled()
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
formValue?: any
|
formValue?: any
|
||||||
formfieldList?: Array<FormField>
|
formfieldList?: Array<FormField>
|
||||||
@ -24,6 +27,7 @@ const props = defineProps<{
|
|||||||
view?: boolean
|
view?: boolean
|
||||||
// 选中的值
|
// 选中的值
|
||||||
modelValue?: any
|
modelValue?: any
|
||||||
|
disabled?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const selected = (activeValue: string | number) => {
|
const selected = (activeValue: string | number) => {
|
||||||
@ -66,6 +70,16 @@ const option_list = computed(() => {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
|
.is-disabled {
|
||||||
|
border: 1px solid var(--el-card-border-color);
|
||||||
|
background-color: var(--el-fill-color-light);
|
||||||
|
color: var(--el-text-color-placeholder);
|
||||||
|
cursor: not-allowed;
|
||||||
|
&:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
.active {
|
.active {
|
||||||
border: 1px solid var(--el-color-primary);
|
border: 1px solid var(--el-color-primary);
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
v-for="item in option_list"
|
v-for="item in option_list"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
class="item"
|
class="item"
|
||||||
:class="[modelValue == item[valueField] ? 'active' : '']"
|
:class="[inputDisabled ? 'is-disabled' : '', modelValue == item[valueField] ? 'active' : '']"
|
||||||
@click="selected(item[valueField])"
|
@click="selected(item[valueField])"
|
||||||
>
|
>
|
||||||
{{ item[textField] }}
|
{{ item[textField] }}
|
||||||
@ -14,6 +14,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { FormField } from '@/components/dynamics-form/type'
|
import type { FormField } from '@/components/dynamics-form/type'
|
||||||
|
import { useFormDisabled } from 'element-plus'
|
||||||
|
const inputDisabled = useFormDisabled()
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
formValue?: any
|
formValue?: any
|
||||||
formfieldList?: Array<FormField>
|
formfieldList?: Array<FormField>
|
||||||
@ -54,7 +56,15 @@ const option_list = computed(() => {
|
|||||||
padding: 3px 4px;
|
padding: 3px 4px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
.is-disabled {
|
||||||
|
border: 1px solid var(--el-card-border-color);
|
||||||
|
background-color: var(--el-fill-color-light);
|
||||||
|
color: var(--el-text-color-placeholder);
|
||||||
|
cursor: not-allowed;
|
||||||
|
&:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
.active {
|
.active {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: var(--el-color-primary-light-9);
|
background: var(--el-color-primary-light-9);
|
||||||
|
|||||||
@ -10,7 +10,10 @@
|
|||||||
v-model="form_data"
|
v-model="form_data"
|
||||||
:model="form_data"
|
:model="form_data"
|
||||||
></DynamicsForm>
|
></DynamicsForm>
|
||||||
<el-button :type="is_submit ? 'info' : 'primary'" :disabled="is_submit||loading" @click="submit"
|
<el-button
|
||||||
|
:type="is_submit ? 'info' : 'primary'"
|
||||||
|
:disabled="is_submit || loading"
|
||||||
|
@click="submit"
|
||||||
>提交</el-button
|
>提交</el-button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -18,13 +21,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(
|
||||||
form_setting: string
|
defineProps<{
|
||||||
loading?:boolean
|
form_setting: string
|
||||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
loading?: boolean
|
||||||
}>(),{
|
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||||
loading:false
|
child_node?: any
|
||||||
})
|
chat_record_id?: string
|
||||||
|
runtime_node_id?: string
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
)
|
||||||
const form_setting_data = computed(() => {
|
const form_setting_data = computed(() => {
|
||||||
if (props.form_setting) {
|
if (props.form_setting) {
|
||||||
return JSON.parse(props.form_setting)
|
return JSON.parse(props.form_setting)
|
||||||
@ -69,11 +78,11 @@ const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
|||||||
const submit = () => {
|
const submit = () => {
|
||||||
dynamicsFormRef.value?.validate().then(() => {
|
dynamicsFormRef.value?.validate().then(() => {
|
||||||
_submit.value = true
|
_submit.value = true
|
||||||
const setting = JSON.parse(props.form_setting)
|
|
||||||
if (props.sendMessage) {
|
if (props.sendMessage) {
|
||||||
props.sendMessage('', 'old', {
|
props.sendMessage('', 'old', {
|
||||||
runtime_node_id: setting.runtime_node_id,
|
child_node: props.child_node,
|
||||||
chat_record_id: setting.chat_record_id,
|
runtime_node_id: props.runtime_node_id,
|
||||||
|
chat_record_id: props.chat_record_id,
|
||||||
node_data: form_data.value
|
node_data: form_data.value
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
>
|
>
|
||||||
<template #defFooters>
|
<template #defFooters>
|
||||||
<el-button text type="info" @click="openDialog">
|
<el-button text type="info" @click="openDialog">
|
||||||
<AppIcon iconName="app-magnify" style="font-size: 16px"></AppIcon>
|
<AppIcon class="color-secondary" iconName="app-magnify" style="font-size: 16px"></AppIcon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</MdEditor>
|
</MdEditor>
|
||||||
|
|||||||
@ -17,6 +17,9 @@
|
|||||||
:option="item.content"
|
:option="item.content"
|
||||||
></EchartsRander>
|
></EchartsRander>
|
||||||
<FormRander
|
<FormRander
|
||||||
|
:chat_record_id="chat_record_id"
|
||||||
|
:runtime_node_id="runtime_node_id"
|
||||||
|
:child_node="child_node"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:send-message="sendMessage"
|
:send-message="sendMessage"
|
||||||
v-else-if="item.type === 'form_rander'"
|
v-else-if="item.type === 'form_rander'"
|
||||||
@ -64,6 +67,9 @@ const props = withDefaults(
|
|||||||
source?: string
|
source?: string
|
||||||
inner_suffix?: boolean
|
inner_suffix?: boolean
|
||||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||||
|
child_node?: any
|
||||||
|
chat_record_id?: string
|
||||||
|
runtime_node_id?: string
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export default {
|
|||||||
jump_tip: 'Jumping to the authentication source page for authentication',
|
jump_tip: 'Jumping to the authentication source page for authentication',
|
||||||
jump: 'Jump',
|
jump: 'Jump',
|
||||||
oauth2: {
|
oauth2: {
|
||||||
title: 'OAUTH2 Settings',
|
title: 'OAuth2 Settings',
|
||||||
authEndpoint: 'Auth Endpoint',
|
authEndpoint: 'Auth Endpoint',
|
||||||
authEndpointPlaceholder: 'Please enter Auth Endpoint',
|
authEndpointPlaceholder: 'Please enter Auth Endpoint',
|
||||||
tokenEndpoint: 'Token Endpoint',
|
tokenEndpoint: 'Token Endpoint',
|
||||||
@ -82,7 +82,7 @@ export default {
|
|||||||
redirectUrlPlaceholder: 'Please enter Redirect URL',
|
redirectUrlPlaceholder: 'Please enter Redirect URL',
|
||||||
filedMapping: 'Field Mapping',
|
filedMapping: 'Field Mapping',
|
||||||
filedMappingPlaceholder: 'Please enter Field Mapping',
|
filedMappingPlaceholder: 'Please enter Field Mapping',
|
||||||
enableAuthentication: 'Enable OAUTH2 Authentication',
|
enableAuthentication: 'Enable OAuth2 Authentication',
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
saveSuccess: 'Save Success'
|
saveSuccess: 'Save Success'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export default {
|
|||||||
jump_tip: '即将跳转至认证源页面进行认证',
|
jump_tip: '即将跳转至认证源页面进行认证',
|
||||||
jump: '跳转',
|
jump: '跳转',
|
||||||
oauth2: {
|
oauth2: {
|
||||||
title: 'OAUTH2 设置',
|
title: 'OAuth2 设置',
|
||||||
authEndpoint: '授权端地址',
|
authEndpoint: '授权端地址',
|
||||||
authEndpointPlaceholder: '请输入授权端地址',
|
authEndpointPlaceholder: '请输入授权端地址',
|
||||||
tokenEndpoint: 'Token 端地址',
|
tokenEndpoint: 'Token 端地址',
|
||||||
@ -82,7 +82,7 @@ export default {
|
|||||||
redirectUrlPlaceholder: '请输入回调地址',
|
redirectUrlPlaceholder: '请输入回调地址',
|
||||||
filedMapping: '字段映射',
|
filedMapping: '字段映射',
|
||||||
filedMappingPlaceholder: '请输入字段映射',
|
filedMappingPlaceholder: '请输入字段映射',
|
||||||
enableAuthentication: '启用 OAUTH2 认证',
|
enableAuthentication: '启用 OAuth2 认证',
|
||||||
save: '保存',
|
save: '保存',
|
||||||
saveSuccess: '保存成功'
|
saveSuccess: '保存成功'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -256,6 +256,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-select__placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
.el-select__placeholder.is-transparent {
|
.el-select__placeholder.is-transparent {
|
||||||
color: var(--app-input-color-placeholder);
|
color: var(--app-input-color-placeholder);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
.md-editor {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
.md-editor-preview {
|
.md-editor-preview {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="show" class="workflow-dropdown-menu border border-r-4">
|
<div v-show="show" class="workflow-dropdown-menu border border-r-4">
|
||||||
<el-tabs v-model="activeName" class="workflow-dropdown-tabs">
|
<el-tabs v-model="activeName" class="workflow-dropdown-tabs">
|
||||||
<div style="display: flex; width: 100%; justify-content: center">
|
<div style="display: flex; width: 100%; justify-content: center" class="mb-4">
|
||||||
<el-input v-model="search_text" style="width: 240px" placeholder="按名称搜索">
|
<el-input v-model="search_text" style="width: 240px" placeholder="按名称搜索">
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
<el-icon class="el-input__icon"><search /></el-icon>
|
<el-icon class="el-input__icon"><search /></el-icon>
|
||||||
@ -61,32 +61,37 @@
|
|||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="应用" name="application">
|
<el-tab-pane label="应用" name="application">
|
||||||
<el-scrollbar height="400">
|
<el-scrollbar height="400">
|
||||||
<template v-for="(item, index) in filter_application_list" :key="index">
|
<div v-if="filter_application_list.length > 0">
|
||||||
<div
|
<template v-for="(item, index) in filter_application_list" :key="index">
|
||||||
class="workflow-dropdown-item cursor flex p-8-12"
|
<div
|
||||||
@click.stop="clickNodes(applicationNode, item, 'application')"
|
class="workflow-dropdown-item cursor flex p-8-12"
|
||||||
@mousedown.stop="onmousedown(applicationNode, item, 'application')"
|
@click.stop="clickNodes(applicationNode, item, 'application')"
|
||||||
>
|
@mousedown.stop="onmousedown(applicationNode, item, 'application')"
|
||||||
<component
|
>
|
||||||
:is="iconComponent(`application-node-icon`)"
|
<component
|
||||||
class="mr-8 mt-4"
|
:is="iconComponent(`application-node-icon`)"
|
||||||
:size="32"
|
class="mr-8 mt-4"
|
||||||
:item="item"
|
:size="32"
|
||||||
/>
|
:item="item"
|
||||||
<div class="pre-wrap" style="width: 60%">
|
/>
|
||||||
<auto-tooltip :content="item.name" style="width: 80%" class="lighter">
|
<div class="pre-wrap" style="width: 60%">
|
||||||
{{ item.name }}
|
<auto-tooltip :content="item.name" style="width: 80%" class="lighter">
|
||||||
</auto-tooltip>
|
{{ item.name }}
|
||||||
<el-text type="info" size="small" style="width: 80%">{{ item.desc }}</el-text>
|
</auto-tooltip>
|
||||||
|
<el-text type="info" size="small" style="width: 80%">{{ item.desc }}</el-text>
|
||||||
|
</div>
|
||||||
|
<div class="status-tag" style="margin-left: auto">
|
||||||
|
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px"
|
||||||
|
>高级编排</el-tag
|
||||||
|
>
|
||||||
|
<el-tag class="blue-tag" v-else style="height: 22px">简单配置</el-tag>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-tag" style="margin-left: auto">
|
</template>
|
||||||
<el-tag type="warning" v-if="isWorkFlow(item.type)" style="height: 22px"
|
</div>
|
||||||
>高级编排</el-tag
|
<div v-else class="ml-16 mt-8">
|
||||||
>
|
<el-text type="info">没有找到相关结果</el-text>
|
||||||
<el-tag class="blue-tag" v-else style="height: 22px">简单配置</el-tag>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|||||||
@ -409,6 +409,7 @@
|
|||||||
link
|
link
|
||||||
@click="openTTSParamSettingDialog"
|
@click="openTTSParamSettingDialog"
|
||||||
:disabled="!applicationForm.tts_model_id"
|
:disabled="!applicationForm.tts_model_id"
|
||||||
|
class="mr-8"
|
||||||
>
|
>
|
||||||
<el-icon class="mr-4"><Setting /></el-icon>
|
<el-icon class="mr-4"><Setting /></el-icon>
|
||||||
设置
|
设置
|
||||||
@ -424,6 +425,7 @@
|
|||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="applicationForm.tts_type"
|
v-model="applicationForm.tts_type"
|
||||||
v-show="applicationForm.tts_model_enable"
|
v-show="applicationForm.tts_model_enable"
|
||||||
|
class="mb-8"
|
||||||
>
|
>
|
||||||
<el-radio value="BROWSER">浏览器播放(免费)</el-radio>
|
<el-radio value="BROWSER">浏览器播放(免费)</el-radio>
|
||||||
<el-radio value="TTS">TTS模型</el-radio>
|
<el-radio value="TTS">TTS模型</el-radio>
|
||||||
|
|||||||
@ -78,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { reactive, ref, watch, onMounted } from 'vue'
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
import authApi from '@/api/auth-setting'
|
import authApi from '@/api/auth-setting'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { t } from '@/locales'
|
import { t } from '@/locales'
|
||||||
@ -86,7 +86,7 @@ import { MsgSuccess } from '@/utils/message'
|
|||||||
|
|
||||||
const form = ref<any>({
|
const form = ref<any>({
|
||||||
id: '',
|
id: '',
|
||||||
auth_type: 'OAUTH2',
|
auth_type: 'OAuth2',
|
||||||
config_data: {
|
config_data: {
|
||||||
authEndpoint: '',
|
authEndpoint: '',
|
||||||
tokenEndpoint: '',
|
tokenEndpoint: '',
|
||||||
@ -11,15 +11,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import LDAP from './component/LDAP.vue'
|
import LDAP from './component/LDAP.vue'
|
||||||
import CAS from './component/CAS.vue'
|
import CAS from './component/CAS.vue'
|
||||||
import OIDC from './component/OIDC.vue'
|
import OIDC from './component/OIDC.vue'
|
||||||
import SCAN from './component/SCAN.vue'
|
import SCAN from './component/SCAN.vue'
|
||||||
|
import OAuth2 from './component/OAuth2.vue'
|
||||||
import { t } from '@/locales'
|
import { t } from '@/locales'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
import OAUTH2 from '@/views/authentication/component/OAUTH2.vue'
|
|
||||||
|
|
||||||
const { user } = useStore()
|
const { user } = useStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -43,8 +43,8 @@ const tabList = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t('login.oauth2.title'),
|
label: t('login.oauth2.title'),
|
||||||
name: 'OAUTH2',
|
name: 'OAuth2',
|
||||||
component: OAUTH2
|
component: OAuth2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '扫码登录',
|
label: '扫码登录',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="生成关联问题"
|
title="生成问题"
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
width="600"
|
width="600"
|
||||||
class="select-dataset-dialog"
|
class="select-dataset-dialog"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
>
|
>
|
||||||
<template #header="{ titleId, titleClass }">
|
<template #header="{ titleId, titleClass }">
|
||||||
<div class="my-header flex">
|
<div class="my-header flex">
|
||||||
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
<h4 :id="titleId" :class="titleClass">生成问题</h4>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="content-height">
|
<div class="content-height">
|
||||||
@ -174,7 +174,7 @@ const submitHandle = async (formEl: FormInstance) => {
|
|||||||
prompt.save(user.userInfo?.id as string, form.value)
|
prompt.save(user.userInfo?.id as string, form.value)
|
||||||
const data = { ...form.value, document_id_list: documentIdList.value }
|
const data = { ...form.value, document_id_list: documentIdList.value }
|
||||||
documentApi.batchGenerateRelated(id, data).then(() => {
|
documentApi.batchGenerateRelated(id, data).then(() => {
|
||||||
MsgSuccess('生成关联问题成功')
|
MsgSuccess('生成问题成功')
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row :gutter="3" v-for="status in statusTable" :key="status.type">
|
<div v-for="status in statusTable" :key="status.type">
|
||||||
<el-col :span="4">{{ taskTypeMap[status.type] }} </el-col>
|
<span> {{ taskTypeMap[status.type] }}:</span>
|
||||||
<el-col :span="4">
|
<span>
|
||||||
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
|
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
|
||||||
<el-icon class="success"><SuccessFilled /></el-icon>
|
<el-icon class="success"><SuccessFilled /></el-icon>
|
||||||
{{ stateMap[status.state](status.type) }}
|
{{ stateMap[status.state](status.type) }}
|
||||||
@ -22,23 +22,22 @@
|
|||||||
<el-icon class="is-loading primary"><Loading /></el-icon>
|
<el-icon class="is-loading primary"><Loading /></el-icon>
|
||||||
{{ stateMap[status.state](status.type) }}
|
{{ stateMap[status.state](status.type) }}
|
||||||
</el-text>
|
</el-text>
|
||||||
</el-col>
|
</span>
|
||||||
<el-col :span="7">
|
<span
|
||||||
<span
|
class="ml-8 lighter"
|
||||||
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
|
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
|
||||||
>
|
>
|
||||||
完成
|
完成
|
||||||
{{
|
{{
|
||||||
Object.keys(status.aggs ? status.aggs : {})
|
Object.keys(status.aggs ? status.aggs : {})
|
||||||
.filter((k) => k == State.SUCCESS)
|
.filter((k) => k == State.SUCCESS)
|
||||||
.map((k) => status.aggs[k])
|
.map((k) => status.aggs[k])
|
||||||
.reduce((x: any, y: any) => x + y, 0)
|
.reduce((x: any, y: any) => x + y, 0)
|
||||||
}}/{{
|
}}/{{
|
||||||
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
||||||
}}</span
|
}}</span
|
||||||
>
|
>
|
||||||
</el-col>
|
<el-text type="info" class="ml-4">
|
||||||
<el-col :span="9">
|
|
||||||
{{
|
{{
|
||||||
status.time
|
status.time
|
||||||
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
|
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
|
||||||
@ -47,8 +46,8 @@
|
|||||||
)
|
)
|
||||||
: undefined
|
: undefined
|
||||||
}}
|
}}
|
||||||
</el-col>
|
</el-text>
|
||||||
</el-row>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
向量化
|
向量化
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="openGenerateDialog()" :disabled="multipleSelection.length === 0">
|
<el-button @click="openGenerateDialog()" :disabled="multipleSelection.length === 0">
|
||||||
关联问题
|
生成问题
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="openBatchEditDocument" :disabled="multipleSelection.length === 0">
|
<el-button @click="openBatchEditDocument" :disabled="multipleSelection.length === 0">
|
||||||
设置
|
设置
|
||||||
@ -636,7 +636,7 @@ function batchGenerateRelated() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
documentApi.batchGenerateRelated(id, arr, loading).then(() => {
|
documentApi.batchGenerateRelated(id, arr, loading).then(() => {
|
||||||
MsgSuccess('批量关联问题成功')
|
MsgSuccess('批量生成问题成功')
|
||||||
multipleTableRef.value?.clearSelection()
|
multipleTableRef.value?.clearSelection()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,7 +67,13 @@
|
|||||||
class="login-button-circle color-secondary"
|
class="login-button-circle color-secondary"
|
||||||
@click="changeMode(item)"
|
@click="changeMode(item)"
|
||||||
>
|
>
|
||||||
<span style="font-size: 10px">{{ item }}</span>
|
<span
|
||||||
|
:style="{
|
||||||
|
'font-size': item === 'OAUTH2' ? '8px' : '10px',
|
||||||
|
color: user.themeInfo?.theme
|
||||||
|
}"
|
||||||
|
>{{ item }}</span
|
||||||
|
>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="item === 'QR_CODE' && loginMode !== item"
|
v-if="item === 'QR_CODE' && loginMode !== item"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="生成关联问题"
|
title="生成问题"
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
width="600"
|
width="600"
|
||||||
class="select-dataset-dialog"
|
class="select-dataset-dialog"
|
||||||
@ -9,7 +9,7 @@
|
|||||||
>
|
>
|
||||||
<template #header="{ titleId, titleClass }">
|
<template #header="{ titleId, titleClass }">
|
||||||
<div class="my-header flex">
|
<div class="my-header flex">
|
||||||
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
<h4 :id="titleId" :class="titleClass">生成问题</h4>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="content-height">
|
<div class="content-height">
|
||||||
@ -174,7 +174,7 @@ const submitHandle = async (formEl: FormInstance) => {
|
|||||||
|
|
||||||
const data = { ...form.value, paragraph_id_list: paragraphIdList.value }
|
const data = { ...form.value, paragraph_id_list: paragraphIdList.value }
|
||||||
paragraphApi.batchGenerateRelated(id, documentId, data).then(() => {
|
paragraphApi.batchGenerateRelated(id, documentId, data).then(() => {
|
||||||
MsgSuccess('生成关联问题成功')
|
MsgSuccess('生成问题成功')
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
})
|
})
|
||||||
|
|||||||
@ -123,7 +123,7 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item @click="openGenerateDialog(item)">
|
<el-dropdown-item @click="openGenerateDialog(item)">
|
||||||
<el-icon><Connection /></el-icon>
|
<el-icon><Connection /></el-icon>
|
||||||
生成关联问题</el-dropdown-item
|
生成问题</el-dropdown-item
|
||||||
>
|
>
|
||||||
<el-dropdown-item @click="openSelectDocumentDialog(item)">
|
<el-dropdown-item @click="openSelectDocumentDialog(item)">
|
||||||
<AppIcon iconName="app-migrate"></AppIcon>
|
<AppIcon iconName="app-migrate"></AppIcon>
|
||||||
@ -147,7 +147,7 @@
|
|||||||
|
|
||||||
<div class="mul-operation border-t w-full" v-if="isBatch === true">
|
<div class="mul-operation border-t w-full" v-if="isBatch === true">
|
||||||
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
|
<el-button :disabled="multipleSelection.length === 0" @click="openGenerateDialog()">
|
||||||
生成关联问题
|
生成问题
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
||||||
迁移
|
迁移
|
||||||
|
|||||||
@ -93,6 +93,7 @@ const allChecked: any = ref({
|
|||||||
if (val) {
|
if (val) {
|
||||||
filterData.value.map((item: any) => {
|
filterData.value.map((item: any) => {
|
||||||
item.operate[TeamEnum.MANAGE] = true
|
item.operate[TeamEnum.MANAGE] = true
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
filterData.value.map((item: any) => {
|
filterData.value.map((item: any) => {
|
||||||
@ -113,6 +114,7 @@ const allChecked: any = ref({
|
|||||||
} else {
|
} else {
|
||||||
filterData.value.map((item: any) => {
|
filterData.value.map((item: any) => {
|
||||||
item.operate[TeamEnum.USE] = false
|
item.operate[TeamEnum.USE] = false
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,6 +146,11 @@ function checkedOperateChange(Name: string | number, row: any, e: boolean) {
|
|||||||
props.data.map((item: any) => {
|
props.data.map((item: any) => {
|
||||||
if (item.id === row.id) {
|
if (item.id === row.id) {
|
||||||
item.operate[Name] = e
|
item.operate[Name] = e
|
||||||
|
if (Name === TeamEnum.MANAGE && e) {
|
||||||
|
item.operate[TeamEnum.USE] = true
|
||||||
|
} else if (Name === TeamEnum.USE && !e) {
|
||||||
|
item.operate[TeamEnum.MANAGE] = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,6 +112,9 @@ const validate = () => {
|
|||||||
}
|
}
|
||||||
return Promise.resolve('')
|
return Promise.resolve('')
|
||||||
}
|
}
|
||||||
|
props.nodeModel.graphModel.eventCenter.on('refresh_incoming_node_field', () => {
|
||||||
|
getIncomingNode(props.nodeModel.id)
|
||||||
|
})
|
||||||
defineExpose({ validate })
|
defineExpose({ validate })
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
options.value = getIncomingNode(props.nodeModel.id)
|
options.value = getIncomingNode(props.nodeModel.id)
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<div @mousemove.stop @mousedown.stop @keydown.stop @click.stop>
|
<div @mousemove.stop @mousedown.stop @keydown.stop @click.stop>
|
||||||
<el-button text @click="showNode = !showNode" class="mr-4">
|
<el-button text @click="showNode = !showNode" class="mr-4">
|
||||||
<el-icon class="arrow-icon" :class="showNode ? 'rotate-180' : ''"
|
<el-icon class="arrow-icon color-secondary" :class="showNode ? 'rotate-180' : ''"
|
||||||
><ArrowDownBold />
|
><ArrowDownBold />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|||||||
@ -12,17 +12,17 @@
|
|||||||
<el-divider direction="vertical" />
|
<el-divider direction="vertical" />
|
||||||
<el-button link @click="retract">
|
<el-button link @click="retract">
|
||||||
<el-tooltip class="box-item" effect="dark" content="收起全部节点" placement="top">
|
<el-tooltip class="box-item" effect="dark" content="收起全部节点" placement="top">
|
||||||
<AppIcon iconName="app-retract" title="收起全部节点"></AppIcon>
|
<AppIcon style="font-size: 16px" iconName="app-retract" title="收起全部节点"></AppIcon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button link @click="extend">
|
<el-button link @click="extend">
|
||||||
<el-tooltip class="box-item" effect="dark" content="展开全部节点" placement="top">
|
<el-tooltip class="box-item" effect="dark" content="展开全部节点" placement="top">
|
||||||
<AppIcon iconName="app-extend" title="展开全部节点"></AppIcon>
|
<AppIcon style="font-size: 16px" iconName="app-extend" title="展开全部节点"></AppIcon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button link @click="layout">
|
<el-button link @click="layout">
|
||||||
<el-tooltip class="box-item" effect="dark" content="一键美化" placement="top">
|
<el-tooltip class="box-item" effect="dark" content="一键美化" placement="top">
|
||||||
<AppIcon iconName="app-beautify" title="一键美化"></AppIcon>
|
<AppIcon style="font-size: 16px" iconName="app-beautify" title="一键美化"></AppIcon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|||||||
@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
<el-table-column prop="default_value" label="默认值">
|
<el-table-column prop="default_value" label="默认值">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span :title="row.default_value" class="ellipsis-1">{{ row.default_value }}</span>
|
<span :title="row.default_value" class="ellipsis-1">{{ getDefaultValue(row) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="必填">
|
<el-table-column label="必填">
|
||||||
@ -126,6 +126,18 @@ function refreshFieldList(data: any, index: any) {
|
|||||||
props.nodeModel.graphModel.eventCenter.emit('refreshFieldList')
|
props.nodeModel.graphModel.eventCenter.emit('refreshFieldList')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getDefaultValue = (row: any) => {
|
||||||
|
if (row.default_value) {
|
||||||
|
const default_value = row.option_list?.filter((v: any) => row.default_value.indexOf(v.value) > -1)
|
||||||
|
.map((v: any) => v.label).join(',')
|
||||||
|
if (default_value) {
|
||||||
|
return default_value
|
||||||
|
}
|
||||||
|
return row.default_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!props.nodeModel.properties.user_input_field_list) {
|
if (!props.nodeModel.properties.user_input_field_list) {
|
||||||
if (props.nodeModel.properties.input_field_list) {
|
if (props.nodeModel.properties.input_field_list) {
|
||||||
|
|||||||
@ -1,122 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<NodeContainer :nodeModel="nodeModel">
|
<NodeContainer :nodeModel="nodeModel">
|
||||||
<el-form
|
<h5 class="title-decoration-1 mb-8">节点设置</h5>
|
||||||
@submit.prevent
|
<el-card shadow="never" class="card-never" style="--el-card-padding: 12px">
|
||||||
:model="form_data"
|
<el-form
|
||||||
label-position="top"
|
@submit.prevent
|
||||||
require-asterisk-position="right"
|
:model="form_data"
|
||||||
label-width="auto"
|
label-position="top"
|
||||||
ref="formNodeFormRef"
|
require-asterisk-position="right"
|
||||||
hide-required-asterisk
|
label-width="auto"
|
||||||
>
|
ref="formNodeFormRef"
|
||||||
<el-form-item
|
hide-required-asterisk
|
||||||
label="表单输出内容"
|
|
||||||
prop="form_content_format"
|
|
||||||
:rules="{
|
|
||||||
required: true,
|
|
||||||
message: '请表单输出内容',
|
|
||||||
trigger: 'blur'
|
|
||||||
}"
|
|
||||||
>
|
>
|
||||||
<template #label>
|
<el-form-item
|
||||||
<div class="flex align-center">
|
label="表单输出内容"
|
||||||
<div class="mr-4">
|
prop="form_content_format"
|
||||||
<span>表单输出内容<span class="danger">*</span></span>
|
:rules="{
|
||||||
</div>
|
required: true,
|
||||||
<el-tooltip effect="dark" placement="right" popper-class="max-w-200">
|
message: '请表单输出内容',
|
||||||
<template #content>
|
trigger: 'blur'
|
||||||
设置执行该节点输出的内容,{{ '{ form }' }}为表单的占位符。
|
}"
|
||||||
</template>
|
|
||||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
|
||||||
</el-tooltip>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<MdEditorMagnify
|
|
||||||
title="表单输出内容"
|
|
||||||
v-model="form_data.form_content_format"
|
|
||||||
style="height: 150px"
|
|
||||||
@submitDialog="submitDialog"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="表单配置" @click.prevent>
|
|
||||||
<template #label>
|
|
||||||
<div class="flex-between mb-16">
|
|
||||||
<h5 class="lighter">{{ '表单配置' }}</h5>
|
|
||||||
<el-button link type="primary" @click="openAddFormCollect()">
|
|
||||||
<el-icon class="mr-4">
|
|
||||||
<Plus />
|
|
||||||
</el-icon>
|
|
||||||
添加
|
|
||||||
</el-button>
|
|
||||||
</div></template
|
|
||||||
>
|
>
|
||||||
|
<template #label>
|
||||||
<el-table
|
<div class="flex align-center">
|
||||||
v-if="form_data.form_field_list.length > 0"
|
<div class="mr-4">
|
||||||
:data="form_data.form_field_list"
|
<span>表单输出内容<span class="danger">*</span></span>
|
||||||
class="mb-16"
|
|
||||||
>
|
|
||||||
<el-table-column prop="field" label="参数">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="label" label="显示名称">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">
|
|
||||||
<span :title="row.label.label" class="ellipsis-1">
|
|
||||||
{{ row.label.label }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
|
||||||
<span :title="row.label" class="ellipsis-1">
|
|
||||||
{{ row.label }}
|
|
||||||
</span></span
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column label="组件类型" width="110px">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-tag type="info" class="info-tag">{{
|
|
||||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
|
||||||
}}</el-tag>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column prop="default_value" label="默认值">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span :title="row.default_value" class="ellipsis-1">{{ row.default_value }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="必填">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<div @click.stop>
|
|
||||||
<el-switch disabled size="small" v-model="row.required" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<el-tooltip effect="dark" placement="right" popper-class="max-w-200">
|
||||||
</el-table-column>
|
<template #content>
|
||||||
<el-table-column label="操作" align="left" width="80">
|
设置执行该节点输出的内容,{{ '{ form }' }}为表单的占位符。
|
||||||
<template #default="{ row, $index }">
|
</template>
|
||||||
<span class="mr-4">
|
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||||
<el-tooltip effect="dark" content="修改" placement="top">
|
</el-tooltip>
|
||||||
<el-button type="primary" text @click.stop="openEditFormCollect(row, $index)">
|
</div>
|
||||||
<el-icon><EditPen /></el-icon>
|
</template>
|
||||||
|
<MdEditorMagnify
|
||||||
|
title="表单输出内容"
|
||||||
|
v-model="form_data.form_content_format"
|
||||||
|
style="height: 150px"
|
||||||
|
@submitDialog="submitDialog"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="表单配置" @click.prevent>
|
||||||
|
<template #label>
|
||||||
|
<div class="flex-between">
|
||||||
|
<h5 class="lighter">{{ '表单配置' }}</h5>
|
||||||
|
<el-button link type="primary" @click="openAddFormCollect()">
|
||||||
|
<el-icon class="mr-4">
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
添加
|
||||||
|
</el-button>
|
||||||
|
</div></template
|
||||||
|
>
|
||||||
|
|
||||||
|
<el-table class="border" v-if="form_data.form_field_list.length > 0" :data="form_data.form_field_list">
|
||||||
|
<el-table-column prop="field" label="参数">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="label" label="显示名称">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">
|
||||||
|
<span :title="row.label.label" class="ellipsis-1">
|
||||||
|
{{ row.label.label }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
<span :title="row.label" class="ellipsis-1">
|
||||||
|
{{ row.label }}
|
||||||
|
</span></span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="组件类型" width="110px">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag type="info" class="info-tag">{{
|
||||||
|
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||||
|
}}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<el-table-column prop="default_value" label="默认值">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :title="row.default_value" class="ellipsis-1">{{ getDefaultValue(row) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="必填">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div @click.stop>
|
||||||
|
<el-switch disabled size="small" v-model="row.required" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="left" width="80">
|
||||||
|
<template #default="{ row, $index }">
|
||||||
|
<span class="mr-4">
|
||||||
|
<el-tooltip effect="dark" content="修改" placement="top">
|
||||||
|
<el-button type="primary" text @click.stop="openEditFormCollect(row, $index)">
|
||||||
|
<el-icon><EditPen /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</span>
|
||||||
|
<el-tooltip effect="dark" content="删除" placement="top">
|
||||||
|
<el-button type="primary" text @click="deleteField(row)">
|
||||||
|
<el-icon>
|
||||||
|
<Delete />
|
||||||
|
</el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</span>
|
</template>
|
||||||
<el-tooltip effect="dark" content="删除" placement="top">
|
</el-table-column>
|
||||||
<el-button type="primary" text @click="deleteField(row)">
|
</el-table>
|
||||||
<el-icon>
|
</el-form-item>
|
||||||
<Delete />
|
</el-form>
|
||||||
</el-icon>
|
</el-card>
|
||||||
</el-button>
|
|
||||||
</el-tooltip>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<AddFormCollect ref="addFormCollectRef" :addFormField="addFormField"></AddFormCollect>
|
<AddFormCollect ref="addFormCollectRef" :addFormField="addFormField"></AddFormCollect>
|
||||||
<EditFormCollect ref="editFormCollectRef" :editFormField="editFormField"></EditFormCollect>
|
<EditFormCollect ref="editFormCollectRef" :editFormField="editFormField"></EditFormCollect>
|
||||||
</NodeContainer>
|
</NodeContainer>
|
||||||
@ -169,7 +168,7 @@ const openAddFormCollect = () => {
|
|||||||
addFormCollectRef.value?.open()
|
addFormCollectRef.value?.open()
|
||||||
}
|
}
|
||||||
const openEditFormCollect = (form_field_data: any, index: number) => {
|
const openEditFormCollect = (form_field_data: any, index: number) => {
|
||||||
editFormCollectRef.value?.open(form_field_data, index)
|
editFormCollectRef.value?.open(cloneDeep(form_field_data), index)
|
||||||
}
|
}
|
||||||
const deleteField = (form_field_data: any) => {
|
const deleteField = (form_field_data: any) => {
|
||||||
form_data.value.form_field_list = form_data.value.form_field_list.filter(
|
form_data.value.form_field_list = form_data.value.form_field_list.filter(
|
||||||
@ -197,6 +196,20 @@ const form_data = computed({
|
|||||||
set(props.nodeModel.properties, 'node_data', value)
|
set(props.nodeModel.properties, 'node_data', value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getDefaultValue = (row: any) => {
|
||||||
|
if (row.default_value) {
|
||||||
|
const default_value = row.option_list
|
||||||
|
?.filter((v: any) => row.default_value.indexOf(v.value) > -1)
|
||||||
|
.map((v: any) => v.label)
|
||||||
|
.join(',')
|
||||||
|
if (default_value) {
|
||||||
|
return default_value
|
||||||
|
}
|
||||||
|
return row.default_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const validate = () => {
|
const validate = () => {
|
||||||
return formNodeFormRef.value?.validate()
|
return formNodeFormRef.value?.validate()
|
||||||
}
|
}
|
||||||
@ -206,6 +219,7 @@ function submitDialog(val: string) {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
set(props.nodeModel, 'validate', validate)
|
set(props.nodeModel, 'validate', validate)
|
||||||
sync_form_field_list()
|
sync_form_field_list()
|
||||||
|
props.nodeModel.graphModel.eventCenter.emit('refresh_incoming_node_field')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user