mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 10:12:51 +00:00
Merge branch 'main' of https://github.com/maxkb-dev/maxkb
This commit is contained in:
commit
7811e8cdf3
|
|
@ -46,6 +46,8 @@ class BaseFormNode(IFormNode):
|
|||
if form_data is not None:
|
||||
self.context['is_submit'] = True
|
||||
self.context['form_data'] = form_data
|
||||
for key in form_data:
|
||||
self.context[key] = form_data.get(key)
|
||||
else:
|
||||
self.context['is_submit'] = False
|
||||
form_setting = {"form_field_list": form_field_list, "runtime_node_id": self.runtime_node_id,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,8 @@ class XlsxSplitHandle(BaseParseTableHandle):
|
|||
for sheetname in workbook.sheetnames:
|
||||
sheet = workbook[sheetname] if sheetname else workbook.active
|
||||
rows = self.fill_merged_cells(sheet, image_dict)
|
||||
|
||||
if len(rows) == 0:
|
||||
continue
|
||||
# 提取表头和内容
|
||||
|
||||
headers = [f"{key}" for key, value in rows[0].items()]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import math
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db.models import TextChoices
|
||||
|
||||
|
|
@ -93,7 +95,8 @@ class BaseActionCommand(BaseCommand):
|
|||
'services', nargs='+', choices=Services.export_services_values(), help='Service',
|
||||
)
|
||||
parser.add_argument('-d', '--daemon', nargs="?", const=True)
|
||||
parser.add_argument('-w', '--worker', type=int, nargs="?", default=3 if os.cpu_count() > 3 else os.cpu_count())
|
||||
parser.add_argument('-w', '--worker', type=int, nargs="?",
|
||||
default=3 if os.cpu_count() > 6 else math.floor(os.cpu_count() / 2))
|
||||
parser.add_argument('-f', '--force', nargs="?", const=True)
|
||||
|
||||
def initial_util(self, *args, **options):
|
||||
|
|
|
|||
|
|
@ -102,3 +102,12 @@ def valid_license(model=None, count=None, message=None):
|
|||
return run
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
def bulk_create_in_batches(model, data, batch_size=1000):
|
||||
if len(data) == 0:
|
||||
return
|
||||
for i in range(0, len(data), batch_size):
|
||||
batch = data[i:i + batch_size]
|
||||
model.objects.bulk_create(batch)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from functools import reduce
|
|||
from typing import Dict, List
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from celery_once import AlreadyQueued, QueueOnce
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.core import validators
|
||||
from django.db import transaction, models
|
||||
|
|
@ -732,6 +733,7 @@ class DataSetSerializers(serializers.ModelSerializer):
|
|||
delete_embedding_by_dataset(self.data.get('id'))
|
||||
return True
|
||||
|
||||
@transaction.atomic
|
||||
def re_embedding(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
|
|
@ -743,7 +745,10 @@ class DataSetSerializers(serializers.ModelSerializer):
|
|||
State.PENDING)
|
||||
ListenerManagement.get_aggregation_document_status_by_dataset_id(self.data.get('id'))()
|
||||
embedding_model_id = get_embedding_model_id_by_dataset_id(self.data.get('id'))
|
||||
embedding_by_dataset.delay(self.data.get('id'), embedding_model_id)
|
||||
try:
|
||||
embedding_by_dataset.delay(self.data.get('id'), embedding_model_id)
|
||||
except AlreadyQueued as e:
|
||||
raise AppApiException(500, "向量化任务发送失败,请稍后再试!")
|
||||
|
||||
def list_application(self, with_valid=True):
|
||||
if with_valid:
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ from common.handle.impl.table.xls_parse_table_handle import XlsSplitHandle
|
|||
from common.handle.impl.table.xlsx_parse_table_handle import XlsxSplitHandle
|
||||
from common.handle.impl.text_split_handle import TextSplitHandle
|
||||
from common.mixins.api_mixin import ApiMixin
|
||||
from common.util.common import post, flat_map
|
||||
from common.util.common import post, flat_map, bulk_create_in_batches
|
||||
from common.util.field_message import ErrMessage
|
||||
from common.util.file_util import get_file_content
|
||||
from common.util.fork import Fork
|
||||
|
|
@ -301,6 +301,8 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
ListenerManagement.update_status(QuerySet(Paragraph).filter(document_id__in=document_id_list),
|
||||
TaskType.EMBEDDING,
|
||||
State.PENDING)
|
||||
ListenerManagement.get_aggregation_document_status_by_query_set(
|
||||
QuerySet(Document).filter(id__in=document_id_list))()
|
||||
embedding_by_document_list.delay(document_id_list, model_id)
|
||||
else:
|
||||
update_embedding_dataset_id(pid_list, target_dataset_id)
|
||||
|
|
@ -621,6 +623,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
_document.save()
|
||||
return self.one()
|
||||
|
||||
@transaction.atomic
|
||||
def refresh(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
|
|
@ -952,12 +955,11 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
# 插入文档
|
||||
QuerySet(Document).bulk_create(document_model_list) if len(document_model_list) > 0 else None
|
||||
# 批量插入段落
|
||||
QuerySet(Paragraph).bulk_create(paragraph_model_list) if len(paragraph_model_list) > 0 else None
|
||||
bulk_create_in_batches(Paragraph, paragraph_model_list, batch_size=1000)
|
||||
# 批量插入问题
|
||||
QuerySet(Problem).bulk_create(problem_model_list) if len(problem_model_list) > 0 else None
|
||||
bulk_create_in_batches(Problem, problem_model_list, batch_size=1000)
|
||||
# 批量插入关联问题
|
||||
QuerySet(ProblemParagraphMapping).bulk_create(problem_paragraph_mapping_list) if len(
|
||||
problem_paragraph_mapping_list) > 0 else None
|
||||
bulk_create_in_batches(ProblemParagraphMapping, problem_paragraph_mapping_list, batch_size=1000)
|
||||
# 查询文档
|
||||
query_set = QuerySet(model=Document)
|
||||
if len(document_model_list) == 0:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
:chat_record_id="answer_text.chat_record_id"
|
||||
:child_node="answer_text.child_node"
|
||||
:runtime_node_id="answer_text.runtime_node_id"
|
||||
:loading="loading"
|
||||
:disabled="loading || type == 'log'"
|
||||
v-else-if="answer_text.content"
|
||||
:source="answer_text.content"
|
||||
:send-message="chatMessage"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<LogoIcon v-else height="32px" width="32px" />
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-card shadow="always" class="dialog-card">
|
||||
<el-card shadow="always" class="dialog-card" style="--el-card-padding: 10px 16px 12px">
|
||||
<MdRenderer :source="prologue" :send-message="sendMessage"></MdRenderer>
|
||||
</el-card>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
:max-scale="7"
|
||||
:min-scale="0.2"
|
||||
:preview-src-list="getAttrsArray(image_list, 'url')"
|
||||
:initial-index="index"
|
||||
alt=""
|
||||
fit="cover"
|
||||
style="width: 170px; height: 170px; display: block"
|
||||
|
|
|
|||
|
|
@ -26,34 +26,6 @@
|
|||
.text {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.problem-button {
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: var(--app-layout-bg-color);
|
||||
height: 46px;
|
||||
padding: 0 12px;
|
||||
line-height: 46px;
|
||||
box-sizing: border-box;
|
||||
color: var(--el-text-color-regular);
|
||||
-webkit-line-clamp: 1;
|
||||
word-break: break-all;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
&:hover {
|
||||
background: var(--app-layout-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
&__operate {
|
||||
background: #f3f7f9;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,23 @@
|
|||
<template>
|
||||
<div class="radio_content" v-resize="resize" :style="radioContentStyle">
|
||||
<el-card
|
||||
v-for="item in option_list"
|
||||
:key="item.value"
|
||||
class="item"
|
||||
shadow="never"
|
||||
:class="[inputDisabled ? 'is-disabled' : '', modelValue == item[valueField] ? 'active' : '']"
|
||||
@click="inputDisabled ? () => {} : selected(item[valueField])"
|
||||
>
|
||||
{{ item[textField] }}
|
||||
</el-card>
|
||||
<div class="radio_content" :style="radioContentStyle">
|
||||
<el-row :gutter="12" class="w-full">
|
||||
<template v-for="(item,index) in option_list" :key="index">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<el-card
|
||||
:key="item.value"
|
||||
class="item"
|
||||
shadow="never"
|
||||
:class="[
|
||||
inputDisabled ? 'is-disabled' : '',
|
||||
modelValue == item[valueField] ? 'active' : ''
|
||||
]"
|
||||
@click="inputDisabled ? () => {} : selected(item[valueField])"
|
||||
>
|
||||
{{ item[textField] }}
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<DynamicsForm
|
||||
:disabled="is_submit"
|
||||
:disabled="is_submit || disabled"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
ref="dynamicsFormRef"
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
></DynamicsForm>
|
||||
<el-button
|
||||
:type="is_submit ? 'info' : 'primary'"
|
||||
:disabled="is_submit || loading"
|
||||
:disabled="is_submit || disabled"
|
||||
@click="submit"
|
||||
>提交</el-button
|
||||
>
|
||||
|
|
@ -24,14 +24,14 @@ import DynamicsForm from '@/components/dynamics-form/index.vue'
|
|||
const props = withDefaults(
|
||||
defineProps<{
|
||||
form_setting: string
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
sendMessage?: (question: string, type: 'old' | 'new', other_params_data?: any) => void
|
||||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
}>(),
|
||||
{
|
||||
loading: false
|
||||
disabled: false
|
||||
}
|
||||
)
|
||||
const form_setting_data = computed(() => {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div
|
||||
v-if="item.type === 'question'"
|
||||
@click="sendMessage ? sendMessage(item.content, 'new') : (content: string) => {}"
|
||||
class="problem-button ellipsis-2 mb-8"
|
||||
class="problem-button ellipsis-2 mt-4 mb-4"
|
||||
:class="sendMessage ? 'cursor' : 'disabled'"
|
||||
>
|
||||
<el-icon>
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
:chat_record_id="chat_record_id"
|
||||
:runtime_node_id="runtime_node_id"
|
||||
:child_node="child_node"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
:send-message="sendMessage"
|
||||
v-else-if="item.type === 'form_rander'"
|
||||
:form_setting="item.content"
|
||||
|
|
@ -70,11 +70,11 @@ const props = withDefaults(
|
|||
child_node?: any
|
||||
chat_record_id?: string
|
||||
runtime_node_id?: string
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{
|
||||
source: '',
|
||||
loading: false
|
||||
disabled: false
|
||||
}
|
||||
)
|
||||
const editorRef = ref()
|
||||
|
|
|
|||
|
|
@ -277,6 +277,10 @@ h5 {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.line-height-22 {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.border {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
|
|
@ -745,5 +749,5 @@ h5 {
|
|||
|
||||
//企业微信
|
||||
.wwLogin_qrcode_head {
|
||||
padding:20px 0 !important;
|
||||
}
|
||||
padding: 20px 0 !important;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ const selectUserId = ref('all')
|
|||
|
||||
const searchValue = ref('')
|
||||
|
||||
const apiInputParams = ref([])
|
||||
|
||||
function copyApplication(row: any) {
|
||||
application.asyncGetApplicationDetail(row.id, loading).then((res: any) => {
|
||||
CopyApplicationDialogRef.value.open({ ...res.data, model_id: res.data.model })
|
||||
|
|
@ -234,9 +236,44 @@ function searchHandle() {
|
|||
paginationConfig.total = 0
|
||||
getList()
|
||||
}
|
||||
|
||||
function mapToUrlParams(map: any[]) {
|
||||
const params = new URLSearchParams()
|
||||
|
||||
map.forEach((item: any) => {
|
||||
params.append(encodeURIComponent(item.name), encodeURIComponent(item.value))
|
||||
})
|
||||
|
||||
return params.toString() // 返回 URL 查询字符串
|
||||
}
|
||||
|
||||
function getAccessToken(id: string) {
|
||||
applicationList.value.filter((app)=>app.id === id)[0]?.work_flow?.nodes
|
||||
?.filter((v: any) => v.id === 'base-node')
|
||||
.map((v: any) => {
|
||||
apiInputParams.value = v.properties.api_input_field_list
|
||||
? v.properties.api_input_field_list
|
||||
.map((v: any) => {
|
||||
return {
|
||||
name: v.variable,
|
||||
value: v.default_value
|
||||
}
|
||||
})
|
||||
: v.properties.input_field_list
|
||||
? v.properties.input_field_list
|
||||
.filter((v: any) => v.assignment_method === 'api_input')
|
||||
.map((v: any) => {
|
||||
return {
|
||||
name: v.variable,
|
||||
value: v.default_value
|
||||
}
|
||||
})
|
||||
: []
|
||||
})
|
||||
|
||||
const apiParams = mapToUrlParams(apiInputParams.value) ? '?' + mapToUrlParams(apiInputParams.value) : ''
|
||||
application.asyncGetAccessToken(id, loading).then((res: any) => {
|
||||
window.open(application.location + res?.data?.access_token)
|
||||
window.open(application.location + res?.data?.access_token + apiParams)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<template>
|
||||
<el-popover v-model:visible="visible" placement="top" :width="450" trigger="hover">
|
||||
<el-popover
|
||||
v-model:visible="visible"
|
||||
placement="top"
|
||||
trigger="hover"
|
||||
:popper-style="{ width: 'auto' }"
|
||||
>
|
||||
<template #default
|
||||
><StatusTable
|
||||
v-if="visible"
|
||||
|
|
@ -43,18 +48,21 @@ const checkList: Array<string> = [
|
|||
State.REVOKE,
|
||||
State.STARTED,
|
||||
State.PENDING,
|
||||
State.REVOKED,
|
||||
State.FAILURE,
|
||||
State.REVOKED,
|
||||
State.SUCCESS
|
||||
]
|
||||
const aggStatus = computed(() => {
|
||||
let obj = { key: 0, value: '' }
|
||||
for (const i in checkList) {
|
||||
const state = checkList[i]
|
||||
const index = props.status.indexOf(state)
|
||||
if (index > -1) {
|
||||
return { key: props.status.length - index, value: state }
|
||||
obj = { key: props.status.length - index, value: state }
|
||||
break
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
const startedMap = {
|
||||
[TaskType.EMBEDDING]: '索引中',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-for="status in statusTable" :key="status.type">
|
||||
<div v-for="status in statusTable" :key="status.type" >
|
||||
<span> {{ taskTypeMap[status.type] }}:</span>
|
||||
<span>
|
||||
<el-text v-if="status.state === State.SUCCESS || status.state === State.REVOKED">
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
Object.values(status.aggs ? status.aggs : {}).reduce((x: any, y: any) => x + y, 0)
|
||||
}}</span
|
||||
>
|
||||
<el-text type="info" class="ml-4">
|
||||
<el-text type="info" class="ml-12">
|
||||
{{
|
||||
status.time
|
||||
? status.time[status.state == State.REVOKED ? State.REVOKED : State.PENDING]?.substring(
|
||||
|
|
|
|||
|
|
@ -252,11 +252,6 @@ onMounted(() => {
|
|||
}
|
||||
})
|
||||
})
|
||||
onBeforeMount(() => {
|
||||
if (user.isEnterprise()) {
|
||||
user.theme(loading)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scope>
|
||||
.login-gradient-divider {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const validate = () => {
|
|||
return Promise.resolve('')
|
||||
}
|
||||
props.nodeModel.graphModel.eventCenter.on('refresh_incoming_node_field', () => {
|
||||
getIncomingNode(props.nodeModel.id)
|
||||
options.value = getIncomingNode(props.nodeModel.id)
|
||||
})
|
||||
defineExpose({ validate })
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
<div class="flex align-center">
|
||||
<img class="mr-12" src="@/assets/icon_file-doc.svg" alt="" />
|
||||
<div>
|
||||
<p>文档(TXT、MD、DOCX、HTML、CSV、XLSX、XLS、PDF)</p>
|
||||
<p class="line-height-22 mt-4">文档(TXT、MD、DOCX、HTML、CSV、XLSX、XLS、PDF)</p>
|
||||
<el-text class="color-secondary">需要使用“文档内容提取”节点解析文档内容</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
<div class="flex align-center">
|
||||
<img class="mr-12" src="@/assets/icon_file-image.svg" alt="" />
|
||||
<div>
|
||||
<p>图片(JPG、JPEG、PNG、GIF)</p>
|
||||
<p class="line-height-22 mt-4">图片(JPG、JPEG、PNG、GIF)</p>
|
||||
<el-text class="color-secondary">需要使用“图片理解”节点解析图片内容</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue