@meistrari/chat-nuxt 1.2.3 → 1.4.0
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 +70 -38
- package/dist/module.d.mts +24 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +38 -29
- package/dist/runtime/components/MeistrariChatEmbed.d.vue.ts +9 -2
- package/dist/runtime/components/MeistrariChatEmbed.vue +11 -4
- package/dist/runtime/components/MeistrariChatEmbed.vue.d.ts +9 -2
- package/dist/runtime/components/app-markdown-render.d.vue.ts +3 -1
- package/dist/runtime/components/app-markdown-render.vue +15 -4
- package/dist/runtime/components/app-markdown-render.vue.d.ts +3 -1
- package/dist/runtime/components/chat/message-bubble.vue +10 -2
- package/dist/runtime/components/chat/thinking-indicator.vue +28 -17
- package/dist/runtime/composables/useChat.js +8 -1
- package/dist/runtime/composables/useChatAction.d.ts +10 -0
- package/dist/runtime/composables/useChatAction.js +12 -0
- package/dist/runtime/composables/useEmbedConfig.d.ts +10 -1
- package/dist/runtime/composables/useEmbedConfig.js +24 -3
- package/dist/runtime/embed/components/ChatConfigurationModal.vue +8 -6
- package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +9 -2
- package/dist/runtime/embed/components/ChatEmbed.vue +22 -13
- package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +9 -2
- package/dist/runtime/embed/components/ChatEmbedInner.vue +5 -1
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +7 -4
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +1 -1
- package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +15 -9
- package/dist/runtime/server/api/conversations/[id]/usage.get.js +1 -1
- package/dist/runtime/server/api/conversations/generate-title.post.js +18 -5
- package/dist/runtime/server/api/workspace/usage.get.js +1 -1
- package/dist/runtime/server/db/index.d.ts +20 -3
- package/dist/runtime/server/db/index.js +149 -10
- package/dist/runtime/server/db/schema/_schema.d.ts +2 -0
- package/dist/runtime/server/db/schema/_schema.js +3 -0
- package/dist/runtime/server/db/schema/conversation-usage.d.ts +1 -1
- package/dist/runtime/server/db/schema/conversation-usage.js +3 -2
- package/dist/runtime/server/db/schema/conversations.d.ts +1 -1
- package/dist/runtime/server/db/schema/conversations.js +3 -2
- package/dist/runtime/server/db/schema/external-skills.d.ts +1 -1
- package/dist/runtime/server/db/schema/external-skills.js +3 -2
- package/dist/runtime/server/db/schema/index.d.ts +1 -0
- package/dist/runtime/server/db/schema/index.js +1 -0
- package/dist/runtime/server/db/schema/messages.d.ts +1 -1
- package/dist/runtime/server/db/schema/messages.js +3 -2
- package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
- package/dist/runtime/server/db/schema/workspace-settings.js +3 -2
- package/dist/runtime/server/plugins/init-db.d.ts +2 -0
- package/dist/runtime/server/plugins/init-db.js +11 -0
- package/dist/runtime/server/utils/agent-api.d.ts +1 -1
- package/dist/runtime/server/utils/agent-api.js +2 -5
- package/dist/runtime/server/utils/billing.d.ts +26 -4
- package/dist/runtime/server/utils/billing.js +52 -13
- package/dist/runtime/server/utils/chat-workspace-settings.d.ts +2 -1
- package/dist/runtime/server/utils/chat-workspace-settings.js +11 -2
- package/dist/runtime/utils/chat-loading-messages.d.ts +12 -0
- package/dist/runtime/utils/chat-loading-messages.js +40 -0
- package/dist/runtime/utils/custom-markdown-components.d.ts +9 -0
- package/dist/runtime/utils/custom-markdown-components.js +54 -0
- package/dist/runtime/utils/features.d.ts +6 -0
- package/dist/runtime/utils/features.js +15 -0
- package/dist/types.d.mts +1 -1
- package/drizzle/0000_material_hiroim.sql +28 -0
- package/drizzle/0001_famous_scalphunter.sql +1 -0
- package/drizzle/0002_gorgeous_big_bertha.sql +1 -0
- package/drizzle/0003_massive_forge.sql +2 -0
- package/drizzle/0004_absent_black_tom.sql +2 -0
- package/drizzle/0005_first_kid_colt.sql +1 -0
- package/drizzle/0006_sloppy_mauler.sql +13 -0
- package/drizzle/0007_grey_rhino.sql +1 -0
- package/drizzle/0008_chilly_blue_blade.sql +27 -0
- package/drizzle/0009_cultured_maddog.sql +16 -0
- package/drizzle/0010_small_barracuda.sql +1 -0
- package/drizzle/0011_fat_lady_mastermind.sql +6 -0
- package/drizzle/0012_puzzling_rick_jones.sql +1 -0
- package/drizzle/0013_blue_gwen_stacy.sql +11 -0
- package/drizzle/meta/0000_snapshot.json +239 -0
- package/drizzle/meta/0001_snapshot.json +245 -0
- package/drizzle/meta/0002_snapshot.json +251 -0
- package/drizzle/meta/0003_snapshot.json +263 -0
- package/drizzle/meta/0004_snapshot.json +275 -0
- package/drizzle/meta/0005_snapshot.json +281 -0
- package/drizzle/meta/0006_snapshot.json +320 -0
- package/drizzle/meta/0007_snapshot.json +326 -0
- package/drizzle/meta/0008_snapshot.json +539 -0
- package/drizzle/meta/0009_snapshot.json +646 -0
- package/drizzle/meta/0010_snapshot.json +653 -0
- package/drizzle/meta/0011_snapshot.json +689 -0
- package/drizzle/meta/0012_snapshot.json +695 -0
- package/drizzle/meta/0013_snapshot.json +699 -0
- package/drizzle/meta/_journal.json +104 -0
- package/package.json +5 -3
- package/dist/runtime/server/tasks/send-daily-usage-report.d.ts +0 -2
- package/dist/runtime/server/tasks/send-daily-usage-report.js +0 -74
- package/dist/runtime/server/utils/auth-api.d.ts +0 -1
- package/dist/runtime/server/utils/auth-api.js +0 -27
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ChatActionPayload = {
|
|
2
|
+
name: string;
|
|
3
|
+
data: unknown;
|
|
4
|
+
messageId?: string;
|
|
5
|
+
};
|
|
6
|
+
export type ChatActionHandler = (payload: ChatActionPayload) => void;
|
|
7
|
+
export declare function provideChatAction(handler: ChatActionHandler): void;
|
|
8
|
+
export declare function useChatAction(): {
|
|
9
|
+
emit(name: string, data?: unknown, messageId?: string): void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const CHAT_ACTION_KEY = Symbol("chat-nuxt:action");
|
|
2
|
+
export function provideChatAction(handler) {
|
|
3
|
+
provide(CHAT_ACTION_KEY, handler);
|
|
4
|
+
}
|
|
5
|
+
export function useChatAction() {
|
|
6
|
+
const handler = inject(CHAT_ACTION_KEY, null);
|
|
7
|
+
return {
|
|
8
|
+
emit(name, data, messageId) {
|
|
9
|
+
handler?.({ name, data, messageId });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import type { DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
|
|
1
|
+
import type { Component, DeepReadonly, MaybeRefOrGetter, Ref } from 'vue';
|
|
2
|
+
import type { ChatLoadingMessageMode } from '../utils/chat-loading-messages.js';
|
|
2
3
|
import type { WorkspaceSettings } from './useWorkspaceSettings.js';
|
|
3
4
|
export type EmbedConfig = {
|
|
4
5
|
workspaceId: Ref<string>;
|
|
5
6
|
workspaceSettings: Ref<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
6
7
|
hideSidebar: Ref<boolean>;
|
|
8
|
+
loadingMessages: Ref<readonly string[] | null | undefined>;
|
|
9
|
+
loadingMessagesMode: Ref<ChatLoadingMessageMode | null | undefined>;
|
|
10
|
+
customComponents: Ref<Record<string, Component> | undefined>;
|
|
11
|
+
customHtmlTags: Ref<readonly string[] | undefined>;
|
|
7
12
|
};
|
|
8
13
|
type EmbedConfigInput = {
|
|
9
14
|
workspaceId: MaybeRefOrGetter<string>;
|
|
10
15
|
workspaceSettings?: MaybeRefOrGetter<DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null | undefined>;
|
|
11
16
|
hideSidebar?: MaybeRefOrGetter<boolean | undefined>;
|
|
17
|
+
loadingMessages?: MaybeRefOrGetter<readonly string[] | null | undefined>;
|
|
18
|
+
loadingMessagesMode?: MaybeRefOrGetter<ChatLoadingMessageMode | null | undefined>;
|
|
19
|
+
customComponents?: MaybeRefOrGetter<Record<string, Component> | undefined>;
|
|
20
|
+
customHtmlTags?: MaybeRefOrGetter<readonly string[] | undefined>;
|
|
12
21
|
};
|
|
13
22
|
type ProvideEmbedConfigOptions = {
|
|
14
23
|
syncGlobal?: boolean;
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
const EMBED_CONFIG_KEY = "embed-config";
|
|
2
|
+
function useGlobalCustomComponentsState() {
|
|
3
|
+
const nuxtApp = useNuxtApp();
|
|
4
|
+
nuxtApp._chatEmbedConfigCustomComponents ??= shallowRef(void 0);
|
|
5
|
+
return nuxtApp._chatEmbedConfigCustomComponents;
|
|
6
|
+
}
|
|
2
7
|
function useGlobalEmbedConfigState() {
|
|
3
8
|
return {
|
|
4
9
|
provided: useState("chat-embed-config-provided", () => false),
|
|
5
10
|
workspaceId: useState("chat-embed-config-workspace-id", () => ""),
|
|
6
11
|
workspaceSettings: useState("chat-embed-config-workspace-settings", () => void 0),
|
|
7
|
-
hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0)
|
|
12
|
+
hideSidebar: useState("chat-embed-config-hide-sidebar", () => void 0),
|
|
13
|
+
loadingMessages: useState("chat-embed-config-loading-messages", () => void 0),
|
|
14
|
+
loadingMessagesMode: useState("chat-embed-config-loading-messages-mode", () => void 0),
|
|
15
|
+
customComponents: useGlobalCustomComponentsState(),
|
|
16
|
+
customHtmlTags: useState("chat-embed-config-custom-html-tags", () => void 0)
|
|
8
17
|
};
|
|
9
18
|
}
|
|
10
19
|
export function provideEmbedConfig(input, options = {}) {
|
|
11
20
|
const config = {
|
|
12
21
|
workspaceId: computed(() => toValue(input.workspaceId)),
|
|
13
22
|
workspaceSettings: computed(() => toValue(input.workspaceSettings)),
|
|
14
|
-
hideSidebar: computed(() => toValue(input.hideSidebar) ?? true)
|
|
23
|
+
hideSidebar: computed(() => toValue(input.hideSidebar) ?? true),
|
|
24
|
+
loadingMessages: computed(() => toValue(input.loadingMessages)),
|
|
25
|
+
loadingMessagesMode: computed(() => toValue(input.loadingMessagesMode)),
|
|
26
|
+
customComponents: computed(() => toValue(input.customComponents)),
|
|
27
|
+
customHtmlTags: computed(() => toValue(input.customHtmlTags))
|
|
15
28
|
};
|
|
16
29
|
const globalConfig = useGlobalEmbedConfigState();
|
|
17
30
|
if (options.syncGlobal ?? true) {
|
|
@@ -20,6 +33,10 @@ export function provideEmbedConfig(input, options = {}) {
|
|
|
20
33
|
globalConfig.workspaceId.value = config.workspaceId.value;
|
|
21
34
|
globalConfig.workspaceSettings.value = config.workspaceSettings.value;
|
|
22
35
|
globalConfig.hideSidebar.value = config.hideSidebar.value;
|
|
36
|
+
globalConfig.loadingMessages.value = config.loadingMessages.value;
|
|
37
|
+
globalConfig.loadingMessagesMode.value = config.loadingMessagesMode.value;
|
|
38
|
+
globalConfig.customComponents.value = config.customComponents.value;
|
|
39
|
+
globalConfig.customHtmlTags.value = config.customHtmlTags.value;
|
|
23
40
|
});
|
|
24
41
|
}
|
|
25
42
|
provide(EMBED_CONFIG_KEY, config);
|
|
@@ -34,6 +51,10 @@ export function useEmbedConfig() {
|
|
|
34
51
|
return {
|
|
35
52
|
workspaceId: computed(() => globalConfig.workspaceId.value),
|
|
36
53
|
workspaceSettings: computed(() => globalConfig.workspaceSettings.value),
|
|
37
|
-
hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true)
|
|
54
|
+
hideSidebar: computed(() => globalConfig.hideSidebar.value ?? true),
|
|
55
|
+
loadingMessages: computed(() => globalConfig.loadingMessages.value),
|
|
56
|
+
loadingMessagesMode: computed(() => globalConfig.loadingMessagesMode.value),
|
|
57
|
+
customComponents: computed(() => globalConfig.customComponents.value),
|
|
58
|
+
customHtmlTags: computed(() => globalConfig.customHtmlTags.value)
|
|
38
59
|
};
|
|
39
60
|
}
|
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
fetchSettings,
|
|
19
19
|
updateSettings
|
|
20
20
|
} = useWorkspaceSettings();
|
|
21
|
+
const features = useChatNuxtFeatures();
|
|
21
22
|
const searchQuery = ref("");
|
|
22
23
|
const activeTab = ref(props.initialTab);
|
|
23
24
|
const showDiscardConfirm = ref(false);
|
|
@@ -34,14 +35,14 @@ const telaTools = useWorkspaceTelaTools(canvas, projects, searchQuery, currentCa
|
|
|
34
35
|
const knowledgeSources = useWorkspaceKnowledgeSources(workstations, canvas, projects, searchQuery, currentKnowledgeSources);
|
|
35
36
|
const contextFiles = useWorkspaceContextFiles(searchQuery, currentContextFiles);
|
|
36
37
|
const externalSkills = useWorkspaceExternalSkills(currentExternalSkills, searchQuery);
|
|
37
|
-
const settingsTabs = [
|
|
38
|
+
const settingsTabs = computed(() => [
|
|
38
39
|
{ value: "personalization", label: "Personaliza\xE7\xE3o", icon: "i-ph-sliders" },
|
|
39
40
|
{ value: "context-files", label: "Arquivos de contexto", icon: "i-ph-file-text" },
|
|
40
41
|
{ value: "knowledge-sources", label: "Tela workstations", icon: "i-ph-database" },
|
|
41
42
|
{ value: "tela-tools", label: "Skills Tela", icon: "i-ph-puzzle-piece" },
|
|
42
43
|
{ value: "external-skills", label: "Skills Externas", icon: "i-ph-wrench" },
|
|
43
|
-
{ value: "credentials", label: "Credenciais", icon: "i-ph-password-bold" }
|
|
44
|
-
];
|
|
44
|
+
...features.credentials ? [{ value: "credentials", label: "Credenciais", icon: "i-ph-password-bold" }] : []
|
|
45
|
+
]);
|
|
45
46
|
const hasChanges = computed(
|
|
46
47
|
() => personalization.hasChanges.value || contextFiles.hasChanges.value || telaTools.hasChanges.value || knowledgeSources.hasChanges.value || externalSkills.hasChanges.value
|
|
47
48
|
);
|
|
@@ -84,9 +85,10 @@ function initializeConfigurationState() {
|
|
|
84
85
|
externalSkills.initialize();
|
|
85
86
|
}
|
|
86
87
|
function activateTab(tab) {
|
|
87
|
-
|
|
88
|
+
const isVisible = settingsTabs.value.some((t) => t.value === tab);
|
|
89
|
+
activeTab.value = isVisible ? tab : "personalization";
|
|
88
90
|
searchQuery.value = "";
|
|
89
|
-
if (
|
|
91
|
+
if (activeTab.value === "external-skills")
|
|
90
92
|
void externalSkills.fetchGitHubConnection();
|
|
91
93
|
}
|
|
92
94
|
async function loadSettings() {
|
|
@@ -336,7 +338,7 @@ function cancelDiscard() {
|
|
|
336
338
|
/>
|
|
337
339
|
|
|
338
340
|
<ChatConfigurationCredentialsTab
|
|
339
|
-
v-else-if="activeTab === 'credentials'"
|
|
341
|
+
v-else-if="activeTab === 'credentials' && features.credentials"
|
|
340
342
|
:search-query="searchQuery"
|
|
341
343
|
@update:search-query="searchQuery = $event"
|
|
342
344
|
/>
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import type { DeepReadonly } from 'vue';
|
|
1
|
+
import type { Component, DeepReadonly } from 'vue';
|
|
2
|
+
import { type ChatActionPayload } from '../../composables/useChatAction.js';
|
|
2
3
|
import type { WorkspaceSettings } from '../../composables/useWorkspaceSettings.js';
|
|
4
|
+
import type { ChatLoadingMessageMode } from '../../utils/chat-loading-messages.js';
|
|
3
5
|
import type { ChatActor, ChatFeatureConfig } from '../../utils/tela-chat.js';
|
|
4
6
|
type EmbedUser = ChatActor;
|
|
5
7
|
type __VLS_Props = {
|
|
6
|
-
workspaceId: string;
|
|
7
8
|
workspaceSettings?: DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null;
|
|
8
9
|
conversationId?: string | null;
|
|
9
10
|
initialConversationId?: string | null;
|
|
10
11
|
hideSidebar?: boolean;
|
|
11
12
|
user?: EmbedUser | null;
|
|
12
13
|
features?: Partial<ChatFeatureConfig>;
|
|
14
|
+
loadingMessages?: readonly string[] | null;
|
|
15
|
+
loadingMessagesMode?: ChatLoadingMessageMode | null;
|
|
16
|
+
customComponents?: Record<string, Component>;
|
|
17
|
+
customHtmlTags?: readonly string[];
|
|
13
18
|
};
|
|
14
19
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
15
20
|
"update:conversationId": (value: string | null) => any;
|
|
21
|
+
action: (payload: ChatActionPayload) => any;
|
|
16
22
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
17
23
|
"onUpdate:conversationId"?: ((value: string | null) => any) | undefined;
|
|
24
|
+
onAction?: ((payload: ChatActionPayload) => any) | undefined;
|
|
18
25
|
}>, {
|
|
19
26
|
initialConversationId: string | null;
|
|
20
27
|
hideSidebar: boolean;
|
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { provideEmbedConfig, useEmbedConfig } from "../../composables/useEmbedConfig";
|
|
3
|
+
import { provideChatAction } from "../../composables/useChatAction";
|
|
3
4
|
import ChatEmbedInner from "./ChatEmbedInner.vue";
|
|
4
5
|
const props = defineProps({
|
|
5
|
-
workspaceId: { type: String, required: true },
|
|
6
6
|
workspaceSettings: { type: null, required: false },
|
|
7
7
|
conversationId: { type: [String, null], required: false },
|
|
8
8
|
initialConversationId: { type: [String, null], required: false, default: null },
|
|
9
9
|
hideSidebar: { type: Boolean, required: false, default: true },
|
|
10
10
|
user: { type: [Object, null], required: false },
|
|
11
|
-
features: { type: Object, required: false }
|
|
11
|
+
features: { type: Object, required: false },
|
|
12
|
+
loadingMessages: { type: [Array, null], required: false },
|
|
13
|
+
loadingMessagesMode: { type: [String, null], required: false },
|
|
14
|
+
customComponents: { type: Object, required: false },
|
|
15
|
+
customHtmlTags: { type: Array, required: false }
|
|
12
16
|
});
|
|
13
|
-
defineEmits(["update:conversationId"]);
|
|
17
|
+
const emit = defineEmits(["update:conversationId", "action"]);
|
|
14
18
|
const parentEmbedConfig = useEmbedConfig();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const { activeOrganization } = useTelaApplicationAuth();
|
|
20
|
+
const workspaceId = computed(() => activeOrganization.value?.id?.trim() ?? "");
|
|
21
|
+
provideEmbedConfig({
|
|
22
|
+
workspaceId,
|
|
23
|
+
workspaceSettings: computed(() => props.workspaceSettings),
|
|
24
|
+
hideSidebar: computed(() => props.hideSidebar),
|
|
25
|
+
loadingMessages: computed(() => props.loadingMessages),
|
|
26
|
+
loadingMessagesMode: computed(() => props.loadingMessagesMode),
|
|
27
|
+
customComponents: computed(() => props.customComponents),
|
|
28
|
+
customHtmlTags: computed(() => props.customHtmlTags)
|
|
29
|
+
}, { syncGlobal: !parentEmbedConfig });
|
|
30
|
+
provideChatAction((payload) => {
|
|
31
|
+
emit("action", payload);
|
|
32
|
+
});
|
|
25
33
|
</script>
|
|
26
34
|
|
|
27
35
|
<template>
|
|
28
36
|
<!-- Force a clean state namespace when the workspace changes. -->
|
|
29
37
|
<ChatEmbedInner
|
|
38
|
+
v-if="workspaceId"
|
|
30
39
|
:key="workspaceId"
|
|
31
40
|
:conversation-id="conversationId"
|
|
32
41
|
:initial-conversation-id="initialConversationId"
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
import type { DeepReadonly } from 'vue';
|
|
1
|
+
import type { Component, DeepReadonly } from 'vue';
|
|
2
|
+
import { type ChatActionPayload } from '../../composables/useChatAction.js';
|
|
2
3
|
import type { WorkspaceSettings } from '../../composables/useWorkspaceSettings.js';
|
|
4
|
+
import type { ChatLoadingMessageMode } from '../../utils/chat-loading-messages.js';
|
|
3
5
|
import type { ChatActor, ChatFeatureConfig } from '../../utils/tela-chat.js';
|
|
4
6
|
type EmbedUser = ChatActor;
|
|
5
7
|
type __VLS_Props = {
|
|
6
|
-
workspaceId: string;
|
|
7
8
|
workspaceSettings?: DeepReadonly<WorkspaceSettings> | WorkspaceSettings | null;
|
|
8
9
|
conversationId?: string | null;
|
|
9
10
|
initialConversationId?: string | null;
|
|
10
11
|
hideSidebar?: boolean;
|
|
11
12
|
user?: EmbedUser | null;
|
|
12
13
|
features?: Partial<ChatFeatureConfig>;
|
|
14
|
+
loadingMessages?: readonly string[] | null;
|
|
15
|
+
loadingMessagesMode?: ChatLoadingMessageMode | null;
|
|
16
|
+
customComponents?: Record<string, Component>;
|
|
17
|
+
customHtmlTags?: readonly string[];
|
|
13
18
|
};
|
|
14
19
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
15
20
|
"update:conversationId": (value: string | null) => any;
|
|
21
|
+
action: (payload: ChatActionPayload) => any;
|
|
16
22
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
17
23
|
"onUpdate:conversationId"?: ((value: string | null) => any) | undefined;
|
|
24
|
+
onAction?: ((payload: ChatActionPayload) => any) | undefined;
|
|
18
25
|
}>, {
|
|
19
26
|
initialConversationId: string | null;
|
|
20
27
|
hideSidebar: boolean;
|
|
@@ -28,7 +28,11 @@ const activeConversationId = ref(
|
|
|
28
28
|
);
|
|
29
29
|
const loadingConversationId = ref(null);
|
|
30
30
|
const isReady = ref(false);
|
|
31
|
-
const
|
|
31
|
+
const moduleFeatures = useChatNuxtFeatures();
|
|
32
|
+
const resolvedFeatures = computed(() => resolveChatFeatures({
|
|
33
|
+
showUsageTab: moduleFeatures.usage ?? props.features?.showUsageTab,
|
|
34
|
+
showDebugOption: props.features?.showDebugOption
|
|
35
|
+
}));
|
|
32
36
|
const dropZoneRef = ref(null);
|
|
33
37
|
const activeTab = ref("chat");
|
|
34
38
|
const messageInputRef = ref(null);
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
serializeResolvedWorkspaceSettings
|
|
14
14
|
} from "#chat-runtime/server/utils/chat-workspace-settings";
|
|
15
15
|
import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
|
|
16
|
+
import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
16
17
|
import { mergeFileUrls } from "#chat-runtime/utils/file";
|
|
17
18
|
import { isValidModel } from "#chat-runtime/types/chat";
|
|
18
19
|
export default defineEventHandler(async (event) => {
|
|
@@ -107,10 +108,12 @@ export default defineEventHandler(async (event) => {
|
|
|
107
108
|
try {
|
|
108
109
|
if (needsNewThread) {
|
|
109
110
|
let environmentVariables = {};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
if (useChatNuxtFeatures().credentials) {
|
|
112
|
+
try {
|
|
113
|
+
environmentVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger.warn({ conversationId, messageId, error }, "Failed to resolve workspace credentials on retry, proceeding without env vars");
|
|
116
|
+
}
|
|
114
117
|
}
|
|
115
118
|
const hasEnvironmentVariables = Object.keys(environmentVariables).length > 0;
|
|
116
119
|
const result = await createChatAgentSession(token, {
|
|
@@ -131,7 +131,7 @@ export default defineEventHandler(async (event) => {
|
|
|
131
131
|
}
|
|
132
132
|
if (agentSession.status === "completed" || agentSession.status === "waiting_messages") {
|
|
133
133
|
if (hasNewReasoning) {
|
|
134
|
-
const content = extractContentFromSession(agentSession
|
|
134
|
+
const content = extractContentFromSession(agentSession);
|
|
135
135
|
updateData.content = content;
|
|
136
136
|
updateData.status = "completed";
|
|
137
137
|
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
@@ -9,10 +9,12 @@ import { logger } from "#chat-runtime/server/utils/logger";
|
|
|
9
9
|
import {
|
|
10
10
|
getConversationAppliedSettings,
|
|
11
11
|
getWorkspaceSettingsUpdatedAt,
|
|
12
|
+
parseWorkspaceSettingsSnapshot,
|
|
12
13
|
resolveWorkspaceSettings,
|
|
13
14
|
serializeResolvedWorkspaceSettings
|
|
14
15
|
} from "#chat-runtime/server/utils/chat-workspace-settings";
|
|
15
16
|
import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
|
|
17
|
+
import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
16
18
|
import { mergeFileUrls } from "#chat-runtime/utils/file";
|
|
17
19
|
import { isValidModel } from "#chat-runtime/types/chat";
|
|
18
20
|
export default defineEventHandler(async (event) => {
|
|
@@ -52,14 +54,16 @@ export default defineEventHandler(async (event) => {
|
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
const effectiveModel = body.model ?? (isValidModel(conversation.model) ? conversation.model : void 0);
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
+
const explicitSettings = !conversation.appliedSettingsSnapshot && body.workspaceSettingsSnapshot !== void 0 ? parseWorkspaceSettingsSnapshot(body.workspaceSettingsSnapshot) : void 0;
|
|
58
|
+
const trustedSettings = conversation.appliedSettingsSnapshot || explicitSettings ? null : await resolveWorkspaceSettings(event, context, db);
|
|
59
|
+
const appliedSettings = getConversationAppliedSettings(conversation, trustedSettings, explicitSettings);
|
|
57
60
|
const now = /* @__PURE__ */ new Date();
|
|
58
61
|
const assistantCreatedAt = new Date(now.getTime() + 1);
|
|
59
|
-
|
|
62
|
+
const snapshotSettings = explicitSettings ?? trustedSettings;
|
|
63
|
+
if (!conversation.appliedSettingsSnapshot && snapshotSettings) {
|
|
60
64
|
await db.update(schema.conversations).set({
|
|
61
|
-
appliedSettingsAt: getWorkspaceSettingsUpdatedAt(
|
|
62
|
-
appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(
|
|
65
|
+
appliedSettingsAt: getWorkspaceSettingsUpdatedAt(snapshotSettings),
|
|
66
|
+
appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(snapshotSettings),
|
|
63
67
|
updatedAt: now
|
|
64
68
|
}).where(and(
|
|
65
69
|
eq(schema.conversations.id, conversationId),
|
|
@@ -113,10 +117,12 @@ export default defineEventHandler(async (event) => {
|
|
|
113
117
|
const skills = externalSkillRefs.length > 0 ? externalSkillRefs : void 0;
|
|
114
118
|
if (needsNewThread) {
|
|
115
119
|
let environmentVariables = {};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
if (useChatNuxtFeatures().credentials) {
|
|
121
|
+
try {
|
|
122
|
+
environmentVariables = await resolveWorkspaceCredentials(event, context.workspaceId);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.warn({ conversationId, error }, "Failed to resolve workspace credentials, proceeding without env vars");
|
|
125
|
+
}
|
|
120
126
|
}
|
|
121
127
|
const hasEnvironmentVariables = Object.keys(environmentVariables).length > 0;
|
|
122
128
|
createChatAgentSession(token, {
|
|
@@ -31,7 +31,7 @@ export default defineEventHandler(async (event) => {
|
|
|
31
31
|
eq(schema.conversationUsage.workspaceId, context.workspaceId)
|
|
32
32
|
)
|
|
33
33
|
);
|
|
34
|
-
const billing = await getWorkspaceBilling(
|
|
34
|
+
const billing = await getWorkspaceBilling(event);
|
|
35
35
|
if (!usage) {
|
|
36
36
|
return { usage: null, billing };
|
|
37
37
|
}
|
|
@@ -3,7 +3,20 @@ import { useRuntimeConfig } from "nitropack/runtime";
|
|
|
3
3
|
import { createTelaClient } from "@meistrari/tela-sdk-js";
|
|
4
4
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
5
5
|
import { requireUser } from "#chat-runtime/server/utils/auth";
|
|
6
|
+
import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
6
7
|
const TITLE_CANVAS_ID = "ba06c4a0-ef05-4fb6-b4ef-5af3d63a1ba2";
|
|
8
|
+
const FALLBACK_TITLE_MAX_LENGTH = 60;
|
|
9
|
+
const FALLBACK_TITLE_DEFAULT = "New chat";
|
|
10
|
+
function buildFallbackTitle(userMessage) {
|
|
11
|
+
const normalized = userMessage.replace(/\s+/g, " ").trim();
|
|
12
|
+
if (!normalized) {
|
|
13
|
+
return FALLBACK_TITLE_DEFAULT;
|
|
14
|
+
}
|
|
15
|
+
if (normalized.length <= FALLBACK_TITLE_MAX_LENGTH) {
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
return `${normalized.slice(0, FALLBACK_TITLE_MAX_LENGTH - 1).trimEnd()}\u2026`;
|
|
19
|
+
}
|
|
7
20
|
export default defineEventHandler(async (event) => {
|
|
8
21
|
requireUser(event);
|
|
9
22
|
const { userMessage, assistantResponse } = await readBody(event);
|
|
@@ -14,11 +27,11 @@ export default defineEventHandler(async (event) => {
|
|
|
14
27
|
});
|
|
15
28
|
}
|
|
16
29
|
const { telaApiKey } = useRuntimeConfig();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
30
|
+
const features = useChatNuxtFeatures();
|
|
31
|
+
if (!features.generateTitle || !telaApiKey) {
|
|
32
|
+
const fallback = buildFallbackTitle(userMessage);
|
|
33
|
+
logger.debug({ titleLength: fallback.length, reason: !features.generateTitle ? "feature-disabled" : "no-tela-api-key" }, "Title generated via fallback");
|
|
34
|
+
return { title: fallback };
|
|
22
35
|
}
|
|
23
36
|
try {
|
|
24
37
|
const tela = createTelaClient({
|
|
@@ -54,7 +54,7 @@ export default defineEventHandler(async (event) => {
|
|
|
54
54
|
}).from(schema.conversationUsage).where(and(...conditions)).orderBy(desc(schema.conversationUsage.createdAt)).limit(limit + 1);
|
|
55
55
|
const hasMore = records.length > limit;
|
|
56
56
|
const paginatedRecords = records.slice(0, limit);
|
|
57
|
-
const billing = await getWorkspaceBilling(
|
|
57
|
+
const billing = await getWorkspaceBilling(event);
|
|
58
58
|
const formattedRecords = paginatedRecords.map((r) => {
|
|
59
59
|
const transformedCosts = transformCostForBilling({
|
|
60
60
|
promptCost: Number(r.promptCost),
|
|
@@ -1,6 +1,23 @@
|
|
|
1
|
+
import { drizzle as drizzlePostgres } from 'drizzle-orm/postgres-js';
|
|
2
|
+
import type { PgliteDatabase } from 'drizzle-orm/pglite';
|
|
1
3
|
import * as schema from './schema.js';
|
|
2
4
|
export type { ContextFile, ExternalSkill } from './schema.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
type PostgresDb = ReturnType<typeof drizzlePostgres<typeof schema>>;
|
|
6
|
+
type PgliteDb = PgliteDatabase<typeof schema>;
|
|
7
|
+
type AnyDb = PostgresDb | PgliteDb;
|
|
8
|
+
export declare function isPgliteUrl(url: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Eagerly initializes the database during Nitro startup. Idempotent. Both
|
|
11
|
+
* Postgres and pglite paths now run migrations on boot, so the chat-nuxt
|
|
12
|
+
* tables in the `chat` Postgres schema are guaranteed to exist before the
|
|
13
|
+
* first request hits a route that calls useDb().
|
|
14
|
+
*/
|
|
15
|
+
export declare function initDb(): Promise<AnyDb>;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the initialized database instance. initDb() must have already run
|
|
18
|
+
* via the boot plugin so migrations have been applied — bare useDb() now
|
|
19
|
+
* throws if called before init, regardless of driver, instead of silently
|
|
20
|
+
* skipping migrations on the Postgres path.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useDb(): AnyDb;
|
|
6
23
|
export { schema };
|
|
@@ -1,20 +1,159 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
1
3
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
4
|
+
import { sql } from "drizzle-orm";
|
|
2
5
|
import postgres from "postgres";
|
|
3
|
-
import { drizzle } from "drizzle-orm/postgres-js";
|
|
6
|
+
import { drizzle as drizzlePostgres } from "drizzle-orm/postgres-js";
|
|
7
|
+
import { migrate as migratePostgres } from "drizzle-orm/postgres-js/migrator";
|
|
4
8
|
import * as schema from "./schema/index.js";
|
|
5
|
-
import {
|
|
9
|
+
import { CHAT_SCHEMA_NAME } from "./schema/_schema.js";
|
|
10
|
+
import { logger } from "../utils/logger.js";
|
|
6
11
|
let dbInstance = null;
|
|
7
|
-
|
|
12
|
+
let dbInitPromise = null;
|
|
13
|
+
const LEGACY_MIGRATIONS_SCHEMA = "drizzle";
|
|
14
|
+
const MIGRATIONS_TABLE = "__drizzle_migrations";
|
|
15
|
+
const MIGRATIONS_LEDGER_LOCK_KEY = 581073927;
|
|
16
|
+
export function isPgliteUrl(url) {
|
|
17
|
+
return url.startsWith("pglite:") || url.startsWith("file:");
|
|
18
|
+
}
|
|
19
|
+
function resolvePgliteDataDir(url) {
|
|
20
|
+
if (url.startsWith("pglite:")) {
|
|
21
|
+
return url.slice("pglite:".length);
|
|
22
|
+
}
|
|
23
|
+
if (url.startsWith("file://")) {
|
|
24
|
+
return url.slice("file://".length);
|
|
25
|
+
}
|
|
26
|
+
if (url.startsWith("file:")) {
|
|
27
|
+
return url.slice("file:".length);
|
|
28
|
+
}
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
const IGNORED_NOTICE_CODES = /* @__PURE__ */ new Set(["42P06", "42P07"]);
|
|
32
|
+
function getRows(result) {
|
|
33
|
+
if (Array.isArray(result)) {
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) {
|
|
37
|
+
return result.rows;
|
|
38
|
+
}
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
async function tableExists(db, schemaName, tableName) {
|
|
42
|
+
const result = await db.execute(sql`
|
|
43
|
+
select exists (
|
|
44
|
+
select 1
|
|
45
|
+
from information_schema.tables
|
|
46
|
+
where table_schema = ${schemaName}
|
|
47
|
+
and table_name = ${tableName}
|
|
48
|
+
) as "exists"
|
|
49
|
+
`);
|
|
50
|
+
const [row] = getRows(result);
|
|
51
|
+
return row?.exists ?? false;
|
|
52
|
+
}
|
|
53
|
+
async function tableHasRows(db, schemaName, tableName) {
|
|
54
|
+
const result = await db.execute(sql`
|
|
55
|
+
select exists (
|
|
56
|
+
select 1
|
|
57
|
+
from ${sql.identifier(schemaName)}.${sql.identifier(tableName)}
|
|
58
|
+
) as "exists"
|
|
59
|
+
`);
|
|
60
|
+
const [row] = getRows(result);
|
|
61
|
+
return row?.exists ?? false;
|
|
62
|
+
}
|
|
63
|
+
async function seedChatMigrationsLedger(db) {
|
|
64
|
+
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.identifier(CHAT_SCHEMA_NAME)}`);
|
|
65
|
+
await db.execute(sql`
|
|
66
|
+
CREATE TABLE IF NOT EXISTS ${sql.identifier(CHAT_SCHEMA_NAME)}.${sql.identifier(MIGRATIONS_TABLE)} (
|
|
67
|
+
id SERIAL PRIMARY KEY,
|
|
68
|
+
hash text NOT NULL,
|
|
69
|
+
created_at bigint
|
|
70
|
+
)
|
|
71
|
+
`);
|
|
72
|
+
if (await tableHasRows(db, CHAT_SCHEMA_NAME, MIGRATIONS_TABLE)) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!await tableExists(db, LEGACY_MIGRATIONS_SCHEMA, MIGRATIONS_TABLE)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await db.execute(sql`
|
|
79
|
+
INSERT INTO ${sql.identifier(CHAT_SCHEMA_NAME)}.${sql.identifier(MIGRATIONS_TABLE)} ("hash", "created_at")
|
|
80
|
+
SELECT "hash", "created_at"
|
|
81
|
+
FROM ${sql.identifier(LEGACY_MIGRATIONS_SCHEMA)}.${sql.identifier(MIGRATIONS_TABLE)}
|
|
82
|
+
ORDER BY "created_at"
|
|
83
|
+
`);
|
|
84
|
+
}
|
|
85
|
+
async function initPostgresAsync(databaseUrl, migrationsDir) {
|
|
86
|
+
const client = postgres(databaseUrl, {
|
|
87
|
+
max: 10,
|
|
88
|
+
onnotice: (notice) => {
|
|
89
|
+
if (notice.code && IGNORED_NOTICE_CODES.has(notice.code))
|
|
90
|
+
return;
|
|
91
|
+
logger.info({ notice }, "Postgres notice");
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const db = drizzlePostgres(client, { schema });
|
|
95
|
+
if (migrationsDir) {
|
|
96
|
+
await db.transaction(async (tx) => {
|
|
97
|
+
await tx.execute(sql`SELECT pg_advisory_xact_lock(${MIGRATIONS_LEDGER_LOCK_KEY})`);
|
|
98
|
+
await seedChatMigrationsLedger(tx);
|
|
99
|
+
});
|
|
100
|
+
await migratePostgres(db, { migrationsFolder: migrationsDir, migrationsSchema: CHAT_SCHEMA_NAME });
|
|
101
|
+
logger.info({ migrationsSchema: CHAT_SCHEMA_NAME }, "Postgres connection pool initialized and migrations applied");
|
|
102
|
+
} else {
|
|
103
|
+
logger.warn("Postgres connection pool initialized without migrations directory \u2014 schema may be out of date");
|
|
104
|
+
}
|
|
105
|
+
return db;
|
|
106
|
+
}
|
|
107
|
+
async function initPgliteAsync(databaseUrl, migrationsDir) {
|
|
108
|
+
const dataDir = resolvePgliteDataDir(databaseUrl);
|
|
109
|
+
await mkdir(dirname(dataDir), { recursive: true });
|
|
110
|
+
const [{ PGlite }, { drizzle }, { migrate }] = await Promise.all([
|
|
111
|
+
import("@electric-sql/pglite"),
|
|
112
|
+
import("drizzle-orm/pglite"),
|
|
113
|
+
import("drizzle-orm/pglite/migrator")
|
|
114
|
+
]);
|
|
115
|
+
const client = new PGlite(dataDir);
|
|
116
|
+
const db = drizzle(client, { schema });
|
|
117
|
+
if (migrationsDir) {
|
|
118
|
+
await seedChatMigrationsLedger(db);
|
|
119
|
+
await migrate(db, { migrationsFolder: migrationsDir, migrationsSchema: CHAT_SCHEMA_NAME });
|
|
120
|
+
logger.info({ dataDir, migrationsSchema: CHAT_SCHEMA_NAME }, "pglite database initialized and migrated");
|
|
121
|
+
} else {
|
|
122
|
+
logger.warn({ dataDir }, "pglite database initialized without migrations directory \u2014 schema may be empty");
|
|
123
|
+
}
|
|
124
|
+
return db;
|
|
125
|
+
}
|
|
126
|
+
export async function initDb() {
|
|
8
127
|
if (dbInstance) {
|
|
9
128
|
return dbInstance;
|
|
10
129
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
130
|
+
if (dbInitPromise) {
|
|
131
|
+
return dbInitPromise;
|
|
132
|
+
}
|
|
133
|
+
dbInitPromise = (async () => {
|
|
134
|
+
const config = useRuntimeConfig();
|
|
135
|
+
const databaseUrl = config.databaseUrl;
|
|
136
|
+
const migrationsDir = config.chatNuxt?.migrationsDir ?? "";
|
|
137
|
+
if (!databaseUrl) {
|
|
138
|
+
throw new Error("DATABASE_URL is not configured and no pglite fallback path was set");
|
|
139
|
+
}
|
|
140
|
+
const db = isPgliteUrl(databaseUrl) ? await initPgliteAsync(databaseUrl, migrationsDir) : await initPostgresAsync(databaseUrl, migrationsDir);
|
|
141
|
+
dbInstance = db;
|
|
142
|
+
return db;
|
|
143
|
+
})();
|
|
144
|
+
try {
|
|
145
|
+
return await dbInitPromise;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
dbInitPromise = null;
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function useDb() {
|
|
152
|
+
if (dbInstance) {
|
|
153
|
+
return dbInstance;
|
|
14
154
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return dbInstance;
|
|
155
|
+
throw new Error(
|
|
156
|
+
"chat-nuxt database is not initialized. The chat-nuxt boot plugin should have called initDb() before any request \u2014 check that the module registered server/plugins/init-db.ts."
|
|
157
|
+
);
|
|
19
158
|
}
|
|
20
159
|
export { schema };
|