@meistrari/chat-nuxt 4.5.0-rc.0 → 4.5.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/README.md +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/embed/components/chat-embed-inner.d.vue.ts +12 -12
- package/dist/runtime/embed/components/chat-embed-inner.vue +11 -0
- package/dist/runtime/embed/components/chat-embed-inner.vue.d.ts +12 -12
- package/dist/runtime/lightweight/messages/composables/chat-model-catalog.js +1 -1
- package/dist/runtime/lightweight/messages/composables/chat.d.ts +1 -0
- package/dist/runtime/messages/composables/chat.d.ts +1 -0
- package/dist/runtime/messages/composables/chat.js +48 -13
- package/dist/runtime/messages/model-availability.js +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ Declared in `src/runtime/embed/types.ts`; prop, feature-flag, and event payload
|
|
|
88
88
|
| `initialConversationId` | `string \| null` | `null` | Conversation to open once on mount when `conversationId` is uncontrolled |
|
|
89
89
|
| `conversationScope` | `string \| null` | `null` | Isolate conversation history by technical scope within the current workspace and runtime. Scoped embeds only see same-scope conversations; values are trimmed, blank becomes `null`, and the server accepts 1-200 chars from `A-Z`, `a-z`, `0-9`, `.`, `_`, `:`, `/`, `-` |
|
|
90
90
|
| `defaultConversationCreatorFilter` | `'all' \| 'mine'` | `'all'` | Initial sidebar creator filter: every conversation in scope, or only the current user's |
|
|
91
|
-
| `availableModels` | `AvailableChatModels` | Workspace catalog | Optional
|
|
91
|
+
| `availableModels` | `AvailableChatModels` | Workspace catalog | Optional ordered subset of models for the active workspace. With the served catalog (lightweight harness), ids the catalog does not offer as available are dropped, and the full catalog is used when none remain; otherwise unknown ids fail at runtime. A restored unavailable selection stays visible and blocked until the user chooses an available model |
|
|
92
92
|
| `defaultModel` | `ChatModel` | Package default | Initial model for untouched drafts. Explicit selections and existing conversations take precedence; `availableModels` still limits the selection |
|
|
93
93
|
| `defaultReasoningEffort` | `ChatReasoningEffort` | Automatic (`null`) | Initial reasoning effort for untouched drafts. Explicit user choices (including automatic) take precedence |
|
|
94
94
|
| `conversationCreatorFilter` | `'all' \| 'mine'` | — | Controlled sidebar creator filter; use with `v-model:conversation-creator-filter` when the host changes it at runtime |
|
package/dist/module.json
CHANGED
|
@@ -19,29 +19,29 @@ type __VLS_Props = {
|
|
|
19
19
|
user?: EmbedUser | null;
|
|
20
20
|
features?: Partial<ChatFeatureConfig>;
|
|
21
21
|
};
|
|
22
|
-
declare var
|
|
22
|
+
declare var __VLS_14: {}, __VLS_88: {}, __VLS_101: {
|
|
23
23
|
message: useChatAuth;
|
|
24
|
-
},
|
|
24
|
+
}, __VLS_113: {}, __VLS_141: {}, __VLS_150: {}, __VLS_214: {}, __VLS_233: {
|
|
25
25
|
message: useChatAuth;
|
|
26
|
-
},
|
|
26
|
+
}, __VLS_245: {};
|
|
27
27
|
type __VLS_Slots = {} & {
|
|
28
|
-
'sidebar-bottom'?: (props: typeof
|
|
28
|
+
'sidebar-bottom'?: (props: typeof __VLS_14) => any;
|
|
29
29
|
} & {
|
|
30
|
-
'composer-bottom'?: (props: typeof
|
|
30
|
+
'composer-bottom'?: (props: typeof __VLS_88) => any;
|
|
31
31
|
} & {
|
|
32
|
-
'failed-message-actions'?: (props: typeof
|
|
32
|
+
'failed-message-actions'?: (props: typeof __VLS_101) => any;
|
|
33
33
|
} & {
|
|
34
|
-
'composer-bottom'?: (props: typeof
|
|
34
|
+
'composer-bottom'?: (props: typeof __VLS_113) => any;
|
|
35
35
|
} & {
|
|
36
|
-
'topbar-leading'?: (props: typeof
|
|
36
|
+
'topbar-leading'?: (props: typeof __VLS_141) => any;
|
|
37
37
|
} & {
|
|
38
|
-
'topbar-leading'?: (props: typeof
|
|
38
|
+
'topbar-leading'?: (props: typeof __VLS_150) => any;
|
|
39
39
|
} & {
|
|
40
|
-
'composer-bottom'?: (props: typeof
|
|
40
|
+
'composer-bottom'?: (props: typeof __VLS_214) => any;
|
|
41
41
|
} & {
|
|
42
|
-
'failed-message-actions'?: (props: typeof
|
|
42
|
+
'failed-message-actions'?: (props: typeof __VLS_233) => any;
|
|
43
43
|
} & {
|
|
44
|
-
'composer-bottom'?: (props: typeof
|
|
44
|
+
'composer-bottom'?: (props: typeof __VLS_245) => any;
|
|
45
45
|
};
|
|
46
46
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
47
47
|
"update:conversationId": (value: string | null) => any;
|
|
@@ -203,6 +203,7 @@ const {
|
|
|
203
203
|
generatingTitle,
|
|
204
204
|
animatedTitle,
|
|
205
205
|
composerLocked,
|
|
206
|
+
isPolling,
|
|
206
207
|
outboundOccupied,
|
|
207
208
|
sessionReady,
|
|
208
209
|
agentSessionRewakeable,
|
|
@@ -221,6 +222,7 @@ const {
|
|
|
221
222
|
retryMessage,
|
|
222
223
|
cancelConversation,
|
|
223
224
|
startPolling,
|
|
225
|
+
reconnect,
|
|
224
226
|
clearConversation,
|
|
225
227
|
error: chatError
|
|
226
228
|
} = createChat({
|
|
@@ -906,6 +908,15 @@ async function handleDelete() {
|
|
|
906
908
|
rounded-8px bg-error text-error p-12px body-14-regular
|
|
907
909
|
>
|
|
908
910
|
{{ chatError }}
|
|
911
|
+
<TelaButton
|
|
912
|
+
v-if="composerLocked && !isPolling"
|
|
913
|
+
variant="ghost-danger"
|
|
914
|
+
size="sm"
|
|
915
|
+
icon="i-ph-arrow-clockwise"
|
|
916
|
+
@click="reconnect"
|
|
917
|
+
>
|
|
918
|
+
Reconectar
|
|
919
|
+
</TelaButton>
|
|
909
920
|
</div>
|
|
910
921
|
<!-- Sidebar -->
|
|
911
922
|
<div
|
|
@@ -19,29 +19,29 @@ type __VLS_Props = {
|
|
|
19
19
|
user?: EmbedUser | null;
|
|
20
20
|
features?: Partial<ChatFeatureConfig>;
|
|
21
21
|
};
|
|
22
|
-
declare var
|
|
22
|
+
declare var __VLS_14: {}, __VLS_88: {}, __VLS_101: {
|
|
23
23
|
message: useChatAuth;
|
|
24
|
-
},
|
|
24
|
+
}, __VLS_113: {}, __VLS_141: {}, __VLS_150: {}, __VLS_214: {}, __VLS_233: {
|
|
25
25
|
message: useChatAuth;
|
|
26
|
-
},
|
|
26
|
+
}, __VLS_245: {};
|
|
27
27
|
type __VLS_Slots = {} & {
|
|
28
|
-
'sidebar-bottom'?: (props: typeof
|
|
28
|
+
'sidebar-bottom'?: (props: typeof __VLS_14) => any;
|
|
29
29
|
} & {
|
|
30
|
-
'composer-bottom'?: (props: typeof
|
|
30
|
+
'composer-bottom'?: (props: typeof __VLS_88) => any;
|
|
31
31
|
} & {
|
|
32
|
-
'failed-message-actions'?: (props: typeof
|
|
32
|
+
'failed-message-actions'?: (props: typeof __VLS_101) => any;
|
|
33
33
|
} & {
|
|
34
|
-
'composer-bottom'?: (props: typeof
|
|
34
|
+
'composer-bottom'?: (props: typeof __VLS_113) => any;
|
|
35
35
|
} & {
|
|
36
|
-
'topbar-leading'?: (props: typeof
|
|
36
|
+
'topbar-leading'?: (props: typeof __VLS_141) => any;
|
|
37
37
|
} & {
|
|
38
|
-
'topbar-leading'?: (props: typeof
|
|
38
|
+
'topbar-leading'?: (props: typeof __VLS_150) => any;
|
|
39
39
|
} & {
|
|
40
|
-
'composer-bottom'?: (props: typeof
|
|
40
|
+
'composer-bottom'?: (props: typeof __VLS_214) => any;
|
|
41
41
|
} & {
|
|
42
|
-
'failed-message-actions'?: (props: typeof
|
|
42
|
+
'failed-message-actions'?: (props: typeof __VLS_233) => any;
|
|
43
43
|
} & {
|
|
44
|
-
'composer-bottom'?: (props: typeof
|
|
44
|
+
'composer-bottom'?: (props: typeof __VLS_245) => any;
|
|
45
45
|
};
|
|
46
46
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
47
47
|
"update:conversationId": (value: string | null) => any;
|
|
@@ -14,7 +14,7 @@ export function useChatModelCatalog() {
|
|
|
14
14
|
const catalogKey = computed(
|
|
15
15
|
() => chatApi.withChatHeaders().headers?.["x-chat-workspace-id"] ?? "__default__"
|
|
16
16
|
);
|
|
17
|
-
const models = computed(() => catalogs.value[catalogKey.value] ?? []);
|
|
17
|
+
const models = computed(() => (catalogs.value[catalogKey.value] ?? []).filter((model) => model.status === "available"));
|
|
18
18
|
const isLoaded = computed(() => loadedCatalogs.value[catalogKey.value] ?? false);
|
|
19
19
|
const isLoading = computed(() => loadingCatalogs.value[catalogKey.value] ?? false);
|
|
20
20
|
const defaultModel = computed(
|
|
@@ -35,6 +35,7 @@ export declare function useChat(options?: Parameters<typeof useSharedChat>[0]):
|
|
|
35
35
|
retryMessage: (messageId: string, prepare?: () => Promise<void>) => Promise<boolean>;
|
|
36
36
|
cancelConversation: () => Promise<boolean>;
|
|
37
37
|
startPolling: () => void;
|
|
38
|
+
reconnect: () => void;
|
|
38
39
|
stopPolling: () => void;
|
|
39
40
|
clearConversation: () => void;
|
|
40
41
|
clearConversationThread: () => Promise<boolean>;
|
|
@@ -57,6 +57,7 @@ export declare function useChat(options?: {
|
|
|
57
57
|
retryMessage: (messageId: string, prepare?: () => Promise<void>) => Promise<boolean>;
|
|
58
58
|
cancelConversation: () => Promise<boolean>;
|
|
59
59
|
startPolling: () => void;
|
|
60
|
+
reconnect: () => void;
|
|
60
61
|
stopPolling: () => void;
|
|
61
62
|
clearConversation: () => void;
|
|
62
63
|
clearConversationThread: () => Promise<boolean>;
|
|
@@ -28,6 +28,12 @@ import { usePolling } from "../../shared/composables/polling.js";
|
|
|
28
28
|
import { ChatMessageStatus, terminalMessageTransitions, useChatMessageTerminal } from "./chat-message-terminal.js";
|
|
29
29
|
import { useDirectStream } from "./use-direct-stream.js";
|
|
30
30
|
import { useOutboundQueue } from "./outbound-queue.js";
|
|
31
|
+
const POLL_OUTAGE_NOTICE_FAILURES = 5;
|
|
32
|
+
const POLL_OUTAGE_BUDGET_MS = 6e4;
|
|
33
|
+
const POLL_INTERVAL_MS = 500;
|
|
34
|
+
const POLL_MAX_BACKOFF_MS = 5e3;
|
|
35
|
+
const CHAT_REQUEST_TIMEOUT_MS = 15e3;
|
|
36
|
+
const CHAT_UNAVAILABLE_TEXT = "Chat temporariamente indispon\xEDvel. Tente novamente.";
|
|
31
37
|
const WORKSPACE_AGENT_NOT_READY_TEXT = "O agente deste workspace ainda n\xE3o foi configurado no Tela. Abra as configura\xE7\xF5es do workspace para criar o agente.";
|
|
32
38
|
export function resolveTelaAgentInputs(messageInputs, configuredInputs) {
|
|
33
39
|
const inputs = messageInputs ?? configuredInputs;
|
|
@@ -241,6 +247,9 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
241
247
|
}
|
|
242
248
|
}
|
|
243
249
|
let pollGeneration = 0;
|
|
250
|
+
let consecutivePollFailures = 0;
|
|
251
|
+
let pollOutageStartedAt = null;
|
|
252
|
+
const pollInterval = ref(POLL_INTERVAL_MS);
|
|
244
253
|
const pollMessages = async () => {
|
|
245
254
|
if (!currentConversation.value)
|
|
246
255
|
return;
|
|
@@ -251,14 +260,17 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
251
260
|
try {
|
|
252
261
|
const runtime = chatApi.conversationV2Enabled ? await $fetch(
|
|
253
262
|
chatApi.runtimePath(`/conversations/${conversationId}/runtime`),
|
|
254
|
-
chatApi.withChatHeaders()
|
|
263
|
+
chatApi.withChatHeaders({ timeout: CHAT_REQUEST_TIMEOUT_MS, retry: 0 })
|
|
255
264
|
) : null;
|
|
256
265
|
const response = await $fetch(
|
|
257
266
|
chatApi.path(`/conversations/${conversationId}/messages`),
|
|
258
|
-
chatApi.withChatHeaders()
|
|
267
|
+
chatApi.withChatHeaders({ timeout: CHAT_REQUEST_TIMEOUT_MS, retry: 0 })
|
|
259
268
|
);
|
|
260
269
|
if (generation !== pollGeneration || currentConversation.value?.id !== conversationId)
|
|
261
270
|
return;
|
|
271
|
+
resetPollOutage();
|
|
272
|
+
if (error.value === CHAT_UNAVAILABLE_TEXT)
|
|
273
|
+
error.value = null;
|
|
262
274
|
if (runtime)
|
|
263
275
|
runtimeBlocked.value = runtime.conversation.runtimeState === "blocked";
|
|
264
276
|
if (response.agentSession) {
|
|
@@ -332,16 +344,28 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
332
344
|
Sentry.captureException(err);
|
|
333
345
|
if (generation !== pollGeneration)
|
|
334
346
|
return;
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
|
|
347
|
+
const statusCode = getChatApiErrorStatus(err);
|
|
348
|
+
const terminalError = statusCode === 501 || statusCode !== null && statusCode >= 400 && statusCode < 500 && statusCode !== 408 && statusCode !== 429;
|
|
349
|
+
consecutivePollFailures++;
|
|
350
|
+
pollOutageStartedAt ??= Date.now();
|
|
351
|
+
pollInterval.value = Math.min(POLL_INTERVAL_MS * 2 ** consecutivePollFailures, POLL_MAX_BACKOFF_MS);
|
|
352
|
+
if (!terminalError && consecutivePollFailures >= POLL_OUTAGE_NOTICE_FAILURES)
|
|
353
|
+
error.value = CHAT_UNAVAILABLE_TEXT;
|
|
354
|
+
const outageExhausted = Date.now() - pollOutageStartedAt >= POLL_OUTAGE_BUDGET_MS;
|
|
355
|
+
if (terminalError || outageExhausted) {
|
|
356
|
+
const message = statusCode === 501 || !terminalError ? CHAT_UNAVAILABLE_TEXT : getChatApiErrorMessage(err) ?? "Erro ao atualizar mensagens";
|
|
357
|
+
if (!terminalError || statusCode === 501 || outbound.occupied.value) {
|
|
358
|
+
error.value = message;
|
|
359
|
+
stopPolling();
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
338
362
|
const previousMessages = messages.value;
|
|
339
|
-
const failedMessages = markPendingAssistantMessagesFailed(previousMessages).map((
|
|
340
|
-
if (
|
|
341
|
-
return
|
|
363
|
+
const failedMessages = markPendingAssistantMessagesFailed(previousMessages).map((message2) => {
|
|
364
|
+
if (message2.role !== "assistant" || message2.status !== ChatMessageStatus.Completed || !pendingStreamReconciliationIds.value.has(message2.id)) {
|
|
365
|
+
return message2;
|
|
342
366
|
}
|
|
343
367
|
return {
|
|
344
|
-
...
|
|
368
|
+
...message2,
|
|
345
369
|
content: GENERATION_FAILED_TEXT,
|
|
346
370
|
status: ChatMessageStatus.Failed
|
|
347
371
|
};
|
|
@@ -352,20 +376,30 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
352
376
|
messages.value = failedMessages;
|
|
353
377
|
for (const terminalMessage of terminalMessages)
|
|
354
378
|
chatMessageTerminal.emit(terminalMessage);
|
|
355
|
-
error.value =
|
|
379
|
+
error.value = message;
|
|
356
380
|
stopPolling();
|
|
357
381
|
}
|
|
358
382
|
}
|
|
359
383
|
};
|
|
360
384
|
const { isPolling, start: beginPolling, stop: endPolling } = usePolling({
|
|
361
385
|
callback: pollMessages,
|
|
362
|
-
interval:
|
|
386
|
+
interval: pollInterval,
|
|
363
387
|
stopCondition
|
|
364
388
|
});
|
|
389
|
+
function resetPollOutage() {
|
|
390
|
+
consecutivePollFailures = 0;
|
|
391
|
+
pollOutageStartedAt = null;
|
|
392
|
+
pollInterval.value = POLL_INTERVAL_MS;
|
|
393
|
+
}
|
|
365
394
|
function startPolling() {
|
|
395
|
+
resetPollOutage();
|
|
366
396
|
pollGeneration++;
|
|
367
397
|
beginPolling();
|
|
368
398
|
}
|
|
399
|
+
function reconnect() {
|
|
400
|
+
error.value = null;
|
|
401
|
+
startPolling();
|
|
402
|
+
}
|
|
369
403
|
function stopPolling() {
|
|
370
404
|
pollGeneration++;
|
|
371
405
|
endPolling();
|
|
@@ -614,7 +648,7 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
614
648
|
v2AdmissionRequested = true;
|
|
615
649
|
const accepted = await $fetch(
|
|
616
650
|
chatApi.runtimePath(`/conversations/${conversationId2}/messages`),
|
|
617
|
-
chatApi.withChatHeaders({ method: "POST", body: v2Body, retry: 1 })
|
|
651
|
+
chatApi.withChatHeaders({ method: "POST", body: v2Body, retry: 1, timeout: CHAT_REQUEST_TIMEOUT_MS })
|
|
618
652
|
);
|
|
619
653
|
response = accepted;
|
|
620
654
|
if (currentConversation.value?.id !== conversationId2)
|
|
@@ -661,7 +695,7 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
661
695
|
} catch (err) {
|
|
662
696
|
const code = getChatApiErrorCode(err);
|
|
663
697
|
const status = getChatApiErrorStatus(err);
|
|
664
|
-
if (v2AdmissionRequested && !controller.signal.aborted && (status === null || status === 408 || status === 429 || status >= 500)) {
|
|
698
|
+
if (v2AdmissionRequested && !controller.signal.aborted && (status === null || status === 408 || status === 429 || status >= 500 && status !== 501)) {
|
|
665
699
|
const attempts = (queuedSend?.attempts ?? 0) + 1;
|
|
666
700
|
const queuedAt = queuedSend?.queuedAt ?? Date.now();
|
|
667
701
|
if (outboundQueueEnabled.value) {
|
|
@@ -1029,6 +1063,7 @@ export function useChat(options = {}, adapter = {}) {
|
|
|
1029
1063
|
retryMessage,
|
|
1030
1064
|
cancelConversation,
|
|
1031
1065
|
startPolling,
|
|
1066
|
+
reconnect,
|
|
1032
1067
|
stopPolling,
|
|
1033
1068
|
clearConversation,
|
|
1034
1069
|
clearConversationThread,
|
|
@@ -7,13 +7,16 @@ export function resolveAvailableChatModels(availableModels, catalogModels) {
|
|
|
7
7
|
throw new Error("Agent API model catalog must contain at least one model");
|
|
8
8
|
if (availableModels === void 0)
|
|
9
9
|
return [...new Set(catalog)];
|
|
10
|
+
if (catalogModels) {
|
|
11
|
+
const offeredModels = [...new Set(availableModels.filter((model) => catalog.includes(model)))];
|
|
12
|
+
return offeredModels.length ? offeredModels : [...new Set(catalog)];
|
|
13
|
+
}
|
|
10
14
|
if (availableModels.length === 0)
|
|
11
15
|
throw new Error("availableModels must contain at least one model");
|
|
12
16
|
const uniqueModels = [];
|
|
13
17
|
for (const model of availableModels) {
|
|
14
|
-
if (!catalog.includes(model))
|
|
15
|
-
throw new Error(
|
|
16
|
-
}
|
|
18
|
+
if (!catalog.includes(model))
|
|
19
|
+
throw new Error(`${model} is not a supported chat model`);
|
|
17
20
|
if (!uniqueModels.includes(model))
|
|
18
21
|
uniqueModels.push(model);
|
|
19
22
|
}
|