@nexo-labs/payload-typesense 1.3.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +370 -190
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +504 -587
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -1,51 +1,25 @@
|
|
|
1
|
+
import Typesense from "typesense";
|
|
1
2
|
import OpenAI from "openai";
|
|
2
3
|
import { GoogleGenerativeAI, TaskType } from "@google/generative-ai";
|
|
3
|
-
import Typesense from "typesense";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { MarkdownTextSplitter, RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
|
|
6
|
+
import { convertLexicalToMarkdown, editorConfigFactory } from "@payloadcms/richtext-lexical";
|
|
6
7
|
|
|
7
|
-
//#region src/core/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
singleton(token, instance) {
|
|
15
|
-
this.services.set(token, instance);
|
|
16
|
-
}
|
|
17
|
-
resolve(token) {
|
|
18
|
-
if (this.services.has(token)) return this.services.get(token);
|
|
19
|
-
if (this.factories.has(token)) {
|
|
20
|
-
const factory = this.factories.get(token);
|
|
21
|
-
if (factory) return factory();
|
|
22
|
-
}
|
|
23
|
-
throw new Error(`Service not found: ${token.toString()}`);
|
|
24
|
-
}
|
|
25
|
-
has(token) {
|
|
26
|
-
return this.services.has(token) || this.factories.has(token);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Clears all registered services and factories.
|
|
30
|
-
* Useful for testing.
|
|
31
|
-
*/
|
|
32
|
-
clear() {
|
|
33
|
-
this.services.clear();
|
|
34
|
-
this.factories.clear();
|
|
35
|
-
}
|
|
8
|
+
//#region src/core/client/typesense-client.ts
|
|
9
|
+
const createTypesenseClient = (typesenseConfig) => {
|
|
10
|
+
return new Typesense.Client({
|
|
11
|
+
apiKey: typesenseConfig.apiKey,
|
|
12
|
+
connectionTimeoutSeconds: typesenseConfig.connectionTimeoutSeconds || 2,
|
|
13
|
+
nodes: typesenseConfig.nodes
|
|
14
|
+
});
|
|
36
15
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
EMBEDDING_PROVIDER: Symbol.for("EmbeddingProvider"),
|
|
45
|
-
EMBEDDING_SERVICE: Symbol.for("EmbeddingService"),
|
|
46
|
-
SEARCH_SERVICE: Symbol.for("SearchService"),
|
|
47
|
-
SYNC_SERVICE: Symbol.for("SyncService"),
|
|
48
|
-
RAG_SERVICE: Symbol.for("RAGService")
|
|
16
|
+
const testTypesenseConnection = async (client) => {
|
|
17
|
+
try {
|
|
18
|
+
await client.health.retrieve();
|
|
19
|
+
return true;
|
|
20
|
+
} catch (_error) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
49
23
|
};
|
|
50
24
|
|
|
51
25
|
//#endregion
|
|
@@ -246,198 +220,6 @@ const ErrorCodes = {
|
|
|
246
220
|
VALIDATION_ERROR: "ERR_9002"
|
|
247
221
|
};
|
|
248
222
|
|
|
249
|
-
//#endregion
|
|
250
|
-
//#region src/features/embedding/providers/openai-provider.ts
|
|
251
|
-
var OpenAIEmbeddingProvider = class {
|
|
252
|
-
client;
|
|
253
|
-
model;
|
|
254
|
-
dimensions;
|
|
255
|
-
constructor(config, logger$1) {
|
|
256
|
-
this.logger = logger$1;
|
|
257
|
-
if (!config.apiKey) throw new Error("OpenAI API key is required");
|
|
258
|
-
this.client = new OpenAI({ apiKey: config.apiKey });
|
|
259
|
-
this.model = config.model || DEFAULT_EMBEDDING_MODEL;
|
|
260
|
-
this.dimensions = config.dimensions || DEFAULT_EMBEDDING_DIMENSIONS;
|
|
261
|
-
}
|
|
262
|
-
async generateEmbedding(text) {
|
|
263
|
-
if (!text || text.trim().length < MIN_EMBEDDING_TEXT_LENGTH) return null;
|
|
264
|
-
try {
|
|
265
|
-
const response = await this.client.embeddings.create({
|
|
266
|
-
model: this.model,
|
|
267
|
-
input: text.trim(),
|
|
268
|
-
dimensions: this.dimensions
|
|
269
|
-
});
|
|
270
|
-
const embedding = response.data[0]?.embedding;
|
|
271
|
-
if (!embedding) return null;
|
|
272
|
-
return {
|
|
273
|
-
embedding,
|
|
274
|
-
usage: {
|
|
275
|
-
promptTokens: response.usage?.prompt_tokens || 0,
|
|
276
|
-
totalTokens: response.usage?.total_tokens || 0
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
} catch (error) {
|
|
280
|
-
this.logger.error("OpenAI embedding generation failed", error, { model: this.model });
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
async generateBatchEmbeddings(texts) {
|
|
285
|
-
const validTexts = texts.filter((t) => t && t.trim().length >= MIN_EMBEDDING_TEXT_LENGTH);
|
|
286
|
-
if (validTexts.length === 0) return null;
|
|
287
|
-
try {
|
|
288
|
-
const response = await this.client.embeddings.create({
|
|
289
|
-
model: this.model,
|
|
290
|
-
input: validTexts.map((t) => t.trim()),
|
|
291
|
-
dimensions: this.dimensions
|
|
292
|
-
});
|
|
293
|
-
return {
|
|
294
|
-
embeddings: response.data.map((d) => d.embedding),
|
|
295
|
-
usage: {
|
|
296
|
-
promptTokens: response.usage?.prompt_tokens || 0,
|
|
297
|
-
totalTokens: response.usage?.total_tokens || 0
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
} catch (error) {
|
|
301
|
-
this.logger.error("OpenAI batch embedding generation failed", error, {
|
|
302
|
-
model: this.model,
|
|
303
|
-
count: texts.length
|
|
304
|
-
});
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
//#endregion
|
|
311
|
-
//#region src/features/embedding/providers/gemini-provider.ts
|
|
312
|
-
var GeminiEmbeddingProvider = class {
|
|
313
|
-
client;
|
|
314
|
-
model;
|
|
315
|
-
constructor(config, logger$1) {
|
|
316
|
-
this.logger = logger$1;
|
|
317
|
-
if (!config.apiKey) throw new Error("Gemini API key is required");
|
|
318
|
-
this.client = new GoogleGenerativeAI(config.apiKey);
|
|
319
|
-
this.model = config.model || DEFAULT_GEMINI_EMBEDDING_MODEL;
|
|
320
|
-
}
|
|
321
|
-
async generateEmbedding(text) {
|
|
322
|
-
if (!text || text.trim().length < MIN_EMBEDDING_TEXT_LENGTH) return null;
|
|
323
|
-
try {
|
|
324
|
-
const embedding = (await this.client.getGenerativeModel({ model: this.model }).embedContent({
|
|
325
|
-
content: {
|
|
326
|
-
role: "user",
|
|
327
|
-
parts: [{ text: text.trim() }]
|
|
328
|
-
},
|
|
329
|
-
taskType: TaskType.RETRIEVAL_DOCUMENT
|
|
330
|
-
})).embedding.values;
|
|
331
|
-
const estimatedTokens = Math.ceil(text.length / 4);
|
|
332
|
-
return {
|
|
333
|
-
embedding,
|
|
334
|
-
usage: {
|
|
335
|
-
promptTokens: estimatedTokens,
|
|
336
|
-
totalTokens: estimatedTokens
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
} catch (error) {
|
|
340
|
-
this.logger.error("Gemini embedding generation failed", error, { model: this.model });
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
async generateBatchEmbeddings(texts) {
|
|
345
|
-
const validTexts = texts.filter((t) => t && t.trim().length >= MIN_EMBEDDING_TEXT_LENGTH);
|
|
346
|
-
if (validTexts.length === 0) return null;
|
|
347
|
-
try {
|
|
348
|
-
const model = this.client.getGenerativeModel({ model: this.model });
|
|
349
|
-
const embeddings = [];
|
|
350
|
-
let totalTokens = 0;
|
|
351
|
-
for (const text of validTexts) {
|
|
352
|
-
const result = await model.embedContent({
|
|
353
|
-
content: {
|
|
354
|
-
role: "user",
|
|
355
|
-
parts: [{ text: text.trim() }]
|
|
356
|
-
},
|
|
357
|
-
taskType: TaskType.RETRIEVAL_DOCUMENT
|
|
358
|
-
});
|
|
359
|
-
embeddings.push(result.embedding.values);
|
|
360
|
-
totalTokens += Math.ceil(text.length / 4);
|
|
361
|
-
}
|
|
362
|
-
return {
|
|
363
|
-
embeddings,
|
|
364
|
-
usage: {
|
|
365
|
-
promptTokens: totalTokens,
|
|
366
|
-
totalTokens
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
} catch (error) {
|
|
370
|
-
this.logger.error("Gemini batch embedding generation failed", error, {
|
|
371
|
-
model: this.model,
|
|
372
|
-
count: texts.length
|
|
373
|
-
});
|
|
374
|
-
return null;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
//#endregion
|
|
380
|
-
//#region src/features/embedding/embedding-service.ts
|
|
381
|
-
var EmbeddingServiceImpl = class {
|
|
382
|
-
constructor(provider, logger$1, config) {
|
|
383
|
-
this.provider = provider;
|
|
384
|
-
this.logger = logger$1;
|
|
385
|
-
this.config = config;
|
|
386
|
-
}
|
|
387
|
-
async getEmbedding(text) {
|
|
388
|
-
const result = await this.provider.generateEmbedding(text);
|
|
389
|
-
if (!result) return null;
|
|
390
|
-
return result.embedding;
|
|
391
|
-
}
|
|
392
|
-
async getEmbeddingsBatch(texts) {
|
|
393
|
-
const result = await this.provider.generateBatchEmbeddings(texts);
|
|
394
|
-
if (!result) return null;
|
|
395
|
-
return result.embeddings;
|
|
396
|
-
}
|
|
397
|
-
getDimensions() {
|
|
398
|
-
return this.config.dimensions || DEFAULT_EMBEDDING_DIMENSIONS;
|
|
399
|
-
}
|
|
400
|
-
};
|
|
401
|
-
|
|
402
|
-
//#endregion
|
|
403
|
-
//#region src/core/di/setup.ts
|
|
404
|
-
const setupContainer = (config) => {
|
|
405
|
-
const container = new DIContainer();
|
|
406
|
-
container.singleton(TOKENS.CONFIG, config);
|
|
407
|
-
const logger$1 = new Logger({
|
|
408
|
-
enabled: true,
|
|
409
|
-
prefix: "[payload-typesense]"
|
|
410
|
-
});
|
|
411
|
-
container.singleton(TOKENS.LOGGER, logger$1);
|
|
412
|
-
const embeddingConfig = config.features.embedding;
|
|
413
|
-
if (!embeddingConfig) throw new Error("Embedding configuration missing");
|
|
414
|
-
let provider;
|
|
415
|
-
if (embeddingConfig.type === "gemini") provider = new GeminiEmbeddingProvider(embeddingConfig, logger$1);
|
|
416
|
-
else provider = new OpenAIEmbeddingProvider(embeddingConfig, logger$1);
|
|
417
|
-
container.singleton(TOKENS.EMBEDDING_PROVIDER, provider);
|
|
418
|
-
container.register(TOKENS.EMBEDDING_SERVICE, () => new EmbeddingServiceImpl(provider, logger$1, embeddingConfig));
|
|
419
|
-
logger$1.debug("Embedding service registered", { provider: embeddingConfig });
|
|
420
|
-
return container;
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
//#endregion
|
|
424
|
-
//#region src/core/client/typesense-client.ts
|
|
425
|
-
const createTypesenseClient = (typesenseConfig) => {
|
|
426
|
-
return new Typesense.Client({
|
|
427
|
-
apiKey: typesenseConfig.apiKey,
|
|
428
|
-
connectionTimeoutSeconds: typesenseConfig.connectionTimeoutSeconds || 2,
|
|
429
|
-
nodes: typesenseConfig.nodes
|
|
430
|
-
});
|
|
431
|
-
};
|
|
432
|
-
const testTypesenseConnection = async (client) => {
|
|
433
|
-
try {
|
|
434
|
-
await client.health.retrieve();
|
|
435
|
-
return true;
|
|
436
|
-
} catch (_error) {
|
|
437
|
-
return false;
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
|
|
441
223
|
//#endregion
|
|
442
224
|
//#region src/features/embedding/embeddings.ts
|
|
443
225
|
let openaiClient = null;
|
|
@@ -1402,39 +1184,222 @@ async function createNewSession(payload, userId, conversationId, newUserMessage,
|
|
|
1402
1184
|
}
|
|
1403
1185
|
|
|
1404
1186
|
//#endregion
|
|
1405
|
-
//#region src/features/rag/
|
|
1187
|
+
//#region src/features/rag/endpoints/chat/validators/request-validator.ts
|
|
1406
1188
|
/**
|
|
1407
|
-
*
|
|
1189
|
+
* JSON Response helper
|
|
1408
1190
|
*/
|
|
1409
|
-
|
|
1191
|
+
const jsonResponse = (data, options) => {
|
|
1410
1192
|
return new Response(JSON.stringify(data), {
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
"Content-Type": "application/json",
|
|
1414
|
-
...init?.headers
|
|
1415
|
-
}
|
|
1193
|
+
headers: { "Content-Type": "application/json" },
|
|
1194
|
+
...options
|
|
1416
1195
|
});
|
|
1196
|
+
};
|
|
1197
|
+
/**
|
|
1198
|
+
* Validates chat request and extracts required data
|
|
1199
|
+
*/
|
|
1200
|
+
async function validateChatRequest(request, config) {
|
|
1201
|
+
if (!await config.checkPermissions(request)) return {
|
|
1202
|
+
success: false,
|
|
1203
|
+
error: jsonResponse({ error: "No tienes permisos para acceder a esta sesión." }, { status: 403 })
|
|
1204
|
+
};
|
|
1205
|
+
if (!request.url || !request.user) return {
|
|
1206
|
+
success: false,
|
|
1207
|
+
error: jsonResponse({ error: "URL not found" }, { status: 400 })
|
|
1208
|
+
};
|
|
1209
|
+
const { id: userId, email } = request.user;
|
|
1210
|
+
const userEmail = email || "";
|
|
1211
|
+
const payload = await config.getPayload();
|
|
1212
|
+
const body = await request.json?.();
|
|
1213
|
+
if (!body) return {
|
|
1214
|
+
success: false,
|
|
1215
|
+
error: jsonResponse({ error: "Body not found" }, { status: 400 })
|
|
1216
|
+
};
|
|
1217
|
+
if (!body.message || typeof body.message !== "string" || body.message.trim() === "") return {
|
|
1218
|
+
success: false,
|
|
1219
|
+
error: jsonResponse({ error: "Se requiere un mensaje." }, { status: 400 })
|
|
1220
|
+
};
|
|
1221
|
+
return {
|
|
1222
|
+
success: true,
|
|
1223
|
+
userId,
|
|
1224
|
+
userEmail,
|
|
1225
|
+
payload,
|
|
1226
|
+
userMessage: body.message.trim(),
|
|
1227
|
+
body
|
|
1228
|
+
};
|
|
1417
1229
|
}
|
|
1418
1230
|
|
|
1419
1231
|
//#endregion
|
|
1420
|
-
//#region src/features/
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1232
|
+
//#region src/features/embedding/services/embedding-service.ts
|
|
1233
|
+
var EmbeddingServiceImpl = class {
|
|
1234
|
+
constructor(provider, logger$1, config) {
|
|
1235
|
+
this.provider = provider;
|
|
1236
|
+
this.logger = logger$1;
|
|
1237
|
+
this.config = config;
|
|
1238
|
+
}
|
|
1239
|
+
async getEmbedding(text) {
|
|
1240
|
+
const result = await this.provider.generateEmbedding(text);
|
|
1241
|
+
if (!result) return null;
|
|
1242
|
+
return result.embedding;
|
|
1243
|
+
}
|
|
1244
|
+
async getEmbeddingsBatch(texts) {
|
|
1245
|
+
const result = await this.provider.generateBatchEmbeddings(texts);
|
|
1246
|
+
if (!result) return null;
|
|
1247
|
+
return result.embeddings;
|
|
1248
|
+
}
|
|
1249
|
+
getDimensions() {
|
|
1250
|
+
return this.config.dimensions || DEFAULT_EMBEDDING_DIMENSIONS;
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
//#endregion
|
|
1255
|
+
//#region src/features/embedding/providers/openai-provider.ts
|
|
1256
|
+
var OpenAIEmbeddingProvider = class {
|
|
1257
|
+
client;
|
|
1258
|
+
model;
|
|
1259
|
+
dimensions;
|
|
1260
|
+
constructor(config, logger$1) {
|
|
1261
|
+
this.logger = logger$1;
|
|
1262
|
+
if (!config.apiKey) throw new Error("OpenAI API key is required");
|
|
1263
|
+
this.client = new OpenAI({ apiKey: config.apiKey });
|
|
1264
|
+
this.model = config.model || DEFAULT_EMBEDDING_MODEL;
|
|
1265
|
+
this.dimensions = config.dimensions || DEFAULT_EMBEDDING_DIMENSIONS;
|
|
1266
|
+
}
|
|
1267
|
+
async generateEmbedding(text) {
|
|
1268
|
+
if (!text || text.trim().length < MIN_EMBEDDING_TEXT_LENGTH) return null;
|
|
1269
|
+
try {
|
|
1270
|
+
const response = await this.client.embeddings.create({
|
|
1271
|
+
model: this.model,
|
|
1272
|
+
input: text.trim(),
|
|
1273
|
+
dimensions: this.dimensions
|
|
1274
|
+
});
|
|
1275
|
+
const embedding = response.data[0]?.embedding;
|
|
1276
|
+
if (!embedding) return null;
|
|
1277
|
+
return {
|
|
1278
|
+
embedding,
|
|
1279
|
+
usage: {
|
|
1280
|
+
promptTokens: response.usage?.prompt_tokens || 0,
|
|
1281
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
} catch (error) {
|
|
1285
|
+
this.logger.error("OpenAI embedding generation failed", error, { model: this.model });
|
|
1286
|
+
return null;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
async generateBatchEmbeddings(texts) {
|
|
1290
|
+
const validTexts = texts.filter((t) => t && t.trim().length >= MIN_EMBEDDING_TEXT_LENGTH);
|
|
1291
|
+
if (validTexts.length === 0) return null;
|
|
1292
|
+
try {
|
|
1293
|
+
const response = await this.client.embeddings.create({
|
|
1294
|
+
model: this.model,
|
|
1295
|
+
input: validTexts.map((t) => t.trim()),
|
|
1296
|
+
dimensions: this.dimensions
|
|
1297
|
+
});
|
|
1298
|
+
return {
|
|
1299
|
+
embeddings: response.data.map((d) => d.embedding),
|
|
1300
|
+
usage: {
|
|
1301
|
+
promptTokens: response.usage?.prompt_tokens || 0,
|
|
1302
|
+
totalTokens: response.usage?.total_tokens || 0
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
this.logger.error("OpenAI batch embedding generation failed", error, {
|
|
1307
|
+
model: this.model,
|
|
1308
|
+
count: texts.length
|
|
1309
|
+
});
|
|
1310
|
+
return null;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
|
|
1315
|
+
//#endregion
|
|
1316
|
+
//#region src/features/embedding/providers/gemini-provider.ts
|
|
1317
|
+
var GeminiEmbeddingProvider = class {
|
|
1318
|
+
client;
|
|
1319
|
+
model;
|
|
1320
|
+
constructor(config, logger$1) {
|
|
1321
|
+
this.logger = logger$1;
|
|
1322
|
+
if (!config.apiKey) throw new Error("Gemini API key is required");
|
|
1323
|
+
this.client = new GoogleGenerativeAI(config.apiKey);
|
|
1324
|
+
this.model = config.model || DEFAULT_GEMINI_EMBEDDING_MODEL;
|
|
1325
|
+
}
|
|
1326
|
+
async generateEmbedding(text) {
|
|
1327
|
+
if (!text || text.trim().length < MIN_EMBEDDING_TEXT_LENGTH) return null;
|
|
1328
|
+
try {
|
|
1329
|
+
const embedding = (await this.client.getGenerativeModel({ model: this.model }).embedContent({
|
|
1330
|
+
content: {
|
|
1331
|
+
role: "user",
|
|
1332
|
+
parts: [{ text: text.trim() }]
|
|
1333
|
+
},
|
|
1334
|
+
taskType: TaskType.RETRIEVAL_DOCUMENT
|
|
1335
|
+
})).embedding.values;
|
|
1336
|
+
const estimatedTokens = Math.ceil(text.length / 4);
|
|
1337
|
+
return {
|
|
1338
|
+
embedding,
|
|
1339
|
+
usage: {
|
|
1340
|
+
promptTokens: estimatedTokens,
|
|
1341
|
+
totalTokens: estimatedTokens
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
this.logger.error("Gemini embedding generation failed", error, { model: this.model });
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
async generateBatchEmbeddings(texts) {
|
|
1350
|
+
const validTexts = texts.filter((t) => t && t.trim().length >= MIN_EMBEDDING_TEXT_LENGTH);
|
|
1351
|
+
if (validTexts.length === 0) return null;
|
|
1352
|
+
try {
|
|
1353
|
+
const model = this.client.getGenerativeModel({ model: this.model });
|
|
1354
|
+
const embeddings = [];
|
|
1355
|
+
let totalTokens = 0;
|
|
1356
|
+
for (const text of validTexts) {
|
|
1357
|
+
const result = await model.embedContent({
|
|
1358
|
+
content: {
|
|
1359
|
+
role: "user",
|
|
1360
|
+
parts: [{ text: text.trim() }]
|
|
1361
|
+
},
|
|
1362
|
+
taskType: TaskType.RETRIEVAL_DOCUMENT
|
|
1363
|
+
});
|
|
1364
|
+
embeddings.push(result.embedding.values);
|
|
1365
|
+
totalTokens += Math.ceil(text.length / 4);
|
|
1366
|
+
}
|
|
1367
|
+
return {
|
|
1368
|
+
embeddings,
|
|
1369
|
+
usage: {
|
|
1370
|
+
promptTokens: totalTokens,
|
|
1371
|
+
totalTokens
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
this.logger.error("Gemini batch embedding generation failed", error, {
|
|
1376
|
+
model: this.model,
|
|
1377
|
+
count: texts.length
|
|
1378
|
+
});
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/features/rag/endpoints/chat/handlers/embedding-handler.ts
|
|
1386
|
+
/**
|
|
1387
|
+
* Generates embedding and tracks usage
|
|
1388
|
+
*/
|
|
1389
|
+
async function generateEmbeddingWithTracking(userMessage, config, spendingEntries) {
|
|
1390
|
+
logger.debug("Generating embeddings for semantic search");
|
|
1391
|
+
const embeddingConfig = config.embeddingConfig;
|
|
1392
|
+
if (!embeddingConfig) throw new Error("Embedding configuration missing");
|
|
1393
|
+
let provider;
|
|
1394
|
+
const providerType = embeddingConfig.type;
|
|
1395
|
+
const apiKey = embeddingConfig.apiKey;
|
|
1396
|
+
const model = embeddingConfig.model;
|
|
1397
|
+
const dimensions = embeddingConfig.dimensions;
|
|
1398
|
+
const serviceLogger = new Logger({
|
|
1399
|
+
enabled: true,
|
|
1400
|
+
prefix: "[rag-embedding]"
|
|
1401
|
+
});
|
|
1402
|
+
if (providerType === "gemini") provider = new GeminiEmbeddingProvider({
|
|
1438
1403
|
type: "gemini",
|
|
1439
1404
|
apiKey,
|
|
1440
1405
|
model,
|
|
@@ -1463,7 +1428,7 @@ async function generateEmbeddingWithTracking(userMessage, config, spendingEntrie
|
|
|
1463
1428
|
}
|
|
1464
1429
|
|
|
1465
1430
|
//#endregion
|
|
1466
|
-
//#region src/features/rag/
|
|
1431
|
+
//#region src/features/rag/endpoints/chat/handlers/session-handler.ts
|
|
1467
1432
|
/**
|
|
1468
1433
|
* Saves chat session if function is provided
|
|
1469
1434
|
*/
|
|
@@ -1474,7 +1439,7 @@ async function saveChatSessionIfNeeded(config, payload, userId, conversationId,
|
|
|
1474
1439
|
}
|
|
1475
1440
|
|
|
1476
1441
|
//#endregion
|
|
1477
|
-
//#region src/features/rag/
|
|
1442
|
+
//#region src/features/rag/endpoints/chat/handlers/token-limit-handler.ts
|
|
1478
1443
|
/**
|
|
1479
1444
|
* Checks token limits before processing request
|
|
1480
1445
|
*/
|
|
@@ -1510,7 +1475,7 @@ async function checkTokenLimitsIfNeeded(config, payload, userId, userEmail, user
|
|
|
1510
1475
|
}
|
|
1511
1476
|
|
|
1512
1477
|
//#endregion
|
|
1513
|
-
//#region src/features/rag/
|
|
1478
|
+
//#region src/features/rag/endpoints/chat/handlers/usage-stats-handler.ts
|
|
1514
1479
|
/**
|
|
1515
1480
|
* Calculates total usage from spending entries
|
|
1516
1481
|
*/
|
|
@@ -1546,43 +1511,7 @@ async function sendUsageStatsIfNeeded(config, payload, userId, totalTokens, tota
|
|
|
1546
1511
|
}
|
|
1547
1512
|
|
|
1548
1513
|
//#endregion
|
|
1549
|
-
//#region src/features/rag/
|
|
1550
|
-
/**
|
|
1551
|
-
* Validates chat request and extracts required data
|
|
1552
|
-
*/
|
|
1553
|
-
async function validateChatRequest(request, config) {
|
|
1554
|
-
if (!await config.checkPermissions(request)) return {
|
|
1555
|
-
success: false,
|
|
1556
|
-
error: jsonResponse({ error: "No tienes permisos para acceder a esta sesión." }, { status: 403 })
|
|
1557
|
-
};
|
|
1558
|
-
if (!request.url || !request.user) return {
|
|
1559
|
-
success: false,
|
|
1560
|
-
error: jsonResponse({ error: "URL not found" }, { status: 400 })
|
|
1561
|
-
};
|
|
1562
|
-
const { id: userId, email } = request.user;
|
|
1563
|
-
const userEmail = email || "";
|
|
1564
|
-
const payload = await config.getPayload();
|
|
1565
|
-
const body = await request.json?.();
|
|
1566
|
-
if (!body) return {
|
|
1567
|
-
success: false,
|
|
1568
|
-
error: jsonResponse({ error: "Body not found" }, { status: 400 })
|
|
1569
|
-
};
|
|
1570
|
-
if (!body.message || typeof body.message !== "string" || body.message.trim() === "") return {
|
|
1571
|
-
success: false,
|
|
1572
|
-
error: jsonResponse({ error: "Se requiere un mensaje." }, { status: 400 })
|
|
1573
|
-
};
|
|
1574
|
-
return {
|
|
1575
|
-
success: true,
|
|
1576
|
-
userId,
|
|
1577
|
-
userEmail,
|
|
1578
|
-
payload,
|
|
1579
|
-
userMessage: body.message.trim(),
|
|
1580
|
-
body
|
|
1581
|
-
};
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
//#endregion
|
|
1585
|
-
//#region src/features/rag/api/chat/route.ts
|
|
1514
|
+
//#region src/features/rag/endpoints/chat/route.ts
|
|
1586
1515
|
/**
|
|
1587
1516
|
* Create a parameterizable POST handler for chat endpoint
|
|
1588
1517
|
*/
|
|
@@ -1869,7 +1798,7 @@ async function defaultHandleNonStreamingResponse(data, controller, encoder) {
|
|
|
1869
1798
|
}
|
|
1870
1799
|
|
|
1871
1800
|
//#endregion
|
|
1872
|
-
//#region src/features/rag/
|
|
1801
|
+
//#region src/features/rag/endpoints/chat/session/route.ts
|
|
1873
1802
|
/**
|
|
1874
1803
|
* Create a parameterizable GET handler for session endpoint
|
|
1875
1804
|
*
|
|
@@ -1956,7 +1885,7 @@ function createSessionDELETEHandler(config) {
|
|
|
1956
1885
|
}
|
|
1957
1886
|
|
|
1958
1887
|
//#endregion
|
|
1959
|
-
//#region src/features/rag/
|
|
1888
|
+
//#region src/features/rag/endpoints/chunks/[id]/route.ts
|
|
1960
1889
|
/**
|
|
1961
1890
|
* Create a parameterizable GET handler for chunks endpoint
|
|
1962
1891
|
*
|
|
@@ -2001,7 +1930,7 @@ function createChunksGETHandler(config) {
|
|
|
2001
1930
|
}
|
|
2002
1931
|
|
|
2003
1932
|
//#endregion
|
|
2004
|
-
//#region src/features/rag/
|
|
1933
|
+
//#region src/features/rag/endpoints/chat/agents/route.ts
|
|
2005
1934
|
function createAgentsGETHandler(config) {
|
|
2006
1935
|
return async function GET() {
|
|
2007
1936
|
try {
|
|
@@ -2016,7 +1945,7 @@ function createAgentsGETHandler(config) {
|
|
|
2016
1945
|
}
|
|
2017
1946
|
|
|
2018
1947
|
//#endregion
|
|
2019
|
-
//#region src/features/rag/
|
|
1948
|
+
//#region src/features/rag/endpoints.ts
|
|
2020
1949
|
/**
|
|
2021
1950
|
* Creates Payload handlers for RAG endpoints
|
|
2022
1951
|
*/
|
|
@@ -2083,7 +2012,7 @@ function createRAGPayloadHandlers(pluginOptions) {
|
|
|
2083
2012
|
}
|
|
2084
2013
|
|
|
2085
2014
|
//#endregion
|
|
2086
|
-
//#region src/features/search/handlers/collections-handler.ts
|
|
2015
|
+
//#region src/features/search/endpoints/handlers/collections-handler.ts
|
|
2087
2016
|
/**
|
|
2088
2017
|
* Creates a handler for listing available search collections
|
|
2089
2018
|
*/
|
|
@@ -2095,18 +2024,7 @@ const createCollectionsHandler = (pluginOptions) => {
|
|
|
2095
2024
|
const firstEnabledConfig = tableConfigs.find((config) => config.enabled);
|
|
2096
2025
|
if (firstEnabledConfig) {
|
|
2097
2026
|
let fields = [];
|
|
2098
|
-
|
|
2099
|
-
...firstEnabledConfig.fields || [],
|
|
2100
|
-
{
|
|
2101
|
-
name: "chunk_text",
|
|
2102
|
-
index: true
|
|
2103
|
-
},
|
|
2104
|
-
{
|
|
2105
|
-
name: "headers",
|
|
2106
|
-
facet: true
|
|
2107
|
-
}
|
|
2108
|
-
];
|
|
2109
|
-
else fields = firstEnabledConfig.fields;
|
|
2027
|
+
fields = firstEnabledConfig.fields;
|
|
2110
2028
|
const facetFields = fields.filter((f) => f.facet).map((f) => f.name);
|
|
2111
2029
|
const searchFields = fields.filter((f) => f.index !== false).map((f) => f.name);
|
|
2112
2030
|
collections.push({
|
|
@@ -2345,12 +2263,7 @@ const searchTraditionalCollection = async (typesenseClient, collectionName, conf
|
|
|
2345
2263
|
if (options.searchFields) buildOptions.searchFields = options.searchFields;
|
|
2346
2264
|
else if (config) {
|
|
2347
2265
|
let fields = [];
|
|
2348
|
-
|
|
2349
|
-
name: "chunk_text",
|
|
2350
|
-
index: true,
|
|
2351
|
-
type: "string"
|
|
2352
|
-
}];
|
|
2353
|
-
else fields = config.fields;
|
|
2266
|
+
fields = config.fields;
|
|
2354
2267
|
const searchFields = fields.filter((f) => f.index !== false && (f.type === "string" || f.type === "string[]")).map((f) => f.name);
|
|
2355
2268
|
if (searchFields.length > 0) buildOptions.searchFields = searchFields;
|
|
2356
2269
|
}
|
|
@@ -2374,7 +2287,7 @@ const searchTraditionalCollection = async (typesenseClient, collectionName, conf
|
|
|
2374
2287
|
};
|
|
2375
2288
|
|
|
2376
2289
|
//#endregion
|
|
2377
|
-
//#region src/features/search/handlers/executors/traditional-multi-collection-search.ts
|
|
2290
|
+
//#region src/features/search/endpoints/handlers/executors/traditional-multi-collection-search.ts
|
|
2378
2291
|
const performTraditionalMultiCollectionSearch = async (typesenseClient, enabledCollections, query, options) => {
|
|
2379
2292
|
logger.info("Performing traditional multi-collection search", {
|
|
2380
2293
|
query,
|
|
@@ -2390,12 +2303,7 @@ const performTraditionalMultiCollectionSearch = async (typesenseClient, enabledC
|
|
|
2390
2303
|
...searchFieldsOverride ? { searchFields: searchFieldsOverride } : (() => {
|
|
2391
2304
|
if (!config) return {};
|
|
2392
2305
|
let fields = [];
|
|
2393
|
-
|
|
2394
|
-
name: "chunk_text",
|
|
2395
|
-
index: true,
|
|
2396
|
-
type: "string"
|
|
2397
|
-
}];
|
|
2398
|
-
else fields = config.fields;
|
|
2306
|
+
fields = config.fields;
|
|
2399
2307
|
const searchFields = fields.filter((f) => f.index !== false && (f.type === "string" || f.type === "string[]")).map((f) => f.name);
|
|
2400
2308
|
return searchFields.length > 0 ? { searchFields } : {};
|
|
2401
2309
|
})(),
|
|
@@ -2516,12 +2424,7 @@ const buildMultiCollectionVectorSearchParams = (searchVector, enabledCollections
|
|
|
2516
2424
|
let searchFields;
|
|
2517
2425
|
if (config) {
|
|
2518
2426
|
let fields = [];
|
|
2519
|
-
|
|
2520
|
-
name: "chunk_text",
|
|
2521
|
-
index: true,
|
|
2522
|
-
type: "string"
|
|
2523
|
-
}];
|
|
2524
|
-
else fields = config.fields;
|
|
2427
|
+
fields = config.fields;
|
|
2525
2428
|
const extracted = fields.filter((f) => f.index !== false && (f.type === "string" || f.type === "string[]")).map((f) => f.name);
|
|
2526
2429
|
if (extracted.length > 0) searchFields = extracted;
|
|
2527
2430
|
}
|
|
@@ -2615,7 +2518,7 @@ var SearchService = class {
|
|
|
2615
2518
|
};
|
|
2616
2519
|
|
|
2617
2520
|
//#endregion
|
|
2618
|
-
//#region src/features/search/handlers/utils/document-transformer.ts
|
|
2521
|
+
//#region src/features/search/endpoints/handlers/utils/document-transformer.ts
|
|
2619
2522
|
/**
|
|
2620
2523
|
* Helper to resolve document type from collection name
|
|
2621
2524
|
*/
|
|
@@ -2644,7 +2547,7 @@ function transformToSimpleFormat(data) {
|
|
|
2644
2547
|
}
|
|
2645
2548
|
|
|
2646
2549
|
//#endregion
|
|
2647
|
-
//#region src/features/search/handlers/utils/target-resolver.ts
|
|
2550
|
+
//#region src/features/search/endpoints/handlers/utils/target-resolver.ts
|
|
2648
2551
|
var TargetCollectionResolver = class {
|
|
2649
2552
|
allowedTableNames;
|
|
2650
2553
|
constructor(pluginOptions) {
|
|
@@ -2690,7 +2593,7 @@ var TargetCollectionResolver = class {
|
|
|
2690
2593
|
};
|
|
2691
2594
|
|
|
2692
2595
|
//#endregion
|
|
2693
|
-
//#region src/features/search/handlers/utils/config-mapper.ts
|
|
2596
|
+
//#region src/features/search/endpoints/handlers/utils/config-mapper.ts
|
|
2694
2597
|
var SearchConfigMapper = class {
|
|
2695
2598
|
constructor(pluginOptions) {
|
|
2696
2599
|
this.pluginOptions = pluginOptions;
|
|
@@ -2829,7 +2732,7 @@ const extractSearchParams = (query) => {
|
|
|
2829
2732
|
};
|
|
2830
2733
|
|
|
2831
2734
|
//#endregion
|
|
2832
|
-
//#region src/features/search/handlers/validators/search-request-validator.ts
|
|
2735
|
+
//#region src/features/search/endpoints/handlers/validators/search-request-validator.ts
|
|
2833
2736
|
/**
|
|
2834
2737
|
* Validates search request and returns parsed parameters
|
|
2835
2738
|
*/
|
|
@@ -2863,7 +2766,7 @@ function validateSearchRequest(request) {
|
|
|
2863
2766
|
}
|
|
2864
2767
|
|
|
2865
2768
|
//#endregion
|
|
2866
|
-
//#region src/features/search/handlers/search-handler.ts
|
|
2769
|
+
//#region src/features/search/endpoints/handlers/search-handler.ts
|
|
2867
2770
|
/**
|
|
2868
2771
|
* Helper type guard to check if a result is a valid search response
|
|
2869
2772
|
*/
|
|
@@ -2911,7 +2814,7 @@ const createSearchHandler = (typesenseClient, pluginOptions) => {
|
|
|
2911
2814
|
};
|
|
2912
2815
|
|
|
2913
2816
|
//#endregion
|
|
2914
|
-
//#region src/features/search/
|
|
2817
|
+
//#region src/features/search/endpoints.ts
|
|
2915
2818
|
const createSearchEndpoints = (typesenseClient, pluginOptions) => {
|
|
2916
2819
|
return [
|
|
2917
2820
|
{
|
|
@@ -2933,123 +2836,18 @@ const createSearchEndpoints = (typesenseClient, pluginOptions) => {
|
|
|
2933
2836
|
};
|
|
2934
2837
|
|
|
2935
2838
|
//#endregion
|
|
2936
|
-
//#region src/shared/schema/
|
|
2937
|
-
/**
|
|
2938
|
-
* Base fields that every collection should have
|
|
2939
|
-
*/
|
|
2940
|
-
const getBaseFields = () => [
|
|
2941
|
-
{
|
|
2942
|
-
name: "id",
|
|
2943
|
-
type: "string"
|
|
2944
|
-
},
|
|
2945
|
-
{
|
|
2946
|
-
name: "slug",
|
|
2947
|
-
type: "string"
|
|
2948
|
-
},
|
|
2949
|
-
{
|
|
2950
|
-
name: "createdAt",
|
|
2951
|
-
type: "int64"
|
|
2952
|
-
},
|
|
2953
|
-
{
|
|
2954
|
-
name: "updatedAt",
|
|
2955
|
-
type: "int64"
|
|
2956
|
-
}
|
|
2957
|
-
];
|
|
2839
|
+
//#region src/shared/schema/field-mapper.ts
|
|
2958
2840
|
/**
|
|
2959
|
-
*
|
|
2960
|
-
* @param optional - Whether the embedding field is optional
|
|
2961
|
-
* @param dimensions - Number of dimensions for the embedding vector (default: 1536)
|
|
2841
|
+
* Extracts a value from a document using dot notation path
|
|
2962
2842
|
*/
|
|
2963
|
-
const
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
}
|
|
2843
|
+
const getValueByPath = (obj, path) => {
|
|
2844
|
+
if (!obj || typeof obj !== "object") return void 0;
|
|
2845
|
+
return path.split(".").reduce((acc, part) => {
|
|
2846
|
+
if (acc && typeof acc === "object" && part in acc) return acc[part];
|
|
2847
|
+
}, obj);
|
|
2848
|
+
};
|
|
2969
2849
|
/**
|
|
2970
|
-
* Maps
|
|
2971
|
-
*/
|
|
2972
|
-
const mapFieldMappingsToSchema = (fields) => {
|
|
2973
|
-
return fields.map((field) => ({
|
|
2974
|
-
name: field.name,
|
|
2975
|
-
type: field.type === "auto" ? "string" : field.type,
|
|
2976
|
-
facet: field.facet,
|
|
2977
|
-
index: field.index,
|
|
2978
|
-
optional: field.optional
|
|
2979
|
-
}));
|
|
2980
|
-
};
|
|
2981
|
-
/**
|
|
2982
|
-
* Gets chunk-specific fields for chunk collections
|
|
2983
|
-
*/
|
|
2984
|
-
const getChunkFields = () => [
|
|
2985
|
-
{
|
|
2986
|
-
name: "parent_doc_id",
|
|
2987
|
-
type: "string"
|
|
2988
|
-
},
|
|
2989
|
-
{
|
|
2990
|
-
name: "chunk_index",
|
|
2991
|
-
type: "int32"
|
|
2992
|
-
},
|
|
2993
|
-
{
|
|
2994
|
-
name: "chunk_text",
|
|
2995
|
-
type: "string"
|
|
2996
|
-
},
|
|
2997
|
-
{
|
|
2998
|
-
name: "is_chunk",
|
|
2999
|
-
type: "bool"
|
|
3000
|
-
},
|
|
3001
|
-
{
|
|
3002
|
-
name: "headers",
|
|
3003
|
-
type: "string[]",
|
|
3004
|
-
facet: true,
|
|
3005
|
-
optional: true
|
|
3006
|
-
}
|
|
3007
|
-
];
|
|
3008
|
-
/**
|
|
3009
|
-
* Creates a complete schema for a chunk collection
|
|
3010
|
-
*/
|
|
3011
|
-
const getChunkCollectionSchema = (collectionSlug, tableConfig, embeddingDimensions = DEFAULT_EMBEDDING_DIMENSIONS) => {
|
|
3012
|
-
const fields = tableConfig.fields ? mapFieldMappingsToSchema(tableConfig.fields) : [];
|
|
3013
|
-
const userFieldNames = new Set([...fields.map((f) => f.name), ...getChunkFields().map((f) => f.name)]);
|
|
3014
|
-
return {
|
|
3015
|
-
name: collectionSlug,
|
|
3016
|
-
fields: [
|
|
3017
|
-
...getBaseFields().filter((f) => !userFieldNames.has(f.name)),
|
|
3018
|
-
...getChunkFields(),
|
|
3019
|
-
...fields,
|
|
3020
|
-
getEmbeddingField(false, embeddingDimensions)
|
|
3021
|
-
]
|
|
3022
|
-
};
|
|
3023
|
-
};
|
|
3024
|
-
/**
|
|
3025
|
-
* Creates a complete schema for a full document collection
|
|
3026
|
-
*/
|
|
3027
|
-
const getFullDocumentCollectionSchema = (collectionSlug, tableConfig, embeddingDimensions = DEFAULT_EMBEDDING_DIMENSIONS) => {
|
|
3028
|
-
const mappedFields = mapFieldMappingsToSchema(tableConfig.fields);
|
|
3029
|
-
const userFieldNames = new Set(mappedFields.map((f) => f.name));
|
|
3030
|
-
return {
|
|
3031
|
-
name: collectionSlug,
|
|
3032
|
-
fields: [
|
|
3033
|
-
...getBaseFields().filter((f) => !userFieldNames.has(f.name)),
|
|
3034
|
-
...mappedFields,
|
|
3035
|
-
getEmbeddingField(true, embeddingDimensions)
|
|
3036
|
-
]
|
|
3037
|
-
};
|
|
3038
|
-
};
|
|
3039
|
-
|
|
3040
|
-
//#endregion
|
|
3041
|
-
//#region src/shared/schema/field-mapper.ts
|
|
3042
|
-
/**
|
|
3043
|
-
* Extracts a value from a document using dot notation path
|
|
3044
|
-
*/
|
|
3045
|
-
const getValueByPath = (obj, path) => {
|
|
3046
|
-
if (!obj || typeof obj !== "object") return void 0;
|
|
3047
|
-
return path.split(".").reduce((acc, part) => {
|
|
3048
|
-
if (acc && typeof acc === "object" && part in acc) return acc[part];
|
|
3049
|
-
}, obj);
|
|
3050
|
-
};
|
|
3051
|
-
/**
|
|
3052
|
-
* Maps a Payload document to a Typesense document based on field configuration
|
|
2850
|
+
* Maps a Payload document to a Typesense document based on field configuration
|
|
3053
2851
|
*/
|
|
3054
2852
|
const mapPayloadDocumentToTypesense = async (doc, fields) => {
|
|
3055
2853
|
const result = {};
|
|
@@ -3285,8 +3083,37 @@ const extractHeaderMetadata = (chunkText$1) => {
|
|
|
3285
3083
|
};
|
|
3286
3084
|
|
|
3287
3085
|
//#endregion
|
|
3288
|
-
//#region src/features/sync/
|
|
3289
|
-
|
|
3086
|
+
//#region src/features/sync/services/document-syncer.ts
|
|
3087
|
+
/**
|
|
3088
|
+
* Syncs a Payload document to Typesense
|
|
3089
|
+
* Uses Strategy pattern to handle both chunked and full document approaches
|
|
3090
|
+
*/
|
|
3091
|
+
const syncDocumentToTypesense = async (typesenseClient, collectionSlug, doc, operation, tableConfig, embeddingService) => {
|
|
3092
|
+
try {
|
|
3093
|
+
const tableName = tableConfig.tableName || getTypesenseCollectionName(collectionSlug, tableConfig);
|
|
3094
|
+
logger.debug("Syncing document to Typesense", {
|
|
3095
|
+
documentId: doc.id,
|
|
3096
|
+
collection: collectionSlug,
|
|
3097
|
+
tableName,
|
|
3098
|
+
operation
|
|
3099
|
+
});
|
|
3100
|
+
await new DocumentSyncer(typesenseClient, collectionSlug, tableName, tableConfig, embeddingService).sync(doc, operation);
|
|
3101
|
+
logger.info("Document synced successfully to Typesense", {
|
|
3102
|
+
documentId: doc.id,
|
|
3103
|
+
collection: collectionSlug,
|
|
3104
|
+
operation
|
|
3105
|
+
});
|
|
3106
|
+
} catch (error) {
|
|
3107
|
+
const isValidationError = (error instanceof Error ? error.message : String(error)).toLowerCase().includes("validation");
|
|
3108
|
+
logger.error(`Failed to sync document to Typesense`, error, {
|
|
3109
|
+
documentId: doc.id,
|
|
3110
|
+
collection: collectionSlug,
|
|
3111
|
+
operation,
|
|
3112
|
+
isValidationError
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
};
|
|
3116
|
+
var DocumentSyncer = class {
|
|
3290
3117
|
constructor(client, collectionSlug, tableName, config, embeddingService) {
|
|
3291
3118
|
this.client = client;
|
|
3292
3119
|
this.collectionSlug = collectionSlug;
|
|
@@ -3295,7 +3122,28 @@ var ChunkedSyncer = class {
|
|
|
3295
3122
|
this.embeddingService = embeddingService;
|
|
3296
3123
|
}
|
|
3297
3124
|
async sync(doc, operation) {
|
|
3298
|
-
logger.debug(`Syncing document ${doc.id} to table ${this.tableName}
|
|
3125
|
+
logger.debug(`Syncing document ${doc.id} to table ${this.tableName}`);
|
|
3126
|
+
if (this.config.embedding?.chunking) await this.syncChunked(doc, operation);
|
|
3127
|
+
else await this.syncDocument(doc, operation);
|
|
3128
|
+
}
|
|
3129
|
+
async syncDocument(doc, operation) {
|
|
3130
|
+
const typesenseDoc = await mapPayloadDocumentToTypesense(doc, this.config.fields);
|
|
3131
|
+
typesenseDoc.id = String(doc.id);
|
|
3132
|
+
typesenseDoc.slug = doc.slug || "";
|
|
3133
|
+
typesenseDoc.createdAt = new Date(doc.createdAt).getTime();
|
|
3134
|
+
typesenseDoc.updatedAt = new Date(doc.updatedAt).getTime();
|
|
3135
|
+
if (doc.publishedAt) typesenseDoc.publishedAt = new Date(doc.publishedAt).getTime();
|
|
3136
|
+
if (this.config.embedding?.fields && this.embeddingService) {
|
|
3137
|
+
const sourceText = await this.extractSourceText(doc);
|
|
3138
|
+
if (sourceText) {
|
|
3139
|
+
const embedding = await this.embeddingService.getEmbedding(sourceText);
|
|
3140
|
+
if (embedding) typesenseDoc.embedding = embedding;
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
await this.client.collections(this.tableName).documents().upsert(typesenseDoc);
|
|
3144
|
+
logger.info(`Synced document ${doc.id} to ${this.tableName}`);
|
|
3145
|
+
}
|
|
3146
|
+
async syncChunked(doc, operation) {
|
|
3299
3147
|
const sourceText = await this.extractSourceText(doc);
|
|
3300
3148
|
if (!sourceText) {
|
|
3301
3149
|
logger.warn(`No source text found for document ${doc.id}`);
|
|
@@ -3308,7 +3156,12 @@ var ChunkedSyncer = class {
|
|
|
3308
3156
|
if (operation === "update") await this.client.collections(this.tableName).documents().delete({ filter_by: `parent_doc_id:${doc.id}` });
|
|
3309
3157
|
for (const chunk of chunks) {
|
|
3310
3158
|
const headers = buildHeaderHierarchy(chunk.metadata);
|
|
3311
|
-
|
|
3159
|
+
let formattedText = formatChunkWithHeaders(chunk.text, headers);
|
|
3160
|
+
if (this.config.embedding?.chunking?.interceptResult) formattedText = this.config.embedding.chunking.interceptResult({
|
|
3161
|
+
...chunk,
|
|
3162
|
+
headers,
|
|
3163
|
+
formattedText
|
|
3164
|
+
}, doc);
|
|
3312
3165
|
let embedding = [];
|
|
3313
3166
|
if (this.embeddingService) {
|
|
3314
3167
|
const result = await this.embeddingService.getEmbedding(formattedText);
|
|
@@ -3330,68 +3183,13 @@ var ChunkedSyncer = class {
|
|
|
3330
3183
|
}
|
|
3331
3184
|
logger.info(`Synced ${chunks.length} chunks for document ${doc.id} to ${this.tableName}`);
|
|
3332
3185
|
}
|
|
3333
|
-
async extractSourceText(doc) {
|
|
3334
|
-
const textParts = [];
|
|
3335
|
-
for (const sourceField of this.config.sourceFields) {
|
|
3336
|
-
const fieldName = typeof sourceField === "string" ? sourceField : sourceField.field;
|
|
3337
|
-
const transform = typeof sourceField === "string" ? void 0 : sourceField.transform;
|
|
3338
|
-
const val = doc[fieldName];
|
|
3339
|
-
if (transform) {
|
|
3340
|
-
let transformedVal = await transform(val);
|
|
3341
|
-
textParts.push(String(transformedVal || ""));
|
|
3342
|
-
} else if (typeof val === "object" && val !== null && "root" in val) {
|
|
3343
|
-
let transformedVal = JSON.stringify(val);
|
|
3344
|
-
textParts.push(String(transformedVal || ""));
|
|
3345
|
-
}
|
|
3346
|
-
}
|
|
3347
|
-
return textParts.join("\n\n");
|
|
3348
|
-
}
|
|
3349
|
-
async generateChunks(text) {
|
|
3350
|
-
const { strategy, size, overlap } = this.config.chunking;
|
|
3351
|
-
const options = {
|
|
3352
|
-
maxChunkSize: size,
|
|
3353
|
-
overlap
|
|
3354
|
-
};
|
|
3355
|
-
if (strategy === "markdown") return await chunkMarkdown(text, options);
|
|
3356
|
-
else return await chunkText(text, options);
|
|
3357
|
-
}
|
|
3358
|
-
};
|
|
3359
|
-
|
|
3360
|
-
//#endregion
|
|
3361
|
-
//#region src/features/sync/strategies/document-syncer.ts
|
|
3362
|
-
var DocumentSyncer = class {
|
|
3363
|
-
constructor(client, collectionSlug, tableName, config, embeddingService) {
|
|
3364
|
-
this.client = client;
|
|
3365
|
-
this.collectionSlug = collectionSlug;
|
|
3366
|
-
this.tableName = tableName;
|
|
3367
|
-
this.config = config;
|
|
3368
|
-
this.embeddingService = embeddingService;
|
|
3369
|
-
}
|
|
3370
|
-
async sync(doc, operation) {
|
|
3371
|
-
logger.debug(`Syncing document ${doc.id} to table ${this.tableName} (Document Mode)`);
|
|
3372
|
-
const typesenseDoc = await mapPayloadDocumentToTypesense(doc, this.config.fields);
|
|
3373
|
-
typesenseDoc.id = String(doc.id);
|
|
3374
|
-
typesenseDoc.slug = doc.slug || "";
|
|
3375
|
-
typesenseDoc.createdAt = new Date(doc.createdAt).getTime();
|
|
3376
|
-
typesenseDoc.updatedAt = new Date(doc.updatedAt).getTime();
|
|
3377
|
-
if (doc.publishedAt) typesenseDoc.publishedAt = new Date(doc.publishedAt).getTime();
|
|
3378
|
-
if (this.config.sourceFields && this.embeddingService) {
|
|
3379
|
-
const sourceText = await this.extractSourceText(doc);
|
|
3380
|
-
if (sourceText) {
|
|
3381
|
-
const embedding = await this.embeddingService.getEmbedding(sourceText);
|
|
3382
|
-
if (embedding) typesenseDoc.embedding = embedding;
|
|
3383
|
-
}
|
|
3384
|
-
}
|
|
3385
|
-
await this.client.collections(this.tableName).documents().upsert(typesenseDoc);
|
|
3386
|
-
logger.info(`Synced document ${doc.id} to ${this.tableName}`);
|
|
3387
|
-
}
|
|
3388
3186
|
/**
|
|
3389
3187
|
* Extract and transform source fields for embedding generation
|
|
3390
3188
|
*/
|
|
3391
3189
|
async extractSourceText(doc) {
|
|
3392
|
-
if (!this.config.
|
|
3190
|
+
if (!this.config.embedding?.fields) return "";
|
|
3393
3191
|
const textParts = [];
|
|
3394
|
-
for (const sourceField of this.config.
|
|
3192
|
+
for (const sourceField of this.config.embedding.fields) {
|
|
3395
3193
|
let fieldName;
|
|
3396
3194
|
let transform;
|
|
3397
3195
|
if (typeof sourceField === "string") fieldName = sourceField;
|
|
@@ -3406,44 +3204,20 @@ var DocumentSyncer = class {
|
|
|
3406
3204
|
}
|
|
3407
3205
|
return textParts.join("\n\n");
|
|
3408
3206
|
}
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
try {
|
|
3419
|
-
const tableName = tableConfig.tableName || getTypesenseCollectionName(collectionSlug, tableConfig);
|
|
3420
|
-
logger.debug("Syncing document to Typesense", {
|
|
3421
|
-
documentId: doc.id,
|
|
3422
|
-
collection: collectionSlug,
|
|
3423
|
-
tableName,
|
|
3424
|
-
operation,
|
|
3425
|
-
mode: tableConfig.mode
|
|
3426
|
-
});
|
|
3427
|
-
if (tableConfig.mode === "chunked") await new ChunkedSyncer(typesenseClient, collectionSlug, tableName, tableConfig, embeddingService).sync(doc, operation);
|
|
3428
|
-
else await new DocumentSyncer(typesenseClient, collectionSlug, tableName, tableConfig, embeddingService).sync(doc, operation);
|
|
3429
|
-
logger.info("Document synced successfully to Typesense", {
|
|
3430
|
-
documentId: doc.id,
|
|
3431
|
-
collection: collectionSlug,
|
|
3432
|
-
operation
|
|
3433
|
-
});
|
|
3434
|
-
} catch (error) {
|
|
3435
|
-
const isValidationError = (error instanceof Error ? error.message : String(error)).toLowerCase().includes("validation");
|
|
3436
|
-
logger.error(`Failed to sync document to Typesense`, error, {
|
|
3437
|
-
documentId: doc.id,
|
|
3438
|
-
collection: collectionSlug,
|
|
3439
|
-
operation,
|
|
3440
|
-
isValidationError
|
|
3441
|
-
});
|
|
3207
|
+
async generateChunks(text) {
|
|
3208
|
+
if (!this.config.embedding?.chunking) return [];
|
|
3209
|
+
const { strategy, size, overlap } = this.config.embedding.chunking;
|
|
3210
|
+
const options = {
|
|
3211
|
+
maxChunkSize: size,
|
|
3212
|
+
overlap
|
|
3213
|
+
};
|
|
3214
|
+
if (strategy === "markdown") return await chunkMarkdown(text, options);
|
|
3215
|
+
else return await chunkText(text, options);
|
|
3442
3216
|
}
|
|
3443
3217
|
};
|
|
3444
3218
|
|
|
3445
3219
|
//#endregion
|
|
3446
|
-
//#region src/features/sync/document-delete.ts
|
|
3220
|
+
//#region src/features/sync/services/document-delete.ts
|
|
3447
3221
|
/**
|
|
3448
3222
|
* Deletes a document from Typesense
|
|
3449
3223
|
* Handles both direct document deletion and chunk deletion
|
|
@@ -3494,7 +3268,148 @@ const deleteDocumentFromTypesense = async (typesenseClient, collectionSlug, docI
|
|
|
3494
3268
|
};
|
|
3495
3269
|
|
|
3496
3270
|
//#endregion
|
|
3497
|
-
//#region src/features/sync/
|
|
3271
|
+
//#region src/features/sync/hooks.ts
|
|
3272
|
+
/**
|
|
3273
|
+
* Applies sync hooks to Payload collections
|
|
3274
|
+
*/
|
|
3275
|
+
const applySyncHooks = (config, pluginOptions, typesenseClient, embeddingService) => {
|
|
3276
|
+
if (!pluginOptions.features.sync?.enabled || pluginOptions.features.sync.autoSync === false || !pluginOptions.collections) return config;
|
|
3277
|
+
return (config || []).map((collection) => {
|
|
3278
|
+
const tableConfigs = pluginOptions.collections?.[collection.slug];
|
|
3279
|
+
if (tableConfigs && Array.isArray(tableConfigs) && tableConfigs.some((tableConfig) => tableConfig.enabled)) {
|
|
3280
|
+
logger.debug("Registering sync hooks for collection", {
|
|
3281
|
+
collection: collection.slug,
|
|
3282
|
+
tableCount: tableConfigs?.length || 0
|
|
3283
|
+
});
|
|
3284
|
+
return {
|
|
3285
|
+
...collection,
|
|
3286
|
+
hooks: {
|
|
3287
|
+
...collection.hooks,
|
|
3288
|
+
afterChange: [...collection.hooks?.afterChange || [], async ({ doc, operation, req: _req }) => {
|
|
3289
|
+
if (tableConfigs && Array.isArray(tableConfigs)) {
|
|
3290
|
+
for (const tableConfig of tableConfigs) if (tableConfig.enabled) await syncDocumentToTypesense(typesenseClient, collection.slug, doc, operation, tableConfig, embeddingService);
|
|
3291
|
+
}
|
|
3292
|
+
}],
|
|
3293
|
+
afterDelete: [...collection.hooks?.afterDelete || [], async ({ doc, req: _req }) => {
|
|
3294
|
+
if (tableConfigs && Array.isArray(tableConfigs)) {
|
|
3295
|
+
for (const tableConfig of tableConfigs) if (tableConfig.enabled) await deleteDocumentFromTypesense(typesenseClient, collection.slug, doc.id, tableConfig);
|
|
3296
|
+
}
|
|
3297
|
+
}]
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
return collection;
|
|
3302
|
+
});
|
|
3303
|
+
};
|
|
3304
|
+
|
|
3305
|
+
//#endregion
|
|
3306
|
+
//#region src/shared/schema/collection-schemas.ts
|
|
3307
|
+
/**
|
|
3308
|
+
* Base fields that every collection should have
|
|
3309
|
+
*/
|
|
3310
|
+
const getBaseFields = () => [
|
|
3311
|
+
{
|
|
3312
|
+
name: "id",
|
|
3313
|
+
type: "string"
|
|
3314
|
+
},
|
|
3315
|
+
{
|
|
3316
|
+
name: "slug",
|
|
3317
|
+
type: "string"
|
|
3318
|
+
},
|
|
3319
|
+
{
|
|
3320
|
+
name: "createdAt",
|
|
3321
|
+
type: "int64"
|
|
3322
|
+
},
|
|
3323
|
+
{
|
|
3324
|
+
name: "updatedAt",
|
|
3325
|
+
type: "int64"
|
|
3326
|
+
}
|
|
3327
|
+
];
|
|
3328
|
+
/**
|
|
3329
|
+
* Creates embedding field definition
|
|
3330
|
+
* @param optional - Whether the embedding field is optional
|
|
3331
|
+
* @param dimensions - Number of dimensions for the embedding vector (default: 1536)
|
|
3332
|
+
*/
|
|
3333
|
+
const getEmbeddingField = (optional = true, dimensions = DEFAULT_EMBEDDING_DIMENSIONS) => ({
|
|
3334
|
+
name: "embedding",
|
|
3335
|
+
type: "float[]",
|
|
3336
|
+
num_dim: dimensions,
|
|
3337
|
+
...optional && { optional: true }
|
|
3338
|
+
});
|
|
3339
|
+
/**
|
|
3340
|
+
* Maps FieldMapping to TypesenseFieldSchema
|
|
3341
|
+
*/
|
|
3342
|
+
const mapFieldMappingsToSchema = (fields) => {
|
|
3343
|
+
return fields.map((field) => ({
|
|
3344
|
+
name: field.name,
|
|
3345
|
+
type: field.type === "auto" ? "string" : field.type,
|
|
3346
|
+
facet: field.facet,
|
|
3347
|
+
index: field.index,
|
|
3348
|
+
optional: field.optional
|
|
3349
|
+
}));
|
|
3350
|
+
};
|
|
3351
|
+
/**
|
|
3352
|
+
* Gets chunk-specific fields for chunk collections
|
|
3353
|
+
*/
|
|
3354
|
+
const getChunkFields = () => [
|
|
3355
|
+
{
|
|
3356
|
+
name: "parent_doc_id",
|
|
3357
|
+
type: "string",
|
|
3358
|
+
facet: true
|
|
3359
|
+
},
|
|
3360
|
+
{
|
|
3361
|
+
name: "chunk_index",
|
|
3362
|
+
type: "int32"
|
|
3363
|
+
},
|
|
3364
|
+
{
|
|
3365
|
+
name: "chunk_text",
|
|
3366
|
+
type: "string"
|
|
3367
|
+
},
|
|
3368
|
+
{
|
|
3369
|
+
name: "is_chunk",
|
|
3370
|
+
type: "bool"
|
|
3371
|
+
},
|
|
3372
|
+
{
|
|
3373
|
+
name: "headers",
|
|
3374
|
+
type: "string[]",
|
|
3375
|
+
facet: true,
|
|
3376
|
+
optional: true
|
|
3377
|
+
}
|
|
3378
|
+
];
|
|
3379
|
+
/**
|
|
3380
|
+
* Creates a complete schema for a chunk collection
|
|
3381
|
+
*/
|
|
3382
|
+
const getChunkCollectionSchema = (collectionSlug, tableConfig, embeddingDimensions = DEFAULT_EMBEDDING_DIMENSIONS) => {
|
|
3383
|
+
const fields = tableConfig.fields ? mapFieldMappingsToSchema(tableConfig.fields) : [];
|
|
3384
|
+
const userFieldNames = new Set([...fields.map((f) => f.name), ...getChunkFields().map((f) => f.name)]);
|
|
3385
|
+
return {
|
|
3386
|
+
name: collectionSlug,
|
|
3387
|
+
fields: [
|
|
3388
|
+
...getBaseFields().filter((f) => !userFieldNames.has(f.name)),
|
|
3389
|
+
...getChunkFields(),
|
|
3390
|
+
...fields,
|
|
3391
|
+
getEmbeddingField(false, embeddingDimensions)
|
|
3392
|
+
]
|
|
3393
|
+
};
|
|
3394
|
+
};
|
|
3395
|
+
/**
|
|
3396
|
+
* Creates a complete schema for a full document collection
|
|
3397
|
+
*/
|
|
3398
|
+
const getFullDocumentCollectionSchema = (collectionSlug, tableConfig, embeddingDimensions = DEFAULT_EMBEDDING_DIMENSIONS) => {
|
|
3399
|
+
const mappedFields = mapFieldMappingsToSchema(tableConfig.fields);
|
|
3400
|
+
const userFieldNames = new Set(mappedFields.map((f) => f.name));
|
|
3401
|
+
return {
|
|
3402
|
+
name: collectionSlug,
|
|
3403
|
+
fields: [
|
|
3404
|
+
...getBaseFields().filter((f) => !userFieldNames.has(f.name)),
|
|
3405
|
+
...mappedFields,
|
|
3406
|
+
getEmbeddingField(true, embeddingDimensions)
|
|
3407
|
+
]
|
|
3408
|
+
};
|
|
3409
|
+
};
|
|
3410
|
+
|
|
3411
|
+
//#endregion
|
|
3412
|
+
//#region src/features/sync/services/schema-manager.ts
|
|
3498
3413
|
var SchemaManager = class {
|
|
3499
3414
|
constructor(client, config) {
|
|
3500
3415
|
this.client = client;
|
|
@@ -3522,7 +3437,7 @@ var SchemaManager = class {
|
|
|
3522
3437
|
async syncTable(collectionSlug, tableConfig, embeddingDimensions) {
|
|
3523
3438
|
const tableName = getTypesenseCollectionName(collectionSlug, tableConfig);
|
|
3524
3439
|
let targetSchema;
|
|
3525
|
-
if (tableConfig.
|
|
3440
|
+
if (tableConfig.embedding?.chunking) targetSchema = getChunkCollectionSchema(tableName, tableConfig, embeddingDimensions);
|
|
3526
3441
|
else targetSchema = getFullDocumentCollectionSchema(tableName, tableConfig, embeddingDimensions);
|
|
3527
3442
|
try {
|
|
3528
3443
|
const collection = await this.client.collections(tableName).retrieve();
|
|
@@ -3557,7 +3472,7 @@ var SchemaManager = class {
|
|
|
3557
3472
|
};
|
|
3558
3473
|
|
|
3559
3474
|
//#endregion
|
|
3560
|
-
//#region src/features/rag/agent-manager.ts
|
|
3475
|
+
//#region src/features/rag/services/agent-manager.ts
|
|
3561
3476
|
var AgentManager = class {
|
|
3562
3477
|
constructor(client, config) {
|
|
3563
3478
|
this.client = client;
|
|
@@ -3657,10 +3572,17 @@ var AgentManager = class {
|
|
|
3657
3572
|
* @returns Payload config modifier function
|
|
3658
3573
|
*/
|
|
3659
3574
|
const typesenseSearch = (pluginOptions) => (config) => {
|
|
3660
|
-
const container = setupContainer(pluginOptions);
|
|
3661
3575
|
const typesenseClient = createTypesenseClient(pluginOptions.typesense);
|
|
3576
|
+
const logger$1 = new Logger({
|
|
3577
|
+
enabled: true,
|
|
3578
|
+
prefix: "[payload-typesense]"
|
|
3579
|
+
});
|
|
3662
3580
|
let embeddingService;
|
|
3663
|
-
|
|
3581
|
+
const embeddingConfig = pluginOptions.features.embedding;
|
|
3582
|
+
if (embeddingConfig) {
|
|
3583
|
+
embeddingService = new EmbeddingServiceImpl(embeddingConfig.type === "gemini" ? new GeminiEmbeddingProvider(embeddingConfig, logger$1) : new OpenAIEmbeddingProvider(embeddingConfig, logger$1), logger$1, embeddingConfig);
|
|
3584
|
+
logger$1.debug("Embedding service initialized", { provider: embeddingConfig.type });
|
|
3585
|
+
}
|
|
3664
3586
|
const searchEndpoints = createSearchEndpoints(typesenseClient, pluginOptions);
|
|
3665
3587
|
const ragEndpoints = pluginOptions.features.rag?.enabled ? createRAGPayloadHandlers(pluginOptions) : [];
|
|
3666
3588
|
config.endpoints = [
|
|
@@ -3668,53 +3590,48 @@ const typesenseSearch = (pluginOptions) => (config) => {
|
|
|
3668
3590
|
...searchEndpoints,
|
|
3669
3591
|
...ragEndpoints
|
|
3670
3592
|
];
|
|
3671
|
-
logger.debug("Search and RAG endpoints registered", {
|
|
3593
|
+
logger$1.debug("Search and RAG endpoints registered", {
|
|
3672
3594
|
searchEndpointsCount: searchEndpoints.length,
|
|
3673
3595
|
ragEndpointsCount: ragEndpoints.length
|
|
3674
3596
|
});
|
|
3675
|
-
if (
|
|
3676
|
-
const tableConfigs = pluginOptions.collections?.[collection.slug];
|
|
3677
|
-
if (tableConfigs && Array.isArray(tableConfigs) && tableConfigs.some((tableConfig) => tableConfig.enabled)) {
|
|
3678
|
-
logger.debug("Registering sync hooks for collection", {
|
|
3679
|
-
collection: collection.slug,
|
|
3680
|
-
tableCount: tableConfigs?.length || 0
|
|
3681
|
-
});
|
|
3682
|
-
return {
|
|
3683
|
-
...collection,
|
|
3684
|
-
hooks: {
|
|
3685
|
-
...collection.hooks,
|
|
3686
|
-
afterChange: [...collection.hooks?.afterChange || [], async ({ doc, operation, req: _req }) => {
|
|
3687
|
-
if (tableConfigs && Array.isArray(tableConfigs)) {
|
|
3688
|
-
for (const tableConfig of tableConfigs) if (tableConfig.enabled) await syncDocumentToTypesense(typesenseClient, collection.slug, doc, operation, tableConfig, embeddingService);
|
|
3689
|
-
}
|
|
3690
|
-
}],
|
|
3691
|
-
afterDelete: [...collection.hooks?.afterDelete || [], async ({ doc, req: _req }) => {
|
|
3692
|
-
if (tableConfigs && Array.isArray(tableConfigs)) {
|
|
3693
|
-
for (const tableConfig of tableConfigs) if (tableConfig.enabled) await deleteDocumentFromTypesense(typesenseClient, collection.slug, doc.id, tableConfig);
|
|
3694
|
-
}
|
|
3695
|
-
}]
|
|
3696
|
-
}
|
|
3697
|
-
};
|
|
3698
|
-
}
|
|
3699
|
-
return collection;
|
|
3700
|
-
});
|
|
3597
|
+
if (config.collections) config.collections = applySyncHooks(config.collections, pluginOptions, typesenseClient, embeddingService);
|
|
3701
3598
|
const incomingOnInit = config.onInit;
|
|
3702
3599
|
config.onInit = async (payload) => {
|
|
3703
3600
|
if (incomingOnInit) await incomingOnInit(payload);
|
|
3704
3601
|
try {
|
|
3705
|
-
logger.info("Initializing Typesense collections...");
|
|
3602
|
+
logger$1.info("Initializing Typesense collections...");
|
|
3706
3603
|
await new SchemaManager(typesenseClient, pluginOptions).syncCollections();
|
|
3707
3604
|
if (pluginOptions.features.rag?.enabled) {
|
|
3708
|
-
logger.info("Initializing RAG agents...");
|
|
3605
|
+
logger$1.info("Initializing RAG agents...");
|
|
3709
3606
|
await new AgentManager(typesenseClient, pluginOptions).syncAgents();
|
|
3710
3607
|
}
|
|
3711
3608
|
} catch (error) {
|
|
3712
|
-
logger.error("Error initializing Typesense resources", error);
|
|
3609
|
+
logger$1.error("Error initializing Typesense resources", error);
|
|
3713
3610
|
}
|
|
3714
3611
|
};
|
|
3715
3612
|
return config;
|
|
3716
3613
|
};
|
|
3717
3614
|
|
|
3718
3615
|
//#endregion
|
|
3719
|
-
|
|
3616
|
+
//#region src/core/utils/transforms.ts
|
|
3617
|
+
/**
|
|
3618
|
+
* Transforms Lexical editor state to Markdown
|
|
3619
|
+
* @param value - The serialized editor state
|
|
3620
|
+
* @param config - Optional Payload config. If provided, it will be used to generate the editor config.
|
|
3621
|
+
*/
|
|
3622
|
+
const transformLexicalToMarkdown = async (value, config) => {
|
|
3623
|
+
if (!value) return "";
|
|
3624
|
+
try {
|
|
3625
|
+
return await convertLexicalToMarkdown({
|
|
3626
|
+
data: value,
|
|
3627
|
+
editorConfig: await editorConfigFactory.default({ config })
|
|
3628
|
+
});
|
|
3629
|
+
} catch (error) {
|
|
3630
|
+
console.error("Error transforming lexical to markdown", error);
|
|
3631
|
+
return "";
|
|
3632
|
+
}
|
|
3633
|
+
};
|
|
3634
|
+
|
|
3635
|
+
//#endregion
|
|
3636
|
+
export { CHUNK_HEADER_SEPARATOR, DEFAULT_CACHE_TTL_MS, DEFAULT_CHUNK_OVERLAP, DEFAULT_CHUNK_SIZE, DEFAULT_EMBEDDING_DIMENSIONS, DEFAULT_EMBEDDING_MODEL, DEFAULT_HYBRID_SEARCH_ALPHA, DEFAULT_RAG_CONTEXT_LIMIT, DEFAULT_RAG_LLM_MODEL, DEFAULT_RAG_MAX_TOKENS, DEFAULT_SEARCH_LIMIT, DEFAULT_SESSION_TTL_SEC, ErrorCodes, buildContextText, buildConversationalUrl, buildHybridSearchParams, buildMultiSearchRequestBody, buildMultiSearchRequests, closeSession, configureLogger, createLogger, createSSEForwardStream, createTypesenseClient, deleteDocumentFromTypesense, ensureConversationCollection, executeRAGSearch, extractContentOnly, extractHeaderMetadata, extractSourcesFromResults, fetchChunkById, formatChunkWithHeaders, formatSSEEvent, generateEmbedding, generateEmbeddingWithUsage, generateEmbeddingsBatchWithUsage, getActiveSession, getDefaultRAGConfig, getSessionByConversationId, jsonResponse, logger, mergeRAGConfigWithDefaults, parseChunkText, parseConversationEvent, processConversationStream, saveChatSession, sendSSEEvent, testTypesenseConnection, transformLexicalToMarkdown, typesenseSearch };
|
|
3720
3637
|
//# sourceMappingURL=index.mjs.map
|