feat: 生成关联问题
--story=1016225 --user=刘瑞斌 【北区】在现有的分段基础上,加个 「生成问题」 ,支持调用大模型 生成问题并关联到分段。 https://www.tapd.cn/57709429/s/1589572
This commit is contained in:
parent
ccc0be788a
commit
bb6fd01200
@ -22,6 +22,7 @@ class Status(models.TextChoices):
|
|||||||
success = 1, '已完成'
|
success = 1, '已完成'
|
||||||
error = 2, '导入失败'
|
error = 2, '导入失败'
|
||||||
queue_up = 3, '排队中'
|
queue_up = 3, '排队中'
|
||||||
|
generating = 4, '生成问题中'
|
||||||
|
|
||||||
|
|
||||||
class Type(models.TextChoices):
|
class Type(models.TextChoices):
|
||||||
|
|||||||
@ -47,7 +47,7 @@ from dataset.models.data_set import DataSet, Document, Paragraph, Problem, Type,
|
|||||||
from dataset.serializers.common_serializers import BatchSerializer, MetaSerializer, ProblemParagraphManage, \
|
from dataset.serializers.common_serializers import BatchSerializer, MetaSerializer, ProblemParagraphManage, \
|
||||||
get_embedding_model_id_by_dataset_id
|
get_embedding_model_id_by_dataset_id
|
||||||
from dataset.serializers.paragraph_serializers import ParagraphSerializers, ParagraphInstanceSerializer
|
from dataset.serializers.paragraph_serializers import ParagraphSerializers, ParagraphInstanceSerializer
|
||||||
from dataset.task import sync_web_document
|
from dataset.task import sync_web_document, generate_related_by_document_id
|
||||||
from embedding.task.embedding import embedding_by_document, delete_embedding_by_document_list, \
|
from embedding.task.embedding import embedding_by_document, delete_embedding_by_document_list, \
|
||||||
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
|
||||||
@ -960,6 +960,37 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||||||
except AlreadyQueued as e:
|
except AlreadyQueued as e:
|
||||||
raise AppApiException(500, "任务正在执行中,请勿重复下发")
|
raise AppApiException(500, "任务正在执行中,请勿重复下发")
|
||||||
|
|
||||||
|
class GenerateRelated(ApiMixin, serializers.Serializer):
|
||||||
|
document_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("文档id"))
|
||||||
|
|
||||||
|
def is_valid(self, *, raise_exception=False):
|
||||||
|
super().is_valid(raise_exception=True)
|
||||||
|
document_id = self.data.get('document_id')
|
||||||
|
if not QuerySet(Document).filter(id=document_id).exists():
|
||||||
|
raise AppApiException(500, "文档id不存在")
|
||||||
|
|
||||||
|
def generate_related(self, model_id, prompt, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
document_id = self.data.get('document_id')
|
||||||
|
QuerySet(Document).filter(id=document_id).update(status=Status.queue_up)
|
||||||
|
generate_related_by_document_id.delay(document_id, model_id, prompt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BatchGenerateRelated(ApiMixin, serializers.Serializer):
|
||||||
|
dataset_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("知识库id"))
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def batch_generate_related(self, instance: Dict, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
document_id_list = instance.get("document_id_list")
|
||||||
|
model_id = instance.get("model_id")
|
||||||
|
prompt = instance.get("prompt")
|
||||||
|
for document_id in document_id_list:
|
||||||
|
DocumentSerializers.GenerateRelated(data={'document_id': document_id}).generate_related(model_id, prompt)
|
||||||
|
|
||||||
|
|
||||||
class FileBufferHandle:
|
class FileBufferHandle:
|
||||||
buffer = None
|
buffer = None
|
||||||
|
|||||||
@ -27,6 +27,7 @@ from embedding.models import SourceType
|
|||||||
from embedding.task.embedding import embedding_by_problem as embedding_by_problem_task, embedding_by_problem, \
|
from embedding.task.embedding import embedding_by_problem as embedding_by_problem_task, embedding_by_problem, \
|
||||||
delete_embedding_by_source, enable_embedding_by_paragraph, disable_embedding_by_paragraph, embedding_by_paragraph, \
|
delete_embedding_by_source, enable_embedding_by_paragraph, disable_embedding_by_paragraph, embedding_by_paragraph, \
|
||||||
delete_embedding_by_paragraph, delete_embedding_by_paragraph_ids, update_embedding_document_id
|
delete_embedding_by_paragraph, delete_embedding_by_paragraph_ids, update_embedding_document_id
|
||||||
|
from dataset.task import generate_related_by_paragraph_id_list
|
||||||
|
|
||||||
|
|
||||||
class ParagraphSerializer(serializers.ModelSerializer):
|
class ParagraphSerializer(serializers.ModelSerializer):
|
||||||
@ -719,3 +720,20 @@ class ParagraphSerializers(ApiMixin, serializers.Serializer):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BatchGenerateRelated(ApiMixin, serializers.Serializer):
|
||||||
|
dataset_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("知识库id"))
|
||||||
|
document_id = serializers.UUIDField(required=True, error_messages=ErrMessage.uuid("文档id"))
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def batch_generate_related(self, instance: Dict, with_valid=True):
|
||||||
|
if with_valid:
|
||||||
|
self.is_valid(raise_exception=True)
|
||||||
|
paragraph_id_list = instance.get("paragraph_id_list")
|
||||||
|
model_id = instance.get("model_id")
|
||||||
|
prompt = instance.get("prompt")
|
||||||
|
generate_related_by_paragraph_id_list.delay(paragraph_id_list, model_id, prompt)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,3 +7,4 @@
|
|||||||
@desc:
|
@desc:
|
||||||
"""
|
"""
|
||||||
from .sync import *
|
from .sync import *
|
||||||
|
from .generate import *
|
||||||
|
|||||||
64
apps/dataset/task/generate.py
Normal file
64
apps/dataset/task/generate.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import logging
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
from celery_once import QueueOnce
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from langchain_core.messages import HumanMessage
|
||||||
|
|
||||||
|
from common.config.embedding_config import ModelManage
|
||||||
|
from dataset.models import Paragraph, Document, Status
|
||||||
|
from dataset.task.tools import save_problem
|
||||||
|
from ops import celery_app
|
||||||
|
from setting.models import Model
|
||||||
|
from setting.models_provider import get_model
|
||||||
|
|
||||||
|
max_kb_error = logging.getLogger("max_kb_error")
|
||||||
|
max_kb = logging.getLogger("max_kb")
|
||||||
|
|
||||||
|
|
||||||
|
def get_llm_model(model_id):
|
||||||
|
model = QuerySet(Model).filter(id=model_id).first()
|
||||||
|
return ModelManage.get_model(model_id, lambda _id: get_model(model))
|
||||||
|
|
||||||
|
|
||||||
|
@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):
|
||||||
|
llm_model = get_llm_model(model_id)
|
||||||
|
offset = 0
|
||||||
|
page_size = 10
|
||||||
|
QuerySet(Document).filter(id=document_id).update(status=Status.generating)
|
||||||
|
|
||||||
|
count = QuerySet(Paragraph).filter(document_id=document_id).count()
|
||||||
|
for i in range(0, ceil(count / page_size)):
|
||||||
|
paragraph_list = QuerySet(Paragraph).filter(document_id=document_id).all()[offset:offset + page_size]
|
||||||
|
offset += page_size
|
||||||
|
for paragraph in paragraph_list:
|
||||||
|
res = llm_model.invoke([HumanMessage(content=prompt.replace('{data}', paragraph.content))])
|
||||||
|
if (res.content is None) or (len(res.content) == 0):
|
||||||
|
continue
|
||||||
|
problems = res.content.split('\n')
|
||||||
|
for problem in problems:
|
||||||
|
save_problem(paragraph.dataset_id, paragraph.document_id, paragraph.id, problem)
|
||||||
|
|
||||||
|
QuerySet(Document).filter(id=document_id).update(status=Status.success)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task(base=QueueOnce, once={'keys': ['paragraph_id_list']},
|
||||||
|
name='celery:generate_related_by_paragraph_list')
|
||||||
|
def generate_related_by_paragraph_id_list(paragraph_id_list, model_id, prompt):
|
||||||
|
llm_model = get_llm_model(model_id)
|
||||||
|
offset = 0
|
||||||
|
page_size = 10
|
||||||
|
count = QuerySet(Paragraph).filter(id__in=paragraph_id_list).count()
|
||||||
|
for i in range(0, ceil(count / page_size)):
|
||||||
|
paragraph_list = QuerySet(Paragraph).filter(id__in=paragraph_id_list).all()[offset:offset + page_size]
|
||||||
|
offset += page_size
|
||||||
|
for paragraph in paragraph_list:
|
||||||
|
res = llm_model.invoke([HumanMessage(content=prompt.replace('{data}', paragraph.content))])
|
||||||
|
if (res.content is None) or (len(res.content) == 0):
|
||||||
|
continue
|
||||||
|
problems = res.content.split('\n')
|
||||||
|
for problem in problems:
|
||||||
|
save_problem(paragraph.dataset_id, paragraph.document_id, paragraph.id, problem)
|
||||||
@ -8,6 +8,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from common.util.fork import ChildLink, Fork
|
from common.util.fork import ChildLink, Fork
|
||||||
@ -60,3 +61,23 @@ def get_sync_web_document_handler(dataset_id):
|
|||||||
status=Status.error).save()
|
status=Status.error).save()
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def save_problem(dataset_id, document_id, paragraph_id, problem):
|
||||||
|
from dataset.serializers.paragraph_serializers import ParagraphSerializers
|
||||||
|
# print(f"dataset_id: {dataset_id}")
|
||||||
|
# print(f"document_id: {document_id}")
|
||||||
|
# print(f"paragraph_id: {paragraph_id}")
|
||||||
|
# print(f"problem: {problem}")
|
||||||
|
problem = re.sub(r"^\d+\.\s*", "", problem)
|
||||||
|
pattern = r"<question>(.*?)</question>"
|
||||||
|
match = re.search(pattern, problem)
|
||||||
|
problem = match.group(1) if match else None
|
||||||
|
if problem is None or len(problem) == 0:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
ParagraphSerializers.Problem(
|
||||||
|
data={"dataset_id": dataset_id, 'document_id': document_id,
|
||||||
|
'paragraph_id': paragraph_id}).save(instance={"content": problem}, with_valid=True)
|
||||||
|
except Exception as e:
|
||||||
|
max_kb_error.error(f'关联问题失败: {e}')
|
||||||
|
|||||||
@ -15,6 +15,7 @@ urlpatterns = [
|
|||||||
path('dataset/<str:dataset_id>/sync_web', views.Dataset.SyncWeb.as_view()),
|
path('dataset/<str:dataset_id>/sync_web', views.Dataset.SyncWeb.as_view()),
|
||||||
path('dataset/<str:dataset_id>/hit_test', views.Dataset.HitTest.as_view()),
|
path('dataset/<str:dataset_id>/hit_test', views.Dataset.HitTest.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document', views.Document.as_view(), name='document'),
|
path('dataset/<str:dataset_id>/document', views.Document.as_view(), name='document'),
|
||||||
|
path('dataset/<str:dataset_id>/model', views.Dataset.Model.as_view()),
|
||||||
path('dataset/document/template/export', views.Template.as_view()),
|
path('dataset/document/template/export', views.Template.as_view()),
|
||||||
path('dataset/document/table_template/export', views.TableTemplate.as_view()),
|
path('dataset/document/table_template/export', views.TableTemplate.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/web', views.WebDocument.as_view()),
|
path('dataset/<str:dataset_id>/document/web', views.WebDocument.as_view()),
|
||||||
@ -24,6 +25,7 @@ urlpatterns = [
|
|||||||
path('dataset/<str:dataset_id>/document/batch_hit_handling', views.Document.BatchEditHitHandling.as_view()),
|
path('dataset/<str:dataset_id>/document/batch_hit_handling', views.Document.BatchEditHitHandling.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<int:current_page>/<int:page_size>', views.Document.Page.as_view()),
|
path('dataset/<str:dataset_id>/document/<int:current_page>/<int:page_size>', views.Document.Page.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/batch_refresh', views.Document.BatchRefresh.as_view()),
|
path('dataset/<str:dataset_id>/document/batch_refresh', views.Document.BatchRefresh.as_view()),
|
||||||
|
path('dataset/<str:dataset_id>/document/batch_generate_related', views.Document.BatchGenerateRelated.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>', views.Document.Operate.as_view(),
|
path('dataset/<str:dataset_id>/document/<str:document_id>', views.Document.Operate.as_view(),
|
||||||
name="document_operate"),
|
name="document_operate"),
|
||||||
path('dataset/document/split', views.Document.Split.as_view(),
|
path('dataset/document/split', views.Document.Split.as_view(),
|
||||||
@ -36,12 +38,14 @@ urlpatterns = [
|
|||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/sync', views.Document.SyncWeb.as_view()),
|
path('dataset/<str:dataset_id>/document/<str:document_id>/sync', views.Document.SyncWeb.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/refresh', views.Document.Refresh.as_view()),
|
path('dataset/<str:dataset_id>/document/<str:document_id>/refresh', views.Document.Refresh.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph', views.Paragraph.as_view()),
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph', views.Paragraph.as_view()),
|
||||||
|
path('dataset/<str:dataset_id>/document/batch_generate_related', views.Document.BatchGenerateRelated.as_view()),
|
||||||
path(
|
path(
|
||||||
'dataset/<str:dataset_id>/document/<str:document_id>/paragraph/migrate/dataset/<str:target_dataset_id>/document/<str:target_document_id>',
|
'dataset/<str:dataset_id>/document/<str:document_id>/paragraph/migrate/dataset/<str:target_dataset_id>/document/<str:target_document_id>',
|
||||||
views.Paragraph.BatchMigrate.as_view()),
|
views.Paragraph.BatchMigrate.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/_batch', views.Paragraph.Batch.as_view()),
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/_batch', views.Paragraph.Batch.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<int:current_page>/<int:page_size>',
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<int:current_page>/<int:page_size>',
|
||||||
views.Paragraph.Page.as_view(), name='paragraph_page'),
|
views.Paragraph.Page.as_view(), name='paragraph_page'),
|
||||||
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/batch_generate_related', views.Paragraph.BatchGenerateRelated.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<paragraph_id>',
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<paragraph_id>',
|
||||||
views.Paragraph.Operate.as_view()),
|
views.Paragraph.Operate.as_view()),
|
||||||
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<str:paragraph_id>/problem',
|
path('dataset/<str:dataset_id>/document/<str:document_id>/paragraph/<str:paragraph_id>/problem',
|
||||||
|
|||||||
@ -20,6 +20,7 @@ from common.response import result
|
|||||||
from common.response.result import get_page_request_params, get_page_api_response, get_api_response
|
from common.response.result import get_page_request_params, get_page_api_response, get_api_response
|
||||||
from common.swagger_api.common_api import CommonApi
|
from common.swagger_api.common_api import CommonApi
|
||||||
from dataset.serializers.dataset_serializers import DataSetSerializers
|
from dataset.serializers.dataset_serializers import DataSetSerializers
|
||||||
|
from setting.serializers.provider_serializers import ModelSerializer
|
||||||
|
|
||||||
|
|
||||||
class Dataset(APIView):
|
class Dataset(APIView):
|
||||||
@ -223,3 +224,20 @@ class Dataset(APIView):
|
|||||||
'user_id': str(request.user.id)})
|
'user_id': str(request.user.id)})
|
||||||
d.is_valid()
|
d.is_valid()
|
||||||
return result.success(d.page(current_page, page_size))
|
return result.success(d.page(current_page, page_size))
|
||||||
|
|
||||||
|
class Model(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@action(methods=["GET"], detail=False)
|
||||||
|
@has_permissions(ViewPermission(
|
||||||
|
[RoleConstants.ADMIN, RoleConstants.USER],
|
||||||
|
[lambda r, keywords: Permission(group=Group.DATASET, operate=Operate.MANAGE,
|
||||||
|
dynamic_tag=keywords.get('dataset_id'))],
|
||||||
|
compare=CompareConstants.AND))
|
||||||
|
def get(self, request: Request, dataset_id: str):
|
||||||
|
print(dataset_id)
|
||||||
|
return result.success(
|
||||||
|
ModelSerializer.Query(
|
||||||
|
data={'user_id': request.user.id, 'model_type': 'LLM'}).list(
|
||||||
|
with_valid=True)
|
||||||
|
)
|
||||||
|
|||||||
@ -393,3 +393,14 @@ class Document(APIView):
|
|||||||
data={**query_params_to_single_dict(request.query_params), 'dataset_id': dataset_id})
|
data={**query_params_to_single_dict(request.query_params), 'dataset_id': dataset_id})
|
||||||
d.is_valid(raise_exception=True)
|
d.is_valid(raise_exception=True)
|
||||||
return result.success(d.page(current_page, page_size))
|
return result.success(d.page(current_page, page_size))
|
||||||
|
|
||||||
|
class BatchGenerateRelated(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@action(methods=['PUT'], detail=False)
|
||||||
|
@has_permissions(
|
||||||
|
lambda r, k: Permission(group=Group.DATASET, operate=Operate.MANAGE,
|
||||||
|
dynamic_tag=k.get('dataset_id')))
|
||||||
|
def put(self, request: Request, dataset_id: str):
|
||||||
|
return result.success(DocumentSerializers.BatchGenerateRelated(data={'dataset_id': dataset_id})
|
||||||
|
.batch_generate_related(request.data))
|
||||||
|
|||||||
@ -232,3 +232,15 @@ class Paragraph(APIView):
|
|||||||
'document_id': document_id})
|
'document_id': document_id})
|
||||||
d.is_valid(raise_exception=True)
|
d.is_valid(raise_exception=True)
|
||||||
return result.success(d.page(current_page, page_size))
|
return result.success(d.page(current_page, page_size))
|
||||||
|
|
||||||
|
class BatchGenerateRelated(APIView):
|
||||||
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
@action(methods=['PUT'], detail=False)
|
||||||
|
@has_permissions(
|
||||||
|
lambda r, k: Permission(group=Group.DATASET, operate=Operate.MANAGE,
|
||||||
|
dynamic_tag=k.get('dataset_id')))
|
||||||
|
def put(self, request: Request, dataset_id: str, document_id: str):
|
||||||
|
return result.success(
|
||||||
|
ParagraphSerializers.BatchGenerateRelated(data={'dataset_id': dataset_id, 'document_id': document_id})
|
||||||
|
.batch_generate_related(request.data))
|
||||||
|
|||||||
@ -202,6 +202,22 @@ const exportDataset: (
|
|||||||
return exportExcel(dataset_name + '.xlsx', `dataset/${dataset_id}/export`, undefined, loading)
|
return exportExcel(dataset_name + '.xlsx', `dataset/${dataset_id}/export`, undefined, loading)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户可使用的模型列表
|
||||||
|
* @param application_id
|
||||||
|
* @param loading
|
||||||
|
* @query { query_text: string, top_number: number, similarity: number }
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getDatasetModel: (
|
||||||
|
dataset_id: string,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<Array<any>>> = (dataset_id, loading) => {
|
||||||
|
return get(`${prefix}/${dataset_id}/model`, loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getDataset,
|
getDataset,
|
||||||
getAllDataset,
|
getAllDataset,
|
||||||
@ -215,5 +231,6 @@ export default {
|
|||||||
putSyncWebDataset,
|
putSyncWebDataset,
|
||||||
putReEmbeddingDataset,
|
putReEmbeddingDataset,
|
||||||
postQADataset,
|
postQADataset,
|
||||||
exportDataset
|
exportDataset,
|
||||||
|
getDatasetModel
|
||||||
}
|
}
|
||||||
|
|||||||
@ -317,6 +317,19 @@ const exportDocument: (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const batchGenerateRelated: (
|
||||||
|
dataset_id: string,
|
||||||
|
data: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<boolean>> = (dataset_id, data, loading) => {
|
||||||
|
return put(
|
||||||
|
`${prefix}/${dataset_id}/document/batch_generate_related`,
|
||||||
|
data,
|
||||||
|
undefined,
|
||||||
|
loading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
postSplitDocument,
|
postSplitDocument,
|
||||||
getDocument,
|
getDocument,
|
||||||
@ -338,5 +351,6 @@ export default {
|
|||||||
postQADocument,
|
postQADocument,
|
||||||
postTableDocument,
|
postTableDocument,
|
||||||
exportDocument,
|
exportDocument,
|
||||||
batchRefresh
|
batchRefresh,
|
||||||
|
batchGenerateRelated
|
||||||
}
|
}
|
||||||
|
|||||||
@ -226,6 +226,21 @@ const disassociationProblem: (
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const batchGenerateRelated: (
|
||||||
|
dataset_id: string,
|
||||||
|
document_id: string,
|
||||||
|
data: any,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<boolean>> = (dataset_id, document_id, data, loading) => {
|
||||||
|
return put(
|
||||||
|
`${prefix}/${dataset_id}/document/${document_id}/paragraph/batch_generate_related`,
|
||||||
|
data,
|
||||||
|
undefined,
|
||||||
|
loading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getParagraph,
|
getParagraph,
|
||||||
delParagraph,
|
delParagraph,
|
||||||
@ -236,5 +251,6 @@ export default {
|
|||||||
disassociationProblem,
|
disassociationProblem,
|
||||||
associationProblem,
|
associationProblem,
|
||||||
delMulParagraph,
|
delMulParagraph,
|
||||||
putMigrateMulParagraph
|
putMigrateMulParagraph,
|
||||||
|
batchGenerateRelated
|
||||||
}
|
}
|
||||||
|
|||||||
245
ui/src/views/document/component/GenerateRelatedDialog.vue
Normal file
245
ui/src/views/document/component/GenerateRelatedDialog.vue
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="生成关联问题"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
width="600"
|
||||||
|
class="select-dataset-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
>
|
||||||
|
<template #header="{ titleId, titleClass }">
|
||||||
|
<div class="my-header flex">
|
||||||
|
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="content-height">
|
||||||
|
<el-form
|
||||||
|
ref="FormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-position="top"
|
||||||
|
require-asterisk-position="right"
|
||||||
|
>
|
||||||
|
<div class="update-info flex border-r-4 mb-16 w-full">
|
||||||
|
<div class="mt-4">
|
||||||
|
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||||
|
</div>
|
||||||
|
<div class="ml-16 lighter">
|
||||||
|
<p>提示词中的 {data} 为分段内容的占位符,执行时替换为分段内容发送给 AI 模型;</p>
|
||||||
|
<p>AI 模型根据分段内容生成相关问题,请将生成的问题放至<question></question>标签中,系统会自动关联标签中的问题;</p>
|
||||||
|
<p>生成效果依赖于所选模型和提示词,用户可自行调整至最佳效果。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form-item label="AI 模型" prop="model_id">
|
||||||
|
<el-select
|
||||||
|
v-model="form.model_id"
|
||||||
|
:placeholder="$t('views.application.applicationForm.form.aiModel.placeholder')"
|
||||||
|
class="w-full"
|
||||||
|
popper-class="select-model"
|
||||||
|
:clearable="true"
|
||||||
|
>
|
||||||
|
<el-option-group
|
||||||
|
v-for="(value, label) in modelOptions"
|
||||||
|
:key="value"
|
||||||
|
:label="relatedObject(providerOptions, label, 'provider')?.name"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in value.filter((v: any) => v.status === 'SUCCESS')"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
class="flex-between"
|
||||||
|
>
|
||||||
|
<div class="flex align-center">
|
||||||
|
<span
|
||||||
|
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||||
|
class="model-icon mr-8"
|
||||||
|
></span>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<el-tag
|
||||||
|
v-if="item.permission_type === 'PUBLIC'"
|
||||||
|
type="info"
|
||||||
|
class="info-tag ml-8"
|
||||||
|
>公用
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-icon class="check-icon" v-if="item.id === form.model_id">
|
||||||
|
<Check />
|
||||||
|
</el-icon>
|
||||||
|
</el-option>
|
||||||
|
<!-- 不可用 -->
|
||||||
|
<el-option
|
||||||
|
v-for="item in value.filter((v: any) => v.status !== 'SUCCESS')"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
class="flex-between"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<div class="flex">
|
||||||
|
<span
|
||||||
|
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||||
|
class="model-icon mr-8"
|
||||||
|
></span>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<span class="danger">{{
|
||||||
|
$t('views.application.applicationForm.form.aiModel.unavailable')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<el-icon class="check-icon" v-if="item.id === form.model_id">
|
||||||
|
<Check />
|
||||||
|
</el-icon>
|
||||||
|
</el-option>
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提示词" prop="prompt">
|
||||||
|
<el-input
|
||||||
|
v-model="form.prompt"
|
||||||
|
placeholder="请输入提示词"
|
||||||
|
:rows="7"
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
|
||||||
|
<el-button type="primary" @click="submitHandle(FormRef)" :disabled="!model || loading">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import documentApi from '@/api/document'
|
||||||
|
|
||||||
|
import useStore from '@/stores'
|
||||||
|
import { relatedObject } from '@/utils/utils'
|
||||||
|
import type { Provider } from '@/api/type/model'
|
||||||
|
import datasetApi from '@/api/dataset'
|
||||||
|
import { groupBy } from 'lodash'
|
||||||
|
import { MsgSuccess } from '@/utils/message'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const {
|
||||||
|
params: { id } // id为datasetID
|
||||||
|
} = route as any
|
||||||
|
|
||||||
|
const { model } = useStore()
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
const modelOptions = ref<any>(null)
|
||||||
|
const providerOptions = ref<Array<Provider>>([])
|
||||||
|
const documentIdList = ref<string[]>([])
|
||||||
|
|
||||||
|
const FormRef = ref()
|
||||||
|
const form = ref({
|
||||||
|
model_id: '',
|
||||||
|
prompt: '内容:{data}\n' +
|
||||||
|
'\n' +
|
||||||
|
'请总结上面的内容,并根据内容总结生成 5 个问题。\n' +
|
||||||
|
'回答要求:\n' +
|
||||||
|
'- 请只输出问题;\n' +
|
||||||
|
'- 请将每个问题放置<question></question>标签中。'
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
model_id: [{ required: true, message: '请选择AI 模型', trigger: 'blur' }],
|
||||||
|
prompt: [{ required: true, message: '请输入提示词', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(dialogVisible, (bool) => {
|
||||||
|
if (!bool) {
|
||||||
|
form.value.model_id = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = (document_ids: string[]) => {
|
||||||
|
getProvider()
|
||||||
|
getModel()
|
||||||
|
documentIdList.value = document_ids
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitHandle = async (formEl: FormInstance) => {
|
||||||
|
if (!formEl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
const data = { ...form.value, document_id_list: documentIdList.value }
|
||||||
|
documentApi.batchGenerateRelated(id, data).then(() => {
|
||||||
|
MsgSuccess('生成关联问题成功')
|
||||||
|
emit('refresh')
|
||||||
|
dialogVisible.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getModel() {
|
||||||
|
loading.value = true
|
||||||
|
datasetApi
|
||||||
|
.getDatasetModel(id)
|
||||||
|
.then((res: any) => {
|
||||||
|
modelOptions.value = groupBy(res?.data, 'provider')
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProvider() {
|
||||||
|
loading.value = true
|
||||||
|
model
|
||||||
|
.asyncGetProvider()
|
||||||
|
.then((res: any) => {
|
||||||
|
providerOptions.value = res?.data
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scope>
|
||||||
|
.select-dataset-dialog {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.el-dialog__header {
|
||||||
|
padding: 24px 24px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-info {
|
||||||
|
background: #d6e2ff;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -26,6 +26,9 @@
|
|||||||
<el-button @click="batchRefresh" :disabled="multipleSelection.length === 0">
|
<el-button @click="batchRefresh" :disabled="multipleSelection.length === 0">
|
||||||
重新向量化
|
重新向量化
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button @click="openGenerateDialog()" :disabled="multipleSelection.length === 0">
|
||||||
|
生成关联问题
|
||||||
|
</el-button>
|
||||||
<el-button @click="openBatchEditDocument" :disabled="multipleSelection.length === 0">
|
<el-button @click="openBatchEditDocument" :disabled="multipleSelection.length === 0">
|
||||||
设置
|
设置
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -138,6 +141,9 @@
|
|||||||
<el-text v-else-if="row.status === '3'">
|
<el-text v-else-if="row.status === '3'">
|
||||||
<el-icon class="is-loading primary"><Loading /></el-icon> 排队中
|
<el-icon class="is-loading primary"><Loading /></el-icon> 排队中
|
||||||
</el-text>
|
</el-text>
|
||||||
|
<el-text v-else-if="row.status === '4'">
|
||||||
|
<el-icon class="is-loading primary"><Loading /></el-icon> 生成问题中
|
||||||
|
</el-text>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column width="130">
|
<el-table-column width="130">
|
||||||
@ -258,6 +264,10 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="openGenerateDialog(row)">
|
||||||
|
<el-icon><Connection /></el-icon>
|
||||||
|
生成关联问题
|
||||||
|
</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>
|
||||||
迁移
|
迁移
|
||||||
@ -300,6 +310,10 @@
|
|||||||
<el-dropdown-item icon="Setting" @click="settingDoc(row)"
|
<el-dropdown-item icon="Setting" @click="settingDoc(row)"
|
||||||
>设置</el-dropdown-item
|
>设置</el-dropdown-item
|
||||||
>
|
>
|
||||||
|
<el-dropdown-item @click="openGenerateDialog(row)">
|
||||||
|
<el-icon><Connection /></el-icon>
|
||||||
|
生成关联问题
|
||||||
|
</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>
|
||||||
迁移</el-dropdown-item
|
迁移</el-dropdown-item
|
||||||
@ -324,6 +338,7 @@
|
|||||||
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
|
<SyncWebDialog ref="SyncWebDialogRef" @refresh="refresh" />
|
||||||
<!-- 选择知识库 -->
|
<!-- 选择知识库 -->
|
||||||
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
|
<SelectDatasetDialog ref="SelectDatasetDialogRef" @refresh="refreshMigrate" />
|
||||||
|
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
|
||||||
</div>
|
</div>
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
</template>
|
</template>
|
||||||
@ -340,6 +355,7 @@ import { datetimeFormat } from '@/utils/time'
|
|||||||
import { hitHandlingMethod } from '@/enums/document'
|
import { hitHandlingMethod } from '@/enums/document'
|
||||||
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
import { MsgSuccess, MsgConfirm, MsgError } from '@/utils/message'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
|
import GenerateRelatedDialog from '@/views/document/component/GenerateRelatedDialog.vue'
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const {
|
const {
|
||||||
@ -554,6 +570,19 @@ function batchRefresh() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function batchGenerateRelated() {
|
||||||
|
const arr: string[] = []
|
||||||
|
multipleSelection.value.map((v) => {
|
||||||
|
if (v) {
|
||||||
|
arr.push(v.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
documentApi.batchGenerateRelated(id, arr, loading).then(() => {
|
||||||
|
MsgSuccess('批量生成关联问题成功')
|
||||||
|
multipleTableRef.value?.clearSelection()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function deleteDocument(row: any) {
|
function deleteDocument(row: any) {
|
||||||
MsgConfirm(
|
MsgConfirm(
|
||||||
`是否删除文档:${row.name} ?`,
|
`是否删除文档:${row.name} ?`,
|
||||||
@ -643,6 +672,23 @@ function refresh() {
|
|||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GenerateRelatedDialogRef = ref()
|
||||||
|
function openGenerateDialog(row?: any) {
|
||||||
|
const arr: string[] = []
|
||||||
|
if (row) {
|
||||||
|
arr.push(row.id)
|
||||||
|
} else {
|
||||||
|
multipleSelection.value.map((v) => {
|
||||||
|
if (v) {
|
||||||
|
arr.push(v.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateRelatedDialogRef.value.open(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDetail()
|
getDetail()
|
||||||
if (beforePagination.value) {
|
if (beforePagination.value) {
|
||||||
|
|||||||
246
ui/src/views/paragraph/component/GenerateRelatedDialog.vue
Normal file
246
ui/src/views/paragraph/component/GenerateRelatedDialog.vue
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="生成关联问题"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
width="600"
|
||||||
|
class="select-dataset-dialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
>
|
||||||
|
<template #header="{ titleId, titleClass }">
|
||||||
|
<div class="my-header flex">
|
||||||
|
<h4 :id="titleId" :class="titleClass">生成关联问题</h4>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="content-height">
|
||||||
|
<el-form
|
||||||
|
ref="FormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
label-position="top"
|
||||||
|
require-asterisk-position="right"
|
||||||
|
>
|
||||||
|
<div class="update-info flex border-r-4 mb-16 w-full">
|
||||||
|
<div class="mt-4">
|
||||||
|
<AppIcon iconName="app-warning-colorful" style="font-size: 16px"></AppIcon>
|
||||||
|
</div>
|
||||||
|
<div class="ml-16 lighter">
|
||||||
|
<p>提示词中的 {data} 为分段内容的占位符,执行时替换为分段内容发送给 AI 模型;</p>
|
||||||
|
<p>AI
|
||||||
|
模型根据分段内容生成相关问题,请将生成的问题放至<question></question>标签中,系统会自动关联标签中的问题;</p>
|
||||||
|
<p>生成效果依赖于所选模型和提示词,用户可自行调整至最佳效果。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-form-item label="AI 模型" prop="model_id">
|
||||||
|
<el-select
|
||||||
|
v-model="form.model_id"
|
||||||
|
:placeholder="$t('views.application.applicationForm.form.aiModel.placeholder')"
|
||||||
|
class="w-full"
|
||||||
|
popper-class="select-model"
|
||||||
|
:clearable="true"
|
||||||
|
>
|
||||||
|
<el-option-group
|
||||||
|
v-for="(value, label) in modelOptions"
|
||||||
|
:key="value"
|
||||||
|
:label="relatedObject(providerOptions, label, 'provider')?.name"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in value.filter((v: any) => v.status === 'SUCCESS')"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
class="flex-between"
|
||||||
|
>
|
||||||
|
<div class="flex align-center">
|
||||||
|
<span
|
||||||
|
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||||
|
class="model-icon mr-8"
|
||||||
|
></span>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<el-tag
|
||||||
|
v-if="item.permission_type === 'PUBLIC'"
|
||||||
|
type="info"
|
||||||
|
class="info-tag ml-8"
|
||||||
|
>公用
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-icon class="check-icon" v-if="item.id === form.model_id">
|
||||||
|
<Check />
|
||||||
|
</el-icon>
|
||||||
|
</el-option>
|
||||||
|
<!-- 不可用 -->
|
||||||
|
<el-option
|
||||||
|
v-for="item in value.filter((v: any) => v.status !== 'SUCCESS')"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
class="flex-between"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<div class="flex">
|
||||||
|
<span
|
||||||
|
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||||
|
class="model-icon mr-8"
|
||||||
|
></span>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<span class="danger">{{
|
||||||
|
$t('views.application.applicationForm.form.aiModel.unavailable')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<el-icon class="check-icon" v-if="item.id === form.model_id">
|
||||||
|
<Check />
|
||||||
|
</el-icon>
|
||||||
|
</el-option>
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="提示词" prop="prompt">
|
||||||
|
<el-input
|
||||||
|
v-model="form.prompt"
|
||||||
|
placeholder="请输入提示词"
|
||||||
|
:rows="7"
|
||||||
|
type="textarea"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click.prevent="dialogVisible = false"> 取消 </el-button>
|
||||||
|
<el-button type="primary" @click="submitHandle(FormRef)" :disabled="!model || loading">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import paragraphApi from '@/api/paragraph'
|
||||||
|
|
||||||
|
import useStore from '@/stores'
|
||||||
|
import { relatedObject } from '@/utils/utils'
|
||||||
|
import type { Provider } from '@/api/type/model'
|
||||||
|
import datasetApi from '@/api/dataset'
|
||||||
|
import { groupBy } from 'lodash'
|
||||||
|
import { MsgSuccess } from '@/utils/message'
|
||||||
|
import { t } from '@/locales'
|
||||||
|
import type { FormInstance } from 'element-plus'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const {
|
||||||
|
params: { id, documentId } // id为datasetID
|
||||||
|
} = route as any
|
||||||
|
|
||||||
|
const { model } = useStore()
|
||||||
|
|
||||||
|
|
||||||
|
const emit = defineEmits(['refresh'])
|
||||||
|
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
const modelOptions = ref<any>(null)
|
||||||
|
const providerOptions = ref<Array<Provider>>([])
|
||||||
|
const paragraphIdList = ref<string[]>([])
|
||||||
|
|
||||||
|
const FormRef = ref()
|
||||||
|
const form = ref({
|
||||||
|
model_id: '',
|
||||||
|
prompt: '内容:{data}\n' +
|
||||||
|
'\n' +
|
||||||
|
'请总结上面的内容,并根据内容总结生成 5 个问题。\n' +
|
||||||
|
'回答要求:\n' +
|
||||||
|
'- 请只输出问题;\n' +
|
||||||
|
'- 请将每个问题放置<question></question>标签中。'
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
model_id: [{ required: true, message: '请选择AI 模型', trigger: 'blur' }],
|
||||||
|
prompt: [{ required: true, message: '请输入提示词', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(dialogVisible, (bool) => {
|
||||||
|
if (!bool) {
|
||||||
|
form.value.model_id = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const open = (paragraph_ids: string[]) => {
|
||||||
|
getProvider()
|
||||||
|
getModel()
|
||||||
|
paragraphIdList.value = paragraph_ids
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitHandle = async (formEl: FormInstance) => {
|
||||||
|
if (!formEl) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
const data = { ...form.value, paragraph_id_list: paragraphIdList.value }
|
||||||
|
paragraphApi.batchGenerateRelated(id, documentId, data).then(() => {
|
||||||
|
MsgSuccess('生成关联问题成功')
|
||||||
|
emit('refresh')
|
||||||
|
dialogVisible.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getModel() {
|
||||||
|
loading.value = true
|
||||||
|
datasetApi
|
||||||
|
.getDatasetModel(id)
|
||||||
|
.then((res: any) => {
|
||||||
|
modelOptions.value = groupBy(res?.data, 'provider')
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProvider() {
|
||||||
|
loading.value = true
|
||||||
|
model
|
||||||
|
.asyncGetProvider()
|
||||||
|
.then((res: any) => {
|
||||||
|
providerOptions.value = res?.data
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scope>
|
||||||
|
.select-dataset-dialog {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.el-dialog__header {
|
||||||
|
padding: 24px 24px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding: 0 24px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-info {
|
||||||
|
background: #d6e2ff;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -121,6 +121,10 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="openGenerateDialog(item)">
|
||||||
|
<el-icon><Connection /></el-icon>
|
||||||
|
生成关联问题</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>
|
||||||
迁移</el-dropdown-item
|
迁移</el-dropdown-item
|
||||||
@ -142,6 +146,9 @@
|
|||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
<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>
|
||||||
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
<el-button :disabled="multipleSelection.length === 0" @click="openSelectDocumentDialog()">
|
||||||
迁移
|
迁移
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -154,6 +161,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
<ParagraphDialog ref="ParagraphDialogRef" :title="title" @refresh="refresh" />
|
||||||
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
|
<SelectDocumentDialog ref="SelectDocumentDialogRef" @refresh="refreshMigrateParagraph" />
|
||||||
|
<GenerateRelatedDialog ref="GenerateRelatedDialogRef" @refresh="refresh" />
|
||||||
|
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -166,6 +175,7 @@ import SelectDocumentDialog from './component/SelectDocumentDialog.vue'
|
|||||||
import { numberFormat } from '@/utils/utils'
|
import { numberFormat } from '@/utils/utils'
|
||||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||||
import useStore from '@/stores'
|
import useStore from '@/stores'
|
||||||
|
import GenerateRelatedDialog from './component/GenerateRelatedDialog.vue'
|
||||||
const { paragraph } = useStore()
|
const { paragraph } = useStore()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const {
|
const {
|
||||||
@ -318,6 +328,23 @@ function refresh(data: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const GenerateRelatedDialogRef = ref()
|
||||||
|
function openGenerateDialog(row?: any) {
|
||||||
|
const arr: string[] = []
|
||||||
|
if (row) {
|
||||||
|
arr.push(row.id)
|
||||||
|
} else {
|
||||||
|
multipleSelection.value.map((v) => {
|
||||||
|
if (v) {
|
||||||
|
arr.push(v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateRelatedDialogRef.value.open(arr)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDetail()
|
getDetail()
|
||||||
getParagraphList()
|
getParagraphList()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user