@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,6 +1,7 @@
|
|
|
1
1
|
import { mkdir } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { useRuntimeConfig } from "nitropack/runtime";
|
|
4
|
+
import { sql } from "drizzle-orm";
|
|
4
5
|
import postgres from "postgres";
|
|
5
6
|
import { drizzle as drizzlePostgres } from "drizzle-orm/postgres-js";
|
|
6
7
|
import { migrate as migratePostgres } from "drizzle-orm/postgres-js/migrator";
|
|
@@ -9,6 +10,9 @@ import { CHAT_SCHEMA_NAME } from "./schema/_schema.js";
|
|
|
9
10
|
import { logger } from "../utils/logger.js";
|
|
10
11
|
let dbInstance = null;
|
|
11
12
|
let dbInitPromise = null;
|
|
13
|
+
const LEGACY_MIGRATIONS_SCHEMA = "drizzle";
|
|
14
|
+
const MIGRATIONS_TABLE = "__drizzle_migrations";
|
|
15
|
+
const MIGRATIONS_LEDGER_LOCK_KEY = 581073927;
|
|
12
16
|
export function isPgliteUrl(url) {
|
|
13
17
|
return url.startsWith("pglite:") || url.startsWith("file:");
|
|
14
18
|
}
|
|
@@ -25,6 +29,59 @@ function resolvePgliteDataDir(url) {
|
|
|
25
29
|
return url;
|
|
26
30
|
}
|
|
27
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
|
+
}
|
|
28
85
|
async function initPostgresAsync(databaseUrl, migrationsDir) {
|
|
29
86
|
const client = postgres(databaseUrl, {
|
|
30
87
|
max: 10,
|
|
@@ -36,6 +93,10 @@ async function initPostgresAsync(databaseUrl, migrationsDir) {
|
|
|
36
93
|
});
|
|
37
94
|
const db = drizzlePostgres(client, { schema });
|
|
38
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
|
+
});
|
|
39
100
|
await migratePostgres(db, { migrationsFolder: migrationsDir, migrationsSchema: CHAT_SCHEMA_NAME });
|
|
40
101
|
logger.info({ migrationsSchema: CHAT_SCHEMA_NAME }, "Postgres connection pool initialized and migrations applied");
|
|
41
102
|
} else {
|
|
@@ -54,6 +115,7 @@ async function initPgliteAsync(databaseUrl, migrationsDir) {
|
|
|
54
115
|
const client = new PGlite(dataDir);
|
|
55
116
|
const db = drizzle(client, { schema });
|
|
56
117
|
if (migrationsDir) {
|
|
118
|
+
await seedChatMigrationsLedger(db);
|
|
57
119
|
await migrate(db, { migrationsFolder: migrationsDir, migrationsSchema: CHAT_SCHEMA_NAME });
|
|
58
120
|
logger.info({ dataDir, migrationsSchema: CHAT_SCHEMA_NAME }, "pglite database initialized and migrated");
|
|
59
121
|
} else {
|
|
@@ -88,6 +88,23 @@ export declare const conversations: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
88
88
|
identity: undefined;
|
|
89
89
|
generated: undefined;
|
|
90
90
|
}, {}, {}>;
|
|
91
|
+
telaAgentId: import("drizzle-orm/pg-core").PgColumn<{
|
|
92
|
+
name: "tela_agent_id";
|
|
93
|
+
tableName: "conversations";
|
|
94
|
+
dataType: "string";
|
|
95
|
+
columnType: "PgText";
|
|
96
|
+
data: string;
|
|
97
|
+
driverParam: string;
|
|
98
|
+
notNull: false;
|
|
99
|
+
hasDefault: false;
|
|
100
|
+
isPrimaryKey: false;
|
|
101
|
+
isAutoincrement: false;
|
|
102
|
+
hasRuntimeDefault: false;
|
|
103
|
+
enumValues: [string, ...string[]];
|
|
104
|
+
baseColumn: never;
|
|
105
|
+
identity: undefined;
|
|
106
|
+
generated: undefined;
|
|
107
|
+
}, {}, {}>;
|
|
91
108
|
model: import("drizzle-orm/pg-core").PgColumn<{
|
|
92
109
|
name: "model";
|
|
93
110
|
tableName: "conversations";
|
|
@@ -6,6 +6,7 @@ export const conversations = chatSchema.table("conversations", {
|
|
|
6
6
|
userId: text("user_id").notNull(),
|
|
7
7
|
title: text("title").notNull().default("Nova conversa"),
|
|
8
8
|
threadSessionId: text("thread_session_id"),
|
|
9
|
+
telaAgentId: text("tela_agent_id"),
|
|
9
10
|
model: text("model").default("claude-sonnet-4-5"),
|
|
10
11
|
appliedSettingsAt: timestamp("applied_settings_at", { withTimezone: true }),
|
|
11
12
|
appliedSettingsSnapshot: jsonb("applied_settings_snapshot").$type(),
|
|
@@ -15,5 +16,6 @@ export const conversations = chatSchema.table("conversations", {
|
|
|
15
16
|
}, (table) => [
|
|
16
17
|
index("idx_conversations_workspace").on(table.workspaceId),
|
|
17
18
|
index("idx_conversations_user").on(table.userId),
|
|
18
|
-
index("idx_conversations_updated").on(table.updatedAt)
|
|
19
|
+
index("idx_conversations_updated").on(table.updatedAt),
|
|
20
|
+
index("idx_conversations_workspace_tela_agent_updated").on(table.workspaceId, table.telaAgentId, table.updatedAt)
|
|
19
21
|
]);
|
|
@@ -36,4 +36,4 @@ export declare function cancelSession(token: string, sessionId: string): Promise
|
|
|
36
36
|
error: string;
|
|
37
37
|
}>;
|
|
38
38
|
export declare function extractContentFromReasoning(reasoning: ReasoningItem[]): string;
|
|
39
|
-
export declare function extractContentFromSession(session: AgentApiSession
|
|
39
|
+
export declare function extractContentFromSession(session: AgentApiSession): string;
|
|
@@ -150,11 +150,8 @@ export function extractContentFromReasoning(reasoning) {
|
|
|
150
150
|
}
|
|
151
151
|
return "";
|
|
152
152
|
}
|
|
153
|
-
export function extractContentFromSession(session
|
|
154
|
-
if (
|
|
155
|
-
return extractContentFromReasoning(turnReasoning);
|
|
156
|
-
}
|
|
157
|
-
if (session.result?.content) {
|
|
153
|
+
export function extractContentFromSession(session) {
|
|
154
|
+
if (typeof session.result?.content === "string") {
|
|
158
155
|
return session.result.content;
|
|
159
156
|
}
|
|
160
157
|
if (session.result?.structuredContent) {
|
|
@@ -4,6 +4,11 @@ export type ChatContext = {
|
|
|
4
4
|
user: AppSession['user'];
|
|
5
5
|
workspaceId: string;
|
|
6
6
|
};
|
|
7
|
+
export type ChatRuntimeContext = ChatContext & {
|
|
8
|
+
telaAgentId: string | null;
|
|
9
|
+
};
|
|
7
10
|
export declare function resolveRequestedWorkspaceId(requestedWorkspaceId: string | null | undefined): string | null;
|
|
11
|
+
export declare function resolveRequestedTelaAgentId(requestedTelaAgentId: string | null | undefined): string | null;
|
|
8
12
|
export declare function resolveChatContextFromSession(session: AppSession, requestedWorkspaceId?: string | null): ChatContext;
|
|
9
13
|
export declare function resolveChatContext(event: H3Event): ChatContext;
|
|
14
|
+
export declare function resolveChatRuntimeContext(event: H3Event): ChatRuntimeContext;
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { createError, getHeader } from "h3";
|
|
2
2
|
import { requireUser } from "#chat-runtime/server/utils/auth";
|
|
3
|
+
const TELA_AGENT_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
3
4
|
export function resolveRequestedWorkspaceId(requestedWorkspaceId) {
|
|
4
5
|
const normalized = requestedWorkspaceId?.trim();
|
|
5
6
|
return normalized || null;
|
|
6
7
|
}
|
|
8
|
+
export function resolveRequestedTelaAgentId(requestedTelaAgentId) {
|
|
9
|
+
const normalized = requestedTelaAgentId?.trim();
|
|
10
|
+
if (!normalized)
|
|
11
|
+
return null;
|
|
12
|
+
if (!TELA_AGENT_ID_PATTERN.test(normalized)) {
|
|
13
|
+
throw createError({
|
|
14
|
+
statusCode: 400,
|
|
15
|
+
statusMessage: "Invalid Tela agent ID"
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return normalized;
|
|
19
|
+
}
|
|
7
20
|
export function resolveChatContextFromSession(session, requestedWorkspaceId) {
|
|
8
21
|
const normalizedRequestedWorkspaceId = resolveRequestedWorkspaceId(requestedWorkspaceId);
|
|
9
22
|
if (normalizedRequestedWorkspaceId && normalizedRequestedWorkspaceId !== session.workspace.id) {
|
|
@@ -22,3 +35,10 @@ export function resolveChatContext(event) {
|
|
|
22
35
|
const requestedWorkspaceId = getHeader(event, "x-chat-workspace-id");
|
|
23
36
|
return resolveChatContextFromSession(session, requestedWorkspaceId);
|
|
24
37
|
}
|
|
38
|
+
export function resolveChatRuntimeContext(event) {
|
|
39
|
+
const context = resolveChatContext(event);
|
|
40
|
+
return {
|
|
41
|
+
...context,
|
|
42
|
+
telaAgentId: resolveRequestedTelaAgentId(getHeader(event, "x-chat-tela-agent-id"))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -8,8 +8,9 @@ export declare function normalizeResolvedWorkspaceSettings(settings: ResolvedWor
|
|
|
8
8
|
export declare function serializeResolvedWorkspaceSettings(settings: ResolvedWorkspaceSettings): ResolvedWorkspaceSettings;
|
|
9
9
|
export declare function getConversationAppliedSettings(conversation: {
|
|
10
10
|
appliedSettingsSnapshot?: ResolvedWorkspaceSettingsInput | null;
|
|
11
|
-
}, trustedSettings?: ResolvedWorkspaceSettingsInput | null): ResolvedWorkspaceSettings;
|
|
11
|
+
}, trustedSettings?: ResolvedWorkspaceSettingsInput | null, explicitSettings?: ResolvedWorkspaceSettingsInput | null): ResolvedWorkspaceSettings;
|
|
12
12
|
export declare function hasMaterialWorkspaceSettingsMismatch(currentSettings: ResolvedWorkspaceSettingsInput | null | undefined, trustedSettings: ResolvedWorkspaceSettingsInput | null | undefined): boolean;
|
|
13
|
+
export declare function parseWorkspaceSettingsSnapshot(settings: unknown): ResolvedWorkspaceSettings;
|
|
13
14
|
export declare function resolveDefaultWorkspaceSettings(db: DbInstance, workspaceId: string): Promise<ResolvedWorkspaceSettings>;
|
|
14
15
|
export declare function resolveWorkspaceSettings(event: H3Event, context: ChatContext, db: DbInstance): Promise<ResolvedWorkspaceSettings>;
|
|
15
16
|
export declare function getWorkspaceSettingsUpdatedAt(settings: ResolvedWorkspaceSettings): Date;
|
|
@@ -40,10 +40,13 @@ export function serializeResolvedWorkspaceSettings(settings) {
|
|
|
40
40
|
updatedAt: settings.updatedAt instanceof Date ? settings.updatedAt.toISOString() : settings.updatedAt
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
export function getConversationAppliedSettings(conversation, trustedSettings) {
|
|
43
|
+
export function getConversationAppliedSettings(conversation, trustedSettings, explicitSettings) {
|
|
44
44
|
if (conversation.appliedSettingsSnapshot) {
|
|
45
45
|
return normalizeResolvedWorkspaceSettings(conversation.appliedSettingsSnapshot);
|
|
46
46
|
}
|
|
47
|
+
if (explicitSettings !== void 0) {
|
|
48
|
+
return normalizeResolvedWorkspaceSettings(explicitSettings);
|
|
49
|
+
}
|
|
47
50
|
return normalizeResolvedWorkspaceSettings(trustedSettings);
|
|
48
51
|
}
|
|
49
52
|
export function hasMaterialWorkspaceSettingsMismatch(currentSettings, trustedSettings) {
|
|
@@ -69,6 +72,12 @@ function parseResolverResult(settings) {
|
|
|
69
72
|
}
|
|
70
73
|
return normalizeResolvedWorkspaceSettings(parsedSettings.data);
|
|
71
74
|
}
|
|
75
|
+
export function parseWorkspaceSettingsSnapshot(settings) {
|
|
76
|
+
if (settings === null) {
|
|
77
|
+
return normalizeResolvedWorkspaceSettings(null);
|
|
78
|
+
}
|
|
79
|
+
return parseResolverResult(settings);
|
|
80
|
+
}
|
|
72
81
|
export async function resolveDefaultWorkspaceSettings(db, workspaceId) {
|
|
73
82
|
const [settings, skillRows] = await Promise.all([
|
|
74
83
|
db.select().from(schema.workspaceSettings).where(eq(schema.workspaceSettings.workspaceId, workspaceId)),
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { H3Event } from 'h3';
|
|
2
|
+
import { schema } from '#chat-runtime/server/db';
|
|
3
|
+
import type { useDb } from '#chat-runtime/server/db';
|
|
4
|
+
import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
|
|
5
|
+
import type { FileUploadInput, MessageFile } from '#chat-runtime/types/chat';
|
|
6
|
+
import type { AgentApiChatPayload, ContinueSessionPayload } from '#chat-runtime/types/agent';
|
|
7
|
+
import type { TelaAgentExecutionInput, TelaAgentRunPayload } from '#chat-runtime/types/tela-agent';
|
|
8
|
+
type DbInstance = ReturnType<typeof useDb>;
|
|
9
|
+
type ConversationRecord = typeof schema.conversations.$inferSelect;
|
|
10
|
+
type AgentTurnOperation = 'send' | 'retry';
|
|
11
|
+
type AgentTurnFiles = FileUploadInput[] | MessageFile[] | null | undefined;
|
|
12
|
+
export type PreparedDefaultAgentTurn = {
|
|
13
|
+
needsNewThread: boolean;
|
|
14
|
+
effectiveModel?: string;
|
|
15
|
+
settingsTimestamp: Date;
|
|
16
|
+
messageTitle: string;
|
|
17
|
+
createPayload: AgentApiChatPayload;
|
|
18
|
+
continuePayload: ContinueSessionPayload;
|
|
19
|
+
};
|
|
20
|
+
export declare function buildTelaAgentTurnPayload(event: H3Event, conversation: Pick<ConversationRecord, 'threadSessionId' | 'telaAgentId'>, content: string, files?: FileUploadInput[] | MessageFile[] | null, agentInputs?: TelaAgentExecutionInput[] | null): Promise<TelaAgentRunPayload>;
|
|
21
|
+
export declare function queueTelaAgentTurn(event: H3Event, db: DbInstance, options: {
|
|
22
|
+
conversation: ConversationRecord;
|
|
23
|
+
conversationId: string;
|
|
24
|
+
assistantMessageId: string;
|
|
25
|
+
payload: TelaAgentRunPayload;
|
|
26
|
+
titleContent: string;
|
|
27
|
+
operation: AgentTurnOperation;
|
|
28
|
+
}): void;
|
|
29
|
+
export declare function prepareDefaultAgentTurn(event: H3Event, db: DbInstance, context: ChatRuntimeContext, options: {
|
|
30
|
+
conversation: ConversationRecord;
|
|
31
|
+
conversationId: string;
|
|
32
|
+
content: string;
|
|
33
|
+
files?: AgentTurnFiles;
|
|
34
|
+
requestedModel?: string;
|
|
35
|
+
workspaceSettingsSnapshot?: unknown;
|
|
36
|
+
}): Promise<PreparedDefaultAgentTurn>;
|
|
37
|
+
export declare function resolveDefaultAgentEnvironmentVariables(event: H3Event, context: ChatRuntimeContext, logContext: Record<string, unknown>, failureMessage: string): Promise<Record<string, string>>;
|
|
38
|
+
export declare function withEnvironmentVariables(payload: AgentApiChatPayload, environmentVariables: Record<string, string>): AgentApiChatPayload;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { and, eq, isNull } from "drizzle-orm";
|
|
2
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
import { schema } from "#chat-runtime/server/db";
|
|
4
|
+
import { buildMessageWithContext, renderSystemPrompt, truncateTitle } from "#chat-runtime/server/utils/chat-message-builder";
|
|
5
|
+
import {
|
|
6
|
+
getConversationAppliedSettings,
|
|
7
|
+
getWorkspaceSettingsUpdatedAt,
|
|
8
|
+
parseWorkspaceSettingsSnapshot,
|
|
9
|
+
resolveWorkspaceSettings,
|
|
10
|
+
serializeResolvedWorkspaceSettings
|
|
11
|
+
} from "#chat-runtime/server/utils/chat-workspace-settings";
|
|
12
|
+
import { logger } from "#chat-runtime/server/utils/logger";
|
|
13
|
+
import { getTelaAgent, runTelaAgent } from "#chat-runtime/server/utils/tela-agent-api";
|
|
14
|
+
import { toTelaAgentAttachments, toTelaAgentInputSchema } from "#chat-runtime/server/utils/tela-agent-session";
|
|
15
|
+
import { resolveWorkspaceCredentials } from "#chat-runtime/server/utils/workspace";
|
|
16
|
+
import { resolveChatNuxtFeatures } from "#chat-runtime/utils/features";
|
|
17
|
+
import { mergeFileUrls } from "#chat-runtime/utils/file";
|
|
18
|
+
import { isValidModel } from "#chat-runtime/types/chat";
|
|
19
|
+
function getFileVaultRef(file) {
|
|
20
|
+
return file.vaultReference?.trim() || (file.url.startsWith("vault://") ? file.url : null);
|
|
21
|
+
}
|
|
22
|
+
function filterMappedFiles(files, inputSchema) {
|
|
23
|
+
const mappedVaultRefs = new Set(
|
|
24
|
+
inputSchema.filter((input) => input.type === "file").map((input) => input.vaultRef)
|
|
25
|
+
);
|
|
26
|
+
if (mappedVaultRefs.size === 0)
|
|
27
|
+
return files ?? [];
|
|
28
|
+
return (files ?? []).filter((file) => {
|
|
29
|
+
const vaultRef = getFileVaultRef(file);
|
|
30
|
+
return !vaultRef || !mappedVaultRefs.has(vaultRef);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export async function buildTelaAgentTurnPayload(event, conversation, content, files, agentInputs) {
|
|
34
|
+
let inputSchema = agentInputs ?? [];
|
|
35
|
+
let attachmentFiles = filterMappedFiles(files, inputSchema);
|
|
36
|
+
if (inputSchema.length === 0 && conversation.telaAgentId && files?.length) {
|
|
37
|
+
const agentResult = await getTelaAgent(event, conversation.telaAgentId);
|
|
38
|
+
if (agentResult.success) {
|
|
39
|
+
const fileInputCount = agentResult.data.inputSchema.filter((input) => input.type === "file" && input.name.trim()).length;
|
|
40
|
+
inputSchema = toTelaAgentInputSchema(files, agentResult.data.inputSchema);
|
|
41
|
+
if (inputSchema.length > 0) {
|
|
42
|
+
attachmentFiles = [];
|
|
43
|
+
} else if (fileInputCount > 1) {
|
|
44
|
+
logger.warn({
|
|
45
|
+
agentId: conversation.telaAgentId,
|
|
46
|
+
fileInputCount
|
|
47
|
+
}, "Tela agent file input auto-mapping skipped");
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
logger.warn({ agentId: conversation.telaAgentId, error: agentResult.error }, "Tela agent metadata fetch failed");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const attachments = toTelaAgentAttachments(attachmentFiles);
|
|
54
|
+
return {
|
|
55
|
+
message: content.trim(),
|
|
56
|
+
...conversation.threadSessionId ? { sessionId: conversation.threadSessionId } : {},
|
|
57
|
+
...inputSchema.length > 0 ? { inputSchema } : {},
|
|
58
|
+
...attachments.length > 0 ? { attachments } : {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function queueTelaAgentTurn(event, db, options) {
|
|
62
|
+
const needsNewThread = options.conversation.threadSessionId === null;
|
|
63
|
+
const logContext = {
|
|
64
|
+
conversationId: options.conversationId,
|
|
65
|
+
messageId: options.assistantMessageId
|
|
66
|
+
};
|
|
67
|
+
const telaAgentId = options.conversation.telaAgentId;
|
|
68
|
+
if (!telaAgentId) {
|
|
69
|
+
logger.error(logContext, "Tela agent turn queued without agent id");
|
|
70
|
+
void db.update(schema.messages).set({
|
|
71
|
+
content: "Erro: Agente Tela n\xE3o configurado",
|
|
72
|
+
status: "failed"
|
|
73
|
+
}).where(eq(schema.messages.id, options.assistantMessageId));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
runTelaAgent(event, telaAgentId, options.payload).then(async (result) => {
|
|
77
|
+
if (result.success) {
|
|
78
|
+
await db.update(schema.conversations).set({
|
|
79
|
+
...needsNewThread ? { title: truncateTitle(options.titleContent) } : {},
|
|
80
|
+
threadSessionId: result.sessionId,
|
|
81
|
+
model: null,
|
|
82
|
+
appliedSettingsAt: null,
|
|
83
|
+
appliedSettingsSnapshot: null,
|
|
84
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
85
|
+
}).where(eq(schema.conversations.id, options.conversationId));
|
|
86
|
+
} else {
|
|
87
|
+
logger.warn({ ...logContext, error: result.error }, options.operation === "retry" ? "Tela agent retry failed" : "Tela agent run failed");
|
|
88
|
+
await db.update(schema.messages).set({
|
|
89
|
+
content: `Erro: ${result.error}`,
|
|
90
|
+
status: "failed"
|
|
91
|
+
}).where(eq(schema.messages.id, options.assistantMessageId));
|
|
92
|
+
}
|
|
93
|
+
}).catch(async (error) => {
|
|
94
|
+
logger.error({ ...logContext, error: error.message }, options.operation === "retry" ? "Tela agent retry threw exception" : "Tela agent run threw exception");
|
|
95
|
+
await db.update(schema.messages).set({
|
|
96
|
+
content: "Erro: Falha ao conectar ao agente",
|
|
97
|
+
status: "failed"
|
|
98
|
+
}).where(eq(schema.messages.id, options.assistantMessageId));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export async function prepareDefaultAgentTurn(event, db, context, options) {
|
|
102
|
+
const explicitSettings = !options.conversation.appliedSettingsSnapshot && options.workspaceSettingsSnapshot !== void 0 ? parseWorkspaceSettingsSnapshot(options.workspaceSettingsSnapshot) : void 0;
|
|
103
|
+
const trustedSettings = options.conversation.appliedSettingsSnapshot || explicitSettings ? null : await resolveWorkspaceSettings(event, context, db);
|
|
104
|
+
const appliedSettings = getConversationAppliedSettings(options.conversation, trustedSettings, explicitSettings);
|
|
105
|
+
const settingsTimestamp = getWorkspaceSettingsUpdatedAt(appliedSettings);
|
|
106
|
+
const snapshotSettings = explicitSettings ?? trustedSettings;
|
|
107
|
+
if (!options.conversation.appliedSettingsSnapshot && snapshotSettings) {
|
|
108
|
+
await db.update(schema.conversations).set({
|
|
109
|
+
appliedSettingsAt: getWorkspaceSettingsUpdatedAt(snapshotSettings),
|
|
110
|
+
appliedSettingsSnapshot: serializeResolvedWorkspaceSettings(snapshotSettings),
|
|
111
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
112
|
+
}).where(and(
|
|
113
|
+
eq(schema.conversations.id, options.conversationId),
|
|
114
|
+
isNull(schema.conversations.appliedSettingsSnapshot)
|
|
115
|
+
));
|
|
116
|
+
}
|
|
117
|
+
const needsNewThread = options.conversation.threadSessionId === null;
|
|
118
|
+
const effectiveModel = options.requestedModel ?? (isValidModel(options.conversation.model) ? options.conversation.model : void 0);
|
|
119
|
+
const content = options.content.trim();
|
|
120
|
+
const knowledgeSources = appliedSettings.knowledgeSources ?? null;
|
|
121
|
+
const hasKnowledgeSources = knowledgeSources !== null && knowledgeSources.length > 0;
|
|
122
|
+
const externalSkillRefs = appliedSettings.externalSkills?.map((skill) => skill.ref) ?? [];
|
|
123
|
+
const skills = externalSkillRefs.length > 0 ? externalSkillRefs : void 0;
|
|
124
|
+
const fileUrls = mergeFileUrls(appliedSettings.contextFiles, options.files);
|
|
125
|
+
const { vaultUrl } = useRuntimeConfig();
|
|
126
|
+
const messageWithContext = buildMessageWithContext(
|
|
127
|
+
content,
|
|
128
|
+
appliedSettings.systemMessage ?? null,
|
|
129
|
+
knowledgeSources,
|
|
130
|
+
needsNewThread,
|
|
131
|
+
needsNewThread ? renderSystemPrompt(vaultUrl) : ""
|
|
132
|
+
);
|
|
133
|
+
return {
|
|
134
|
+
needsNewThread,
|
|
135
|
+
effectiveModel,
|
|
136
|
+
settingsTimestamp,
|
|
137
|
+
messageTitle: truncateTitle(options.content),
|
|
138
|
+
createPayload: {
|
|
139
|
+
message: messageWithContext,
|
|
140
|
+
fileUrls,
|
|
141
|
+
model: effectiveModel,
|
|
142
|
+
customTools: appliedSettings.canvasTools?.length ? { canvas: appliedSettings.canvasTools } : void 0,
|
|
143
|
+
tools: hasKnowledgeSources ? { workstationQueryTool: true } : void 0,
|
|
144
|
+
skills
|
|
145
|
+
},
|
|
146
|
+
continuePayload: {
|
|
147
|
+
content,
|
|
148
|
+
fileUrls,
|
|
149
|
+
model: effectiveModel
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export async function resolveDefaultAgentEnvironmentVariables(event, context, logContext, failureMessage) {
|
|
154
|
+
if (!resolveChatNuxtFeatures(useRuntimeConfig()).credentials)
|
|
155
|
+
return {};
|
|
156
|
+
try {
|
|
157
|
+
return await resolveWorkspaceCredentials(event, context.workspaceId);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
logger.warn({ ...logContext, error }, failureMessage);
|
|
160
|
+
return {};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export function withEnvironmentVariables(payload, environmentVariables) {
|
|
164
|
+
if (Object.keys(environmentVariables).length === 0)
|
|
165
|
+
return payload;
|
|
166
|
+
return {
|
|
167
|
+
...payload,
|
|
168
|
+
environmentVariables
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { useDb } from '#chat-runtime/server/db';
|
|
2
|
+
import type { MessageFile } from '#chat-runtime/types/chat';
|
|
3
|
+
type DbInstance = ReturnType<typeof useDb>;
|
|
4
|
+
type MessageUpdateWithFiles = {
|
|
5
|
+
content?: string;
|
|
6
|
+
status?: string;
|
|
7
|
+
files?: MessageFile[];
|
|
8
|
+
};
|
|
9
|
+
export declare function attachNewAssistantFilesToMessageUpdate(db: DbInstance, conversationId: string, updateData: MessageUpdateWithFiles, authToken: string): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { and, eq, isNotNull } from "drizzle-orm";
|
|
2
|
+
import { useRuntimeConfig } from "nitropack/runtime";
|
|
3
|
+
import { APIKeyAuthStrategy, VaultFile } from "@meistrari/vault-sdk";
|
|
4
|
+
import { schema } from "#chat-runtime/server/db";
|
|
5
|
+
import { logger } from "#chat-runtime/server/utils/logger";
|
|
6
|
+
import { detectFilesInText } from "#chat-runtime/utils/file";
|
|
7
|
+
async function getExistingMessageFileUrls(db, conversationId) {
|
|
8
|
+
const messagesWithFiles = await db.select({ files: schema.messages.files }).from(schema.messages).where(and(
|
|
9
|
+
eq(schema.messages.conversationId, conversationId),
|
|
10
|
+
isNotNull(schema.messages.files)
|
|
11
|
+
));
|
|
12
|
+
const urls = /* @__PURE__ */ new Set();
|
|
13
|
+
for (const message of messagesWithFiles) {
|
|
14
|
+
if (Array.isArray(message.files)) {
|
|
15
|
+
for (const file of message.files) {
|
|
16
|
+
if (file?.url) {
|
|
17
|
+
urls.add(file.url);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return urls;
|
|
23
|
+
}
|
|
24
|
+
async function extractAssistantFiles(content, authToken) {
|
|
25
|
+
const detected = detectFilesInText(content);
|
|
26
|
+
const vaultFiles = detected.filter((file) => file.type === "vault");
|
|
27
|
+
if (vaultFiles.length === 0)
|
|
28
|
+
return [];
|
|
29
|
+
const { vaultUrl } = useRuntimeConfig();
|
|
30
|
+
return Promise.all(
|
|
31
|
+
vaultFiles.map(async (file) => {
|
|
32
|
+
let name = file.name;
|
|
33
|
+
let mimeType = file.mimeType !== "unknown" ? file.mimeType : void 0;
|
|
34
|
+
let size;
|
|
35
|
+
if (vaultUrl) {
|
|
36
|
+
try {
|
|
37
|
+
const authStrategy = new APIKeyAuthStrategy(authToken);
|
|
38
|
+
const vaultFile = await VaultFile.fromVaultReference({
|
|
39
|
+
reference: file.reference,
|
|
40
|
+
config: { authStrategy, vaultUrl }
|
|
41
|
+
});
|
|
42
|
+
await vaultFile.populateMetadata();
|
|
43
|
+
if (vaultFile.metadata?.originalFileName) {
|
|
44
|
+
name = vaultFile.metadata.originalFileName;
|
|
45
|
+
}
|
|
46
|
+
if (vaultFile.metadata?.mimeType) {
|
|
47
|
+
mimeType = vaultFile.metadata.mimeType;
|
|
48
|
+
}
|
|
49
|
+
if (vaultFile.metadata?.size) {
|
|
50
|
+
size = vaultFile.metadata.size;
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
logger.debug({ reference: file.reference }, "Vault metadata fetch failed for assistant file");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
url: file.reference,
|
|
58
|
+
name,
|
|
59
|
+
mimeType,
|
|
60
|
+
source: "assistant",
|
|
61
|
+
size
|
|
62
|
+
};
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
export async function attachNewAssistantFilesToMessageUpdate(db, conversationId, updateData, authToken) {
|
|
67
|
+
if (!updateData.content || updateData.status !== "completed")
|
|
68
|
+
return;
|
|
69
|
+
const assistantFiles = await extractAssistantFiles(updateData.content, authToken);
|
|
70
|
+
if (assistantFiles.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
const existingUrls = await getExistingMessageFileUrls(db, conversationId);
|
|
73
|
+
const newFiles = assistantFiles.filter((file) => !existingUrls.has(file.url));
|
|
74
|
+
if (newFiles.length > 0) {
|
|
75
|
+
updateData.files = newFiles;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { useDb } from '#chat-runtime/server/db';
|
|
2
|
+
import type { AgentApiSession, MessageStatus, ReasoningItem } from '#chat-runtime/types/agent';
|
|
3
|
+
import type { ChatRuntimeContext } from '#chat-runtime/server/utils/chat-context';
|
|
4
|
+
import type { Message, MessageFile } from '#chat-runtime/types/chat';
|
|
5
|
+
import type { TelaAgentUsageValues } from '#chat-runtime/server/utils/tela-agent-session';
|
|
6
|
+
type DbInstance = ReturnType<typeof useDb>;
|
|
7
|
+
export type MessageUpdate = {
|
|
8
|
+
reasoningData?: ReasoningItem[];
|
|
9
|
+
externalUuid?: string;
|
|
10
|
+
content?: string;
|
|
11
|
+
status?: MessageStatus;
|
|
12
|
+
ttft?: string;
|
|
13
|
+
updatedAt?: Date;
|
|
14
|
+
files?: MessageFile[];
|
|
15
|
+
};
|
|
16
|
+
type MessageUpdateResponse = {
|
|
17
|
+
messages: Message[];
|
|
18
|
+
newFiles?: MessageFile[];
|
|
19
|
+
};
|
|
20
|
+
export declare function mapAgentApiSessionToMessageUpdate(agentSession: AgentApiSession, options: {
|
|
21
|
+
pendingMessage: Pick<Message, 'ttft' | 'createdAt'>;
|
|
22
|
+
lastAssistantMessage?: Pick<Message, 'externalUuid'> | null;
|
|
23
|
+
userMessage?: Pick<Message, 'createdAt'> | null;
|
|
24
|
+
now?: Date;
|
|
25
|
+
}): MessageUpdate;
|
|
26
|
+
export declare function hasMessageUpdate(updateData: MessageUpdate): boolean;
|
|
27
|
+
export declare function isTerminalMessageUpdate(updateData: MessageUpdate): boolean;
|
|
28
|
+
export declare function buildMessageUpdateResponse(messages: Message[], messageId: string, updateData: MessageUpdate): MessageUpdateResponse;
|
|
29
|
+
export declare function trackTelaAgentConversationUsage(db: DbInstance, context: ChatRuntimeContext, conversationId: string, sessionId: string, usage: TelaAgentUsageValues | null): void;
|
|
30
|
+
export declare function trackAgentApiConversationUsage(db: DbInstance, context: ChatRuntimeContext, conversationId: string, sessionId: string, agentSession: AgentApiSession): void;
|
|
31
|
+
export {};
|