This commit is contained in:
liqiang-fit2cloud 2024-11-27 14:10:17 +08:00
commit 58c0c34b2b
26 changed files with 303 additions and 184 deletions

View File

@ -35,12 +35,14 @@ class BaseDocumentExtractNode(IDocumentExtractNode):
return NodeResult({'content': splitter.join(content)}, {}) return NodeResult({'content': splitter.join(content)}, {})
def get_details(self, index: int, **kwargs): def get_details(self, index: int, **kwargs):
# 不保存content全部内容因为content内容可能会很大
content = (self.context.get('content')[:500] + '...') if len(self.context.get('content')) > 0 else ''
return { return {
'name': self.node.properties.get('stepName'), 'name': self.node.properties.get('stepName'),
"index": index, "index": index,
'run_time': self.context.get('run_time'), 'run_time': self.context.get('run_time'),
'type': self.node.type, 'type': self.node.type,
# 'content': self.context.get('content'), # 不保存content内容因为content内容可能会很大 'content': content,
'status': self.status, 'status': self.status,
'err_message': self.err_message, 'err_message': self.err_message,
'document_list': self.context.get('document_list') 'document_list': self.context.get('document_list')

View File

@ -154,6 +154,7 @@ def get_post_handler(chat_info: ChatInfo):
details=manage.get_details(), details=manage.get_details(),
message_tokens=manage.context['message_tokens'], message_tokens=manage.context['message_tokens'],
answer_tokens=manage.context['answer_tokens'], answer_tokens=manage.context['answer_tokens'],
answer_text_list=[answer_text],
run_time=manage.context['run_time'], run_time=manage.context['run_time'],
index=len(chat_info.chat_record_list) + 1) index=len(chat_info.chat_record_list) + 1)
chat_info.append_chat_record(chat_record, client_id) chat_info.append_chat_record(chat_record, client_id)

View File

@ -395,7 +395,8 @@ class ChatRecordSerializerModel(serializers.ModelSerializer):
class Meta: class Meta:
model = ChatRecord model = ChatRecord
fields = ['id', 'chat_id', 'vote_status', 'problem_text', 'answer_text', fields = ['id', 'chat_id', 'vote_status', 'problem_text', 'answer_text',
'message_tokens', 'answer_tokens', 'const', 'improve_paragraph_id_list', 'run_time', 'index','answer_text_list', 'message_tokens', 'answer_tokens', 'const', 'improve_paragraph_id_list', 'run_time', 'index',
'answer_text_list',
'create_time', 'update_time'] 'create_time', 'update_time']
@ -457,6 +458,7 @@ class ChatRecordSerializer(serializers.Serializer):
def reset_chat_record(chat_record): def reset_chat_record(chat_record):
dataset_list = [] dataset_list = []
paragraph_list = [] paragraph_list = []
if 'search_step' in chat_record.details and chat_record.details.get('search_step').get( if 'search_step' in chat_record.details and chat_record.details.get('search_step').get(
'paragraph_list') is not None: 'paragraph_list') is not None:
paragraph_list = chat_record.details.get('search_step').get( paragraph_list = chat_record.details.get('search_step').get(
@ -468,6 +470,14 @@ class ChatRecordSerializer(serializers.Serializer):
row in row in
paragraph_list], paragraph_list],
{}).items()] {}).items()]
if len(chat_record.improve_paragraph_id_list) > 0:
paragraph_model_list = QuerySet(Paragraph).filter(id__in=chat_record.improve_paragraph_id_list)
if len(paragraph_model_list) < len(chat_record.improve_paragraph_id_list):
paragraph_model_id_list = [str(p.id) for p in paragraph_model_list]
chat_record.improve_paragraph_id_list = list(
filter(lambda p_id: paragraph_model_id_list.__contains__(p_id),
chat_record.improve_paragraph_id_list))
chat_record.save()
return { return {
**ChatRecordSerializerModel(chat_record).data, **ChatRecordSerializerModel(chat_record).data,
@ -608,13 +618,11 @@ class ChatRecordSerializer(serializers.Serializer):
title=instance.get("title") if 'title' in instance else '') title=instance.get("title") if 'title' in instance else '')
problem_text = instance.get('problem_text') if instance.get( problem_text = instance.get('problem_text') if instance.get(
'problem_text') is not None else chat_record.problem_text 'problem_text') is not None else chat_record.problem_text
problem = Problem(id=uuid.uuid1(), content=problem_text, dataset_id=dataset_id) problem, _ = Problem.objects.get_or_create(content=problem_text, dataset_id=dataset_id)
problem_paragraph_mapping = ProblemParagraphMapping(id=uuid.uuid1(), dataset_id=dataset_id, problem_paragraph_mapping = ProblemParagraphMapping(id=uuid.uuid1(), dataset_id=dataset_id,
document_id=document_id, document_id=document_id,
problem_id=problem.id, problem_id=problem.id,
paragraph_id=paragraph.id) paragraph_id=paragraph.id)
# 插入问题
problem.save()
# 插入段落 # 插入段落
paragraph.save() paragraph.save()
# 插入关联问题 # 插入关联问题

View File

@ -36,8 +36,9 @@ def update_execute(sql: str, params):
""" """
with connection.cursor() as cursor: with connection.cursor() as cursor:
cursor.execute(sql, params) cursor.execute(sql, params)
affected_rows = cursor.rowcount
cursor.close() cursor.close()
return None return affected_rows
def select_list(sql: str, params: List): def select_list(sql: str, params: List):

View File

@ -10,11 +10,12 @@ import datetime
import logging import logging
import os import os
import threading import threading
import time
import traceback import traceback
from typing import List from typing import List
import django.db.models import django.db.models
from django.db import models from django.db import models, transaction
from django.db.models import QuerySet from django.db.models import QuerySet
from django.db.models.functions import Substr, Reverse from django.db.models.functions import Substr, Reverse
from langchain_core.embeddings import Embeddings from langchain_core.embeddings import Embeddings
@ -168,6 +169,7 @@ class ListenerManagement:
@staticmethod @staticmethod
def get_aggregation_document_status(document_id): def get_aggregation_document_status(document_id):
def aggregation_document_status(): def aggregation_document_status():
pass
sql = get_file_content( sql = get_file_content(
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql')) os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql'))
native_update({'document_custom_sql': QuerySet(Document).filter(id=document_id)}, sql, with_table_name=True) native_update({'document_custom_sql': QuerySet(Document).filter(id=document_id)}, sql, with_table_name=True)
@ -179,7 +181,8 @@ class ListenerManagement:
def aggregation_document_status(): def aggregation_document_status():
sql = get_file_content( sql = get_file_content(
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql')) os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql'))
native_update({'document_custom_sql': QuerySet(Document).filter(dataset_id=dataset_id)}, sql) native_update({'document_custom_sql': QuerySet(Document).filter(dataset_id=dataset_id)}, sql,
with_table_name=True)
return aggregation_document_status return aggregation_document_status
@ -188,7 +191,7 @@ class ListenerManagement:
def aggregation_document_status(): def aggregation_document_status():
sql = get_file_content( sql = get_file_content(
os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql')) os.path.join(PROJECT_DIR, "apps", "dataset", 'sql', 'update_document_status_meta.sql'))
native_update({'document_custom_sql': queryset}, sql) native_update({'document_custom_sql': queryset}, sql, with_table_name=True)
return aggregation_document_status return aggregation_document_status
@ -247,19 +250,23 @@ class ListenerManagement:
""" """
if not try_lock('embedding' + str(document_id)): if not try_lock('embedding' + str(document_id)):
return return
max_kb.info(f"开始--->向量化文档:{document_id}")
# 批量修改状态为PADDING
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), TaskType.EMBEDDING, State.STARTED)
try: try:
# 删除文档向量数据
VectorStore.get_embedding_vector().delete_by_document_id(document_id)
def is_the_task_interrupted(): def is_the_task_interrupted():
document = QuerySet(Document).filter(id=document_id).first() document = QuerySet(Document).filter(id=document_id).first()
if document is None or Status(document.status)[TaskType.EMBEDDING] == State.REVOKE: if document is None or Status(document.status)[TaskType.EMBEDDING] == State.REVOKE:
return True return True
return False return False
if is_the_task_interrupted():
return
max_kb.info(f"开始--->向量化文档:{document_id}")
# 批量修改状态为PADDING
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), TaskType.EMBEDDING,
State.STARTED)
# 删除文档向量数据
VectorStore.get_embedding_vector().delete_by_document_id(document_id)
# 根据段落进行向量化处理 # 根据段落进行向量化处理
page(QuerySet(Paragraph).filter(document_id=document_id).values('id'), 5, page(QuerySet(Paragraph).filter(document_id=document_id).values('id'), 5,
ListenerManagement.get_embedding_paragraph_apply(embedding_model, is_the_task_interrupted, ListenerManagement.get_embedding_paragraph_apply(embedding_model, is_the_task_interrupted,

View File

@ -198,4 +198,4 @@ class DocSplitHandle(BaseSplitHandle):
return self.to_md(doc, image_list, get_image_id_func()) return self.to_md(doc, image_list, get_image_id_func())
except BaseException as e: except BaseException as e:
traceback.print_exception(e) traceback.print_exception(e)
return '' return f'{e}'

View File

@ -70,4 +70,4 @@ class HTMLSplitHandle(BaseSplitHandle):
return html2text(content) return html2text(content)
except BaseException as e: except BaseException as e:
traceback.print_exception(e) traceback.print_exception(e)
return '' return f'{e}'

View File

@ -321,4 +321,4 @@ class PdfSplitHandle(BaseSplitHandle):
return self.handle_pdf_content(file, pdf_document) return self.handle_pdf_content(file, pdf_document)
except BaseException as e: except BaseException as e:
traceback.print_exception(e) traceback.print_exception(e)
return '' return f'{e}'

View File

@ -57,4 +57,4 @@ class TextSplitHandle(BaseSplitHandle):
return buffer.decode(detect(buffer)['encoding']) return buffer.decode(detect(buffer)['encoding'])
except BaseException as e: except BaseException as e:
traceback.print_exception(e) traceback.print_exception(e)
return '' return f'{e}'

View File

@ -18,10 +18,11 @@ def page(query_set, page_size, handler, is_the_task_interrupted=lambda: False):
@param is_the_task_interrupted: 任务是否被中断 @param is_the_task_interrupted: 任务是否被中断
@return: @return:
""" """
query = query_set.order_by("id")
count = query_set.count() count = query_set.count()
for i in range(0, ceil(count / page_size)): for i in range(0, ceil(count / page_size)):
if is_the_task_interrupted(): if is_the_task_interrupted():
return return
offset = i * page_size offset = i * page_size
paragraph_list = query_set[offset: offset + page_size] paragraph_list = query.all()[offset: offset + page_size]
handler(paragraph_list) handler(paragraph_list)

View File

@ -7,6 +7,11 @@ import dataset
from common.event import ListenerManagement from common.event import ListenerManagement
from dataset.models import State, TaskType from dataset.models import State, TaskType
sql = """
UPDATE "document"
SET status ="replace"(status, '1', '3')
"""
def updateDocumentStatus(apps, schema_editor): def updateDocumentStatus(apps, schema_editor):
ParagraphModel = apps.get_model('dataset', 'Paragraph') ParagraphModel = apps.get_model('dataset', 'Paragraph')
@ -43,5 +48,6 @@ class Migration(migrations.Migration):
name='status', name='status',
field=models.CharField(default=dataset.models.data_set.Status.__str__, max_length=20, verbose_name='状态'), field=models.CharField(default=dataset.models.data_set.Status.__str__, max_length=20, verbose_name='状态'),
), ),
migrations.RunSQL(sql),
migrations.RunPython(updateDocumentStatus) migrations.RunPython(updateDocumentStatus)
] ]

View File

@ -297,6 +297,9 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
ListenerManagement.update_status(QuerySet(Document).filter(id__in=document_id_list), ListenerManagement.update_status(QuerySet(Document).filter(id__in=document_id_list),
TaskType.EMBEDDING, TaskType.EMBEDDING,
State.PENDING) State.PENDING)
ListenerManagement.update_status(QuerySet(Paragraph).filter(document_id__in=document_id_list),
TaskType.EMBEDDING,
State.PENDING)
embedding_by_document_list.delay(document_id_list, model_id) embedding_by_document_list.delay(document_id_list, model_id)
else: else:
update_embedding_dataset_id(pid_list, target_dataset_id) update_embedding_dataset_id(pid_list, target_dataset_id)
@ -613,7 +616,8 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
document_id = self.data.get("document_id") document_id = self.data.get("document_id")
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), TaskType.EMBEDDING, ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), TaskType.EMBEDDING,
State.PENDING) State.PENDING)
ListenerManagement.update_status(QuerySet(Paragraph).filter(document_id=document_id), TaskType.EMBEDDING, ListenerManagement.update_status(QuerySet(Paragraph).filter(document_id=document_id),
TaskType.EMBEDDING,
State.PENDING) State.PENDING)
ListenerManagement.get_aggregation_document_status(document_id)() ListenerManagement.get_aggregation_document_status(document_id)()
embedding_model_id = get_embedding_model_id_by_dataset_id(dataset_id=self.data.get('dataset_id')) embedding_model_id = get_embedding_model_id_by_dataset_id(dataset_id=self.data.get('dataset_id'))
@ -708,8 +712,8 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
@staticmethod @staticmethod
def post_embedding(result, document_id, dataset_id): def post_embedding(result, document_id, dataset_id):
model_id = get_embedding_model_id_by_dataset_id(dataset_id) DocumentSerializers.Operate(
embedding_by_document.delay(document_id, model_id) data={'dataset_id': dataset_id, 'document_id': document_id}).refresh()
return result return result
@staticmethod @staticmethod
@ -907,8 +911,8 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
@staticmethod @staticmethod
def post_embedding(document_list, dataset_id): def post_embedding(document_list, dataset_id):
for document_dict in document_list: for document_dict in document_list:
model_id = get_embedding_model_id_by_dataset_id(dataset_id) DocumentSerializers.Operate(
embedding_by_document.delay(document_dict.get('id'), model_id) data={'dataset_id': dataset_id, 'document_id': document_dict.get('id')}).refresh()
return document_list return document_list
@post(post_function=post_embedding) @post(post_function=post_embedding)

View File

@ -540,8 +540,16 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
if with_valid: if with_valid:
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')
QuerySet(Paragraph).filter(id=paragraph_id).delete() Paragraph.objects.filter(id=paragraph_id).delete()
QuerySet(ProblemParagraphMapping).filter(paragraph_id=paragraph_id).delete()
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)

View File

@ -51,21 +51,28 @@ def get_generate_problem(llm_model, prompt, post_apply=lambda: None, is_the_task
return generate_problem return generate_problem
@celery_app.task(base=QueueOnce, once={'keys': ['document_id']}, def get_is_the_task_interrupted(document_id):
name='celery:generate_related_by_document')
def generate_related_by_document_id(document_id, model_id, prompt):
try:
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id),
TaskType.GENERATE_PROBLEM,
State.STARTED)
llm_model = get_llm_model(model_id)
def is_the_task_interrupted(): def is_the_task_interrupted():
document = QuerySet(Document).filter(id=document_id).first() document = QuerySet(Document).filter(id=document_id).first()
if document is None or Status(document.status)[TaskType.GENERATE_PROBLEM] == State.REVOKE: if document is None or Status(document.status)[TaskType.GENERATE_PROBLEM] == State.REVOKE:
return True return True
return False return False
return is_the_task_interrupted
@celery_app.task(base=QueueOnce, once={'keys': ['document_id']},
name='celery:generate_related_by_document')
def generate_related_by_document_id(document_id, model_id, prompt):
try:
is_the_task_interrupted = get_is_the_task_interrupted(document_id)
if is_the_task_interrupted():
return
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id),
TaskType.GENERATE_PROBLEM,
State.STARTED)
llm_model = get_llm_model(model_id)
# 生成问题函数 # 生成问题函数
generate_problem = get_generate_problem(llm_model, prompt, generate_problem = get_generate_problem(llm_model, prompt,
ListenerManagement.get_aggregation_document_status( ListenerManagement.get_aggregation_document_status(
@ -82,6 +89,12 @@ def generate_related_by_document_id(document_id, model_id, prompt):
name='celery:generate_related_by_paragraph_list') name='celery:generate_related_by_paragraph_list')
def generate_related_by_paragraph_id_list(document_id, paragraph_id_list, model_id, prompt): def generate_related_by_paragraph_id_list(document_id, paragraph_id_list, model_id, prompt):
try: try:
is_the_task_interrupted = get_is_the_task_interrupted(document_id)
if is_the_task_interrupted():
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id),
TaskType.GENERATE_PROBLEM,
State.REVOKED)
return
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), ListenerManagement.update_status(QuerySet(Document).filter(id=document_id),
TaskType.GENERATE_PROBLEM, TaskType.GENERATE_PROBLEM,
State.STARTED) State.STARTED)

View File

@ -102,6 +102,7 @@ def embedding_by_dataset(dataset_id, model_id):
max_kb.info(f"数据集文档:{[d.name for d in document_list]}") max_kb.info(f"数据集文档:{[d.name for d in document_list]}")
for document in document_list: for document in document_list:
try: try:
print(document.id, model_id)
embedding_by_document.delay(document.id, model_id) embedding_by_document.delay(document.id, model_id)
except Exception as e: except Exception as e:
pass pass

View File

@ -32,9 +32,11 @@ CELERY_WORKER_REDIRECT_STDOUTS = True
CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO" CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO"
CELERY_TASK_SOFT_TIME_LIMIT = 3600 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_once_path = os.path.join(celery_data_dir, "celery_once")
CELERY_ONCE = { CELERY_ONCE = {
'backend': 'celery_once.backends.File', 'backend': 'celery_once.backends.File',
'settings': {'location': os.path.join(celery_data_dir, "celery_once")} 'settings': {'location': celery_once_path}
} }
CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True CELERY_BROKER_CONNECTION_RETRY_ON_STARTUP = True
CELERY_LOG_DIR = os.path.join(PROJECT_DIR, 'logs', 'celery') CELERY_LOG_DIR = os.path.join(PROJECT_DIR, 'logs', 'celery')

View File

@ -63,11 +63,10 @@
<span class="color-secondary">{{ f.label }}:</span> {{ f.value }} <span class="color-secondary">{{ f.label }}:</span> {{ f.value }}
</div> </div>
<div v-if="item.document_list?.length > 0"> <div v-if="item.document_list?.length > 0">
<p class="mb-8 color-secondary">上传的文档:</p> <p class="mb-8 color-secondary">文档:</p>
<el-space wrap> <el-space wrap>
<template v-for="(f, i) in item.document_list" :key="i"> <template v-for="(f, i) in item.document_list" :key="i">
{{ f.name }}
<el-card <el-card
shadow="never" shadow="never"
style="--el-card-padding: 8px" style="--el-card-padding: 8px"
@ -84,7 +83,7 @@
</el-space> </el-space>
</div> </div>
<div v-if="item.image_list?.length > 0"> <div v-if="item.image_list?.length > 0">
<p class="mb-8 color-secondary">上传的图片:</p> <p class="mb-8 color-secondary">图片:</p>
<el-space wrap> <el-space wrap>
<template v-for="(f, i) in item.image_list" :key="i"> <template v-for="(f, i) in item.image_list" :key="i">
@ -219,7 +218,16 @@
<!-- 文档内容提取 --> <!-- 文档内容提取 -->
<template v-if="item.type === WorkflowType.DocumentExtractNode"> <template v-if="item.type === WorkflowType.DocumentExtractNode">
<div class="card-never border-r-4"> <div class="card-never border-r-4">
<h5 class="p-8-12">参数输出</h5> <h5 class="p-8-12">
参数输出
<el-tooltip
effect="dark"
content="每个文档仅支持预览500字"
placement="right"
>
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
</el-tooltip>
</h5>
<div class="p-8-12 border-t-dashed lighter"> <div class="p-8-12 border-t-dashed lighter">
<el-scrollbar height="150"> <el-scrollbar height="150">
<MdPreview <MdPreview

View File

@ -84,14 +84,15 @@
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)" :on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
> >
<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">上传文件最多{{
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 />文件类型{{
getAcceptList().replace(/\./g, '').replace(/,/g, '、').toUpperCase() getAcceptList().replace(/\./g, '').replace(/,/g, '、').toUpperCase()
}}</template }}
> </div>
</template>
<el-button text> <el-button text>
<el-icon><Paperclip /></el-icon> <el-icon><Paperclip /></el-icon>
</el-button> </el-button>
@ -216,6 +217,9 @@ const getAcceptList = () => {
accepts = [...accepts, ...videoExtensions] accepts = [...accepts, ...videoExtensions]
} }
// console.log(accepts) // console.log(accepts)
if (accepts.length === 0) {
return '.请在文件上传配置中选择文件类型'
}
return accepts.map((ext: any) => '.' + ext).join(',') return accepts.map((ext: any) => '.' + ext).join(',')
} }

View File

@ -20,7 +20,7 @@
:key="dynamicsFormRefresh" :key="dynamicsFormRefresh"
v-model="form_data_context" v-model="form_data_context"
:model="form_data_context" :model="form_data_context"
label-position="left" label-position="top"
require-asterisk-position="right" require-asterisk-position="right"
:render_data="inputFieldList" :render_data="inputFieldList"
ref="dynamicsFormRef" ref="dynamicsFormRef"
@ -29,7 +29,7 @@
v-if="type === 'debug-ai-chat'" v-if="type === 'debug-ai-chat'"
v-model="api_form_data_context" v-model="api_form_data_context"
:model="api_form_data_context" :model="api_form_data_context"
label-position="left" label-position="top"
require-asterisk-position="right" require-asterisk-position="right"
:render_data="apiInputFieldList" :render_data="apiInputFieldList"
ref="dynamicsFormRef2" ref="dynamicsFormRef2"

View File

@ -538,7 +538,7 @@
</h4> </h4>
</div> </div>
<div class="scrollbar-height"> <div class="scrollbar-height">
<AiChat :applicationDetails="applicationForm"></AiChat> <AiChat :applicationDetails="applicationForm" :type="'debug-ai-chat'"></AiChat>
</div> </div>
</div> </div>
</el-col> </el-col>

View File

@ -1,51 +1,13 @@
<template> <template>
<el-popover placement="top" :width="450" trigger="hover"> <el-popover v-model:visible="visible" placement="top" :width="450" trigger="hover">
<template #default> <template #default
<el-row :gutter="3" v-for="status in statusTable" :key="status.type"> ><StatusTable
<el-col :span="4">{{ taskTypeMap[status.type] }} </el-col> v-if="visible"
<el-col :span="4"> :status="status"
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED"> :statusMeta="statusMeta"
<el-icon class="success"><SuccessFilled /></el-icon> :taskTypeMap="taskTypeMap"
{{ stateMap[status.state](status.type) }} :stateMap="stateMap"
</el-text> ></StatusTable>
<el-text v-else-if="status.state === State.FAILURE">
<el-icon class="danger"><CircleCloseFilled /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.STARTED">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.PENDING">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="aggStatus?.value === State.REVOKE">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[aggStatus.value](aggStatus.key) }}
</el-text>
</el-col>
<el-col :span="5">
完成
{{
Object.keys(status.aggs ? status.aggs : {})
.filter((k) => k == State.SUCCESS)
.map((k) => status.aggs[k])
.reduce((x: any, y: any) => x + y, 0)
}}/{{
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
}}
</el-col>
<el-col :span="9">
{{
status.time
? status.time[
status.state == State.REVOKED ? State.REVOKED : State.PENDING
]?.substring(0, 19)
: undefined
}}
</el-col>
</el-row>
</template> </template>
<template #reference> <template #reference>
<el-text v-if="aggStatus?.value === State.SUCCESS || aggStatus?.value === State.REVOKED"> <el-text v-if="aggStatus?.value === State.SUCCESS || aggStatus?.value === State.REVOKED">
@ -72,11 +34,11 @@
</el-popover> </el-popover>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, ref } from 'vue'
import { Status, TaskType, State, type TaskTypeInterface } from '@/utils/status' import { TaskType, State } from '@/utils/status'
import { mergeWith } from 'lodash' import StatusTable from '@/views/document/component/StatusTable.vue'
const props = defineProps<{ status: string; statusMeta: any }>() const props = defineProps<{ status: string; statusMeta: any }>()
const visible = ref<boolean>(false)
const checkList: Array<string> = [ const checkList: Array<string> = [
State.REVOKE, State.REVOKE,
State.STARTED, State.STARTED,
@ -112,56 +74,5 @@ const stateMap: any = {
[State.FAILURE]: (type: number) => '失败', [State.FAILURE]: (type: number) => '失败',
[State.SUCCESS]: (type: number) => '成功' [State.SUCCESS]: (type: number) => '成功'
} }
const parseAgg = (agg: { count: number; status: string }) => {
const status = new Status(agg.status)
return Object.keys(TaskType)
.map((key) => {
const value = TaskType[key as keyof TaskTypeInterface]
return { [value]: { [status.task_status[value]]: agg.count } }
})
.reduce((x, y) => ({ ...x, ...y }), {})
}
const customizer: (x: any, y: any) => any = (objValue: any, srcValue: any) => {
if (objValue == undefined && srcValue) {
return srcValue
}
if (srcValue == undefined && objValue) {
return objValue
}
//
if (typeof objValue === 'object' && typeof srcValue === 'object') {
// object
return mergeWith(objValue, srcValue, customizer)
} else {
//
return objValue + srcValue
}
}
const aggs = computed(() => {
return (props.statusMeta.aggs ? props.statusMeta.aggs : [])
.map((agg: any) => {
return parseAgg(agg)
})
.reduce((x: any, y: any) => {
return mergeWith(x, y, customizer)
}, {})
})
const statusTable = computed(() => {
return Object.keys(TaskType)
.map((key) => {
const value = TaskType[key as keyof TaskTypeInterface]
const parseStatus = new Status(props.status)
return {
type: value,
state: parseStatus.task_status[value],
aggs: aggs.value[value],
time: props.statusMeta.state_time[value]
}
})
.filter((item) => item.state !== State.IGNORED)
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -0,0 +1,110 @@
<template>
<el-row :gutter="3" v-for="status in statusTable" :key="status.type">
<el-col :span="4">{{ taskTypeMap[status.type] }} </el-col>
<el-col :span="4">
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
<el-icon class="success"><SuccessFilled /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.FAILURE">
<el-icon class="danger"><CircleCloseFilled /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.STARTED">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.PENDING">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
<el-text v-else-if="status.state === State.REVOKE">
<el-icon class="is-loading primary"><Loading /></el-icon>
{{ stateMap[status.state](status.type) }}
</el-text>
</el-col>
<el-col :span="7">
<span
:style="{ color: [State.FAILURE, State.REVOKED].includes(status.state) ? '#F54A45' : '' }"
>
完成
{{
Object.keys(status.aggs ? status.aggs : {})
.filter((k) => k == State.SUCCESS)
.map((k) => status.aggs[k])
.reduce((x: any, y: any) => x + y, 0)
}}/{{
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
}}</span
>
</el-col>
<el-col :span="9">
{{
status.time
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
0,
19
)
: undefined
}}
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { Status, TaskType, State, type TaskTypeInterface } from '@/utils/status'
import { mergeWith } from 'lodash'
const props = defineProps<{ status: string; statusMeta: any; stateMap: any; taskTypeMap: any }>()
const parseAgg = (agg: { count: number; status: string }) => {
const status = new Status(agg.status)
return Object.keys(TaskType)
.map((key) => {
const value = TaskType[key as keyof TaskTypeInterface]
return { [value]: { [status.task_status[value]]: agg.count } }
})
.reduce((x, y) => ({ ...x, ...y }), {})
}
const customizer: (x: any, y: any) => any = (objValue: any, srcValue: any) => {
if (objValue == undefined && srcValue) {
return srcValue
}
if (srcValue == undefined && objValue) {
return objValue
}
//
if (typeof objValue === 'object' && typeof srcValue === 'object') {
// object
return mergeWith(objValue, srcValue, customizer)
} else {
//
return objValue + srcValue
}
}
const aggs = computed(() => {
return (props.statusMeta.aggs ? props.statusMeta.aggs : [])
.map((agg: any) => {
return parseAgg(agg)
})
.reduce((x: any, y: any) => {
return mergeWith(x, y, customizer)
}, {})
})
const statusTable = computed(() => {
return Object.keys(TaskType)
.map((key) => {
const value = TaskType[key as keyof TaskTypeInterface]
const parseStatus = new Status(props.status)
return {
type: value,
state: parseStatus.task_status[value],
aggs: aggs.value[value],
time: props.statusMeta.state_time[value]
}
})
.filter((item) => item.state !== State.IGNORED)
})
</script>
<style lang="scss"></style>

View File

@ -235,7 +235,25 @@
<template #default="{ row }"> <template #default="{ row }">
<div v-if="datasetDetail.type === '0'"> <div v-if="datasetDetail.type === '0'">
<span class="mr-4"> <span class="mr-4">
<el-tooltip effect="dark" content="向量化" placement="top"> <el-tooltip
effect="dark"
v-if="
([State.STARTED, State.PENDING] as Array<string>).includes(
getTaskState(row.status, TaskType.EMBEDDING)
)
"
content="取消向量化"
placement="top"
>
<el-button
type="primary"
text
@click.stop="cancelTask(row, TaskType.EMBEDDING)"
>
<AppIcon iconName="app-close" style="font-size: 16px"></AppIcon>
</el-button>
</el-tooltip>
<el-tooltip v-else effect="dark" content="向量化" placement="top">
<el-button type="primary" text @click.stop="refreshDocument(row)"> <el-button type="primary" text @click.stop="refreshDocument(row)">
<AppIcon iconName="app-document-refresh" style="font-size: 16px"></AppIcon> <AppIcon iconName="app-document-refresh" style="font-size: 16px"></AppIcon>
</el-button> </el-button>
@ -255,9 +273,20 @@
</el-button> </el-button>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="openGenerateDialog(row)"> <el-dropdown-item
v-if="
([State.STARTED, State.PENDING] as Array<string>).includes(
getTaskState(row.status, TaskType.GENERATE_PROBLEM)
)
"
@click="cancelTask(row, TaskType.GENERATE_PROBLEM)"
>
<el-icon><Connection /></el-icon> <el-icon><Connection /></el-icon>
生成关联问题 取消生成问题
</el-dropdown-item>
<el-dropdown-item v-else @click="openGenerateDialog(row)">
<el-icon><Connection /></el-icon>
生成问题
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item @click="openDatasetDialog(row)"> <el-dropdown-item @click="openDatasetDialog(row)">
<AppIcon iconName="app-migrate"></AppIcon> <AppIcon iconName="app-migrate"></AppIcon>
@ -286,7 +315,11 @@
<span class="mr-4"> <span class="mr-4">
<el-tooltip <el-tooltip
effect="dark" effect="dark"
v-if="getTaskState(row.status, TaskType.EMBEDDING) == State.STARTED" v-if="
([State.STARTED, State.PENDING] as Array<string>).includes(
getTaskState(row.status, TaskType.EMBEDDING)
)
"
content="取消向量化" content="取消向量化"
placement="top" placement="top"
> >
@ -318,7 +351,9 @@
> >
<el-dropdown-item <el-dropdown-item
v-if=" v-if="
getTaskState(row.status, TaskType.GENERATE_PROBLEM) == State.STARTED ([State.STARTED, State.PENDING] as Array<string>).includes(
getTaskState(row.status, TaskType.GENERATE_PROBLEM)
)
" "
@click="cancelTask(row, TaskType.GENERATE_PROBLEM)" @click="cancelTask(row, TaskType.GENERATE_PROBLEM)"
> >

View File

@ -203,7 +203,7 @@ export const documentExtractNode = {
config: { config: {
fields: [ fields: [
{ {
label: '文内容', label: '文内容',
value: 'content' value: 'content'
} }
] ]

View File

@ -131,7 +131,7 @@
<template #label> <template #label>
<div class="flex-between"> <div class="flex-between">
<div>历史聊天记录</div> <div>历史聊天记录</div>
<el-select v-model="form_data.dialogue_type" type="small" style="width: 100px;"> <el-select v-model="form_data.dialogue_type" type="small" style="width: 100px">
<el-option label="节点" value="NODE" /> <el-option label="节点" value="NODE" />
<el-option label="工作流" value="WORKFLOW" /> <el-option label="工作流" value="WORKFLOW" />
</el-select> </el-select>
@ -143,9 +143,13 @@
:value-on-clear="0" :value-on-clear="0"
controls-position="right" controls-position="right"
class="w-full" class="w-full"
:step="1"
:step-strictly="true"
/> />
</el-form-item> </el-form-item>
<el-form-item label="选择图片" :rules="{ <el-form-item
label="选择图片"
:rules="{
type: 'array', type: 'array',
required: true, required: true,
message: '请选择图片', message: '请选择图片',
@ -232,7 +236,7 @@ const form = {
is_result: true, is_result: true,
temperature: null, temperature: null,
max_tokens: null, max_tokens: null,
image_list: ["start-node", "image"] image_list: ['start-node', 'image']
} }
const form_data = computed({ const form_data = computed({
@ -249,7 +253,6 @@ const form_data = computed({
} }
}) })
function getModel() { function getModel() {
if (id) { if (id) {
applicationApi.getApplicationImageModel(id).then((res: any) => { applicationApi.getApplicationImageModel(id).then((res: any) => {
@ -268,9 +271,7 @@ function getProvider() {
}) })
} }
const model_change = (model_id?: string) => { const model_change = (model_id?: string) => {}
}
function submitSystemDialog(val: string) { function submitSystemDialog(val: string) {
set(props.nodeModel.properties.node_data, 'system', val) set(props.nodeModel.properties.node_data, 'system', val)
@ -286,10 +287,6 @@ onMounted(() => {
set(props.nodeModel, 'validate', validate) set(props.nodeModel, 'validate', validate)
}) })
</script> </script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
</style>