@meistrari/chat-nuxt 3.15.0-rc.0 → 3.15.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
3
  "configKey": "chatNuxt",
4
- "version": "3.15.0-rc.0",
4
+ "version": "3.15.0-rc.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "unknown"
@@ -10,16 +10,15 @@ export function terminalMessageTransitions(previousMessages, nextMessages) {
10
10
  const previousById = new Map(previousMessages.map((message) => [message.id, message]));
11
11
  const transitions = [];
12
12
  for (const message of nextMessages) {
13
- const previous = previousById.get(message.id);
14
- if (message.role !== "assistant" || !previous)
13
+ if (message.role !== "assistant" || previousById.get(message.id)?.status !== "pending" /* Pending */)
15
14
  continue;
16
- if (previous.status === "pending" /* Pending */ && message.status === "completed" /* Completed */) {
15
+ if (message.status === "completed" /* Completed */) {
17
16
  transitions.push({
18
17
  conversationId: message.conversationId,
19
18
  messageId: message.id,
20
19
  status: "completed" /* Completed */
21
20
  });
22
- } else if (message.status === "failed" /* Failed */ && (previous.status === "pending" /* Pending */ || previous.status === "completed" /* Completed */)) {
21
+ } else if (message.status === "failed" /* Failed */) {
23
22
  transitions.push({
24
23
  conversationId: message.conversationId,
25
24
  messageId: message.id,
@@ -16,6 +16,7 @@ import {
16
16
  CANCELLATION_FAILED_TEXT,
17
17
  GENERATION_FAILED_TEXT
18
18
  } from "../message-state.js";
19
+ import { useConversations } from "../../conversations/composables/conversations.js";
19
20
  import { exportConversationToMarkdown } from "../../conversations/export.js";
20
21
  import { resolveChatStateScope } from "../../conversations/scope.js";
21
22
  import { getChatApiErrorCode, getChatApiErrorMessage, getChatApiErrorStatus } from "../../shared/chat-api-error.js";
@@ -45,6 +46,7 @@ export function useChat(options = {}) {
45
46
  const embedConfig = useEmbedConfig();
46
47
  const { user: authUser, activeOrganization } = useChatAuth();
47
48
  const { addFilesToConversation } = useConversationFileActions();
49
+ const { updateConversationInList } = useConversations();
48
50
  const chatScope = computed(() => resolveChatStateScope(
49
51
  embedConfig?.workspaceId.value || activeOrganization.value?.id,
50
52
  embedConfig?.telaAgentId.value,
@@ -118,9 +120,26 @@ export function useChat(options = {}) {
118
120
  schedulePreparingStep(0);
119
121
  }
120
122
  const error = ref(null);
121
- const titleGenerated = ref(false);
122
- const generatingTitle = ref(false);
123
- const animatedTitle = ref(null);
123
+ const titleGeneratedBuckets = useState("chat-title-generated", () => ({}));
124
+ const generatingTitleBuckets = useState("chat-generating-title", () => ({}));
125
+ const animatedTitleBuckets = useState("chat-animated-title", () => ({}));
126
+ const generatingTitle = computed(() => {
127
+ const conversationId = currentConversation.value?.id;
128
+ return conversationId ? generatingTitleBuckets.value[conversationId] ?? false : false;
129
+ });
130
+ const animatedTitle = computed(() => {
131
+ const conversationId = currentConversation.value?.id;
132
+ return conversationId ? animatedTitleBuckets.value[conversationId] ?? null : null;
133
+ });
134
+ function markTitleGenerated(conversationId, generated) {
135
+ titleGeneratedBuckets.value = { ...titleGeneratedBuckets.value, [conversationId]: generated };
136
+ }
137
+ function setGeneratingTitle(conversationId, generating) {
138
+ generatingTitleBuckets.value = { ...generatingTitleBuckets.value, [conversationId]: generating };
139
+ }
140
+ function setAnimatedTitle(conversationId, title) {
141
+ animatedTitleBuckets.value = { ...animatedTitleBuckets.value, [conversationId]: title };
142
+ }
124
143
  const hasPendingMessage = computed(
125
144
  () => messages.value.some((m) => m.status === "pending")
126
145
  );
@@ -142,10 +161,10 @@ export function useChat(options = {}) {
142
161
  const messageId = streamingMessageId.value;
143
162
  if (!messageId)
144
163
  return;
145
- const pendingMessage = messages.value.find(
164
+ const isPendingAssistant = messages.value.some(
146
165
  (message) => message.id === messageId && message.role === "assistant" && message.status === ChatMessageStatus.Pending
147
166
  );
148
- if (!pendingMessage)
167
+ if (!isPendingAssistant)
149
168
  return;
150
169
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set([
151
170
  ...pendingStreamReconciliationIds.value,
@@ -160,11 +179,9 @@ export function useChat(options = {}) {
160
179
  content: resolveStreamingContent(message.content, result.text),
161
180
  status: ChatMessageStatus.Completed
162
181
  } : message);
163
- chatMessageTerminal.emit({
164
- conversationId: pendingMessage.conversationId,
165
- messageId,
166
- status: ChatMessageStatus.Completed
167
- });
182
+ }
183
+ function durableKnownMessages() {
184
+ return messages.value.map((message) => pendingStreamReconciliationIds.value.has(message.id) ? { ...message, status: ChatMessageStatus.Pending } : message);
168
185
  }
169
186
  function confirmStreamedResult(result) {
170
187
  if (result.status === "waiting_messages") {
@@ -242,7 +259,7 @@ export function useChat(options = {}) {
242
259
  const optimisticMessage = currentMessagesById.get(message.id);
243
260
  return optimisticMessage?.status === ChatMessageStatus.Completed ? optimisticMessage : message;
244
261
  });
245
- const terminalMessages = terminalMessageTransitions(messages.value, reconciledMessages);
262
+ const terminalMessages = terminalMessageTransitions(durableKnownMessages(), response.messages);
246
263
  pendingStreamReconciliationIds.value = remainingReconciliationIds;
247
264
  pendingRealResultIds.value = remainingRealResultIds;
248
265
  messages.value = reconciledMessages;
@@ -284,7 +301,7 @@ export function useChat(options = {}) {
284
301
  status: ChatMessageStatus.Failed
285
302
  };
286
303
  });
287
- const terminalMessages = terminalMessageTransitions(previousMessages, failedMessages);
304
+ const terminalMessages = terminalMessageTransitions(durableKnownMessages(), failedMessages);
288
305
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
289
306
  pendingRealResultIds.value = /* @__PURE__ */ new Set();
290
307
  messages.value = failedMessages;
@@ -338,7 +355,6 @@ export function useChat(options = {}) {
338
355
  };
339
356
  const adoptConversation = (conv) => {
340
357
  currentConversation.value = { ...conv, messages: messages.value };
341
- titleGenerated.value = false;
342
358
  };
343
359
  const setConversation = (conv) => {
344
360
  if (currentConversation.value && currentConversation.value.id !== conv.id) {
@@ -353,13 +369,11 @@ export function useChat(options = {}) {
353
369
  agentSessionStatus.value = null;
354
370
  agentSessionExecutable.value = false;
355
371
  agentSessionRewakeable.value = false;
356
- titleGenerated.value = false;
357
372
  error.value = null;
358
373
  };
359
374
  const loadConversation = async (id) => {
360
375
  loading.value = true;
361
376
  error.value = null;
362
- titleGenerated.value = false;
363
377
  try {
364
378
  const data = await $fetch(
365
379
  chatApi.path(`/conversations/${id}`),
@@ -369,9 +383,8 @@ export function useChat(options = {}) {
369
383
  messages.value = data.messages;
370
384
  pendingStreamReconciliationIds.value = /* @__PURE__ */ new Set();
371
385
  pendingRealResultIds.value = /* @__PURE__ */ new Set();
372
- if (shouldTreatLoadedConversationTitleAsGenerated(data.messages)) {
373
- titleGenerated.value = true;
374
- }
386
+ if (shouldTreatLoadedConversationTitleAsGenerated(data.messages))
387
+ markTitleGenerated(data.id, true);
375
388
  const pendingAssistant = data.messages.findLast((m) => m.role === "assistant" && m.status === "pending");
376
389
  if (pendingAssistant) {
377
390
  streamingMessageId.value = pendingAssistant.id;
@@ -750,30 +763,35 @@ export function useChat(options = {}) {
750
763
  return false;
751
764
  }
752
765
  };
753
- const animateTitle = (title) => {
766
+ const animateTitle = (title, conversationId) => {
754
767
  return new Promise((resolve) => {
755
- animatedTitle.value = "";
768
+ setAnimatedTitle(conversationId, "");
756
769
  let index = 0;
757
770
  const interval = setInterval(() => {
758
- if (index < title.length) {
759
- animatedTitle.value = title.slice(0, index + 1);
771
+ if (currentConversation.value?.id !== conversationId) {
772
+ clearInterval(interval);
773
+ setAnimatedTitle(conversationId, null);
774
+ resolve(false);
775
+ } else if (index < title.length) {
776
+ setAnimatedTitle(conversationId, title.slice(0, index + 1));
760
777
  index++;
761
778
  } else {
762
779
  clearInterval(interval);
763
- animatedTitle.value = null;
764
- resolve();
780
+ setAnimatedTitle(conversationId, null);
781
+ resolve(true);
765
782
  }
766
783
  }, 30);
767
784
  });
768
785
  };
769
786
  const generateTitle = async () => {
770
- if (!currentConversation.value || titleGenerated.value)
787
+ const conversation = currentConversation.value;
788
+ if (!conversation || titleGeneratedBuckets.value[conversation.id])
771
789
  return;
772
790
  const titleMessages = selectTitleGenerationMessages(messages.value);
773
791
  if (!titleMessages)
774
792
  return;
775
- titleGenerated.value = true;
776
- generatingTitle.value = true;
793
+ markTitleGenerated(conversation.id, true);
794
+ setGeneratingTitle(conversation.id, true);
777
795
  try {
778
796
  const { title } = await $fetch(
779
797
  chatApi.path("/conversations/generate-title"),
@@ -785,24 +803,27 @@ export function useChat(options = {}) {
785
803
  }
786
804
  })
787
805
  );
788
- if (title && currentConversation.value) {
789
- await animateTitle(title);
790
- await $fetch(chatApi.path(`/conversations/${currentConversation.value.id}`), {
791
- ...chatApi.withChatHeaders({
792
- method: "PATCH",
793
- body: { title }
794
- })
795
- });
796
- currentConversation.value = {
797
- ...currentConversation.value,
798
- title
799
- };
806
+ if (!title)
807
+ return;
808
+ const updated = await $fetch(
809
+ chatApi.path(`/conversations/${conversation.id}`),
810
+ chatApi.withChatHeaders({
811
+ method: "PATCH",
812
+ body: { title }
813
+ })
814
+ );
815
+ updateConversationInList(updated);
816
+ if (currentConversation.value?.id === conversation.id && await animateTitle(title, conversation.id)) {
817
+ const current = currentConversation.value;
818
+ if (current?.id === conversation.id)
819
+ currentConversation.value = { ...current, title };
800
820
  }
801
821
  } catch (err) {
822
+ markTitleGenerated(conversation.id, false);
802
823
  Sentry.captureException(err);
803
824
  } finally {
804
- generatingTitle.value = false;
805
- animatedTitle.value = null;
825
+ setGeneratingTitle(conversation.id, false);
826
+ setAnimatedTitle(conversation.id, null);
806
827
  }
807
828
  };
808
829
  const exportToMarkdown = () => {
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { proxyChatApiRequest } from "../../../utils/chat-api-proxy.js";
3
+ export default defineEventHandler((event) => proxyChatApiRequest(event, "conversations/generate-title"));
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import { defineEventHandler, getRouterParam } from "h3";
2
+ import { prefixProxyPath, proxyChatApiRequest } from "../../../../utils/chat-api-proxy.js";
3
+ export default defineEventHandler((event) => {
4
+ const groupId = getRouterParam(event, "groupId") || "";
5
+ return proxyChatApiRequest(event, prefixProxyPath("conversations/groups", groupId));
6
+ });
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { proxyChatApiRequest } from "../../../utils/chat-api-proxy.js";
3
+ export default defineEventHandler((event) => proxyChatApiRequest(event, "conversations/groups"));
@@ -0,0 +1,2 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { defineEventHandler } from "h3";
2
+ import { proxyChatApiRequest } from "../../../utils/chat-api-proxy.js";
3
+ export default defineEventHandler((event) => proxyChatApiRequest(event, "conversations/metadata"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/chat-nuxt",
3
- "version": "3.15.0-rc.0",
3
+ "version": "3.15.0-rc.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {