@meistrari/chat-nuxt 1.3.0 → 1.5.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 +51 -16
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -8
- package/dist/runtime/components/MeistrariChatEmbed.d.vue.ts +5 -16
- package/dist/runtime/components/MeistrariChatEmbed.vue +28 -5
- package/dist/runtime/components/MeistrariChatEmbed.vue.d.ts +5 -16
- 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/message-input.d.vue.ts +30 -4
- package/dist/runtime/components/chat/message-input.vue +268 -24
- package/dist/runtime/components/chat/message-input.vue.d.ts +30 -4
- package/dist/runtime/composables/useChat.d.ts +3 -5
- package/dist/runtime/composables/useChat.js +24 -3
- package/dist/runtime/composables/useChatAction.d.ts +10 -0
- package/dist/runtime/composables/useChatAction.js +12 -0
- package/dist/runtime/composables/useChatApi.js +4 -0
- package/dist/runtime/composables/useConversations.js +8 -2
- package/dist/runtime/composables/useEmbedConfig.d.ts +12 -2
- package/dist/runtime/composables/useEmbedConfig.js +24 -3
- package/dist/runtime/composables/useFeatureFlags.d.ts +1 -2
- package/dist/runtime/composables/useTelaAgentMetadata.d.ts +8 -0
- package/dist/runtime/composables/useTelaAgentMetadata.js +65 -0
- package/dist/runtime/composables/useWorkspaceSettings.d.ts +1 -19
- package/dist/runtime/embed/components/ChatConfigurationModal.d.vue.ts +1 -1
- package/dist/runtime/embed/components/ChatConfigurationModal.vue.d.ts +1 -1
- package/dist/runtime/embed/components/ChatEmbed.d.vue.ts +6 -21
- package/dist/runtime/embed/components/ChatEmbed.vue +44 -11
- package/dist/runtime/embed/components/ChatEmbed.vue.d.ts +6 -21
- package/dist/runtime/embed/components/ChatEmbedInner.vue +36 -12
- package/dist/runtime/plugins/markstream.d.ts +1 -1
- package/dist/runtime/plugins/markstream.js +1 -1
- package/dist/runtime/server/api/conversations/[id]/cancel.post.d.ts +1 -1
- package/dist/runtime/server/api/conversations/[id]/cancel.post.js +37 -17
- package/dist/runtime/server/api/conversations/[id]/clear.post.js +28 -13
- package/dist/runtime/server/api/conversations/[id]/duplicate.post.js +11 -13
- package/dist/runtime/server/api/conversations/[id]/files.get.js +5 -7
- package/dist/runtime/server/api/conversations/[id]/index.delete.js +13 -12
- package/dist/runtime/server/api/conversations/[id]/index.get.js +6 -13
- package/dist/runtime/server/api/conversations/[id]/index.patch.js +11 -10
- package/dist/runtime/server/api/conversations/[id]/messages/[messageId]/retry.post.js +46 -80
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.d.ts +1 -14
- package/dist/runtime/server/api/conversations/[id]/messages/index.get.js +81 -187
- package/dist/runtime/server/api/conversations/[id]/messages/index.post.js +68 -83
- package/dist/runtime/server/api/conversations/[id]/usage.get.js +4 -8
- package/dist/runtime/server/api/conversations/generate-title.post.js +4 -3
- package/dist/runtime/server/api/conversations/index.get.js +5 -4
- package/dist/runtime/server/api/conversations/index.post.js +5 -4
- package/dist/runtime/server/api/tela/agents/[id].get.d.ts +6 -0
- package/dist/runtime/server/api/tela/agents/[id].get.js +25 -0
- package/dist/runtime/server/api/workspace/settings.patch.js +1 -1
- package/dist/runtime/server/db/index.js +62 -0
- package/dist/runtime/server/db/schema/conversations.d.ts +17 -0
- package/dist/runtime/server/db/schema/conversations.js +3 -1
- package/dist/runtime/server/db/schema/workspace-settings.d.ts +1 -1
- 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/chat-context.d.ts +5 -0
- package/dist/runtime/server/utils/chat-context.js +20 -0
- package/dist/runtime/server/utils/chat-workspace-settings.d.ts +2 -1
- package/dist/runtime/server/utils/chat-workspace-settings.js +10 -1
- package/dist/runtime/server/utils/conversation-agent-turn.d.ts +39 -0
- package/dist/runtime/server/utils/conversation-agent-turn.js +170 -0
- package/dist/runtime/server/utils/conversation-message-files.d.ts +10 -0
- package/dist/runtime/server/utils/conversation-message-files.js +77 -0
- package/dist/runtime/server/utils/conversation-message-sync.d.ts +31 -0
- package/dist/runtime/server/utils/conversation-message-sync.js +132 -0
- package/dist/runtime/server/utils/conversation-scope.d.ts +2 -0
- package/dist/runtime/server/utils/conversation-scope.js +12 -0
- package/dist/runtime/server/utils/tela-agent-api.d.ts +29 -0
- package/dist/runtime/server/utils/tela-agent-api.js +107 -0
- package/dist/runtime/server/utils/tela-agent-session.d.ts +48 -0
- package/dist/runtime/server/utils/tela-agent-session.js +350 -0
- package/dist/runtime/types/embed.d.ts +29 -0
- package/dist/runtime/types/embed.js +0 -0
- package/dist/runtime/types/tela-agent.d.ts +243 -0
- package/dist/runtime/types/tela-agent.js +98 -0
- package/dist/runtime/types/workspace-settings-data.d.ts +19 -0
- package/dist/runtime/types/workspace-settings-data.js +0 -0
- package/dist/runtime/types/workspace-settings.d.ts +1 -1
- package/dist/runtime/utils/agent-input-readiness.d.ts +14 -0
- package/dist/runtime/utils/agent-input-readiness.js +15 -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 +9 -0
- package/dist/runtime/utils/features.js +5 -4
- package/dist/runtime/utils/file.js +46 -7
- package/dist/runtime/utils/markdown-nodes.d.ts +1 -0
- package/dist/runtime/utils/markdown-nodes.js +6 -0
- package/dist/runtime/utils/tela-chat.d.ts +6 -0
- package/dist/runtime/utils/tela-chat.js +12 -1
- package/drizzle/0014_faulty_lake.sql +2 -0
- package/drizzle/meta/0014_snapshot.json +732 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/dist/runtime/types/feature-flags.d.ts +0 -7
- package/dist/runtime/types/feature-flags.js +0 -6
|
@@ -1,74 +1,23 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery, getRouterParam } from "h3";
|
|
2
|
-
import {
|
|
3
|
-
import { and, asc, eq, gt, isNotNull } from "drizzle-orm";
|
|
4
|
-
import { APIKeyAuthStrategy, VaultFile } from "@meistrari/vault-sdk";
|
|
2
|
+
import { and, asc, eq, gt } from "drizzle-orm";
|
|
5
3
|
import { schema, useDb } from "#chat-runtime/server/db";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { getAgentDataToken, getSessionStatus } from "#chat-runtime/server/utils/agent-api";
|
|
5
|
+
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
6
|
+
import { attachNewAssistantFilesToMessageUpdate } from "#chat-runtime/server/utils/conversation-message-files";
|
|
7
|
+
import {
|
|
8
|
+
buildMessageUpdateResponse,
|
|
9
|
+
hasMessageUpdate,
|
|
10
|
+
isTerminalMessageUpdate,
|
|
11
|
+
mapAgentApiSessionToMessageUpdate,
|
|
12
|
+
trackAgentApiConversationUsage,
|
|
13
|
+
trackTelaAgentConversationUsage
|
|
14
|
+
} from "#chat-runtime/server/utils/conversation-message-sync";
|
|
15
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
16
|
+
import { getTelaAgentSession } from "#chat-runtime/server/utils/tela-agent-api";
|
|
17
|
+
import { mapTelaAgentSessionToMessageUpdate, mapTelaAgentUsage } from "#chat-runtime/server/utils/tela-agent-session";
|
|
9
18
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
10
|
-
async function getExistingFileUrls(db, conversationId) {
|
|
11
|
-
const messagesWithFiles = await db.select({ files: schema.messages.files }).from(schema.messages).where(and(
|
|
12
|
-
eq(schema.messages.conversationId, conversationId),
|
|
13
|
-
isNotNull(schema.messages.files)
|
|
14
|
-
));
|
|
15
|
-
const urls = /* @__PURE__ */ new Set();
|
|
16
|
-
for (const message of messagesWithFiles) {
|
|
17
|
-
if (Array.isArray(message.files)) {
|
|
18
|
-
for (const file of message.files) {
|
|
19
|
-
if (file?.url) {
|
|
20
|
-
urls.add(file.url);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return urls;
|
|
26
|
-
}
|
|
27
|
-
async function extractAssistantFiles(content, authToken) {
|
|
28
|
-
const detected = detectFilesInText(content);
|
|
29
|
-
const vaultFiles = detected.filter((f) => f.type === "vault");
|
|
30
|
-
if (vaultFiles.length === 0)
|
|
31
|
-
return [];
|
|
32
|
-
const { vaultUrl } = useRuntimeConfig();
|
|
33
|
-
const filesWithMetadata = await Promise.all(
|
|
34
|
-
vaultFiles.map(async (file) => {
|
|
35
|
-
let name = file.name;
|
|
36
|
-
let mimeType = file.mimeType !== "unknown" ? file.mimeType : void 0;
|
|
37
|
-
let size;
|
|
38
|
-
if (vaultUrl) {
|
|
39
|
-
try {
|
|
40
|
-
const authStrategy = new APIKeyAuthStrategy(authToken);
|
|
41
|
-
const vaultFile = await VaultFile.fromVaultReference({
|
|
42
|
-
reference: file.reference,
|
|
43
|
-
config: { authStrategy, vaultUrl }
|
|
44
|
-
});
|
|
45
|
-
await vaultFile.populateMetadata();
|
|
46
|
-
if (vaultFile.metadata?.originalFileName) {
|
|
47
|
-
name = vaultFile.metadata.originalFileName;
|
|
48
|
-
}
|
|
49
|
-
if (vaultFile.metadata?.mimeType) {
|
|
50
|
-
mimeType = vaultFile.metadata.mimeType;
|
|
51
|
-
}
|
|
52
|
-
if (vaultFile.metadata?.size) {
|
|
53
|
-
size = vaultFile.metadata.size;
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
logger.debug({ reference: file.reference }, "Vault metadata fetch failed for assistant file");
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
url: file.reference,
|
|
61
|
-
name,
|
|
62
|
-
mimeType,
|
|
63
|
-
source: "assistant",
|
|
64
|
-
size
|
|
65
|
-
};
|
|
66
|
-
})
|
|
67
|
-
);
|
|
68
|
-
return filesWithMetadata;
|
|
69
|
-
}
|
|
70
19
|
export default defineEventHandler(async (event) => {
|
|
71
|
-
const context =
|
|
20
|
+
const context = resolveChatRuntimeContext(event);
|
|
72
21
|
const token = getAgentDataToken(event);
|
|
73
22
|
const db = useDb();
|
|
74
23
|
const conversationId = getRouterParam(event, "id");
|
|
@@ -88,12 +37,7 @@ export default defineEventHandler(async (event) => {
|
|
|
88
37
|
}
|
|
89
38
|
}
|
|
90
39
|
const [conversationResult, messages] = await Promise.all([
|
|
91
|
-
db.select().from(schema.conversations).where(
|
|
92
|
-
and(
|
|
93
|
-
eq(schema.conversations.id, conversationId),
|
|
94
|
-
eq(schema.conversations.workspaceId, context.workspaceId)
|
|
95
|
-
)
|
|
96
|
-
),
|
|
40
|
+
db.select().from(schema.conversations).where(conversationScopeConditions(context, conversationId)),
|
|
97
41
|
db.select().from(schema.messages).where(and(...conditions)).orderBy(asc(schema.messages.createdAt))
|
|
98
42
|
]);
|
|
99
43
|
const [conversation] = conversationResult;
|
|
@@ -103,120 +47,70 @@ export default defineEventHandler(async (event) => {
|
|
|
103
47
|
statusMessage: "Conversation not found"
|
|
104
48
|
});
|
|
105
49
|
}
|
|
106
|
-
const pendingMessage = messages.find((
|
|
107
|
-
const lastAssistantMessage = messages.findLast((
|
|
108
|
-
if (pendingMessage
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
updateData.ttft = String(ttft);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (agentSession.status === "completed" || agentSession.status === "waiting_messages") {
|
|
133
|
-
if (hasNewReasoning) {
|
|
134
|
-
const content = extractContentFromSession(agentSession, newReasoning);
|
|
135
|
-
updateData.content = content;
|
|
136
|
-
updateData.status = "completed";
|
|
137
|
-
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
138
|
-
logger.info({ conversationId, messageId: pendingMessage.id, status: "completed" }, "Message completed");
|
|
139
|
-
const assistantFiles = await extractAssistantFiles(content, token);
|
|
140
|
-
if (assistantFiles.length > 0) {
|
|
141
|
-
const existingUrls = await getExistingFileUrls(db, conversationId);
|
|
142
|
-
const newFiles = assistantFiles.filter((f) => !existingUrls.has(f.url));
|
|
143
|
-
if (newFiles.length > 0) {
|
|
144
|
-
updateData.files = newFiles;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
} else if (agentSession.status === "failed") {
|
|
149
|
-
const errorContent = agentSession.error || "Agent execution failed";
|
|
150
|
-
updateData.content = `Error: ${errorContent}`;
|
|
151
|
-
updateData.status = agentSession.status;
|
|
152
|
-
updateData.updatedAt = /* @__PURE__ */ new Date();
|
|
153
|
-
logger.warn({ conversationId, messageId: pendingMessage.id, status: "failed" }, "Message failed");
|
|
154
|
-
}
|
|
155
|
-
if (Object.keys(updateData).length > 0) {
|
|
156
|
-
await db.update(schema.messages).set(updateData).where(eq(schema.messages.id, pendingMessage.id));
|
|
157
|
-
if ((updateData.status === "completed" || updateData.status === "failed") && agentSession.result?.usage) {
|
|
158
|
-
const usage = agentSession.result.usage;
|
|
159
|
-
const breakdown = agentSession.result?.usageBreakdown;
|
|
160
|
-
const serverToolUse = breakdown?.serverToolUse;
|
|
161
|
-
const webSearchRequests = typeof serverToolUse?.web_search_requests === "number" ? serverToolUse.web_search_requests : null;
|
|
162
|
-
const webFetchRequests = typeof serverToolUse?.web_fetch_requests === "number" ? serverToolUse.web_fetch_requests : null;
|
|
163
|
-
void db.insert(schema.conversationUsage).values({
|
|
164
|
-
workspaceId: context.workspaceId,
|
|
165
|
-
conversationId,
|
|
166
|
-
promptTokens: usage.promptTokens ?? 0,
|
|
167
|
-
completionTokens: usage.completionTokens ?? 0,
|
|
168
|
-
totalTokens: usage.totalTokens ?? 0,
|
|
169
|
-
promptCost: String(usage.promptCost ?? 0),
|
|
170
|
-
completionCost: String(usage.completionCost ?? 0),
|
|
171
|
-
totalCost: String(usage.totalCost ?? 0),
|
|
172
|
-
durationMs: usage.duration ?? null,
|
|
173
|
-
turns: usage.turns ?? null,
|
|
174
|
-
userId: context.user.id,
|
|
175
|
-
userEmail: context.user.email ?? null,
|
|
176
|
-
model: agentSession.model ?? null,
|
|
177
|
-
sessionId: conversation.threadSessionId,
|
|
178
|
-
cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
|
|
179
|
-
cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
|
|
180
|
-
modelUsage: breakdown?.modelUsage ?? null,
|
|
181
|
-
durationApiMs: breakdown?.durationApiMs ?? null,
|
|
182
|
-
webSearchRequests,
|
|
183
|
-
webFetchRequests
|
|
184
|
-
}).onConflictDoUpdate({
|
|
185
|
-
target: schema.conversationUsage.conversationId,
|
|
186
|
-
set: {
|
|
187
|
-
promptTokens: usage.promptTokens ?? 0,
|
|
188
|
-
completionTokens: usage.completionTokens ?? 0,
|
|
189
|
-
totalTokens: usage.totalTokens ?? 0,
|
|
190
|
-
promptCost: String(usage.promptCost ?? 0),
|
|
191
|
-
completionCost: String(usage.completionCost ?? 0),
|
|
192
|
-
totalCost: String(usage.totalCost ?? 0),
|
|
193
|
-
durationMs: usage.duration ?? null,
|
|
194
|
-
turns: usage.turns ?? null,
|
|
195
|
-
model: agentSession.model ?? null,
|
|
196
|
-
cacheReadInputTokens: breakdown?.cacheReadInputTokens ?? null,
|
|
197
|
-
cacheCreationInputTokens: breakdown?.cacheCreationInputTokens ?? null,
|
|
198
|
-
modelUsage: breakdown?.modelUsage ?? null,
|
|
199
|
-
durationApiMs: breakdown?.durationApiMs ?? null,
|
|
200
|
-
webSearchRequests,
|
|
201
|
-
webFetchRequests,
|
|
202
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
203
|
-
}
|
|
204
|
-
}).catch((error) => {
|
|
205
|
-
console.warn("Failed to track conversation usage:", error);
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
const updatedMessages = messages.map(
|
|
209
|
-
(m) => m.id === pendingMessage.id ? { ...m, ...updateData } : m
|
|
210
|
-
);
|
|
211
|
-
if (updateData.files && updateData.files.length > 0) {
|
|
212
|
-
return {
|
|
213
|
-
messages: updatedMessages,
|
|
214
|
-
newFiles: updateData.files
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
return { messages: updatedMessages };
|
|
218
|
-
}
|
|
50
|
+
const pendingMessage = messages.find((message) => message.status === "pending");
|
|
51
|
+
const lastAssistantMessage = messages.findLast((message) => message.role === "assistant" && message.status === "completed");
|
|
52
|
+
if (!pendingMessage || !conversation.threadSessionId) {
|
|
53
|
+
return { messages };
|
|
54
|
+
}
|
|
55
|
+
logger.debug({ conversationId, messageId: pendingMessage.id, sessionId: conversation.threadSessionId }, "Polling pending message");
|
|
56
|
+
if (conversation.telaAgentId) {
|
|
57
|
+
const sessionResult2 = await getTelaAgentSession(event, conversation.threadSessionId);
|
|
58
|
+
if (!sessionResult2.success) {
|
|
59
|
+
return { messages };
|
|
60
|
+
}
|
|
61
|
+
const agentSession2 = sessionResult2.data;
|
|
62
|
+
const userMessage2 = messages.findLast((message) => message.role === "user");
|
|
63
|
+
const updateData2 = mapTelaAgentSessionToMessageUpdate(agentSession2, {
|
|
64
|
+
lastSyncedStepId: pendingMessage.externalUuid ?? lastAssistantMessage?.externalUuid,
|
|
65
|
+
existingReasoningData: Array.isArray(pendingMessage.reasoningData) ? pendingMessage.reasoningData : null,
|
|
66
|
+
pendingMessageTtft: pendingMessage.ttft,
|
|
67
|
+
userMessageCreatedAt: userMessage2?.createdAt ?? null
|
|
68
|
+
});
|
|
69
|
+
await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData2, token);
|
|
70
|
+
if (!hasMessageUpdate(updateData2)) {
|
|
71
|
+
return { messages };
|
|
219
72
|
}
|
|
73
|
+
await db.update(schema.messages).set(updateData2).where(eq(schema.messages.id, pendingMessage.id));
|
|
74
|
+
if (isTerminalMessageUpdate(updateData2)) {
|
|
75
|
+
trackTelaAgentConversationUsage(
|
|
76
|
+
db,
|
|
77
|
+
context,
|
|
78
|
+
conversationId,
|
|
79
|
+
conversation.threadSessionId,
|
|
80
|
+
mapTelaAgentUsage(agentSession2)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return buildMessageUpdateResponse(messages, pendingMessage.id, updateData2);
|
|
84
|
+
}
|
|
85
|
+
const sessionResult = await getSessionStatus(token, conversation.threadSessionId);
|
|
86
|
+
if (!sessionResult.success) {
|
|
87
|
+
return { messages };
|
|
88
|
+
}
|
|
89
|
+
const agentSession = sessionResult.data;
|
|
90
|
+
const userMessage = messages.findLast((message) => message.role === "user");
|
|
91
|
+
const updateData = mapAgentApiSessionToMessageUpdate(agentSession, {
|
|
92
|
+
pendingMessage,
|
|
93
|
+
lastAssistantMessage,
|
|
94
|
+
userMessage
|
|
95
|
+
});
|
|
96
|
+
if (updateData.status === "completed") {
|
|
97
|
+
logger.info({ conversationId, messageId: pendingMessage.id, status: "completed" }, "Message completed");
|
|
98
|
+
} else if (updateData.status === "failed") {
|
|
99
|
+
logger.warn({ conversationId, messageId: pendingMessage.id, status: "failed" }, "Message failed");
|
|
100
|
+
}
|
|
101
|
+
await attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData, token);
|
|
102
|
+
if (!hasMessageUpdate(updateData)) {
|
|
103
|
+
return { messages };
|
|
104
|
+
}
|
|
105
|
+
await db.update(schema.messages).set(updateData).where(eq(schema.messages.id, pendingMessage.id));
|
|
106
|
+
if (isTerminalMessageUpdate(updateData)) {
|
|
107
|
+
trackAgentApiConversationUsage(
|
|
108
|
+
db,
|
|
109
|
+
context,
|
|
110
|
+
conversationId,
|
|
111
|
+
conversation.threadSessionId,
|
|
112
|
+
agentSession
|
|
113
|
+
);
|
|
220
114
|
}
|
|
221
|
-
return
|
|
115
|
+
return buildMessageUpdateResponse(messages, pendingMessage.id, updateData);
|
|
222
116
|
});
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getRouterParam, readBody } from "h3";
|
|
2
|
-
import {
|
|
3
|
-
import { eq, and, isNull } from "drizzle-orm";
|
|
2
|
+
import { eq } from "drizzle-orm";
|
|
4
3
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
5
|
+
import {
|
|
6
|
+
buildTelaAgentTurnPayload,
|
|
7
|
+
prepareDefaultAgentTurn,
|
|
8
|
+
queueTelaAgentTurn,
|
|
9
|
+
resolveDefaultAgentEnvironmentVariables,
|
|
10
|
+
withEnvironmentVariables
|
|
11
|
+
} from "#chat-runtime/server/utils/conversation-agent-turn";
|
|
12
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
7
13
|
import { getAgentDataToken, createChatAgentSession, continueSession } from "#chat-runtime/server/utils/agent-api";
|
|
8
14
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
9
|
-
import {
|
|
10
|
-
getConversationAppliedSettings,
|
|
11
|
-
getWorkspaceSettingsUpdatedAt,
|
|
12
|
-
resolveWorkspaceSettings,
|
|
13
|
-
serializeResolvedWorkspaceSettings
|
|
14
|
-
} from "#chat-runtime/server/utils/chat-workspace-settings";
|
|
15
|
-
import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
|
|
16
|
-
import { useChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
17
|
-
import { mergeFileUrls } from "#chat-runtime/utils/file";
|
|
18
15
|
import { isValidModel } from "#chat-runtime/types/chat";
|
|
16
|
+
import { telaAgentExecutionInputSchema } from "#chat-runtime/types/tela-agent";
|
|
19
17
|
export default defineEventHandler(async (event) => {
|
|
20
|
-
const context =
|
|
21
|
-
const token = getAgentDataToken(event);
|
|
18
|
+
const context = resolveChatRuntimeContext(event);
|
|
22
19
|
const db = useDb();
|
|
23
20
|
const conversationId = getRouterParam(event, "id");
|
|
24
21
|
const body = await readBody(event);
|
|
@@ -34,39 +31,37 @@ export default defineEventHandler(async (event) => {
|
|
|
34
31
|
statusMessage: "Message content is required"
|
|
35
32
|
});
|
|
36
33
|
}
|
|
37
|
-
|
|
34
|
+
const [conversation] = await db.select().from(schema.conversations).where(conversationScopeConditions(context, conversationId));
|
|
35
|
+
if (!conversation) {
|
|
36
|
+
throw createError({
|
|
37
|
+
statusCode: 404,
|
|
38
|
+
statusMessage: "Conversation not found"
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (!conversation.telaAgentId && body.model !== void 0 && !isValidModel(body.model)) {
|
|
38
42
|
throw createError({
|
|
39
43
|
statusCode: 400,
|
|
40
44
|
statusMessage: "Invalid model"
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
eq(schema.conversations.id, conversationId),
|
|
46
|
-
eq(schema.conversations.workspaceId, context.workspaceId)
|
|
47
|
-
)
|
|
48
|
-
);
|
|
49
|
-
if (!conversation) {
|
|
47
|
+
const agentInputs = conversation.telaAgentId && body.agentInputs !== void 0 ? telaAgentExecutionInputSchema.array().safeParse(body.agentInputs) : null;
|
|
48
|
+
if (agentInputs && !agentInputs.success) {
|
|
50
49
|
throw createError({
|
|
51
|
-
statusCode:
|
|
52
|
-
statusMessage: "
|
|
50
|
+
statusCode: 400,
|
|
51
|
+
statusMessage: "Invalid Tela agent inputs"
|
|
53
52
|
});
|
|
54
53
|
}
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
54
|
+
const telaAgentPayload = conversation.telaAgentId ? await buildTelaAgentTurnPayload(event, conversation, body.content, body.files, agentInputs?.data) : null;
|
|
55
|
+
const defaultAgentTurn = conversation.telaAgentId ? null : await prepareDefaultAgentTurn(event, db, context, {
|
|
56
|
+
conversation,
|
|
57
|
+
conversationId,
|
|
58
|
+
content: body.content,
|
|
59
|
+
files: body.files,
|
|
60
|
+
requestedModel: body.model,
|
|
61
|
+
workspaceSettingsSnapshot: body.workspaceSettingsSnapshot
|
|
62
|
+
});
|
|
58
63
|
const now = /* @__PURE__ */ new Date();
|
|
59
64
|
const assistantCreatedAt = new Date(now.getTime() + 1);
|
|
60
|
-
if (!conversation.appliedSettingsSnapshot && trustedSettings) {
|
|
61
|
-
await db.update(schema.conversations).set({
|
|
62
|
-
appliedSettingsAt: getWorkspaceSettingsUpdatedAt(trustedSettings),
|
|
63
|
-
appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(trustedSettings),
|
|
64
|
-
updatedAt: now
|
|
65
|
-
}).where(and(
|
|
66
|
-
eq(schema.conversations.id, conversationId),
|
|
67
|
-
isNull(schema.conversations.appliedSettingsSnapshot)
|
|
68
|
-
));
|
|
69
|
-
}
|
|
70
65
|
const [userMessages, assistantMessages] = await Promise.all([
|
|
71
66
|
db.insert(schema.messages).values({
|
|
72
67
|
conversationId,
|
|
@@ -95,49 +90,42 @@ export default defineEventHandler(async (event) => {
|
|
|
95
90
|
}
|
|
96
91
|
logger.info({ conversationId, userMessageId: userMessage.id, hasFiles: !!body.files?.length }, "Message sent");
|
|
97
92
|
const assistantMessageId = assistantMessage.id;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
fileUrls,
|
|
128
|
-
model: effectiveModel,
|
|
129
|
-
customTools: canvasTools?.length ? { canvas: canvasTools } : void 0,
|
|
130
|
-
tools: hasKnowledgeSources ? { workstationQueryTool: true } : void 0,
|
|
131
|
-
environmentVariables: hasEnvironmentVariables ? environmentVariables : void 0,
|
|
132
|
-
skills
|
|
133
|
-
}).then(async (result) => {
|
|
93
|
+
if (conversation.telaAgentId) {
|
|
94
|
+
queueTelaAgentTurn(event, db, {
|
|
95
|
+
conversation,
|
|
96
|
+
conversationId,
|
|
97
|
+
assistantMessageId,
|
|
98
|
+
payload: telaAgentPayload,
|
|
99
|
+
titleContent: body.content,
|
|
100
|
+
operation: "send"
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
userMessage,
|
|
104
|
+
assistantMessage
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (!defaultAgentTurn) {
|
|
108
|
+
throw createError({
|
|
109
|
+
statusCode: 500,
|
|
110
|
+
statusMessage: "Failed to prepare agent turn"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const token = getAgentDataToken(event);
|
|
114
|
+
if (defaultAgentTurn.needsNewThread) {
|
|
115
|
+
const environmentVariables = await resolveDefaultAgentEnvironmentVariables(
|
|
116
|
+
event,
|
|
117
|
+
context,
|
|
118
|
+
{ conversationId },
|
|
119
|
+
"Failed to resolve workspace credentials, proceeding without env vars"
|
|
120
|
+
);
|
|
121
|
+
createChatAgentSession(token, withEnvironmentVariables(defaultAgentTurn.createPayload, environmentVariables)).then(async (result) => {
|
|
134
122
|
if (result.success) {
|
|
135
123
|
logger.info({ conversationId, sessionId: result.sessionId }, "New agent thread created");
|
|
136
124
|
await db.update(schema.conversations).set({
|
|
137
125
|
threadSessionId: result.sessionId,
|
|
138
|
-
title: messageTitle,
|
|
139
|
-
appliedSettingsAt: settingsTimestamp,
|
|
140
|
-
model: effectiveModel ?? null,
|
|
126
|
+
title: defaultAgentTurn.messageTitle,
|
|
127
|
+
appliedSettingsAt: defaultAgentTurn.settingsTimestamp,
|
|
128
|
+
model: defaultAgentTurn.effectiveModel ?? null,
|
|
141
129
|
updatedAt: /* @__PURE__ */ new Date()
|
|
142
130
|
}).where(eq(schema.conversations.id, conversationId));
|
|
143
131
|
} else {
|
|
@@ -155,20 +143,17 @@ export default defineEventHandler(async (event) => {
|
|
|
155
143
|
}).where(eq(schema.messages.id, assistantMessageId));
|
|
156
144
|
});
|
|
157
145
|
} else {
|
|
158
|
-
continueSession(token, conversation.threadSessionId, {
|
|
159
|
-
content: body.content.trim(),
|
|
160
|
-
fileUrls,
|
|
161
|
-
model: effectiveModel
|
|
162
|
-
}).then(async (result) => {
|
|
146
|
+
continueSession(token, conversation.threadSessionId, defaultAgentTurn.continuePayload).then(async (result) => {
|
|
163
147
|
if (!result.success) {
|
|
164
148
|
logger.warn({ conversationId, sessionId: conversation.threadSessionId, error: result.error }, "Agent session continue failed");
|
|
165
149
|
await db.update(schema.messages).set({
|
|
166
150
|
content: `Erro: ${result.error}`,
|
|
167
151
|
status: "failed"
|
|
168
152
|
}).where(eq(schema.messages.id, assistantMessageId));
|
|
153
|
+
return;
|
|
169
154
|
}
|
|
170
155
|
await db.update(schema.conversations).set({
|
|
171
|
-
...effectiveModel ? { model: effectiveModel } : {},
|
|
156
|
+
...defaultAgentTurn.effectiveModel ? { model: defaultAgentTurn.effectiveModel } : {},
|
|
172
157
|
updatedAt: /* @__PURE__ */ new Date()
|
|
173
158
|
}).where(eq(schema.conversations.id, conversationId));
|
|
174
159
|
}).catch(async (e) => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getRouterParam } from "h3";
|
|
2
2
|
import { eq, and } from "drizzle-orm";
|
|
3
3
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
5
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
5
6
|
import { getWorkspaceBilling, transformCostForBilling } from "#chat-runtime/server/utils/billing";
|
|
6
7
|
export default defineEventHandler(async (event) => {
|
|
7
|
-
const context =
|
|
8
|
+
const context = resolveChatRuntimeContext(event);
|
|
8
9
|
const db = useDb();
|
|
9
10
|
const id = getRouterParam(event, "id");
|
|
10
11
|
if (!id) {
|
|
@@ -13,12 +14,7 @@ export default defineEventHandler(async (event) => {
|
|
|
13
14
|
statusMessage: "Conversation ID is required"
|
|
14
15
|
});
|
|
15
16
|
}
|
|
16
|
-
const [conversation] = await db.select({ id: schema.conversations.id }).from(schema.conversations).where(
|
|
17
|
-
and(
|
|
18
|
-
eq(schema.conversations.id, id),
|
|
19
|
-
eq(schema.conversations.workspaceId, context.workspaceId)
|
|
20
|
-
)
|
|
21
|
-
);
|
|
17
|
+
const [conversation] = await db.select({ id: schema.conversations.id }).from(schema.conversations).where(conversationScopeConditions(context, id));
|
|
22
18
|
if (!conversation) {
|
|
23
19
|
throw createError({
|
|
24
20
|
statusCode: 404,
|
|
@@ -3,7 +3,7 @@ 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 {
|
|
6
|
+
import { resolveChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
7
7
|
const TITLE_CANVAS_ID = "ba06c4a0-ef05-4fb6-b4ef-5af3d63a1ba2";
|
|
8
8
|
const FALLBACK_TITLE_MAX_LENGTH = 60;
|
|
9
9
|
const FALLBACK_TITLE_DEFAULT = "New chat";
|
|
@@ -26,8 +26,9 @@ export default defineEventHandler(async (event) => {
|
|
|
26
26
|
statusMessage: "userMessage and assistantResponse are required"
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
-
const
|
|
30
|
-
const
|
|
29
|
+
const runtimeConfig = useRuntimeConfig();
|
|
30
|
+
const { telaApiKey } = runtimeConfig;
|
|
31
|
+
const features = resolveChatNuxtFeatures(runtimeConfig);
|
|
31
32
|
if (!features.generateTitle || !telaApiKey) {
|
|
32
33
|
const fallback = buildFallbackTitle(userMessage);
|
|
33
34
|
logger.debug({ titleLength: fallback.length, reason: !features.generateTitle ? "feature-disabled" : "no-tela-api-key" }, "Title generated via fallback");
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { defineEventHandler } from "h3";
|
|
2
|
-
import { desc
|
|
2
|
+
import { desc } from "drizzle-orm";
|
|
3
3
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
5
|
+
import { conversationScopeConditions } from "#chat-runtime/server/utils/conversation-scope";
|
|
5
6
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
6
7
|
export default defineEventHandler(async (event) => {
|
|
7
|
-
const context =
|
|
8
|
+
const context = resolveChatRuntimeContext(event);
|
|
8
9
|
const db = useDb();
|
|
9
|
-
const conversations = await db.select().from(schema.conversations).where(
|
|
10
|
+
const conversations = await db.select().from(schema.conversations).where(conversationScopeConditions(context)).orderBy(desc(schema.conversations.updatedAt));
|
|
10
11
|
logger.debug({ workspaceId: context.workspaceId, count: conversations.length }, "Conversations listed");
|
|
11
12
|
return conversations;
|
|
12
13
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getRequestHeader, readBody } from "h3";
|
|
2
2
|
import { useDb, schema } from "#chat-runtime/server/db";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveChatRuntimeContext } from "#chat-runtime/server/utils/chat-context";
|
|
4
4
|
import { logger } from "#chat-runtime/server/utils/logger";
|
|
5
5
|
import { isValidModel } from "#chat-runtime/types/chat";
|
|
6
6
|
export default defineEventHandler(async (event) => {
|
|
7
|
-
const context =
|
|
7
|
+
const context = resolveChatRuntimeContext(event);
|
|
8
8
|
const db = useDb();
|
|
9
9
|
let body = {};
|
|
10
10
|
const contentLength = getRequestHeader(event, "content-length");
|
|
@@ -19,7 +19,7 @@ export default defineEventHandler(async (event) => {
|
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
if (body.model !== void 0 && body.model !== null && !isValidModel(body.model)) {
|
|
22
|
+
if (!context.telaAgentId && body.model !== void 0 && body.model !== null && !isValidModel(body.model)) {
|
|
23
23
|
throw createError({
|
|
24
24
|
statusCode: 400,
|
|
25
25
|
statusMessage: "Invalid model"
|
|
@@ -29,7 +29,8 @@ export default defineEventHandler(async (event) => {
|
|
|
29
29
|
workspaceId: context.workspaceId,
|
|
30
30
|
userId: context.user.id,
|
|
31
31
|
createdBy: context.user.email,
|
|
32
|
-
|
|
32
|
+
telaAgentId: context.telaAgentId,
|
|
33
|
+
...context.telaAgentId ? { model: null } : body.model ? { model: body.model } : {}
|
|
33
34
|
}).returning();
|
|
34
35
|
if (!conversation) {
|
|
35
36
|
throw createError({
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getRouterParam } from "h3";
|
|
2
|
+
import { requireUser } from "#chat-runtime/server/utils/auth";
|
|
3
|
+
import { getTelaAgent } from "#chat-runtime/server/utils/tela-agent-api";
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
requireUser(event);
|
|
6
|
+
const agentId = getRouterParam(event, "id")?.trim();
|
|
7
|
+
if (!agentId) {
|
|
8
|
+
throw createError({
|
|
9
|
+
statusCode: 400,
|
|
10
|
+
statusMessage: "Agent ID is required"
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const result = await getTelaAgent(event, agentId);
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 502,
|
|
17
|
+
statusMessage: result.error
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
id: result.data.id,
|
|
22
|
+
inputSchema: result.data.inputSchema,
|
|
23
|
+
isMultiturn: result.data.isMultiturn ?? null
|
|
24
|
+
};
|
|
25
|
+
});
|
|
@@ -55,7 +55,7 @@ export default defineEventHandler(async (event) => {
|
|
|
55
55
|
name: skill.name,
|
|
56
56
|
description: skill.description,
|
|
57
57
|
runtime: skill.runtime,
|
|
58
|
-
allowedTools: skill.allowedTools,
|
|
58
|
+
allowedTools: [...skill.allowedTools],
|
|
59
59
|
isPublic: skill.isPublic,
|
|
60
60
|
addedByUserId: skill.addedBy?.userId || session.user.id,
|
|
61
61
|
addedByUsername: skill.addedBy?.username || session.user.name || "",
|