@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.
Files changed (78) hide show
  1. package/dist/index.js +134 -89
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
  4. package/dist/types/Components/DataSourceIndexer.class.d.ts +4 -12
  5. package/dist/types/Components/GenAILLM.class.d.ts +5 -5
  6. package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
  7. package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
  8. package/dist/types/Components/index.d.ts +3 -3
  9. package/dist/types/config.d.ts +1 -0
  10. package/dist/types/helpers/Conversation.helper.d.ts +10 -13
  11. package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
  12. package/dist/types/index.d.ts +4 -3
  13. package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
  14. package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +1 -0
  15. package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +11 -4
  16. package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +5 -0
  17. package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
  18. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
  19. package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.d.ts +35 -0
  20. package/dist/types/subsystems/Security/Account.service/AccountConnector.d.ts +2 -2
  21. package/dist/types/subsystems/Security/Vault.service/connectors/SecretsManager.class.d.ts +2 -3
  22. package/dist/types/types/LLM.types.d.ts +23 -0
  23. package/dist/types/types/VectorDB.types.d.ts +4 -0
  24. package/dist/types/utils/string.utils.d.ts +1 -0
  25. package/package.json +3 -3
  26. package/src/Components/APIEndpoint.class.ts +1 -6
  27. package/src/Components/AgentPlugin.class.ts +20 -3
  28. package/src/Components/Classifier.class.ts +79 -16
  29. package/src/Components/Component.class.ts +14 -1
  30. package/src/Components/ForEach.class.ts +34 -6
  31. package/src/Components/GenAILLM.class.ts +75 -34
  32. package/src/Components/LLMAssistant.class.ts +56 -21
  33. package/src/Components/RAG/DataSourceCleaner.class.ts +180 -0
  34. package/src/Components/RAG/DataSourceComponent.class.ts +137 -0
  35. package/src/Components/RAG/DataSourceIndexer.class.ts +260 -0
  36. package/src/Components/{DataSourceLookup.class.ts → RAG/DataSourceLookup.class.ts} +96 -3
  37. package/src/Components/ScrapflyWebScrape.class.ts +7 -0
  38. package/src/Components/ServerlessCode.class.ts +1 -4
  39. package/src/Components/index.ts +3 -3
  40. package/src/config.ts +1 -0
  41. package/src/helpers/Conversation.helper.ts +112 -26
  42. package/src/helpers/S3Cache.helper.ts +2 -1
  43. package/src/helpers/TemplateString.helper.ts +6 -5
  44. package/src/index.ts +213 -212
  45. package/src/index.ts.bak +213 -212
  46. package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +3 -1
  47. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
  48. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +145 -19
  49. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +67 -22
  50. package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -0
  51. package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +2 -1
  52. package/src/subsystems/IO/VectorDB.service/embed/index.ts +16 -0
  53. package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
  54. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
  55. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +35 -10
  56. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +12 -4
  57. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +4 -4
  58. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +192 -139
  59. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +17 -5
  60. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +18 -3
  61. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +14 -5
  62. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +6 -4
  63. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +5 -5
  64. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +8 -3
  65. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
  66. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +9 -8
  67. package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +92 -1
  68. package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +260 -17
  69. package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -3
  70. package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +8 -63
  71. package/src/types/LLM.types.ts +24 -0
  72. package/src/types/VectorDB.types.ts +4 -0
  73. package/src/utils/array.utils.ts +11 -0
  74. package/src/utils/base64.utils.ts +1 -1
  75. package/src/utils/data.utils.ts +6 -4
  76. package/src/utils/string.utils.ts +3 -192
  77. package/src/Components/DataSourceCleaner.class.ts +0 -92
  78. 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.dimensions) _settings.embeddings.dimensions = 3072;
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
- const preparedSource: Record<SchemaFieldNames, any>[] = transformedSource.map((s) => ({
254
- id: s.id,
255
- text: s.metadata?.text,
256
- user_metadata: s.metadata?.[this.USER_METADATA_KEY],
257
- namespaceId: preparedNs,
258
- datasourceId: s.metadata?.datasourceId,
259
- datasourceLabel: s.metadata?.datasourceLabel,
260
- vector: s.source,
261
- acl: s.metadata?.acl,
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: ['id', 'text', this.USER_METADATA_KEY, 'namespaceId', 'datasourceId', 'datasourceLabel', 'vector'],
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
- apiKey: string;
41
+ indexName?: string;
42
+
37
43
  /**
38
- * The Pinecone index name
44
+ * The Pinecone credentials [New unified format]
39
45
  */
40
- indexName: string;
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.dimensions) _settings.embeddings.dimensions = 3072;
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 preparedSource = transformedSource.map((s) => ({
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
- // await pineconeStore.addDocuments(chunks, ids);
239
- await this.client
240
- .Index(this.indexName)
241
- .namespace(this.constructNsName(acRequest.candidate as AccessCandidate, namespace))
242
- .upsert(preparedSource);
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 preparedSource.map((s) => {
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 res = await this.client
276
- .Index(this.indexName)
277
- .namespace(this.constructNsName(acRequest.candidate as AccessCandidate, namespace))
278
- .deleteMany(_ids);
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-ada-002', 'text-embedding-3-large'];
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 : (model as TLLMModel).modelId;
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);