|
|
@@ -9,6 +9,15 @@ import {
|
|
|
} from "../api/conversations.js";
|
|
|
|
|
|
let singleton;
|
|
|
+let _streamAbort = null;
|
|
|
+
|
|
|
+export function resetChatSingleton() {
|
|
|
+ if (_streamAbort) {
|
|
|
+ _streamAbort.abort();
|
|
|
+ _streamAbort = null;
|
|
|
+ }
|
|
|
+ singleton = undefined;
|
|
|
+}
|
|
|
|
|
|
export function useChat() {
|
|
|
if (singleton) return singleton;
|
|
|
@@ -27,6 +36,7 @@ export function useChat() {
|
|
|
const loading = ref(false);
|
|
|
const error = ref("");
|
|
|
const conversationsLoading = ref(false);
|
|
|
+ const conversationsError = ref("");
|
|
|
|
|
|
function normalizeApiMessages(items) {
|
|
|
return (items ?? []).map((m) => ({
|
|
|
@@ -43,6 +53,7 @@ export function useChat() {
|
|
|
|
|
|
async function loadConversationsList() {
|
|
|
conversationsLoading.value = true;
|
|
|
+ conversationsError.value = "";
|
|
|
try {
|
|
|
const data = await listConversations();
|
|
|
conversations.value = (data.items ?? []).map((c) => ({
|
|
|
@@ -52,8 +63,9 @@ export function useChat() {
|
|
|
updatedAt: new Date(c.UpdatedAt).getTime(),
|
|
|
messageCount: 0
|
|
|
}));
|
|
|
- } catch {
|
|
|
+ } catch (err) {
|
|
|
conversations.value = [];
|
|
|
+ conversationsError.value = err?.message || "Erro ao carregar conversas.";
|
|
|
} finally {
|
|
|
conversationsLoading.value = false;
|
|
|
}
|
|
|
@@ -121,11 +133,28 @@ export function useChat() {
|
|
|
return sorted.filter((c) => String(c.title ?? "").toLowerCase().includes(q));
|
|
|
}
|
|
|
|
|
|
+ function clearError() {
|
|
|
+ error.value = "";
|
|
|
+ }
|
|
|
+
|
|
|
+ function cancelCurrentStream() {
|
|
|
+ if (_streamAbort) {
|
|
|
+ _streamAbort.abort();
|
|
|
+ _streamAbort = null;
|
|
|
+ }
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+
|
|
|
async function send(content) {
|
|
|
error.value = "";
|
|
|
const trimmed = String(content ?? "").trim();
|
|
|
if (!trimmed) return;
|
|
|
|
|
|
+ if (_streamAbort) {
|
|
|
+ _streamAbort.abort();
|
|
|
+ }
|
|
|
+ _streamAbort = new AbortController();
|
|
|
+
|
|
|
let convId = activeConversationId.value ? Number(activeConversationId.value) : null;
|
|
|
|
|
|
if (!convId) {
|
|
|
@@ -154,6 +183,7 @@ export function useChat() {
|
|
|
|
|
|
await sendChatStream(trimmed, {
|
|
|
conversationId: convId,
|
|
|
+ signal: _streamAbort.signal,
|
|
|
onChunk: (delta) => {
|
|
|
messages.value[msgIndex].content += delta;
|
|
|
},
|
|
|
@@ -161,6 +191,7 @@ export function useChat() {
|
|
|
messages.value[msgIndex].sources = sources ?? [];
|
|
|
},
|
|
|
onDone: () => {
|
|
|
+ _streamAbort = null;
|
|
|
messages.value[msgIndex].streaming = false;
|
|
|
loading.value = false;
|
|
|
if (convId) {
|
|
|
@@ -174,6 +205,7 @@ export function useChat() {
|
|
|
}
|
|
|
},
|
|
|
onError: (e) => {
|
|
|
+ _streamAbort = null;
|
|
|
error.value = e?.message || "erro";
|
|
|
if (!messages.value[msgIndex].content) {
|
|
|
messages.value[msgIndex].content = "Não consegui responder no momento.";
|
|
|
@@ -193,7 +225,10 @@ export function useChat() {
|
|
|
loading,
|
|
|
error,
|
|
|
conversationsLoading,
|
|
|
+ conversationsError,
|
|
|
send,
|
|
|
+ clearError,
|
|
|
+ cancelCurrentStream,
|
|
|
newConversation,
|
|
|
setActiveConversation,
|
|
|
renameConversation,
|