Bläddra i källkod

add function calls

tycoding 1 år sedan
förälder
incheckning
bc2c0a6f7c

+ 12 - 0
langchat-aigc/src/main/java/cn/tycoding/langchat/aigc/controller/AigcPromptController.java

@@ -13,6 +13,7 @@ import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Date;
+import java.util.List;
 
 /**
  * @author tycoding
@@ -39,6 +40,17 @@ public class AigcPromptController {
         return R.ok(MybatisUtil.getData(iPage));
     }
 
+    @GetMapping("/list")
+    public R list(AigcPrompt data) {
+        List<AigcPrompt> list = aigcPromptService.list();
+        list.forEach(i -> {
+            if (i.getPrompt() != null && i.getPrompt().length() >= 30) {
+                i.setPrompt(StrUtil.sub(i.getPrompt(), 0, 30) + "...");
+            }
+        });
+        return R.ok(list);
+    }
+
     @GetMapping("/{id}")
     public R getById(@PathVariable String id) {
         return R.ok(aigcPromptService.getById(id));

+ 5 - 0
langchat-aigc/src/main/java/cn/tycoding/langchat/aigc/entity/AigcKnowledge.java

@@ -38,6 +38,11 @@ public class AigcKnowledge implements Serializable {
      */
     private String des;
 
+    /**
+     * 是否是结构化数据
+     */
+    private Boolean isStruct = false;
+
     /**
      * 创建时间
      */

+ 8 - 0
langchat-core/src/main/java/cn/tycoding/langchat/core/service/LangDocService.java

@@ -26,6 +26,14 @@ public interface LangDocService {
      */
     void embeddingStruct(DocR req);
 
+    /**
+     * 文本向量搜索
+     */
     TokenStream search(DocR req);
 
+    /**
+     * 结构化数据搜索
+     */
+    TokenStream searchStruct(DocR req);
+
 }

+ 17 - 0
langchat-core/src/main/java/cn/tycoding/langchat/core/service/impl/LangDocServiceImpl.java

@@ -106,6 +106,23 @@ public class LangDocServiceImpl implements LangDocService {
                 .dynamicFilter(filterByUserId)
                 .build();
 
+        Assistant assistant = AiServices.builder(Assistant.class)
+                .streamingChatLanguageModel(chatLanguageModel)
+                .contentRetriever(contentRetriever)
+                .build();
+
+        return assistant.chat(req.getPrompt().toUserMessage());
+    }
+
+    @Override
+    public TokenStream searchStruct(DocR req) {
+        StreamingChatLanguageModel chatLanguageModel = modelProvider.stream(ModelConst.OPENAI);
+        EmbeddingModel model = provider.embed();
+
+        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
+                .embeddingModel(model)
+                .build();
+
         Assistant assistant = AiServices.builder(Assistant.class)
                 .streamingChatLanguageModel(chatLanguageModel)
                 .contentRetriever(contentRetriever)

+ 5 - 4
langchat-core/src/main/java/cn/tycoding/langchat/core/tools/StructTools.java

@@ -1,5 +1,6 @@
 package cn.tycoding.langchat.core.tools;
 
+import cn.hutool.core.util.StrUtil;
 import cn.tycoding.langchat.common.dto.DocR;
 import cn.tycoding.langchat.core.entity.AigcStructCol;
 import cn.tycoding.langchat.core.entity.AigcStructRow;
@@ -31,8 +32,8 @@ public class StructTools {
     @Tool("Gets column name data in Data")
     List<String> getCols() {
         List<AigcStructCol> list = structColService.list(Wrappers.<AigcStructCol>lambdaQuery()
-                .eq(AigcStructCol::getKnowledgeId, req.getKnowledgeId())
-                .eq(AigcStructCol::getDocsId, req.getDocsId())
+                .eq(StrUtil.isNotBlank(req.getKnowledgeId()), AigcStructCol::getKnowledgeId, req.getKnowledgeId())
+                .eq(StrUtil.isNotBlank(req.getDocsId()), AigcStructCol::getDocsId, req.getDocsId())
         );
         return list.stream().map(AigcStructCol::getLabel).toList();
     }
@@ -40,8 +41,8 @@ public class StructTools {
     @Tool("Gets all the data for a column")
     List<String> getColData(int col) {
         List<AigcStructRow> list = structRowService.list(Wrappers.<AigcStructRow>lambdaQuery()
-                .eq(AigcStructRow::getKnowledgeId, req.getKnowledgeId())
-                .eq(AigcStructRow::getDocsId, req.getDocsId())
+                .eq(StrUtil.isNotBlank(req.getKnowledgeId()), AigcStructRow::getKnowledgeId, req.getKnowledgeId())
+                .eq(StrUtil.isNotBlank(req.getDocsId()), AigcStructRow::getDocsId, req.getDocsId())
                 .eq(AigcStructRow::getColIndex, col)
         );
         return list.stream().map(AigcStructRow::getValue).toList();

BIN
langchat-ui/src/assets/images/knowledge.png


+ 28 - 26
langchat-ui/src/components/CheckCard/CheckCard.vue

@@ -1,32 +1,33 @@
 <template>
   <n-space :justify="justify">
-    <div
-      @click="handleChecked(item)"
-      v-for="item in dataSource"
-      :key="item.key"
-      style="height: 96px"
-    >
-      <n-list :class="checked == item.key ? 'check-list-checked' : ''" class="check-list" bordered>
-        <n-list-item>
-          <template #prefix>
-            <n-avatar>
-              <n-icon>
-                <component :is="item.icon" />
+    <template v-for="item in dataSource" :key="item.key">
+      <div v-if="!item.isHidden" @click="handleChecked(item)" style="height: 96px">
+        <n-list
+          :class="checked == item.key ? 'check-list-checked' : ''"
+          class="check-list"
+          bordered
+        >
+          <n-list-item>
+            <template #prefix>
+              <n-avatar>
+                <n-icon>
+                  <component :is="item.icon" />
+                </n-icon>
+              </n-avatar>
+            </template>
+            <n-thing>
+              <template #header>{{ item.title }}</template>
+              <template #description>{{ item.label }}</template>
+            </n-thing>
+            <template #suffix>
+              <n-icon size="20" :color="checked == item.key ? '#18a058' : '#eee'">
+                <CheckmarkCircle />
               </n-icon>
-            </n-avatar>
-          </template>
-          <n-thing>
-            <template #header>{{ item.title }}</template>
-            <template #description>{{ item.label }}</template>
-          </n-thing>
-          <template #suffix>
-            <n-icon size="20" :color="checked == item.key ? '#18a058' : '#eee'">
-              <CheckmarkCircle />
-            </n-icon>
-          </template>
-        </n-list-item>
-      </n-list>
-    </div>
+            </template>
+          </n-list-item>
+        </n-list>
+      </div>
+    </template>
   </n-space>
 </template>
 <script lang="ts">
@@ -38,6 +39,7 @@
     icon: Component;
     title: string;
     label: string;
+    isHidden?: boolean | undefined;
   };
 
   const props = {

+ 94 - 68
langchat-ui/src/views/aigc/chat/index.vue

@@ -1,86 +1,108 @@
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { onMounted, ref } from 'vue';
   import SvgIcon from '@/components/SvgIcon/index.vue';
   import Chat from './components/Chat.vue';
+  import knowledgePng from '@/assets/images/knowledge.png';
+  import { list as getKnowledgeList } from '@/api/aigc/knowledge';
+  import { list as getPromptList } from '@/api/aigc/prompt';
 
   const checked = ref();
-  const radios = [
+  const list = ref();
+  const value = ref();
+  const loading = ref(true);
+  onMounted(async () => {
+    await onUpdate('knowledge');
+  });
+  const options = [
     {
-      name: 'Paypal',
-      description: "It's the faster, safer way to send and receive money.",
-      icon: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-                <path d="M7.60676 23.1864L8.02271 20.5444L7.09617 20.5229H2.67188L5.74654 1.02757C5.75608 0.968712 5.7871 0.913836 5.83243 0.874866C5.87776 0.835896 5.93582 0.814423 5.99626 0.814423H13.4562C15.9328 0.814423 17.642 1.32978 18.5343 2.34698C18.9526 2.82417 19.219 3.32282 19.3479 3.87159C19.4831 4.44739 19.4855 5.13533 19.3535 5.97438L19.3439 6.03562V6.57325L19.7622 6.81025C20.1146 6.99715 20.3945 7.21108 20.6092 7.45604C20.9671 7.86403 21.1986 8.38257 21.2964 8.99734C21.3974 9.62961 21.364 10.382 21.1986 11.2338C21.0077 12.2136 20.6991 13.0669 20.2824 13.7652C19.899 14.4086 19.4107 14.9423 18.8309 15.3558C18.2774 15.7487 17.6197 16.047 16.8761 16.2378C16.1555 16.4255 15.334 16.5202 14.4329 16.5202H13.8523C13.4372 16.5202 13.0339 16.6697 12.7174 16.9377C12.4001 17.2113 12.1901 17.5851 12.1257 17.9939L12.082 18.2317L11.3471 22.8882L11.3137 23.0592C11.3049 23.1133 11.2898 23.1403 11.2676 23.1586C11.2477 23.1753 11.219 23.1864 11.1912 23.1864H7.60676Z" fill="#253B80" />
-                <path d="M20.1586 6.09761C20.1364 6.23997 20.1109 6.38551 20.0823 6.53503C19.0985 11.586 15.7327 13.3309 11.4341 13.3309H9.24541C8.71971 13.3309 8.27673 13.7127 8.19481 14.2312L7.07422 21.3381L6.75689 23.3526C6.70361 23.693 6.96606 24 7.30963 24H11.1915C11.6512 24 12.0417 23.666 12.1141 23.2126L12.1523 23.0154L12.8831 18.3772L12.9301 18.1227C13.0016 17.6678 13.3929 17.3337 13.8526 17.3337H14.4332C18.1942 17.3337 21.1384 15.8067 21.999 11.388C22.3584 9.54209 22.1723 8.00078 21.2212 6.91678C20.9333 6.58991 20.5762 6.31871 20.1586 6.09761Z" fill="#179BD7" />
-                <path d="M19.13 5.68728C18.9797 5.64354 18.8246 5.60378 18.6655 5.56799C18.5057 5.53299 18.3419 5.50198 18.1732 5.47494C17.5831 5.3795 16.9365 5.33417 16.2438 5.33417H10.3967C10.2528 5.33417 10.116 5.36678 9.9935 5.42563C9.72389 5.55526 9.52348 5.81056 9.47496 6.12311L8.2311 14.0014L8.19531 14.2313C8.27723 13.7127 8.72022 13.331 9.24591 13.331H11.4346C15.7332 13.331 19.099 11.5853 20.0828 6.53508C20.1122 6.38556 20.1369 6.24002 20.1591 6.09766C19.9102 5.96564 19.6406 5.85271 19.3503 5.75648C19.2787 5.73262 19.2048 5.70955 19.13 5.68728Z" fill="#222D65" />
-                <path d="M9.47421 6.12308C9.52272 5.81052 9.72314 5.55523 9.99275 5.42639C10.116 5.36753 10.252 5.33493 10.396 5.33493H16.2431C16.9358 5.33493 17.5824 5.38026 18.1725 5.4757C18.3411 5.50274 18.5049 5.53375 18.6648 5.56875C18.8238 5.60453 18.9789 5.6443 19.1292 5.68804C19.204 5.71031 19.278 5.73337 19.3503 5.75644C19.6406 5.85267 19.9102 5.9664 20.1592 6.09763C20.4518 4.23104 20.1568 2.96014 19.1475 1.80933C18.0349 0.5424 16.0267 0 13.4571 0H5.99712C5.47222 0 5.02446 0.381748 4.94334 0.901084L1.83607 20.5969C1.77483 20.9866 2.07546 21.3381 2.46834 21.3381H7.07397L8.23034 14.0014L9.47421 6.12308Z" fill="#253B80" />
-            </svg>`,
+      label: 'Drive My Car',
+      value: 'Drive My Car',
     },
     {
-      name: 'Master Card',
-      description: ' payment-processing corporation worldwide.',
-      icon: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-                <path d="M15.2436 6.17905H8.75391V17.8412H15.2436V6.17905Z" fill="#FF5F00" />
-                <path d="M9.16737 12.0105C9.16635 10.8874 9.42086 9.77873 9.91165 8.76848C10.4024 7.75824 11.1166 6.87289 12.0002 6.17946C10.906 5.31945 9.59201 4.78462 8.20829 4.63611C6.82457 4.48759 5.42699 4.73138 4.17527 5.33961C2.92356 5.94784 1.86822 6.89597 1.12988 8.07562C0.391546 9.25528 0 10.6189 0 12.0105C0 13.4022 0.391546 14.7658 1.12988 15.9455C1.86822 17.1251 2.92356 18.0732 4.17527 18.6815C5.42699 19.2897 6.82457 19.5335 8.20829 19.385C9.59201 19.2365 10.906 18.7016 12.0002 17.8416C11.1166 17.1482 10.4024 16.2628 9.91165 15.2526C9.42087 14.2423 9.16635 13.1337 9.16737 12.0105Z" fill="#EB001B" />
-                <path d="M23.9998 12.0105C23.9998 13.4022 23.6083 14.7658 22.87 15.9454C22.1317 17.1251 21.0764 18.0732 19.8247 18.6814C18.5731 19.2897 17.1755 19.5335 15.7918 19.385C14.4081 19.2365 13.0941 18.7016 12 17.8416C12.8828 17.1475 13.5964 16.262 14.0871 15.2519C14.5778 14.2418 14.8328 13.1335 14.8328 12.0105C14.8328 10.8876 14.5778 9.77925 14.0871 8.76917C13.5964 7.75908 12.8828 6.87359 12 6.17946C13.0941 5.31945 14.4081 4.78462 15.7918 4.63611C17.1755 4.48759 18.5731 4.73139 19.8247 5.33962C21.0764 5.94786 22.1317 6.89599 22.87 8.07565C23.6083 9.25531 23.9998 10.6189 23.9998 12.0105Z" fill="#F79E1B" />
-                <path d="M23.2934 16.6062V16.3674H23.3897V16.3188H23.1445V16.3674H23.2408V16.6062H23.2934ZM23.7695 16.6062V16.3183H23.6943L23.6079 16.5163L23.5214 16.3183H23.4462V16.6062H23.4993V16.389L23.5803 16.5762H23.6354L23.7164 16.3886V16.6062H23.7695Z" fill="#F79E1B" />
-            </svg>`,
-    },
-    {
-      name: 'Visa',
-      description: ' Trusted world leader in digital payment technology',
-      icon: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-                <path d="M11.8832 8.24628L10.2798 15.7425H8.34041L9.94398 8.24628H11.8832ZM20.0422 13.0867L21.063 10.2717L21.6504 13.0867H20.0422ZM22.2067 15.7425H24L22.4334 8.24628H20.7792C20.4064 8.24628 20.0921 8.46243 19.953 8.79575L17.0431 15.7425H19.0799L19.4842 14.623H21.9719L22.2067 15.7425ZM17.1441 13.2952C17.1526 11.3169 14.4092 11.2073 14.4276 10.3233C14.4335 10.0547 14.6898 9.76859 15.2499 9.69542C15.5276 9.65967 16.2939 9.63067 17.1625 10.0309L17.5022 8.44068C17.0357 8.27191 16.4353 8.10938 15.6883 8.10938C13.7711 8.10938 12.4224 9.12773 12.4116 10.5872C12.3993 11.6664 13.375 12.2681 14.1086 12.6276C14.865 12.995 15.1184 13.2305 15.1147 13.5588C15.1094 14.0617 14.5116 14.2844 13.9549 14.2929C12.9793 14.308 12.4138 14.0292 11.9632 13.8191L11.6111 15.4624C12.065 15.6702 12.9013 15.8509 13.7672 15.8602C15.8054 15.8602 17.1381 14.8538 17.1441 13.2952ZM9.1121 8.24628L5.96986 15.7425H3.92017L2.37375 9.75999C2.28001 9.3921 2.19823 9.25688 1.91313 9.10143C1.44678 8.84819 0.676937 8.6113 0 8.46395L0.0458603 8.24628H3.34574C3.76606 8.24628 4.14424 8.52599 4.24051 9.01022L5.05739 13.3483L7.07471 8.24628H9.1121Z" fill="#1434CB" />
-            </svg>`,
+      label: 'Norwegian Wood',
+      value: 'Norwegian Wood',
     },
   ];
+
+  function onCheck(item: any) {
+    checked.value = item.id;
+  }
+  async function onUpdate(val: string) {
+    loading.value = true;
+    console.log(val);
+    if (val === 'knowledge') {
+      list.value = await getKnowledgeList({});
+    }
+    if (val === 'prompt') {
+      list.value = await getPromptList({});
+    }
+    loading.value = false;
+  }
 </script>
 
 <template>
   <n-layout has-sider class="h-full w-full">
-    <n-layout-sider :width="350" bordered>
-      <div class="flex justify-center items-center m-4 rounded">
-        <ul class="mt-2 space-y-3">
-          <li v-for="(item, idx) in radios" :key="idx">
-            <label :for="item.name" class="block relative">
-              <input
-                :id="item.name"
-                type="radio"
-                :checked="idx == 1 ? true : false"
-                name="payment"
-                class="sr-only peer"
-              />
-              <div
-                class="w-full flex gap-x-3 items-start p-4 cursor-pointer rounded-lg border bg-white shadow-sm ring-indigo-600 peer-checked:ring-2 duration-200"
-              >
-                <div class="flex-none">
-                  <div v-html="item.icon"></div>
-                </div>
-                <div>
-                  <h3 class="leading-none text-gray-800 font-medium pr-3">
-                    {{ item.name }}
-                  </h3>
-                  <p class="mt-1 text-sm text-gray-600">
-                    {{ item.description }}
-                  </p>
-                </div>
-              </div>
-              <div
-                class="absolute top-4 right-4 flex-none flex items-center justify-center w-4 h-4 rounded-full border peer-checked:bg-indigo-600 text-white peer-checked:text-white duration-200"
+    <n-layout-sider :width="350" bordered :native-scrollbar="false">
+      <div class="px-2 py-2 pb-0">
+        <n-tabs type="segment" animated @update:value="onUpdate">
+          <n-tab-pane name="knowledge" tab="知识库" />
+          <n-tab-pane name="prompt" tab="提示词库" />
+        </n-tabs>
+      </div>
+      <n-spin :show="loading">
+        <div class="flex justify-center items-center m-4 mt-0 rounded overflow-y-auto">
+          <ul class="mt-2 space-y-3 w-full">
+            <li v-for="(item, idx) in list" :key="idx" @click="onCheck(item)">
+              <n-popselect
+                v-model:value="value"
+                :options="options"
+                placement="right"
+                :show="item.isStruct !== undefined && item.isStruct && item.id == checked"
               >
-                <svg class="w-2.5 h-2.5" viewBox="0 0 12 10">
-                  <polyline
-                    fill="none"
-                    stroke-width="2px"
-                    stroke="currentColor"
-                    stroke-dasharray="16px"
-                    points="1.5 6 4.5 9 10.5 1"
+                <label :for="item.name" class="block relative">
+                  <input
+                    :id="item.name"
+                    type="radio"
+                    :checked="item.id == checked"
+                    name="payment"
+                    class="sr-only peer"
                   />
-                </svg>
-              </div>
-            </label>
-          </li>
-        </ul>
-      </div>
+                  <div
+                    class="w-full flex gap-x-3 items-start p-4 pb-2.5 cursor-pointer rounded-lg border bg-white shadow-sm ring-indigo-600 peer-checked:bg-indigo-50/75 peer-checked:ring-1 duration-200"
+                  >
+                    <div class="flex-none">
+                      <n-avatar :size="35" :src="knowledgePng" class="!bg-white" round />
+                    </div>
+                    <div>
+                      <div class="leading-none text-gray-800 font-medium text-[15px] pr-3">
+                        {{ item.name }}
+                      </div>
+                      <p class="text-xs text-gray-600 mt-[9px]">
+                        <n-ellipsis :line-clamp="2">
+                          {{ item.des == undefined ? item.prompt : item.des }}
+                        </n-ellipsis>
+                      </p>
+                    </div>
+                  </div>
+                  <div
+                    class="absolute top-4 right-4 flex-none flex items-center justify-center w-4 h-4 rounded-full border peer-checked:bg-indigo-600 text-white peer-checked:text-white duration-200"
+                  >
+                    <svg class="w-2.5 h-2.5" viewBox="0 0 12 10">
+                      <polyline
+                        fill="none"
+                        stroke-width="2px"
+                        stroke="currentColor"
+                        stroke-dasharray="16px"
+                        points="1.5 6 4.5 9 10.5 1"
+                      />
+                    </svg>
+                  </div>
+                </label>
+              </n-popselect>
+            </li>
+          </ul>
+        </div>
+      </n-spin>
     </n-layout-sider>
 
     <div class="flex justify-center items-center w-full mt-0">
@@ -112,4 +134,8 @@
   </n-layout>
 </template>
 
-<style scoped lang="less"></style>
+<style scoped lang="less">
+  ::v-deep(.n-tabs.n-tabs--top .n-tab-pane) {
+    padding: 0 !important;
+  }
+</style>

+ 19 - 0
langchat-ui/src/views/aigc/knowledge/columns.ts

@@ -71,6 +71,25 @@ export const formSchemas: FormSchema[] = [
     },
     rules: [{ required: true, message: '请输入知识库名称', trigger: ['blur'] }],
   },
+  {
+    field: 'isStruct',
+    component: 'NRadioGroup',
+    label: '结构化文档',
+    labelMessage: '是否填充的结构化文档数据',
+    defaultValue: false,
+    componentProps: {
+      options: [
+        {
+          label: '非结构化数据',
+          value: false,
+        },
+        {
+          label: '是结构化数据',
+          value: true,
+        },
+      ],
+    },
+  },
   {
     field: 'des',
     component: 'NInput',

+ 6 - 0
langchat-ui/src/views/aigc/knowledge/components/DocsList/columns.ts

@@ -10,6 +10,12 @@ export const columns: BasicColumn[] = [
     title: '文档内容/链接',
     key: 'content',
   },
+  {
+    title: '文件大小/kb',
+    key: 'size',
+    width: 100,
+    align: 'center',
+  },
   {
     title: '创建时间',
     key: 'createTime',

+ 8 - 6
langchat-ui/src/views/aigc/knowledge/components/DocsList/index.vue

@@ -4,7 +4,8 @@
   import { BasicForm, useForm } from '@/components/Form';
   import { del, page as getPage } from '@/api/aigc/docs';
   import { columns, searchSchemas } from './columns';
-  import { DeleteOutlined, EditOutlined, PlusOutlined } from '@vicons/antd';
+  import { AlbumsOutline } from '@vicons/ionicons5';
+  import { DeleteOutlined, EditOutlined } from '@vicons/antd';
   import Edit from './edit.vue';
   import { useDialog, useMessage } from 'naive-ui';
   import { useRouter } from 'vue-router';
@@ -25,6 +26,11 @@
       return h(TableAction as any, {
         style: 'text',
         actions: [
+          {
+            type: 'info',
+            icon: AlbumsOutline,
+            onClick: handleEdit.bind(null, record),
+          },
           {
             type: 'info',
             icon: EditOutlined,
@@ -40,7 +46,7 @@
     },
   });
 
-  const [register, { getFieldsValue, setFieldsValue }] = useForm({
+  const [register, { getFieldsValue }] = useForm({
     gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
     labelWidth: 80,
     showAdvancedButton: false,
@@ -56,10 +62,6 @@
     actionRef.value.reload();
   }
 
-  function handleAdd() {
-    editRef.value.show('');
-  }
-
   function handleEdit(record: Recordable) {
     editRef.value.show('', record.id);
   }

+ 1 - 1
langchat-ui/src/views/aigc/knowledge/components/DocsSlice/columns.ts

@@ -48,7 +48,7 @@ export const searchSchemas: FormSchema[] = [
   {
     field: 'docsId',
     component: 'NInput',
-    label: '文档',
+    label: '所属文档',
     slot: 'docsSlot',
     componentProps: {
       placeholder: '请选择文档',

+ 1 - 1
langchat-ui/src/views/aigc/knowledge/components/DocsSlice/index.vue

@@ -16,7 +16,7 @@
   const docsList = ref();
 
   const actionColumn = reactive({
-    width: 100,
+    width: 80,
     title: '操作',
     key: 'action',
     fixed: 'right',

+ 49 - 24
langchat-ui/src/views/aigc/knowledge/components/ImportFile/components/URLImport.vue

@@ -1,34 +1,59 @@
-<script setup lang="ts">
-  import { DownloadOutline } from '@vicons/ionicons5';
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { useMessage } from 'naive-ui';
+  import { isNullOrWhitespace } from '@/utils/is';
+  import { embeddingText } from '@/api/aigc/embedding';
   import { useRouter } from 'vue-router';
+
   const router = useRouter();
+  const message = useMessage();
+  const form = ref({
+    name: '',
+    content: '',
+  });
+  const rules = ref({
+    name: {
+      required: true,
+      message: '请输入文件名',
+      trigger: ['input', 'blur'],
+    },
+    content: {
+      required: true,
+      message: '请输入文档内容',
+      trigger: ['input', 'blur'],
+    },
+  });
 
-  async function handleImport() {
-    const kbId = router.currentRoute.value.params.id;
+  async function handleSubmit() {
+    if (isNullOrWhitespace(form.value.content)) {
+      message.warning('请输入文档内容');
+      return;
+    }
+    const knowledgeId = router.currentRoute.value.params.id;
+    await embeddingText({
+      ...form.value,
+      knowledgeId,
+    });
+    message.success('文档录入成功,正在解析中...');
+    form.value = {
+      name: '',
+      content: '',
+    };
   }
 </script>
 
 <template>
-  <div>
-    <n-upload
-      multiple
-      directory-dnd
-      action="https://www.mocky.io/v2/5e4bafc63100007100d8b70f"
-      :max="5"
-    >
-      <n-upload-dragger>
-        <div style="margin-bottom: 12px">
-          <n-icon size="48" :depth="3">
-            <DownloadOutline />
-          </n-icon>
-        </div>
-        <n-text style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </n-text>
-        <n-p depth="3" style="margin: 8px 0 0 0">
-          请不要上传敏感数据,比如你的银行卡号和密码,信用卡号有效期和安全码
-        </n-p>
-      </n-upload-dragger>
-    </n-upload>
+  <div class="flex flex-col gap-4">
+    <div>
+      <n-button type="success" @click="handleSubmit">解析线上数据</n-button>
+    </div>
+
+    <n-form :rules="rules" :model="form" label-placement="left" label-width="auto">
+      <n-form-item label="文件URL地址" path="name">
+        <n-input v-model:value="form.name" />
+      </n-form-item>
+    </n-form>
   </div>
 </template>
 
-<style scoped lang="less"></style>
+<style lang="less" scoped></style>

+ 8 - 1
langchat-ui/src/views/aigc/knowledge/components/ImportFile/index.vue

@@ -13,6 +13,11 @@
   import URLImport from './components/URLImport.vue';
   import DocImport from './components/DocImport.vue';
 
+  interface Props {
+    data?: any;
+  }
+  const props = defineProps<Props>();
+
   const dataSource: CheckSource[] = [
     {
       key: 'doc-input',
@@ -30,6 +35,7 @@
       key: 'excel-import',
       icon: PodiumOutline,
       title: 'Excel文件导入',
+      isHidden: !props.data.isStruct,
       label: '处理excel结构化数据,支持对Excel行列数据读取',
     },
     {
@@ -47,8 +53,9 @@
 </script>
 
 <template>
-  <div>
+  <div v-if="data">
     <CheckCard
+      v-if="!data.isStruct"
       :data-source="dataSource"
       :default-checked="checked"
       :justify="'start'"

+ 6 - 6
langchat-ui/src/views/aigc/knowledge/components/index.vue

@@ -36,10 +36,10 @@
     },
   ];
 
-  const kb = ref<any>({});
+  const knowledge = ref<any>({});
   onMounted(async () => {
     const id = router.currentRoute.value.params.id;
-    kb.value = await getById(String(id));
+    knowledge.value = await getById(String(id));
     menu.value = 'doc-list';
   });
 
@@ -67,23 +67,23 @@
               </template>
             </n-button>
             <span>
-              {{ kb.name }}
+              {{ knowledge.name }}
             </span>
           </n-space>
         </template>
 
-        {{ kb.des }}
+        {{ knowledge.des }}
       </n-card>
     </div>
-    <div class="mt-2 h-full" style="height: calc(100vh - 242px) !important">
+    <div class="mt-2 h-full mx-4" style="height: calc(100vh - 242px) !important">
       <n-grid :x-gap="10" class="h-full" cols="2 s:2 m:2 l:24 xl:24 2xl:24" responsive="screen">
         <n-gi class="bg-white pt-2" span="3">
           <n-menu v-model:value="menu" :options="menuOptions" @update:value="handleSelect" />
         </n-gi>
         <n-gi class="h-full overflow-y-auto" span="21">
           <DocList v-if="menu == 'doc-list'" />
-          <ImportFile v-if="menu == 'import-file'" />
           <FileList v-if="menu == 'slice-list'" />
+          <ImportFile :data="knowledge" v-if="menu == 'import-file'" />
         </n-gi>
       </n-grid>
     </div>

+ 8 - 5
langchat-ui/src/views/aigc/knowledge/index.vue

@@ -2,6 +2,7 @@
   import { onMounted, ref } from 'vue';
   import { BasicForm, useForm } from '@/components/Form/index';
   import { del, page as getPage } from '@/api/aigc/knowledge';
+  import knowledgePng from '@/assets/images/knowledge.png';
   import { searchSchemas } from './columns';
   import {
     AntCloudOutlined,
@@ -93,12 +94,10 @@
             <div class="flex items-center gap-2">
               <n-avatar v-if="item.cover" :src="item.cover" :size="48" round>
                 <template #fallback>
-                  <div class="flex justify-center items-center w-full">
-                    {{ item.name.substring(0, 4) }}
-                  </div>
+                  <n-avatar :size="48" :src="knowledgePng" class="!bg-white" round />
                 </template>
               </n-avatar>
-              <n-avatar v-else :size="48" round> {{ item.name.substring(0, 4) }} </n-avatar>
+              <n-avatar v-else :size="48" :src="knowledgePng" class="!bg-white" round />
               <h2 class="text-gray-800 font-semibold">{{ item.name }}</h2>
             </div>
             <button
@@ -112,7 +111,11 @@
             </button>
           </div>
           <div class="p-4 pt-0">
-            <p class="text-gray-600 text-sm">{{ item.des }}</p>
+            <p class="text-gray-600 text-sm">
+              <n-ellipsis :line-clamp="2">
+                {{ item.des }}
+              </n-ellipsis>
+            </p>
           </div>
           <div class="py-3 px-4 border-t flex justify-between items-center">
             <div class="flex gap-1 items-center">