yinbangzhong
2024-08-29 30c1eeca00527a1294c62b1c708edd32ba079b67
src/views/dmx/knowledgeLib/config.vue
@@ -1,136 +1,95 @@
<template>
  <div class="main-container">
  <div ref="scrollContainer" class="main-container">
    <div style="position: absolute;top: 0;left: 0;width: 100%;padding: 0 20px">
      <h4 style="margin-bottom: 10px">配置</h4>
      <h4 style="margin-bottom: 10px"></h4>
      <div style="color: #666666;">在这里更新您的知识库详细信息,尤其是解析方法。</div>
      <a-divider style="margin-top: 20px" />
    </div>
    <div class="main-container-lf">
      <div class="main-container-lf-down">
        <a-form ref="formRef" :size="form.size" :model="form" :style="{width:'100%'}" layout="vertical"
        <a-form ref="formRef" :rules="rules" :model="form" :style="{width:'100%'}" layout="vertical"
                @submit="handleSubmit">
          <a-form-item field="name" label="知识库名称"
                       :rules="[{required:true,message:'name is required'},{minLength:5,message:'must be greater than 5 characters'}]"
                       :validate-trigger="['change','input']"
          >
          <a-form-item field="name" label="知识库名称">
            <a-input v-model="form.name" placeholder="请输入" />
          </a-form-item>
          <a-form-item field="section" label="知识库图片">
            <a-space direction="vertical" :style="{ width: '100%' }">
              <a-upload
                action="/"
                :fileList="file ? [file] : []"
                :show-file-list="false"
                @change="onChange"
                @progress="onProgress"
              >
                <template #upload-button>
                  <div
                    :class="`arco-upload-list-item${
            file && file.status === 'error' ? ' arco-upload-list-item-error' : ''
          }`"
                  >
                    <div
                      class="arco-upload-list-picture custom-upload-avatar"
                      v-if="file && file.url"
                    >
                      <img :src="file.url" />
                      <div class="arco-upload-list-picture-mask">
                        <IconEdit />
                      </div>
                      <a-progress
                        v-if="file.status === 'uploading' && file.percent < 100"
                        :percent="file.percent"
                        type="circle"
                        size="mini"
                        :style="{
                position: 'absolute',
                left: '50%',
                top: '50%',
                transform: 'translateX(-50%) translateY(-50%)',
              }"
                      />
                    </div>
                    <div class="arco-upload-picture-card" v-else>
                      <div class="arco-upload-picture-card-text">
                        <IconPlus />
                        <div style="margin-top: 10px; font-weight: 600">上传</div>
                      </div>
                    </div>
                  </div>
                </template>
              </a-upload>
              <Upload
                v-if="avatarShow"
                :action="uploadAction"
                :limit="1"
                :url="form.avatar ? httpUrl + form.avatar : ''"
                @update:fileList="updateFileList"
                @success="handleSuccess"
              ></Upload>
            </a-space>
          </a-form-item>
          <a-form-item field="section" label="描述">
            <a-textarea placeholder="" allow-clear />
            <a-textarea placeholder=""  v-model="form.description" allow-clear />
          </a-form-item>
          <a-form-item field="section" label="语言" :rules="[{match:/section one/,message:'请选择'}]">
            <a-select v-model="form.section" placeholder="请选择" allow-clear>
              <a-option value="0">中文</a-option>
              <a-option value="1">英文</a-option>
<!--          <a-form-item field="section" label="语言" :rules="[{match:/section one/,message:'请选择'}]">-->
<!--            <a-select v-model="form.section" placeholder="请选择" allow-clear>-->
<!--              <a-option value="0">中文</a-option>-->
<!--              <a-option value="1">英文</a-option>-->
<!--            </a-select>-->
<!--          </a-form-item>-->
          <a-form-item field="embd_id" label="嵌入模型">
            <a-select :size="'large'" v-model="form.embd_id" placeholder="请选择 ..." :disabled="kbObj.chunk_num && kbObj.token_num">
              <!-- <a-optgroup  :label="index" v-for="(item,index) in mobileData" :key=index>
                <a-option
                  v-for="(obj) in item"
                  :key='obj.fid'
                  :value="obj.llm_name"
                >
                  {{obj.llm_name}}
                </a-option>
              </a-optgroup> -->
              <a-option
                  v-for="(item,index) in mobileData"
                  :key='item.name'
                  :value="item.name"
                >
                  {{item.name}}
                </a-option>
            </a-select>
          </a-form-item>
          <a-form-item field="section" label="嵌入模型" :rules="[{match:/section one/,message:'请选择'}]">
            <a-select v-model="form.section" placeholder="请选择" allow-clear>
              <a-option value="section one">Section One</a-option>
              <a-option value="section two">Section Two</a-option>
              <a-option value="section three">Section Three</a-option>
          <a-form-item field="parser_id" label="解析方法">
            <a-select v-model="form.parser_id" placeholder="请选择" :disabled="!!(kbObj.chunk_num  && kbObj.token_num)" >
              <a-option v-for="item in parser_ids" :key="item.value" :label="item.name" :value="item.value"></a-option>
            </a-select>
          </a-form-item>
          <a-form-item field="section" label="解析方法" :rules="[{match:/section one/,message:'请选择'}]">
            <a-select v-model="form.section" placeholder="请选择" allow-clear>
              <a-option value="section one">Section One</a-option>
              <a-option value="section two">Section Two</a-option>
              <a-option value="section three">Section Three</a-option>
            </a-select>
          <a-form-item field="slider" label="块Token数">
            <a-slider v-model="form.chunk_token_num" :min="0" :max="2048"  show-input />
          </a-form-item>
          <a-form-item field="slider" label="块Token数"
                       :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
            <a-slider v-model="form.score" :max="10" />
            <a-input-number v-model="form.score" :style="{width:'100px',marginLeft:'2rem',borderRadius:'4px'}"
                            placeholder="Please Enter" class="input-demo" :min="10" :max="1000" />
          </a-form-item>
          <a-form-item field="slider" label="布局识别" :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
            <a-space direction="vertical" size="large">
              <a-switch v-model="form.score" checked-value="yes" unchecked-value="no" />
          <a-form-item  label="布局识别" >
            <a-space direction="vertical">
              <a-switch v-model="form.layout_recognize" size="small" />
            </a-space>
          </a-form-item>
          <a-form-item field="raptor" label="使用召回增强RAPTOR策略">
            <a-space direction="vertical" size="large">
              <a-switch v-model="form.raptor" @change="onChangeRAPTOR" />
            </a-space>
            <a-switch v-model="form.use_raptor" size="small" />
          </a-form-item>
          <div v-if="form.raptor">
            <a-form-item field="section" label="提示词">
              <a-textarea style="height: 10rem;border: 1px solid  var(--color-fill-3);border-radius: 4px" placeholder=""
                          allow-clear />
          <div v-if="form.use_raptor">
            <a-form-item field="prompt" label="提示词">
              <a-textarea
                v-model="form.prompt"
                style="height: 10rem;border: 1px solid  var(--color-fill-3);border-radius: 4px" placeholder="请填写提示词"  />
            </a-form-item>
            <a-form-item field="slider" label="最大token数"
                         :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
              <a-slider v-model="form.score" :max="10" />
              <a-input-number v-model="form.score" :style="{width:'100px',marginLeft:'2rem',borderRadius:'4px'}"
                              placeholder="Please Enter" class="input-demo" :min="10" :max="1000" />
            <a-form-item field="slider" label="最大token数">
              <a-slider v-model="form.max_token" :min="0" :max="2048" show-input/>
            </a-form-item>
            <a-form-item field="slider" label="阈值" :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
              <a-slider v-model="form.score" :max="10" />
              <a-input-number v-model="form.score" :style="{width:'100px',marginLeft:'2rem',borderRadius:'4px'}"
                              placeholder="Please Enter" class="input-demo" :min="10" :max="1000" />
            <a-form-item field="slider" label="阈值" >
              <a-slider v-model="form.threshold"  :step="0.01" :min="0" :max="1"  show-input/>
            </a-form-item>
            <a-form-item field="slider" label="最大聚类数"
                         :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
              <a-slider v-model="form.score" :max="10" />
              <a-input-number v-model="form.score" :style="{width:'100px',marginLeft:'2rem',borderRadius:'4px'}"
                              placeholder="Please Enter" class="input-demo" :min="10" :max="1000" />
            <a-form-item field="slider" label="最大聚类数">
              <a-slider v-model="form.max_cluster" :min="1" :max="1024" show-input/>
            </a-form-item>
            <a-form-item field="slider" label="随机种子"
                         :rules="[{type:'number', min:5,message:'slider is min than 5'}]">
              <a-input-number v-model="form.score" :style="{width:'300px',marginRight:'1rem',borderRadius:'4px'}"
            <a-form-item field="slider" label="随机种子" v-model="form.random_seed">
              <a-input-number v-model="form.random_seed" :style="{width:'300px',marginRight:'1rem',borderRadius:'4px'}"
                              placeholder="请输入" class="input-demo" :min="10" :max="100" />
              <a-button type="primary" @click="">
              <a-button type="primary" @click="randomNumber">
                <icon-plus />
              </a-button>
            </a-form-item>
          </div>
@@ -139,8 +98,8 @@
          <a-form-item>
            <div style="width: 100%;text-align: right">
              <a-space>
                <a-button type="primary" html-type="submit">保存</a-button>
                <!--               <a-button @click="$refs.formRef.resetFields()">Reset</a-button>-->
                <a-button @click="cancelConfig">取消</a-button>
                <a-button :loading="loading" type="primary" html-type="submit">保存</a-button>
              </a-space>
            </div>
          </a-form-item>
@@ -150,7 +109,7 @@
    <div class="main-container-rt">
      <section data-inspector-line="34" data-inspector-column="4"
               data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx"
               class="categoryPanelWrapper___yI4rY" ><h4
               class="categoryPanelWrapper___yI4rY"><h4
        class="ant-typography topTitle___tNiB9 css-dev-only-do-not-override-1wxecgq" data-inspector-line="37"
        data-inspector-column="10"
        data-inspector-relative-path="src\pages\add-knowledge\components\knowledge-setting\category-panel.tsx">"General"
@@ -198,120 +157,156 @@
</template>
<script lang="ts" setup>
import { onMounted, onBeforeMount, reactive, ref } from "vue";
import { onMounted, onBeforeMount, reactive, ref, nextTick } from "vue";
import { Message } from "@arco-design/web-vue";
const props = defineProps({
  record: {
    type: Object,
    default: () => {
      return {};
    }
  }
import { queryModelList,kbUpdate,getKnowledgeBaseModel } from "@/api/kbList";
import message from "@arco-design/web-vue/es/message";
import useLoading from "@/hooks/loading";
const { loading,setLoading } = useLoading(true);
const scrollContainer = ref();
const props =  defineProps(['kbtenantInfo'])
const emit =  defineEmits(['cancleConfig','saveConfig'])
// 解析方法列表
let parser_ids = props.kbtenantInfo.parser_ids.split(",").map((item) => {
  const [value1, value2] = item.split(":");
  return {
    name: value2,
    value: value1
  };
});
const visible = ref(false);
const configIsShow = ref(false);
const loading = ref(false);
const formRef = ref(null);
let kbObj = reactive({});
let avatarShow = ref(true);
const form = reactive({
  size: "medium",
  name: "",
  age: undefined,
  section: "0",
  province: "haidian",
  options: [],
  date: "",
  time: "",
  radio: "radio one",
  slider: 5,
  score: 5,
  switch: false,
  multiSelect: ["section one"],
  treeSelect: "",
  raptor: false
  name: '',
  avatar: '',//头像
  max_token: 600,
  threshold: 0.32,
  max_cluster: 233,
  random_seed: 1500,
  chunk_token_num: 384,
  use_raptor: false,
  pages: {},
  prompt: "请总结以下段落。 小心数字,不要编造。 段落如下:\n" +
    "      {cluster_content}\n" +
    "以上就是你需要总结的内容。",
  description: "",
  language: "English",
  permission: "",
  embd_id: "",
  parser_id: '',
  layout_recognize: true
});
const options = [
  {
    value: "beijing",
    label: "Beijing",
    children: [
      {
        value: "chaoyang",
        label: "ChaoYang",
        children: [
          {
            value: "datunli",
            label: "Datunli"
          }
        ]
      },
      {
        value: "haidian",
        label: "Haidian"
      },
      {
        value: "dongcheng",
        label: "Dongcheng"
      },
      {
        value: "xicheng",
        label: "XiCheng"
      }
    ]
  },
  {
    value: "shanghai",
    label: "Shanghai",
    children: [
      {
        value: "shanghaishi",
        label: "Shanghai",
        children: [
          {
            value: "huangpu",
            label: "Huangpu"
          }
        ]
      }
    ]
  }
];
const sayHello = () => {
  Message.info("hello");
};
const rules = {
  name: [
    {
      required: true,
      message:'知识库名称不允许为空',
    },
  ],
  embd_id: [
    {
      required: true,
      message:'请选择',
    },
  ],
  parser_id: [
    {
      required: true,
      message:'请选择',
    },
  ],
  prompt: [
    {
      required: true,
      message:'提示词不允许为空',
    },
  ],
}
let visible = ref(false);
const formRef = ref();
const modelList = ref({});
const mobileData= ref<any>([])
const file = ref();
const onChange = (_, currentFile) => {
  file.value = {
    ...currentFile
    // url: URL.createObjectURL(currentFile.file),
  };
const uploadAction = '/api/v1/llm/upload'; // 替换为你的上传API
const fileList = ref([]);
const imageUrls = ref([]);
const httpUrl = localStorage.getItem('httpUrl');
const updateFileList = (newFileList) => {
  fileList.value = newFileList;
  console.log(newFileList, 88);
};
const onProgress = (currentFile) => {
  file.value = currentFile;
const handleSuccess = (urls) => {
  imageUrls.value = urls; // 拿到上传的图片地址
  form.avatar = urls[0];
};
const handleSubmit = ({ values, errors }) => {
  console.log("values:", values, "\nerrors:", errors);
const handleSubmit = async ({ values, errors }) => {
  if (errors){
    return;
  }
  let parms = {
      name: form.name,
      avatar: form.avatar,
      description: form.description,
      language: form.language,
      permission: form.permission,
      embd_id: form.embd_id,
      parser_id: form.parser_id,
      parser_config: {
        chunk_token_num: form.chunk_token_num,
        layout_recognize: form.layout_recognize,
      },
      kb_id: kbObj.id,
   }
   if(form.use_raptor){
     parms.parser_config.raptor = {
       use_raptor: form.use_raptor,
       prompt: form.prompt,
       max_token: form.max_token,
       threshold: form.threshold,
       max_cluster: form.max_cluster,
       random_seed: form.random_seed,
     };
   }
   try {
     setLoading(true)
    const data = await kbUpdate(parms);
    if(data.code == '0'){
      Message.success("配置成功");
      // window.location.reload();
      emit('saveConfig')
    }
    setLoading(false)
  } catch (err) {
    Message.error(err.message);
     setLoading(false)
  }
};
const onChangeRAPTOR = () => {
  console.log(form.raptor);
};
const cancelConfig = () => {
    emit('cancleConfig')
}
const handleClick = () => {
  visible.value = true;
};
function randomNumber() {
  // 生成一个介于min和max之间的随机整数(包含min和max)
  const min = 1; // 最小值
  const max = 10000; // 最大值
  form.random_seed = Math.floor(Math.random() * (max - min + 1)) + min;
}
const handleBeforeOk = (done) => {
  formRef.value.validate().then(res => {
    console.log("form:", form);
@@ -323,31 +318,92 @@
    }
  });
};
const handleCancel = () => {
  visible.value = false;
};
const handleOpened =(el) => {
  Object.assign(form,{
const handleOpened = (el) => {
  Object.assign(form, {
    name: "",// 用户名
    nameJoin: "",// 昵称
    post: "",// 岗位
    txt: ""// 备注
  });
  formRef.value.resetFields();
  console.log(props.record, "record");
  // if(props.edit == 'edit'){
  //   Object.assign(form,props.record);
  // }
  // formRef.value.resetFields();
}
const queryModel = async (params) => {
  try {
    const data = await getKnowledgeBaseModel(params);
    // const res =await queryModelList(params)
    // console.log(data.data, '大模型列表');
    modelList.value = data.data
    for(let key in data.data) {
      if (data.data.hasOwnProperty(key)) { // 确保是对象自有属性,不是原型链上的属性
        if(data.data[key].llm.length > 0){
          mobileData.value= data.data[key].llm.filter(item => item.type == "embedding")
        }
      }
    }
  } catch (err) {
    // you can report use errorHandler or other
  } finally {
  }
};
onBeforeMount(() => {
const changekbObj = (value) => {
   formRef.value.resetFields();
  nextTick(()=>{
    kbObj = value;
    console.log(kbObj, 'kbObj');
    let configObj = {
      name: kbObj.name,
      avatar: kbObj.avatar,
      pages:  kbObj.parser_config.pages,
      chunk_token_num:  kbObj.parser_config.chunk_token_num,
      description: kbObj.description,
      language: "English",
      permission: kbObj.permission,
      embd_id: kbObj.embd_id || props.kbtenantInfo.embd_id,
      parser_id: kbObj.parser_id,
      layout_recognize: true,
    }
    let raptor = {}
    if(kbObj.parser_config && kbObj.parser_config.raptor){
      raptor = kbObj.parser_config.raptor;
      Object.assign(form,{
        ...configObj,
        ...raptor,
        use_raptor: kbObj.parser_config.raptor.use_raptor,
      });
    }else{
      Object.assign(form,{
        ...configObj,
        use_raptor: false,
      });
    }
    imageUrls.value = [kbObj.avatar];
    console.log(form,'配置页面表单信息');
    avatarShow.value = false;
    setTimeout(() => {
      avatarShow.value = true;
    },100);
    scrollContainer.value.scrollTop = 0;
  })
}
});
onMounted(() => {
defineExpose({
  changekbObj
})
});
onBeforeMount(()=>{
  setLoading(false)
  queryModel({})
})
onMounted(()=>{
})
</script>
<script lang="ts">
@@ -366,15 +422,16 @@
  justify-content: space-between;
  height: 100%;
  overflow: auto;
  //background: #626aea;
  background: #ffffff;
  border-radius: 8px;
  background: var(--color-bg-2);
  padding-top: 80px;
  &-lf {
    width: 40%;
    //height: 100%;
    //border: 1px solid #cccccc;
    border-radius: 10px;
    //border: 1px solid var(--color-fill-3);
    &-top {
      width: 100%;
      height: 60px;
@@ -427,9 +484,10 @@
    border-radius: 10px;
    overflow: hidden;
    padding: 20px;
    section{
    section {
      width: 100%;
      color: #333333;
      //color: #333333;
      color: var(--color-text-1);
    }
  }
}