Bläddra i källkod

fix: agent chat

tycoding 9 månader sedan
förälder
incheckning
fc3aaef4db

+ 1 - 1
docs/langchat.sql

@@ -317,7 +317,7 @@ INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `ord
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('35dcd70c8a4008b554b71bf02ab07b61', '删除聊天记录', 'bdd70f2c1ee068c13bd3288eff07c8e2', NULL, 'chat:messages:clean', 'button', 3, NULL, NULL, 0, 0, 0, NULL);
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('374409ab56141b311ccb0f1847dd724a', 'AIGC平台', '0', 'aigc', 'aigc', 'menu', 2, 'CubeOutline', 'Layout', 0, 0, 1, 1);
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('3d1700109ece0187ba5e76217cd71995', '删除对话数据', 'f1ad3c056ac91fa5292a99f223155afc', NULL, 'aigc:message:delete', 'button', 2, NULL, NULL, 0, 0, 0, NULL);
-INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('43563b039d30b990f87af37783115ff4', '应用管理', 'a2ccfe694cd91cf159ad35626e4ea202', 'app', 'aigc:app', 'menu', 2, '', '/app/index', 0, 0, 1, 1);
+INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('43563b039d30b990f87af37783115ff4', 'AI应用管理', 'a2ccfe694cd91cf159ad35626e4ea202', 'list', 'aigc:app', 'menu', 2, '', '/app/index', 0, 0, 1, 1);
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('4488cb5271b1220647d4a83cfbcb7b15', '文档向量化', '43563b039d30b990f87af37783115ff4', NULL, 'aigc:embedding:docs', 'button', 5, NULL, NULL, 0, 0, 0, NULL);
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('510a89f01571d7eaa3b1393c8534ab6f', '删除应用', '43563b039d30b990f87af37783115ff4', NULL, 'aigc:app:delete', 'button', 3, NULL, NULL, 0, 0, 0, NULL);
 INSERT INTO `sys_menu` (`id`, `name`, `parent_id`, `path`, `perms`, `type`, `order_no`, `icon`, `component`, `is_disabled`, `is_ext`, `is_keepalive`, `is_show`) VALUES ('5514605bae6ffdad3e4acff3e9e9742c', '新增应用', '43563b039d30b990f87af37783115ff4', NULL, 'aigc:app:add', 'button', 1, NULL, NULL, 0, 0, 0, NULL);

+ 24 - 24
langchat-client/src/main/java/cn/tycoding/langchat/client/service/impl/ClientChatServiceImpl.java

@@ -53,6 +53,10 @@ public class ClientChatServiceImpl implements ClientChatService {
         long startTime = System.currentTimeMillis();
         StringBuilder text = new StringBuilder();
 
+        // save user message
+        req.setRole(RoleEnum.USER.getName());
+        saveMessage(req, 0, 0);
+
         try {
             langChatService.chat(req)
                     .onNext(e -> {
@@ -64,15 +68,10 @@ public class ClientChatServiceImpl implements ClientChatService {
                         emitter.send(new ChatRes(tokenUsage.totalTokenCount(), startTime));
                         emitter.complete();
 
-                        // save message
-                        if (req.getConversationId() != null) {
-                            req.setRole(RoleEnum.USER.getName());
-                            saveMessage(req, 0, 0);
-
-                            req.setMessage(text.toString());
-                            req.setRole(RoleEnum.ASSISTANT.getName());
-                            saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
-                        }
+                        // save assistant message
+                        req.setMessage(text.toString());
+                        req.setRole(RoleEnum.ASSISTANT.getName());
+                        saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
                     })
                     .onError((e) -> {
                         emitter.error(e.getMessage());
@@ -92,6 +91,10 @@ public class ClientChatServiceImpl implements ClientChatService {
         StreamEmitter emitter = req.getEmitter();
         StringBuilder text = new StringBuilder();
 
+        // save user message
+        req.setRole(RoleEnum.USER.getName());
+        saveMessage(req, 0, 0);
+
         try {
             langChatService.chat(req)
                     .onNext(e -> {
@@ -103,15 +106,10 @@ public class ClientChatServiceImpl implements ClientChatService {
                         emitter.send(new ChatRes(tokenUsage.totalTokenCount(), startTime));
                         emitter.complete();
 
-                        // save message
-                        if (req.getConversationId() != null) {
-                            req.setRole(RoleEnum.USER.getName());
-                            saveMessage(req, 0, 0);
-
-                            req.setMessage(text.toString());
-                            req.setRole(RoleEnum.ASSISTANT.getName());
-                            saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
-                        }
+                        // save assistant message
+                        req.setMessage(text.toString());
+                        req.setRole(RoleEnum.ASSISTANT.getName());
+                        saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
                     })
                     .onError((e) -> {
                         e.printStackTrace();
@@ -126,12 +124,14 @@ public class ClientChatServiceImpl implements ClientChatService {
     }
 
     private void saveMessage(ChatReq req, Integer inputToken, Integer outputToken) {
-        AigcMessage message = new AigcMessage();
-        BeanUtils.copyProperties(req, message);
-        message.setIp(ServletUtil.getIpAddr());
-        message.setPromptTokens(inputToken);
-        message.setTokens(outputToken);
-        aigcMessageService.addMessage(message);
+        if (req.getConversationId() != null) {
+            AigcMessage message = new AigcMessage();
+            BeanUtils.copyProperties(req, message);
+            message.setIp(ServletUtil.getIpAddr());
+            message.setPromptTokens(inputToken);
+            message.setTokens(outputToken);
+            aigcMessageService.addMessage(message);
+        }
     }
 
     @Override

+ 16 - 14
langchat-server/src/main/java/cn/tycoding/langchat/server/service/impl/ChatServiceImpl.java

@@ -66,6 +66,10 @@ public class ChatServiceImpl implements ChatService {
             }
         }
 
+        // save user message
+        req.setRole(RoleEnum.USER.getName());
+        saveMessage(req, 0, 0);
+
         try {
             langChatService
                     .chat(req)
@@ -83,14 +87,10 @@ public class ChatServiceImpl implements ChatService {
                         emitter.send(res);
                         emitter.complete();
 
-                        if (req.getConversationId() != null) {
-                            req.setRole(RoleEnum.USER.getName());
-                            saveMessage(req, 0, 0);
-
-                            req.setMessage(text.toString());
-                            req.setRole(RoleEnum.ASSISTANT.getName());
-                            saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
-                        }
+                        // save assistant message
+                        req.setMessage(text.toString());
+                        req.setRole(RoleEnum.ASSISTANT.getName());
+                        saveMessage(req, tokenUsage.inputTokenCount(), tokenUsage.outputTokenCount());
                     })
                     .onError((e) -> {
                         emitter.error(e.getMessage());
@@ -105,11 +105,13 @@ public class ChatServiceImpl implements ChatService {
     }
 
     private void saveMessage(ChatReq req, Integer inputToken, Integer outputToken) {
-        AigcMessage message = new AigcMessage();
-        BeanUtils.copyProperties(req, message);
-        message.setIp(ServletUtil.getIpAddr());
-        message.setPromptTokens(inputToken);
-        message.setTokens(outputToken);
-        aigcMessageService.addMessage(message);
+        if (req.getConversationId() != null) {
+            AigcMessage message = new AigcMessage();
+            BeanUtils.copyProperties(req, message);
+            message.setIp(ServletUtil.getIpAddr());
+            message.setPromptTokens(inputToken);
+            message.setTokens(outputToken);
+            aigcMessageService.addMessage(message);
+        }
     }
 }

+ 20 - 9
langchat-ui/src/router/base.ts

@@ -101,6 +101,26 @@ export const BaseRoute: Array<any> = [
     ],
   },
 
+  {
+    path: '/app',
+    name: 'app',
+    component: 'Layout',
+    show: false,
+    meta: {
+      title: 'AI应用管理',
+    },
+    children: [
+      {
+        path: 'info/:id?',
+        name: 'appInfo',
+        component: '/app/info',
+        show: false,
+        meta: {
+          title: '应用配置',
+        },
+      },
+    ],
+  },
   {
     path: '/aigc',
     name: 'aigc',
@@ -128,15 +148,6 @@ export const BaseRoute: Array<any> = [
           title: '知识库数据',
         },
       },
-      {
-        path: 'app/info/:id?',
-        name: 'appInfo',
-        component: '/app/info',
-        show: false,
-        meta: {
-          title: '应用配置',
-        },
-      },
     ],
   },
 ];

+ 4 - 3
langchat-ui/src/views/app/base/index.vue

@@ -23,7 +23,7 @@
   import { useMessage } from 'naive-ui';
   import { useAppStore } from '../store';
   import { useChatStore } from '@/views/chat/store/useChatStore';
-  import { getAppInfo, getMessages } from '@/api/aigc/chat';
+  import { getAppInfo } from '@/api/aigc/chat';
   import { formatToDateTime } from '@/utils/dateUtil';
 
   const appStore = useAppStore();
@@ -50,8 +50,9 @@
     appStore.knowledges = data.knowledges == null ? [] : data.knowledges;
     chatStore.modelId = data.modelId == null ? null : data.modelId;
     chatStore.appId = data.id;
-    chatStore.conversationId = data.id;
-    chatStore.messages = await getMessages(chatStore.conversationId!);
+    // 对于应用调试页面,不存储聊天记录
+    // chatStore.conversationId = data.id;
+    // chatStore.messages = await getMessages(chatStore.conversationId!);
     loading.value = false;
   }
 

+ 1 - 1
langchat-ui/src/views/app/index.vue

@@ -66,7 +66,7 @@
   }
 
   async function onInfo(item) {
-    await router.push('/aigc/app/info/' + item.id);
+    await router.push('/app/info/' + item.id);
   }
 
   function handleAdd() {

+ 2 - 12
langchat-ui/src/views/app/info.vue

@@ -21,11 +21,9 @@
   import router from '@/router';
   import { onMounted, ref } from 'vue';
   import { useAppStore } from './store';
-  import { useChatStore } from '@/views/chat/store/useChatStore';
-  import { getAppInfo, getMessages } from '@/api/aigc/chat';
+  import { getAppInfo } from '@/api/aigc/chat';
 
   const appStore = useAppStore();
-  const chatStore = useChatStore();
   const form = ref<any>({});
   const loading = ref(false);
   const activeMenus = [
@@ -45,14 +43,6 @@
       conversationId: null,
     });
     form.value = data;
-    appStore.info = data;
-    appStore.knowledgeIds = data.knowledgeIds == null ? [] : data.knowledgeIds;
-    appStore.modelId = data.modelId == null ? null : data.modelId;
-    appStore.knowledges = data.knowledges == null ? [] : data.knowledges;
-    chatStore.modelId = data.modelId == null ? null : data.modelId;
-    chatStore.appId = data.id;
-    chatStore.conversationId = data.id;
-    chatStore.messages = await getMessages(chatStore.conversationId!);
     loading.value = false;
   }
 </script>
@@ -61,7 +51,7 @@
   <div v-if="form.name !== undefined" class="rounded bg-[#f9f9f9] w-full h-full pb-10">
     <div class="p-4 flex justify-between items-center bg-white rounded">
       <div class="flex gap-5 items-center min-w-20">
-        <n-button text type="primary" @click="router.push('/app/app')">
+        <n-button text type="primary" @click="router.push('/app/list')">
           <SvgIcon class="text-xl" icon="icon-park-outline:back" />
         </n-button>
         <div class="flex gap-2 items-center pr-4">

+ 1 - 1
langchat-ui/src/views/chat/Header.vue

@@ -54,7 +54,7 @@
       <span>{{ title }}</span>
     </div>
     <n-space align="center">
-      <ModelSelect :id="chatStore.modelId" class="w-auto" style="min-width: 140px" />
+      <ModelSelect :id="chatStore.modelId" class="w-auto" style="min-width: 180px" />
 
       <n-button secondary size="small" type="warning" @click="handleClear">
         <template #icon>

+ 1 - 1
langchat-ui/src/views/chat/index.vue

@@ -40,7 +40,7 @@
 
 <template>
   <n-card class="p-4 pt-1 w-full h-full">
-    <Header title="AI自由聊天" @reload="fetch" />
+    <Header title="AI聊天助手" @reload="fetch" />
     <div class="flex flex-col w-full overflow-hidden" style="height: calc(100vh - 180px)">
       <main ref="contentRef" class="flex-1 overflow-hidden overflow-y-auto">
         <Chat />

+ 37 - 27
langchat-ui/src/views/chat/message/Message.vue

@@ -19,7 +19,6 @@
   import { useMessage } from 'naive-ui';
   import TextComponent from './TextComponent.vue';
   import AvatarComponent from './Avatar.vue';
-  import SvgIcon from '@/components/SvgIcon/index.vue';
   import { useBasicLayout } from '../store/useBasicLayout';
   import { useIconRender } from '../store/useIconRender';
   import { copyToClip } from '@/utils/copy';
@@ -37,21 +36,14 @@
   }
 
   const props = defineProps<Props>();
-
   const emit = defineEmits<Emit>();
-
+  const isHover = ref(false);
   const { isMobile } = useBasicLayout();
-
   const { iconRender } = useIconRender();
-
   const message = useMessage();
-
   const textRef = ref<HTMLElement>();
-
   const asRawText = ref(props.inversion);
-
   const messageRef = ref<HTMLElement>();
-
   const options = computed(() => {
     const common = [
       {
@@ -59,15 +51,15 @@
         key: 'copyText',
         icon: iconRender({ icon: 'ri:file-copy-2-line' }),
       },
-      {
-        label: '删除',
-        key: 'delete',
-        icon: iconRender({ icon: 'ri:delete-bin-line' }),
-      },
+      // {
+      //   label: '删除',
+      //   key: 'delete',
+      //   icon: iconRender({ icon: 'ri:delete-bin-line' }),
+      // },
     ];
 
     if (!props.inversion) {
-      common.unshift({
+      common.push({
         label: asRawText.value ? '预览' : '显示原文',
         key: 'toggleRenderType',
         icon: iconRender({ icon: asRawText.value ? 'ic:outline-code-off' : 'ic:outline-code' }),
@@ -116,7 +108,12 @@
       <p :class="[inversion ? 'text-right' : 'text-left']" class="text-xs text-[#b4bbc4]">
         {{ dateTime }}
       </p>
-      <div :class="[inversion ? 'flex-row-reverse' : 'flex-row']" class="flex items-end gap-1 mt-2">
+      <div
+        @mouseover="isHover = true"
+        @mouseleave="isHover = false"
+        :class="[inversion ? 'flex-row-reverse' : 'flex-row']"
+        class="flex items-end gap-1 mt-2 transition-all"
+      >
         <TextComponent
           ref="textRef"
           :as-raw-text="asRawText"
@@ -125,17 +122,30 @@
           :loading="loading"
           :text="text"
         />
-        <div class="flex flex-col">
-          <NDropdown
-            :options="options"
-            :placement="!inversion ? 'right' : 'left'"
-            :trigger="isMobile ? 'click' : 'hover'"
-            @select="handleSelect"
-          >
-            <button class="transition text-neutral-300 hover:text-neutral-800">
-              <SvgIcon icon="ri:more-2-fill" />
-            </button>
-          </NDropdown>
+        <div class="flex flex-col transition-all w-[45px]">
+          <n-space v-if="isHover" class="transition-all gap-1.5 flex-nowrap justify-end">
+            <n-popover v-for="item in options" :key="item" class="custom-popover">
+              <template #trigger>
+                <button
+                  @click="handleSelect(item.key as any)"
+                  class="transition text-neutral-400 hover:text-neutral-800"
+                >
+                  <component :is="item.icon" />
+                </button>
+              </template>
+              {{ item.label }}
+            </n-popover>
+          </n-space>
+          <!--          <NDropdown-->
+          <!--            :options="options"-->
+          <!--            :placement="!inversion ? 'right' : 'left'"-->
+          <!--            :trigger="isMobile ? 'click' : 'hover'"-->
+          <!--            @select="handleSelect"-->
+          <!--          >-->
+          <!--            <button class="transition text-neutral-300 hover:text-neutral-800">-->
+          <!--              <SvgIcon icon="ri:more-2-fill" />-->
+          <!--            </button>-->
+          <!--          </NDropdown>-->
         </div>
       </div>
     </div>