Jelajahi Sumber

ajustes respostas do chat

leonardo 2 bulan lalu
induk
melakukan
2b43d5cfb5
3 mengubah file dengan 34 tambahan dan 6 penghapusan
  1. 2 1
      src/api/chat.js
  2. 25 4
      src/components/ChatWindow.vue
  3. 7 1
      src/composables/useChat.js

+ 2 - 1
src/api/chat.js

@@ -7,7 +7,7 @@ export async function sendChat(message, { conversationId } = {}) {
   });
 }
 
-export async function sendChatStream(message, { conversationId, onChunk, onSources, onDone, onError, signal } = {}) {
+export async function sendChatStream(message, { conversationId, onChunk, onSources, onStatus, onDone, onError, signal } = {}) {
   const token = getAccessToken();
   const headers = { "content-type": "application/json" };
   if (token) headers["Authorization"] = `Bearer ${token}`;
@@ -51,6 +51,7 @@ export async function sendChatStream(message, { conversationId, onChunk, onSourc
           const parsed = JSON.parse(payload);
           if (parsed.type === "delta") onChunk?.(parsed.delta);
           else if (parsed.type === "sources") onSources?.(parsed.sources);
+          else if (parsed.type === "status") onStatus?.(parsed);
           else if (parsed.type === "error") onError?.(new Error(parsed.error));
         } catch {}
       }

+ 25 - 4
src/components/ChatWindow.vue

@@ -54,11 +54,11 @@
                   : 'prose-chat border-gray-200 bg-white/50 text-foreground dark:border-gray-700 dark:bg-gray-800/60'
               "
             >
-              
+
               <span v-if="m.role === 'user'" class="whitespace-pre-wrap">{{ m.content }}</span>
-              
+
               <template v-else>
-                
+
                 <div class="prose-content" v-html="renderMarkdown(m.content)" />
                 <span
                   v-if="m.streaming"
@@ -67,6 +67,23 @@
                 />
               </template>
             </div>
+
+            <div
+              v-if="m.role === 'assistant' && m.sources?.length"
+              class="mt-1.5 flex flex-wrap gap-1"
+            >
+              <span
+                v-for="(src, si) in m.sources"
+                :key="si"
+                class="inline-flex items-center gap-1 rounded-md border border-gray-200 bg-gray-50 px-2 py-0.5 text-[10px] text-gray-500 dark:border-gray-700 dark:bg-gray-800/50 dark:text-gray-400"
+              >
+                <svg class="h-2.5 w-2.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
+                  <path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5.586a1 1 0 0 1 .707.293l5.414 5.414a1 1 0 0 1 .293.707V19a2 2 0 0 1-2 2z" />
+                </svg>
+                {{ src.source }}
+                <span class="opacity-60">{{ Math.round((src.score ?? 0) * 100) }}%</span>
+              </span>
+            </div>
           </div>
         </div>
 
@@ -80,7 +97,7 @@
               <span class="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400 dark:bg-gray-500" style="animation-delay: 0ms" />
               <span class="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400 dark:bg-gray-500" style="animation-delay: 150ms" />
               <span class="h-1.5 w-1.5 animate-bounce rounded-full bg-gray-400 dark:bg-gray-500" style="animation-delay: 300ms" />
-              <span class="ml-1 text-xs text-gray-400 dark:text-gray-500">Buscando documentos...</span>
+              <span class="ml-1 text-xs text-gray-400 dark:text-gray-500">{{ streamingStatusMsg }}</span>
             </span>
           </div>
         </div>
@@ -145,6 +162,10 @@ const copiado = ref(null);
 let copiadoTimeout = null;
 
 const isStreaming = computed(() => props.messages.some((m) => m.streaming && m.content));
+const streamingStatusMsg = computed(() => {
+  const m = [...props.messages].reverse().find((m) => m.streaming);
+  return m?.statusMsg || "Buscando documentos...";
+});
 
 
 const renderer = new Renderer();

+ 7 - 1
src/composables/useChat.js

@@ -231,7 +231,7 @@ export function useChat() {
 
     messages.value.push({ role: "user", content: trimmed, sentAt: Date.now() });
 
-    const assistantMsg = { role: "assistant", content: "", sources: [], sentAt: Date.now(), streaming: true };
+    const assistantMsg = { role: "assistant", content: "", sources: [], sentAt: Date.now(), streaming: true, statusMsg: "" };
     messages.value.push(assistantMsg);
     const msgIndex = messages.value.length - 1;
 
@@ -240,7 +240,13 @@ export function useChat() {
     await sendChatStream(trimmed, {
       conversationId: convId,
       signal: _streamAbort.signal,
+      onStatus: ({ stage, count }) => {
+        if (stage === "buscando") messages.value[msgIndex].statusMsg = "Buscando documentos...";
+        else if (stage === "encontrou") messages.value[msgIndex].statusMsg = `Encontrei ${count} fonte${count !== 1 ? "s" : ""}...`;
+        else if (stage === "gerando") messages.value[msgIndex].statusMsg = "Gerando resposta...";
+      },
       onChunk: (delta) => {
+        messages.value[msgIndex].statusMsg = "";
         messages.value[msgIndex].content += delta;
       },
       onSources: (sources) => {