@smythos/sre 1.7.20 → 1.7.41
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.js +134 -89
- package/dist/index.js.map +1 -1
- package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
- package/dist/types/Components/DataSourceIndexer.class.d.ts +4 -12
- package/dist/types/Components/GenAILLM.class.d.ts +5 -5
- package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
- package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
- package/dist/types/Components/index.d.ts +3 -3
- package/dist/types/config.d.ts +1 -0
- package/dist/types/helpers/Conversation.helper.d.ts +10 -13
- package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
- package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +1 -0
- package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +11 -4
- package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +5 -0
- package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
- package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.d.ts +35 -0
- package/dist/types/subsystems/Security/Account.service/AccountConnector.d.ts +2 -2
- package/dist/types/subsystems/Security/Vault.service/connectors/SecretsManager.class.d.ts +2 -3
- package/dist/types/types/LLM.types.d.ts +23 -0
- package/dist/types/types/VectorDB.types.d.ts +4 -0
- package/dist/types/utils/string.utils.d.ts +1 -0
- package/package.json +3 -3
- package/src/Components/APIEndpoint.class.ts +1 -6
- package/src/Components/AgentPlugin.class.ts +20 -3
- package/src/Components/Classifier.class.ts +79 -16
- package/src/Components/Component.class.ts +14 -1
- package/src/Components/ForEach.class.ts +34 -6
- package/src/Components/GenAILLM.class.ts +75 -34
- package/src/Components/LLMAssistant.class.ts +56 -21
- package/src/Components/RAG/DataSourceCleaner.class.ts +180 -0
- package/src/Components/RAG/DataSourceComponent.class.ts +137 -0
- package/src/Components/RAG/DataSourceIndexer.class.ts +260 -0
- package/src/Components/{DataSourceLookup.class.ts → RAG/DataSourceLookup.class.ts} +96 -3
- package/src/Components/ScrapflyWebScrape.class.ts +7 -0
- package/src/Components/ServerlessCode.class.ts +1 -4
- package/src/Components/index.ts +3 -3
- package/src/config.ts +1 -0
- package/src/helpers/Conversation.helper.ts +112 -26
- package/src/helpers/S3Cache.helper.ts +2 -1
- package/src/helpers/TemplateString.helper.ts +6 -5
- package/src/index.ts +213 -212
- package/src/index.ts.bak +213 -212
- package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +3 -1
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +145 -19
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +67 -22
- package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -0
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +2 -1
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +16 -0
- package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +35 -10
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +12 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +4 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +192 -139
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +17 -5
- package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +18 -3
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +14 -5
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +6 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +5 -5
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +8 -3
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +9 -8
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +92 -1
- package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +260 -17
- package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -3
- package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +8 -63
- package/src/types/LLM.types.ts +24 -0
- package/src/types/VectorDB.types.ts +4 -0
- package/src/utils/array.utils.ts +11 -0
- package/src/utils/base64.utils.ts +1 -1
- package/src/utils/data.utils.ts +6 -4
- package/src/utils/string.utils.ts +3 -192
- package/src/Components/DataSourceCleaner.class.ts +0 -92
- package/src/Components/DataSourceIndexer.class.ts +0 -181
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//==[ SRE: S3Storage ]======================
|
|
2
1
|
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
3
2
|
import { JSONContentHelper } from '@sre/helpers/JsonContent.helper';
|
|
4
3
|
import { Logger } from '@sre/helpers/Log.helper';
|
|
@@ -17,12 +16,21 @@ import {
|
|
|
17
16
|
VectorDBResult,
|
|
18
17
|
VectorsResultData,
|
|
19
18
|
} from '@sre/types/VectorDB.types';
|
|
19
|
+
import { calcSizeMb } from '@sre/utils/string.utils';
|
|
20
20
|
import { CreateIndexSimpleReq, DataType, ErrorCode, FieldType, MilvusClient } from '@zilliz/milvus2-sdk-node';
|
|
21
21
|
import crypto from 'crypto';
|
|
22
22
|
import { jsonrepair } from 'jsonrepair';
|
|
23
23
|
import { EmbeddingsFactory } from '../embed';
|
|
24
24
|
import { BaseEmbedding, TEmbeddings } from '../embed/BaseEmbedding';
|
|
25
25
|
import { DeleteTarget, VectorDBConnector } from '../VectorDBConnector';
|
|
26
|
+
import { NKVConnector } from '@sre/IO/NKV.service/NKVConnector';
|
|
27
|
+
|
|
28
|
+
//! Due to current bug (still investigating), any consumer of this connector
|
|
29
|
+
//! needs to install @zilliz/milvus2-sdk-node in the package.json of the project so it can work
|
|
30
|
+
|
|
31
|
+
//* Note, we are storing Datasources info inside both NKV and Milvus (using some quirks).
|
|
32
|
+
//* The connector favors NKV as storage number 1, and Milvus acting as fallback storage.
|
|
33
|
+
//* This is because Milvus operations are heavy and can be slow the more u add big datasources
|
|
26
34
|
|
|
27
35
|
const console = Logger('Milvus');
|
|
28
36
|
|
|
@@ -42,7 +50,7 @@ export type MilvusConfig = {
|
|
|
42
50
|
};
|
|
43
51
|
|
|
44
52
|
// Define schema field names as a type for strong typing
|
|
45
|
-
type SchemaFieldNames = 'id' | 'text' | 'namespaceId' | 'datasourceId' | 'datasourceLabel' | 'vector' | 'acl' | 'user_metadata';
|
|
53
|
+
type SchemaFieldNames = 'id' | 'text' | 'namespaceId' | 'datasourceId' | 'datasourceLabel' | 'vector' | 'acl' | 'user_metadata' | 'smyth_metadata';
|
|
46
54
|
|
|
47
55
|
type SchemaField = FieldType & { name: SchemaFieldNames };
|
|
48
56
|
|
|
@@ -55,6 +63,7 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
55
63
|
public embedder: BaseEmbedding;
|
|
56
64
|
private SCHEMA_DEFINITION: SchemaField[];
|
|
57
65
|
private INDEX_PARAMS: IndexParams;
|
|
66
|
+
private nkvConnector: NKVConnector;
|
|
58
67
|
|
|
59
68
|
constructor(protected _settings: MilvusConfig) {
|
|
60
69
|
super(_settings);
|
|
@@ -72,16 +81,22 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
72
81
|
|
|
73
82
|
console.log('clientConfig', clientConfig);
|
|
74
83
|
|
|
75
|
-
this.client = new MilvusClient(clientConfig
|
|
84
|
+
this.client = new MilvusClient(clientConfig, undefined, undefined, undefined, {
|
|
85
|
+
'grpc.max_receive_message_length': 50 * 1024 * 1024,
|
|
86
|
+
'grpc.max_send_message_length': 50 * 1024 * 1024,
|
|
87
|
+
max_receive_message_length: 50 * 1024 * 1024,
|
|
88
|
+
max_send_message_length: 50 * 1024 * 1024,
|
|
89
|
+
});
|
|
76
90
|
console.info('Milvus client initialized');
|
|
77
91
|
this.accountConnector = ConnectorService.getAccountConnector();
|
|
78
92
|
this.cache = ConnectorService.getCacheConnector();
|
|
93
|
+
this.nkvConnector = ConnectorService.getNKVConnector();
|
|
79
94
|
|
|
80
95
|
if (!_settings.embeddings) {
|
|
81
96
|
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 3072 };
|
|
82
97
|
}
|
|
83
98
|
|
|
84
|
-
if (!_settings.embeddings
|
|
99
|
+
if (!_settings.embeddings?.dimensions) _settings.embeddings.dimensions = 3072;
|
|
85
100
|
|
|
86
101
|
this.embedder = EmbeddingsFactory.create(_settings.embeddings.provider, _settings.embeddings);
|
|
87
102
|
|
|
@@ -128,6 +143,11 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
128
143
|
data_type: DataType.VarChar,
|
|
129
144
|
max_length: 2048,
|
|
130
145
|
},
|
|
146
|
+
{
|
|
147
|
+
name: 'smyth_metadata',
|
|
148
|
+
data_type: DataType.VarChar,
|
|
149
|
+
max_length: 65535,
|
|
150
|
+
},
|
|
131
151
|
];
|
|
132
152
|
this.INDEX_PARAMS = {
|
|
133
153
|
index_type: 'AUTOINDEX',
|
|
@@ -192,6 +212,11 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
192
212
|
throw new Error(`Error dropping collection: ${res}`);
|
|
193
213
|
}
|
|
194
214
|
|
|
215
|
+
// delete the linked datasources from nkv
|
|
216
|
+
await this.nkvConnector
|
|
217
|
+
.requester(acRequest.candidate as AccessCandidate)
|
|
218
|
+
.deleteAll(`vectorDB:${this.id}:namespaces:${preparedNs}:datasources`);
|
|
219
|
+
|
|
195
220
|
await this.deleteACL(AccessCandidate.clone(acRequest.candidate), namespace);
|
|
196
221
|
}
|
|
197
222
|
|
|
@@ -213,10 +238,13 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
213
238
|
const result = await this.client.search({
|
|
214
239
|
data: _vector as number[],
|
|
215
240
|
collection_name: preparedNs,
|
|
216
|
-
output_fields: ['id', 'text', this.USER_METADATA_KEY, 'namespaceId', 'datasourceId', 'datasourceLabel', 'vector'],
|
|
217
241
|
limit: options.topK || 10,
|
|
218
242
|
});
|
|
219
243
|
|
|
244
|
+
if (result.status.error_code !== ErrorCode.SUCCESS) {
|
|
245
|
+
throw new Error(`Error searching data: ${result.status.detail}`);
|
|
246
|
+
}
|
|
247
|
+
|
|
220
248
|
return result.results.map((match) => {
|
|
221
249
|
let _record = match;
|
|
222
250
|
if (match?.[this.USER_METADATA_KEY]) {
|
|
@@ -249,17 +277,45 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
249
277
|
|
|
250
278
|
const sourceType = this.embedder.detectSourceType(sourceWrapper[0].source);
|
|
251
279
|
if (sourceType === 'unknown' || sourceType === 'url') throw new Error('Unsupported source type');
|
|
280
|
+
|
|
252
281
|
const transformedSource = await this.embedder.transformSource(sourceWrapper, sourceType, acRequest.candidate as AccessCandidate);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
|
|
283
|
+
const liveSchema = await this.client.describeCollection({
|
|
284
|
+
collection_name: preparedNs,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// only incl the fields that are in the live schema
|
|
288
|
+
|
|
289
|
+
const preparedSource: Partial<Record<SchemaFieldNames, any>>[] = transformedSource.map((s) => {
|
|
290
|
+
function schemaFieldExists(field: SchemaFieldNames) {
|
|
291
|
+
return liveSchema.schema.fields.some((f) => f.name === field);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
id: s.id,
|
|
295
|
+
text: s.metadata?.text,
|
|
296
|
+
user_metadata: s.metadata?.[this.USER_METADATA_KEY],
|
|
297
|
+
namespaceId: preparedNs,
|
|
298
|
+
datasourceId: s.metadata?.datasourceId, // legacy field
|
|
299
|
+
datasourceLabel: s.metadata?.datasourceLabel, // legacy field
|
|
300
|
+
vector: s.source,
|
|
301
|
+
acl: s.metadata?.acl,
|
|
302
|
+
...(schemaFieldExists('smyth_metadata')
|
|
303
|
+
? {
|
|
304
|
+
smyth_metadata: JSON.stringify({
|
|
305
|
+
datasource: {
|
|
306
|
+
id: s.metadata?.datasourceId,
|
|
307
|
+
label: s.metadata?.datasourceLabel,
|
|
308
|
+
chunkSize: s.metadata?.chunkSize,
|
|
309
|
+
chunkOverlap: s.metadata?.chunkOverlap,
|
|
310
|
+
createdAt: s.metadata?.createdAt,
|
|
311
|
+
datasourceSizeMb: s.metadata?.datasourceSizeMb,
|
|
312
|
+
chunkIndex: s.metadata?.chunkIndex,
|
|
313
|
+
},
|
|
314
|
+
}),
|
|
315
|
+
}
|
|
316
|
+
: {}),
|
|
317
|
+
};
|
|
318
|
+
});
|
|
263
319
|
|
|
264
320
|
const res = await this.client.insert({
|
|
265
321
|
collection_name: preparedNs,
|
|
@@ -322,6 +378,9 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
322
378
|
const acl = new ACL().addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner);
|
|
323
379
|
const dsId = datasource.id || crypto.randomUUID();
|
|
324
380
|
|
|
381
|
+
if (!datasource.chunkSize) datasource.chunkSize = 1000;
|
|
382
|
+
if (!datasource.chunkOverlap) datasource.chunkOverlap = 200;
|
|
383
|
+
|
|
325
384
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
326
385
|
const chunkedText = this.embedder.chunkText(datasource.text, {
|
|
327
386
|
chunkSize: datasource.chunkSize,
|
|
@@ -329,6 +388,7 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
329
388
|
});
|
|
330
389
|
const ids = Array.from({ length: chunkedText.length }, (_, i) => crypto.randomUUID());
|
|
331
390
|
const label = datasource.label || 'Untitled';
|
|
391
|
+
const totalSizeMb = calcSizeMb(datasource.text);
|
|
332
392
|
const source: IVectorDataSourceDto[] = chunkedText.map<IVectorDataSourceDto>((doc, i) => {
|
|
333
393
|
return {
|
|
334
394
|
id: ids[i],
|
|
@@ -338,6 +398,11 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
338
398
|
namespaceId: formattedNs,
|
|
339
399
|
datasourceId: dsId,
|
|
340
400
|
datasourceLabel: label,
|
|
401
|
+
chunkSize: datasource?.chunkSize?.toString(),
|
|
402
|
+
chunkOverlap: datasource?.chunkOverlap?.toString(),
|
|
403
|
+
createdAt: new Date().getTime().toString(),
|
|
404
|
+
datasourceSizeMb: totalSizeMb.toString(),
|
|
405
|
+
chunkIndex: i,
|
|
341
406
|
user_metadata: datasource.metadata ? jsonrepair(JSON.stringify(datasource.metadata)) : JSON.stringify({}),
|
|
342
407
|
},
|
|
343
408
|
};
|
|
@@ -354,7 +419,16 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
354
419
|
text: datasource.text,
|
|
355
420
|
vectorIds: _vIds.map((v) => v.id),
|
|
356
421
|
id: dsId,
|
|
422
|
+
chunkSize: datasource.chunkSize,
|
|
423
|
+
chunkOverlap: datasource.chunkOverlap,
|
|
424
|
+
createdAt: new Date(),
|
|
425
|
+
datasourceSizeMb: totalSizeMb,
|
|
357
426
|
};
|
|
427
|
+
|
|
428
|
+
await this.nkvConnector
|
|
429
|
+
.requester(acRequest.candidate as AccessCandidate)
|
|
430
|
+
.set(`vectorDB:${this.id}:namespaces:${formattedNs}:datasources`, dsId, JSON.stringify(dsData));
|
|
431
|
+
|
|
358
432
|
if (datasource.returnFullVectorInfo) {
|
|
359
433
|
dsData.vectorInfo = _vIds;
|
|
360
434
|
}
|
|
@@ -367,6 +441,10 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
367
441
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
368
442
|
|
|
369
443
|
await this.delete(acRequest, namespace, { datasourceId });
|
|
444
|
+
|
|
445
|
+
await this.nkvConnector
|
|
446
|
+
.requester(acRequest.candidate as AccessCandidate)
|
|
447
|
+
.delete(`vectorDB:${this.id}:namespaces:${formattedNs}:datasources`, datasourceId);
|
|
370
448
|
}
|
|
371
449
|
|
|
372
450
|
@SecureConnector.AccessControl
|
|
@@ -374,12 +452,26 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
374
452
|
//const teamId = await this.accountConnector.getCandidateTeam(acRequest.candidate);
|
|
375
453
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
376
454
|
|
|
455
|
+
//* [1] Get datasources from NKV
|
|
456
|
+
try {
|
|
457
|
+
const nkvDatasources = await this.nkvConnector
|
|
458
|
+
.requester(acRequest.candidate as AccessCandidate)
|
|
459
|
+
.list(`vectorDB:${this.id}:namespaces:${formattedNs}:datasources`)
|
|
460
|
+
.then((ds) => ds.map((d) => JSONContentHelper.create(d.data?.toString()).tryParse() as IStorageVectorDataSource));
|
|
461
|
+
|
|
462
|
+
return nkvDatasources;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error('[NKV] Error listing datasources: ', error);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
console.info('Trying to get datasources from Milvus');
|
|
468
|
+
//* [2] Get datasources from Milvus [EXPENSIVE OPERATION] that may exhaust rpc channel
|
|
377
469
|
// Use queryIterator for memory-efficient pagination
|
|
378
470
|
const batchSize = 1000; // Process 1000 records at a time
|
|
379
471
|
const iterator = await this.client.queryIterator({
|
|
380
472
|
collection_name: formattedNs,
|
|
381
473
|
batchSize: batchSize,
|
|
382
|
-
output_fields: ['id', 'text', this.USER_METADATA_KEY, 'namespaceId', 'datasourceId', 'datasourceLabel', 'vector'],
|
|
474
|
+
// output_fields: ['id', 'text', this.USER_METADATA_KEY, 'namespaceId', 'datasourceId', 'datasourceLabel', 'vector'],
|
|
383
475
|
});
|
|
384
476
|
|
|
385
477
|
// Group records by datasourceId using Map for efficient lookups
|
|
@@ -395,16 +487,19 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
395
487
|
namespaceId: formattedNs,
|
|
396
488
|
candidateId: acRequest.candidate.id,
|
|
397
489
|
candidateRole: acRequest.candidate.role,
|
|
398
|
-
text: record.text,
|
|
490
|
+
// text: record.text,
|
|
399
491
|
name: record.datasourceLabel,
|
|
400
492
|
metadata: record[this.USER_METADATA_KEY]
|
|
401
493
|
? JSONContentHelper.create(record[this.USER_METADATA_KEY].toString()).tryParse()
|
|
402
494
|
: undefined,
|
|
403
|
-
vectorIds: [],
|
|
404
495
|
id: datasourceId,
|
|
496
|
+
vectorIds: [], // to be filled iteratively
|
|
497
|
+
text: '', // to be filled iteratively
|
|
405
498
|
});
|
|
406
499
|
}
|
|
407
500
|
datasourceMap.get(datasourceId)!.vectorIds.push(record.id);
|
|
501
|
+
// the text here represents the total text of the datasource (not a vector-stored text)
|
|
502
|
+
datasourceMap.get(datasourceId)!.text += record.text;
|
|
408
503
|
}
|
|
409
504
|
}
|
|
410
505
|
} finally {
|
|
@@ -418,10 +513,41 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
418
513
|
protected async getDatasource(acRequest: AccessRequest, namespace: string, datasourceId: string): Promise<IStorageVectorDataSource | undefined> {
|
|
419
514
|
//const teamId = await this.accountConnector.getCandidateTeam(acRequest.candidate);
|
|
420
515
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
516
|
+
|
|
517
|
+
//* [1] Get datasource from NKV
|
|
518
|
+
try {
|
|
519
|
+
const nkvDatasource = await this.nkvConnector
|
|
520
|
+
.requester(acRequest.candidate as AccessCandidate)
|
|
521
|
+
.get(`vectorDB:${this.id}:namespaces:${formattedNs}:datasources`, datasourceId)
|
|
522
|
+
.then((ds) => JSONContentHelper.create(ds?.toString()).tryParse() as IStorageVectorDataSource);
|
|
523
|
+
|
|
524
|
+
if (nkvDatasource) {
|
|
525
|
+
return nkvDatasource;
|
|
526
|
+
} else {
|
|
527
|
+
console.info('Datasource not found in NKV');
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error('[NKV] Error getting datasource: ', error);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
console.info('Trying to get datasource from Milvus');
|
|
534
|
+
|
|
535
|
+
//* [2] Get datasource from Milvus
|
|
421
536
|
const res = await this.client.query({
|
|
422
537
|
collection_name: formattedNs,
|
|
423
538
|
expr: `datasourceId == "${datasourceId}"`,
|
|
424
|
-
output_fields: [
|
|
539
|
+
// output_fields: [
|
|
540
|
+
// 'id',
|
|
541
|
+
// 'text',
|
|
542
|
+
// this.USER_METADATA_KEY,
|
|
543
|
+
// 'namespaceId',
|
|
544
|
+
// 'datasourceId',
|
|
545
|
+
// 'datasourceLabel',
|
|
546
|
+
// 'vector',
|
|
547
|
+
// 'chunkSize',
|
|
548
|
+
// 'chunkOverlap',
|
|
549
|
+
// 'createdAt',
|
|
550
|
+
// ],
|
|
425
551
|
});
|
|
426
552
|
// if 0 results, throw error
|
|
427
553
|
if (res.data.length === 0) {
|
|
@@ -24,20 +24,29 @@ import { CacheConnector } from '@sre/MemoryManager/Cache.service/CacheConnector'
|
|
|
24
24
|
import crypto from 'crypto';
|
|
25
25
|
import { BaseEmbedding, TEmbeddings } from '../embed/BaseEmbedding';
|
|
26
26
|
import { EmbeddingsFactory, SupportedProviders, SupportedModels } from '../embed';
|
|
27
|
-
|
|
27
|
+
import { calcSizeMb } from '@sre/utils/string.utils';
|
|
28
28
|
import { jsonrepair } from 'jsonrepair';
|
|
29
|
+
import { chunkArr } from '@sre/utils/array.utils';
|
|
29
30
|
|
|
30
31
|
const console = Logger('Pinecone VectorDB');
|
|
31
32
|
|
|
32
33
|
export type PineconeConfig = {
|
|
33
34
|
/**
|
|
34
|
-
* The Pinecone API key
|
|
35
|
+
* The Pinecone API key [LEGACY]
|
|
36
|
+
*/
|
|
37
|
+
apiKey?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The Pinecone index name [LEGACY]
|
|
35
40
|
*/
|
|
36
|
-
|
|
41
|
+
indexName?: string;
|
|
42
|
+
|
|
37
43
|
/**
|
|
38
|
-
* The Pinecone
|
|
44
|
+
* The Pinecone credentials [New unified format]
|
|
39
45
|
*/
|
|
40
|
-
|
|
46
|
+
credentials?: {
|
|
47
|
+
apiKey: string;
|
|
48
|
+
indexName: string;
|
|
49
|
+
};
|
|
41
50
|
/**
|
|
42
51
|
* The embeddings model to use
|
|
43
52
|
*/
|
|
@@ -55,28 +64,28 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
55
64
|
|
|
56
65
|
constructor(protected _settings: PineconeConfig) {
|
|
57
66
|
super(_settings);
|
|
58
|
-
if (!_settings.apiKey) {
|
|
67
|
+
if (!_settings.apiKey && !_settings?.credentials?.apiKey) {
|
|
59
68
|
console.warn('Missing Pinecone API key : returning empty Pinecone connector');
|
|
60
69
|
return;
|
|
61
70
|
}
|
|
62
|
-
if (!_settings.indexName) {
|
|
71
|
+
if (!_settings.indexName && !_settings?.credentials?.indexName) {
|
|
63
72
|
console.warn('Missing Pinecone index name : returning empty Pinecone connector');
|
|
64
73
|
return;
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
this.client = new Pinecone({
|
|
68
|
-
apiKey: _settings.apiKey,
|
|
77
|
+
apiKey: _settings.apiKey || _settings.credentials?.apiKey,
|
|
69
78
|
});
|
|
70
79
|
console.info('Pinecone client initialized');
|
|
71
|
-
console.info('Pinecone index name:', _settings.indexName);
|
|
72
|
-
this.indexName = _settings.indexName;
|
|
80
|
+
console.info('Pinecone index name:', _settings.indexName || _settings.credentials?.indexName);
|
|
81
|
+
this.indexName = _settings.indexName || _settings.credentials?.indexName;
|
|
73
82
|
this.accountConnector = ConnectorService.getAccountConnector();
|
|
74
83
|
this.cache = ConnectorService.getCacheConnector();
|
|
75
84
|
this.nkvConnector = ConnectorService.getNKVConnector();
|
|
76
85
|
if (!_settings.embeddings) {
|
|
77
86
|
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 3072 };
|
|
78
87
|
}
|
|
79
|
-
if (!_settings.embeddings
|
|
88
|
+
if (!_settings.embeddings?.dimensions) _settings.embeddings.dimensions = 3072;
|
|
80
89
|
|
|
81
90
|
this.embedder = EmbeddingsFactory.create(_settings.embeddings.provider, _settings.embeddings);
|
|
82
91
|
}
|
|
@@ -192,10 +201,21 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
192
201
|
for (const match of results.matches) {
|
|
193
202
|
if (match.metadata?.isSkeletonVector) continue;
|
|
194
203
|
|
|
204
|
+
// priortize user metadata over the default flat metadata
|
|
195
205
|
if (match.metadata?.[this.USER_METADATA_KEY]) {
|
|
196
206
|
match.metadata[this.USER_METADATA_KEY] = JSONContentHelper.create(match.metadata[this.USER_METADATA_KEY].toString()).tryParse();
|
|
197
207
|
}
|
|
198
208
|
|
|
209
|
+
// if legacy metadata is present, we add it to the fallback metadata obj
|
|
210
|
+
if (match.metadata?.[this.LEGACY_USER_METADATA_KEY]) {
|
|
211
|
+
const parsedMetadata = JSONContentHelper.create(match.metadata[this.LEGACY_USER_METADATA_KEY].toString()).tryParse();
|
|
212
|
+
match.metadata = {
|
|
213
|
+
...match.metadata,
|
|
214
|
+
...parsedMetadata,
|
|
215
|
+
};
|
|
216
|
+
delete match.metadata?.[this.LEGACY_USER_METADATA_KEY];
|
|
217
|
+
}
|
|
218
|
+
|
|
199
219
|
const text = match.metadata?.text as string | undefined;
|
|
200
220
|
delete match.metadata?.text; // delete the text metadata to avoid duplication in case we returned the default raw metadata
|
|
201
221
|
|
|
@@ -229,17 +249,28 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
229
249
|
const sourceType = this.embedder.detectSourceType(sourceWrapper[0].source);
|
|
230
250
|
if (sourceType === 'unknown' || sourceType === 'url') throw new Error('Invalid source type');
|
|
231
251
|
const transformedSource = await this.embedder.transformSource(sourceWrapper, sourceType, acRequest.candidate as AccessCandidate);
|
|
232
|
-
const
|
|
252
|
+
const preparedSources = transformedSource.map((s) => ({
|
|
233
253
|
id: s.id,
|
|
234
254
|
values: s.source as number[],
|
|
235
255
|
metadata: s.metadata,
|
|
236
256
|
}));
|
|
237
257
|
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
258
|
+
// pinecone advices to use batches of 100 at a time
|
|
259
|
+
const batchSize = 100;
|
|
260
|
+
const chunkedVectors = chunkArr(preparedSources, batchSize);
|
|
261
|
+
|
|
262
|
+
// await this.client
|
|
263
|
+
// .Index(this.indexName)
|
|
264
|
+
// .namespace(this.constructNsName(acRequest.candidate as AccessCandidate, namespace))
|
|
265
|
+
// .upsert(preparedSources);
|
|
266
|
+
|
|
267
|
+
const promises = chunkedVectors.map(async (chunk) => {
|
|
268
|
+
await this.client
|
|
269
|
+
.Index(this.indexName)
|
|
270
|
+
.namespace(this.constructNsName(acRequest.candidate as AccessCandidate, namespace))
|
|
271
|
+
.upsert(chunk);
|
|
272
|
+
});
|
|
273
|
+
await Promise.all(promises);
|
|
243
274
|
|
|
244
275
|
const accessCandidate = acRequest.candidate;
|
|
245
276
|
|
|
@@ -249,7 +280,7 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
249
280
|
await this.setACL(acRequest, namespace, acl);
|
|
250
281
|
}
|
|
251
282
|
|
|
252
|
-
return
|
|
283
|
+
return preparedSources.map((s) => {
|
|
253
284
|
const { text, acl, user_metadata, ...restMetadata } = s.metadata || {};
|
|
254
285
|
return {
|
|
255
286
|
id: s.id,
|
|
@@ -272,10 +303,16 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
272
303
|
} else {
|
|
273
304
|
const _ids = Array.isArray(deleteTarget) ? deleteTarget : [deleteTarget];
|
|
274
305
|
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
306
|
+
const batchSize = 800;
|
|
307
|
+
const chunkedIds = chunkArr(_ids, batchSize);
|
|
308
|
+
|
|
309
|
+
const promises = chunkedIds.map(async (chunk) => {
|
|
310
|
+
await this.client
|
|
311
|
+
.Index(this.indexName)
|
|
312
|
+
.namespace(this.constructNsName(acRequest.candidate as AccessCandidate, namespace))
|
|
313
|
+
.deleteMany(chunk);
|
|
314
|
+
});
|
|
315
|
+
await Promise.all(promises);
|
|
279
316
|
}
|
|
280
317
|
}
|
|
281
318
|
|
|
@@ -285,6 +322,9 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
285
322
|
const acl = new ACL().addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner);
|
|
286
323
|
const dsId = datasource.id || crypto.randomUUID();
|
|
287
324
|
|
|
325
|
+
if (!datasource.chunkSize) datasource.chunkSize = 2000;
|
|
326
|
+
if (!datasource.chunkOverlap) datasource.chunkOverlap = 200;
|
|
327
|
+
|
|
288
328
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
289
329
|
const chunkedText = this.embedder.chunkText(datasource.text, {
|
|
290
330
|
chunkSize: datasource.chunkSize,
|
|
@@ -301,6 +341,7 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
301
341
|
namespaceId: formattedNs,
|
|
302
342
|
datasourceId: dsId,
|
|
303
343
|
datasourceLabel: label,
|
|
344
|
+
chunkIndex: i,
|
|
304
345
|
user_metadata: datasource.metadata ? jsonrepair(JSON.stringify(datasource.metadata)) : undefined,
|
|
305
346
|
},
|
|
306
347
|
};
|
|
@@ -317,6 +358,10 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
317
358
|
text: datasource.text,
|
|
318
359
|
vectorIds: _vIds.map((v) => v.id),
|
|
319
360
|
id: dsId,
|
|
361
|
+
datasourceSizeMb: calcSizeMb(datasource.text),
|
|
362
|
+
chunkSize: datasource.chunkSize,
|
|
363
|
+
chunkOverlap: datasource.chunkOverlap,
|
|
364
|
+
createdAt: new Date(),
|
|
320
365
|
};
|
|
321
366
|
if (datasource.returnFullVectorInfo) {
|
|
322
367
|
dsData.vectorInfo = _vIds;
|
|
@@ -18,6 +18,7 @@ export class GoogleEmbeds extends BaseEmbedding {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async embedTexts(texts: string[], candidate: AccessCandidate): Promise<number[][]> {
|
|
21
|
+
// we split into batches to avoid provider limits
|
|
21
22
|
const batches = this.chunkArr(this.processTexts(texts), this.batchSize);
|
|
22
23
|
|
|
23
24
|
const batchRequests = batches.map((batch) => {
|
|
@@ -24,7 +24,7 @@ export class OpenAIEmbeds extends BaseEmbedding {
|
|
|
24
24
|
protected client: OpenAIClient;
|
|
25
25
|
protected clientConfig: ClientOptions;
|
|
26
26
|
|
|
27
|
-
public static models = ['text-embedding-
|
|
27
|
+
public static models = ['text-embedding-3-large', 'text-embedding-ada-002'];
|
|
28
28
|
public canSpecifyDimensions = true;
|
|
29
29
|
|
|
30
30
|
constructor(private settings?: Partial<TEmbeddings>) {
|
|
@@ -41,6 +41,7 @@ export class OpenAIEmbeds extends BaseEmbedding {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async embedTexts(texts: string[], candidate: AccessCandidate): Promise<number[][]> {
|
|
44
|
+
// we split into batches to avoid provider limits
|
|
44
45
|
const batches = this.chunkArr(this.processTexts(texts), this.batchSize);
|
|
45
46
|
|
|
46
47
|
const batchRequests = batches.map((batch) => {
|
|
@@ -34,4 +34,20 @@ export class EmbeddingsFactory {
|
|
|
34
34
|
|
|
35
35
|
return new supportedProviders[provider as SupportedProviders].embedder(config);
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
public static getProviderByModel(model: SupportedModels): SupportedProviders {
|
|
39
|
+
return Object.keys(supportedProviders).find((provider) => supportedProviders[provider].models.includes(model)) as SupportedProviders;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public static getModels() {
|
|
43
|
+
return Object.keys(supportedProviders).reduce((acc, provider) => {
|
|
44
|
+
acc.push(
|
|
45
|
+
...supportedProviders[provider].models.map((model) => ({
|
|
46
|
+
provider,
|
|
47
|
+
model,
|
|
48
|
+
}))
|
|
49
|
+
);
|
|
50
|
+
return acc;
|
|
51
|
+
}, [] as { provider: SupportedProviders; model: SupportedModels[SupportedProviders] }[]);
|
|
52
|
+
}
|
|
37
53
|
}
|
|
@@ -248,4 +248,29 @@ export class LLMHelper {
|
|
|
248
248
|
|
|
249
249
|
return _messages;
|
|
250
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Checks if the given model is part of the Claude 4 family.
|
|
254
|
+
*
|
|
255
|
+
* @param {string} modelId - The model identifier to check.
|
|
256
|
+
* @returns {boolean} True if the model is Claude 4 family, false otherwise.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* const isClaude4 = LLMHelper.isClaude4Family('claude-sonnet-4-20250514');
|
|
260
|
+
* console.log(isClaude4); // true
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* const isClaude4 = LLMHelper.isClaude4Family('claude-opus-4-5');
|
|
264
|
+
* console.log(isClaude4); // true
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* const isClaude4 = LLMHelper.isClaude4Family('gpt-4-turbo');
|
|
268
|
+
* console.log(isClaude4); // false
|
|
269
|
+
*/
|
|
270
|
+
public static isClaude4Family(modelId: string): boolean {
|
|
271
|
+
if (!modelId) return false;
|
|
272
|
+
// Match patterns like: claude-4-*, claude-{variant}-4-*, claude-{variant}-4
|
|
273
|
+
// Examples: claude-opus-4-5, claude-sonnet-4-20250514, claude-4-opus
|
|
274
|
+
return /claude-(?:\w+-)?4(?:-|$)/i.test(modelId);
|
|
275
|
+
}
|
|
251
276
|
}
|
|
@@ -275,7 +275,7 @@ export abstract class LLMConnector extends Connector {
|
|
|
275
275
|
const teamId = await this.getTeamId(candidate);
|
|
276
276
|
|
|
277
277
|
// We need the model entry name for usage reporting
|
|
278
|
-
_params.modelEntryName = typeof model === 'string' ? model :
|
|
278
|
+
_params.modelEntryName = typeof model === 'string' ? model : model?.modelEntryName || model?.modelId;
|
|
279
279
|
_params.teamId = teamId;
|
|
280
280
|
|
|
281
281
|
const modelProviderCandidate = modelsProvider.requester(candidate);
|