@smythos/sre 1.7.1 → 1.7.5
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 +33 -31
- package/dist/index.js.map +1 -1
- package/dist/types/helpers/BinaryInput.helper.d.ts +1 -1
- package/dist/types/helpers/LocalCache.helper.d.ts +18 -0
- package/dist/types/helpers/TemplateString.helper.d.ts +2 -1
- package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +4 -4
- package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/embed/BaseEmbedding.d.ts +16 -9
- package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +4 -1
- package/dist/types/subsystems/LLMManager/LLM.inference.d.ts +36 -2
- package/dist/types/types/LLM.types.d.ts +54 -31
- package/dist/types/types/VectorDB.types.d.ts +6 -3
- package/dist/types/utils/string.utils.d.ts +0 -4
- package/package.json +1 -1
- package/src/Components/Classifier.class.ts +8 -2
- package/src/Components/GenAILLM.class.ts +11 -7
- package/src/Components/LLMAssistant.class.ts +12 -3
- package/src/Components/ScrapflyWebScrape.class.ts +8 -1
- package/src/Components/TavilyWebSearch.class.ts +4 -1
- package/src/Core/SmythRuntime.class.ts +13 -2
- package/src/helpers/BinaryInput.helper.ts +8 -8
- package/src/helpers/Conversation.helper.ts +11 -1
- package/src/helpers/LocalCache.helper.ts +18 -0
- package/src/helpers/TemplateString.helper.ts +20 -9
- package/src/index.ts +208 -208
- package/src/index.ts.bak +208 -208
- package/src/subsystems/AgentManager/Agent.class.ts +2 -0
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +6 -5
- package/src/subsystems/AgentManager/AgentLogger.class.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +15 -4
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +31 -10
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +27 -10
- package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +25 -9
- package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +182 -12
- package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +12 -2
- package/src/subsystems/LLMManager/LLM.inference.ts +76 -17
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +3 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +2 -2
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +2 -2
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +4 -1
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +12 -0
- package/src/types/LLM.types.ts +66 -38
- package/src/types/VectorDB.types.ts +7 -3
- package/src/utils/string.utils.ts +193 -191
|
@@ -9,8 +9,14 @@ import { ACL } from '@sre/Security/AccessControl/ACL.class';
|
|
|
9
9
|
import { AccountConnector } from '@sre/Security/Account.service/AccountConnector';
|
|
10
10
|
import { SecureConnector } from '@sre/Security/SecureConnector.class';
|
|
11
11
|
import { IAccessCandidate, IACL, TAccessLevel } from '@sre/types/ACL.types';
|
|
12
|
-
import {
|
|
13
|
-
|
|
12
|
+
import {
|
|
13
|
+
DatasourceDto,
|
|
14
|
+
IStorageVectorDataSource,
|
|
15
|
+
IVectorDataSourceDto,
|
|
16
|
+
QueryOptions,
|
|
17
|
+
VectorDBResult,
|
|
18
|
+
VectorsResultData,
|
|
19
|
+
} from '@sre/types/VectorDB.types';
|
|
14
20
|
import { CreateIndexSimpleReq, DataType, ErrorCode, FieldType, MilvusClient } from '@zilliz/milvus2-sdk-node';
|
|
15
21
|
import crypto from 'crypto';
|
|
16
22
|
import { jsonrepair } from 'jsonrepair';
|
|
@@ -72,10 +78,10 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
72
78
|
this.cache = ConnectorService.getCacheConnector();
|
|
73
79
|
|
|
74
80
|
if (!_settings.embeddings) {
|
|
75
|
-
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large',
|
|
81
|
+
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 1024 };
|
|
76
82
|
}
|
|
77
|
-
|
|
78
|
-
if (!_settings.embeddings.
|
|
83
|
+
|
|
84
|
+
if (!_settings.embeddings.dimensions) _settings.embeddings.dimensions = 1024;
|
|
79
85
|
|
|
80
86
|
this.embedder = EmbeddingsFactory.create(_settings.embeddings.provider, _settings.embeddings);
|
|
81
87
|
|
|
@@ -231,7 +237,7 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
231
237
|
acRequest: AccessRequest,
|
|
232
238
|
namespace: string,
|
|
233
239
|
sourceWrapper: IVectorDataSourceDto | IVectorDataSourceDto[]
|
|
234
|
-
): Promise<
|
|
240
|
+
): Promise<VectorDBResult[]> {
|
|
235
241
|
//const teamId = await this.accountConnector.getCandidateTeam(acRequest.candidate);
|
|
236
242
|
sourceWrapper = Array.isArray(sourceWrapper) ? sourceWrapper : [sourceWrapper];
|
|
237
243
|
const preparedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
@@ -264,7 +270,18 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
264
270
|
throw new Error(`Error inserting data: ${res?.status?.error_code}`);
|
|
265
271
|
}
|
|
266
272
|
|
|
267
|
-
return preparedSource.map((s) =>
|
|
273
|
+
return preparedSource.map((s) => {
|
|
274
|
+
const { text, acl, user_metadata, ...restMetadata } = s || {};
|
|
275
|
+
return {
|
|
276
|
+
id: s.id,
|
|
277
|
+
values: s.vector as number[],
|
|
278
|
+
text: text as string,
|
|
279
|
+
metadata: {
|
|
280
|
+
...restMetadata,
|
|
281
|
+
...((typeof user_metadata === 'string' ? JSON.parse(user_metadata) : user_metadata) as Record<string, any>),
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
});
|
|
268
285
|
}
|
|
269
286
|
|
|
270
287
|
@SecureConnector.AccessControl
|
|
@@ -306,7 +323,7 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
306
323
|
const dsId = datasource.id || crypto.randomUUID();
|
|
307
324
|
|
|
308
325
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
309
|
-
const chunkedText = chunkText(datasource.text, {
|
|
326
|
+
const chunkedText = this.embedder.chunkText(datasource.text, {
|
|
310
327
|
chunkSize: datasource.chunkSize,
|
|
311
328
|
chunkOverlap: datasource.chunkOverlap,
|
|
312
329
|
});
|
|
@@ -328,16 +345,20 @@ export class MilvusVectorDB extends VectorDBConnector {
|
|
|
328
345
|
|
|
329
346
|
const _vIds = await this.insert(acRequest, namespace, source);
|
|
330
347
|
|
|
331
|
-
|
|
348
|
+
const dsData: IStorageVectorDataSource = {
|
|
332
349
|
namespaceId: formattedNs,
|
|
333
350
|
candidateId: acRequest.candidate.id,
|
|
334
351
|
candidateRole: acRequest.candidate.role,
|
|
335
352
|
name: label,
|
|
336
353
|
metadata: datasource.metadata ? jsonrepair(JSON.stringify(datasource.metadata)) : undefined,
|
|
337
354
|
text: datasource.text,
|
|
338
|
-
vectorIds: _vIds,
|
|
355
|
+
vectorIds: _vIds.map((v) => v.id),
|
|
339
356
|
id: dsId,
|
|
340
357
|
};
|
|
358
|
+
if (datasource.returnFullVectorInfo) {
|
|
359
|
+
dsData.vectorInfo = _vIds;
|
|
360
|
+
}
|
|
361
|
+
return dsData;
|
|
341
362
|
}
|
|
342
363
|
|
|
343
364
|
@SecureConnector.AccessControl
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
IStorageVectorNamespace,
|
|
12
12
|
IVectorDataSourceDto,
|
|
13
13
|
QueryOptions,
|
|
14
|
+
VectorDBResult,
|
|
14
15
|
VectorsResultData,
|
|
15
16
|
} from '@sre/types/VectorDB.types';
|
|
16
17
|
import { Pinecone } from '@pinecone-database/pinecone';
|
|
@@ -23,7 +24,7 @@ import { CacheConnector } from '@sre/MemoryManager/Cache.service/CacheConnector'
|
|
|
23
24
|
import crypto from 'crypto';
|
|
24
25
|
import { BaseEmbedding, TEmbeddings } from '../embed/BaseEmbedding';
|
|
25
26
|
import { EmbeddingsFactory, SupportedProviders, SupportedModels } from '../embed';
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
import { jsonrepair } from 'jsonrepair';
|
|
28
29
|
|
|
29
30
|
const console = Logger('Pinecone VectorDB');
|
|
@@ -73,10 +74,9 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
73
74
|
this.cache = ConnectorService.getCacheConnector();
|
|
74
75
|
this.nkvConnector = ConnectorService.getNKVConnector();
|
|
75
76
|
if (!_settings.embeddings) {
|
|
76
|
-
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large',
|
|
77
|
+
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 1024 };
|
|
77
78
|
}
|
|
78
|
-
if (!_settings.embeddings.
|
|
79
|
-
if (!_settings.embeddings.params?.dimensions) _settings.embeddings.params.dimensions = 1024;
|
|
79
|
+
if (!_settings.embeddings.dimensions) _settings.embeddings.dimensions = 1024;
|
|
80
80
|
|
|
81
81
|
this.embedder = EmbeddingsFactory.create(_settings.embeddings.provider, _settings.embeddings);
|
|
82
82
|
}
|
|
@@ -189,11 +189,14 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
189
189
|
match.metadata[this.USER_METADATA_KEY] = JSONContentHelper.create(match.metadata[this.USER_METADATA_KEY].toString()).tryParse();
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
const text = match.metadata?.text as string | undefined;
|
|
193
|
+
delete match.metadata?.text; // delete the text metadata to avoid duplication in case we returned the default raw metadata
|
|
194
|
+
|
|
192
195
|
matches.push({
|
|
193
196
|
id: match.id,
|
|
194
197
|
values: match.values,
|
|
195
|
-
text:
|
|
196
|
-
metadata: match.metadata?.[this.USER_METADATA_KEY]
|
|
198
|
+
text: text,
|
|
199
|
+
metadata: match.metadata?.[this.USER_METADATA_KEY] || match.metadata, // fallback to the default metadata if the user metadata is not present, this is for backward compatibility
|
|
197
200
|
score: match.score,
|
|
198
201
|
});
|
|
199
202
|
}
|
|
@@ -207,7 +210,7 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
207
210
|
acRequest: AccessRequest,
|
|
208
211
|
namespace: string,
|
|
209
212
|
sourceWrapper: IVectorDataSourceDto | IVectorDataSourceDto[]
|
|
210
|
-
): Promise<
|
|
213
|
+
): Promise<VectorDBResult[]> {
|
|
211
214
|
//const teamId = await this.accountConnector.getCandidateTeam(acRequest.candidate);
|
|
212
215
|
sourceWrapper = Array.isArray(sourceWrapper) ? sourceWrapper : [sourceWrapper];
|
|
213
216
|
|
|
@@ -239,7 +242,18 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
239
242
|
await this.setACL(acRequest, namespace, acl);
|
|
240
243
|
}
|
|
241
244
|
|
|
242
|
-
return preparedSource.map((s) =>
|
|
245
|
+
return preparedSource.map((s) => {
|
|
246
|
+
const { text, acl, user_metadata, ...restMetadata } = s.metadata || {};
|
|
247
|
+
return {
|
|
248
|
+
id: s.id,
|
|
249
|
+
values: s.values as number[],
|
|
250
|
+
text: text as string,
|
|
251
|
+
metadata: {
|
|
252
|
+
...restMetadata,
|
|
253
|
+
...((typeof user_metadata === 'string' ? JSON.parse(user_metadata) : user_metadata) as Record<string, any>),
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
});
|
|
243
257
|
}
|
|
244
258
|
|
|
245
259
|
@SecureConnector.AccessControl
|
|
@@ -265,7 +279,7 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
265
279
|
const dsId = datasource.id || crypto.randomUUID();
|
|
266
280
|
|
|
267
281
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
268
|
-
const chunkedText = chunkText(datasource.text, {
|
|
282
|
+
const chunkedText = this.embedder.chunkText(datasource.text, {
|
|
269
283
|
chunkSize: datasource.chunkSize,
|
|
270
284
|
chunkOverlap: datasource.chunkOverlap,
|
|
271
285
|
});
|
|
@@ -294,9 +308,12 @@ export class PineconeVectorDB extends VectorDBConnector {
|
|
|
294
308
|
name: datasource.label || 'Untitled',
|
|
295
309
|
metadata: datasource.metadata ? jsonrepair(JSON.stringify(datasource.metadata)) : undefined,
|
|
296
310
|
text: datasource.text,
|
|
297
|
-
vectorIds: _vIds,
|
|
311
|
+
vectorIds: _vIds.map((v) => v.id),
|
|
298
312
|
id: dsId,
|
|
299
313
|
};
|
|
314
|
+
if (datasource.returnFullVectorInfo) {
|
|
315
|
+
dsData.vectorInfo = _vIds;
|
|
316
|
+
}
|
|
300
317
|
// const url = `smythfs://${teamId}.team/_datasources/${dsId}.json`;
|
|
301
318
|
// await SmythFS.Instance.write(url, JSON.stringify(dsData), AccessCandidate.team(teamId));
|
|
302
319
|
await this.nkvConnector
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
IStorageVectorNamespace,
|
|
13
13
|
IVectorDataSourceDto,
|
|
14
14
|
QueryOptions,
|
|
15
|
+
VectorDBResult,
|
|
15
16
|
VectorsResultData,
|
|
16
17
|
} from '@sre/types/VectorDB.types';
|
|
17
18
|
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
@@ -21,7 +22,7 @@ import { OpenAIEmbeds } from '@sre/IO/VectorDB.service/embed/OpenAIEmbedding';
|
|
|
21
22
|
import crypto from 'crypto';
|
|
22
23
|
import { BaseEmbedding, TEmbeddings } from '../embed/BaseEmbedding';
|
|
23
24
|
import { EmbeddingsFactory } from '../embed';
|
|
24
|
-
|
|
25
|
+
|
|
25
26
|
import { jsonrepair } from 'jsonrepair';
|
|
26
27
|
|
|
27
28
|
const console = Logger('RAM VectorDB');
|
|
@@ -71,10 +72,10 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
71
72
|
this.accountConnector = ConnectorService.getAccountConnector();
|
|
72
73
|
|
|
73
74
|
if (!_settings.embeddings) {
|
|
74
|
-
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large',
|
|
75
|
+
_settings.embeddings = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 1024 };
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
-
if (!_settings.embeddings.
|
|
77
|
+
|
|
78
|
+
if (!_settings.embeddings.dimensions) _settings.embeddings.dimensions = 1024;
|
|
78
79
|
|
|
79
80
|
this.embedder = EmbeddingsFactory.create(_settings.embeddings.provider, _settings.embeddings);
|
|
80
81
|
}
|
|
@@ -225,7 +226,7 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
225
226
|
acRequest: AccessRequest,
|
|
226
227
|
namespace: string,
|
|
227
228
|
sourceWrapper: IVectorDataSourceDto | IVectorDataSourceDto[]
|
|
228
|
-
): Promise<
|
|
229
|
+
): Promise<VectorDBResult[]> {
|
|
229
230
|
//const teamId = await this.accountConnector.getCandidateTeam(acRequest.candidate);
|
|
230
231
|
const preparedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
231
232
|
|
|
@@ -245,7 +246,7 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
245
246
|
RAMVectorDB.vectors[preparedNs] = [];
|
|
246
247
|
}
|
|
247
248
|
|
|
248
|
-
const insertedIds:
|
|
249
|
+
const insertedIds: VectorDBResult[] = [];
|
|
249
250
|
|
|
250
251
|
for (const source of transformedSource) {
|
|
251
252
|
const vectorData: VectorData = {
|
|
@@ -263,7 +264,18 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
263
264
|
RAMVectorDB.vectors[preparedNs].push(vectorData);
|
|
264
265
|
}
|
|
265
266
|
|
|
266
|
-
|
|
267
|
+
const { text, acl, user_metadata, ...restMetadata } = source.metadata || {};
|
|
268
|
+
|
|
269
|
+
(insertedIds as VectorDBResult[]).push({
|
|
270
|
+
id: source.id,
|
|
271
|
+
values: source.source as number[],
|
|
272
|
+
text: text as string,
|
|
273
|
+
metadata: {
|
|
274
|
+
...restMetadata,
|
|
275
|
+
...((typeof user_metadata === 'string' ? JSON.parse(user_metadata) : user_metadata) as Record<string, any>),
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
//insertedIds.push(source.id);
|
|
267
279
|
}
|
|
268
280
|
|
|
269
281
|
return insertedIds;
|
|
@@ -303,7 +315,7 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
303
315
|
const dsId = datasource.id || crypto.randomUUID();
|
|
304
316
|
|
|
305
317
|
const formattedNs = this.constructNsName(acRequest.candidate as AccessCandidate, namespace);
|
|
306
|
-
const chunkedText = chunkText(datasource.text, {
|
|
318
|
+
const chunkedText = this.embedder.chunkText(datasource.text, {
|
|
307
319
|
chunkSize: datasource.chunkSize,
|
|
308
320
|
chunkOverlap: datasource.chunkOverlap,
|
|
309
321
|
});
|
|
@@ -332,10 +344,14 @@ export class RAMVectorDB extends VectorDBConnector {
|
|
|
332
344
|
name: datasource.label || 'Untitled',
|
|
333
345
|
metadata: datasource.metadata ? jsonrepair(JSON.stringify(datasource.metadata)) : undefined,
|
|
334
346
|
text: datasource.text,
|
|
335
|
-
vectorIds: _vIds,
|
|
347
|
+
vectorIds: _vIds.map((v) => v.id),
|
|
336
348
|
id: dsId,
|
|
337
349
|
};
|
|
338
350
|
|
|
351
|
+
if (datasource.returnFullVectorInfo) {
|
|
352
|
+
dsData.vectorInfo = _vIds;
|
|
353
|
+
}
|
|
354
|
+
|
|
339
355
|
// Store datasource metadata in memory
|
|
340
356
|
if (!RAMVectorDB.datasources[formattedNs]) {
|
|
341
357
|
RAMVectorDB.datasources[formattedNs] = {};
|
|
@@ -4,18 +4,21 @@ import { SupportedProviders, SupportedModels } from './index';
|
|
|
4
4
|
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
5
5
|
|
|
6
6
|
export type TEmbeddings = {
|
|
7
|
-
provider
|
|
8
|
-
model
|
|
7
|
+
provider?: SupportedProviders;
|
|
8
|
+
model?: SupportedModels[SupportedProviders];
|
|
9
9
|
|
|
10
10
|
credentials?: {
|
|
11
11
|
apiKey: string;
|
|
12
12
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
|
|
14
|
+
dimensions?: number;
|
|
15
|
+
timeout?: number;
|
|
16
|
+
chunkSize?: number;
|
|
17
|
+
chunkOverlap?: number;
|
|
18
|
+
batchSize?: number;
|
|
19
|
+
stripNewLines?: boolean;
|
|
20
|
+
|
|
21
|
+
params?: any;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
type SupportedSources = 'text' | 'vector' | 'url';
|
|
@@ -24,16 +27,19 @@ export abstract class BaseEmbedding {
|
|
|
24
27
|
model: string;
|
|
25
28
|
modelName: string;
|
|
26
29
|
chunkSize = 512;
|
|
30
|
+
chunkOverlap = 100;
|
|
27
31
|
stripNewLines = true;
|
|
28
32
|
dimensions?: number;
|
|
29
33
|
timeout?: number;
|
|
34
|
+
batchSize = 10;
|
|
30
35
|
|
|
31
36
|
constructor(fields?: Partial<TEmbeddings>) {
|
|
32
37
|
this.model = fields?.model ?? this.model;
|
|
33
|
-
this.chunkSize = fields?.params?.chunkSize
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
38
|
+
this.chunkSize = fields?.chunkSize || fields?.params?.chunkSize || this.chunkSize;
|
|
39
|
+
this.chunkOverlap = fields?.chunkOverlap || fields?.params?.chunkOverlap || this.chunkOverlap;
|
|
40
|
+
this.stripNewLines = fields?.stripNewLines || fields?.params?.stripNewLines || this.stripNewLines;
|
|
41
|
+
this.timeout = fields?.timeout || fields?.params?.timeout;
|
|
42
|
+
this.dimensions = fields?.dimensions || fields?.params?.dimensions;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
/**
|
|
@@ -58,6 +64,13 @@ export abstract class BaseEmbedding {
|
|
|
58
64
|
}, [] as T[][]);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
public chunkText(text: string, { chunkSize, chunkOverlap }: { chunkSize?: number; chunkOverlap?: number }): string[] {
|
|
68
|
+
const textSplitter = new TextSplitter({
|
|
69
|
+
chunkSize: chunkSize || this.chunkSize,
|
|
70
|
+
chunkOverlap: chunkOverlap || this.chunkOverlap,
|
|
71
|
+
});
|
|
72
|
+
return textSplitter.splitText(text);
|
|
73
|
+
}
|
|
61
74
|
/**
|
|
62
75
|
* Utility method to process multiple texts based on stripNewLines setting
|
|
63
76
|
*/
|
|
@@ -105,3 +118,160 @@ export abstract class BaseEmbedding {
|
|
|
105
118
|
.concat([1]);
|
|
106
119
|
}
|
|
107
120
|
}
|
|
121
|
+
|
|
122
|
+
class TextSplitter {
|
|
123
|
+
private chunkSize: number;
|
|
124
|
+
private chunkOverlap: number;
|
|
125
|
+
private separators: string[] = ['\n\n', '\n', ' ', ''];
|
|
126
|
+
private keepSeparator: boolean = true;
|
|
127
|
+
|
|
128
|
+
constructor({
|
|
129
|
+
chunkSize = 1000,
|
|
130
|
+
chunkOverlap = 200,
|
|
131
|
+
separators,
|
|
132
|
+
keepSeparator,
|
|
133
|
+
}: {
|
|
134
|
+
chunkSize?: number;
|
|
135
|
+
chunkOverlap?: number;
|
|
136
|
+
separators?: string[];
|
|
137
|
+
keepSeparator?: boolean;
|
|
138
|
+
} = {}) {
|
|
139
|
+
this.chunkSize = chunkSize;
|
|
140
|
+
this.chunkOverlap = chunkOverlap;
|
|
141
|
+
|
|
142
|
+
if (separators) {
|
|
143
|
+
this.separators = separators;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (keepSeparator !== undefined) {
|
|
147
|
+
this.keepSeparator = keepSeparator;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (this.chunkOverlap >= this.chunkSize) {
|
|
151
|
+
throw new Error('Cannot have chunkOverlap >= chunkSize');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public splitText(text: string): string[] {
|
|
156
|
+
return this._splitText(text, this.separators);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private _splitText(text: string, separators: string[]): string[] {
|
|
160
|
+
const finalChunks: string[] = [];
|
|
161
|
+
|
|
162
|
+
// Get appropriate separator to use
|
|
163
|
+
let separator: string = separators[separators.length - 1];
|
|
164
|
+
let newSeparators: string[] | undefined;
|
|
165
|
+
|
|
166
|
+
for (let i = 0; i < separators.length; i += 1) {
|
|
167
|
+
const s = separators[i];
|
|
168
|
+
if (s === '') {
|
|
169
|
+
separator = s;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (text.includes(s)) {
|
|
173
|
+
separator = s;
|
|
174
|
+
newSeparators = separators.slice(i + 1);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Split the text using the identified separator
|
|
180
|
+
const splits = this.splitOnSeparator(text, separator);
|
|
181
|
+
|
|
182
|
+
// Process splits, recursively splitting longer texts
|
|
183
|
+
let goodSplits: string[] = [];
|
|
184
|
+
const _separator = this.keepSeparator ? '' : separator;
|
|
185
|
+
|
|
186
|
+
for (const s of splits) {
|
|
187
|
+
if (this.lengthFunction(s) < this.chunkSize) {
|
|
188
|
+
goodSplits.push(s);
|
|
189
|
+
} else {
|
|
190
|
+
if (goodSplits.length) {
|
|
191
|
+
const mergedText = this.mergeSplits(goodSplits, _separator);
|
|
192
|
+
finalChunks.push(...mergedText);
|
|
193
|
+
goodSplits = [];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!newSeparators) {
|
|
197
|
+
finalChunks.push(s);
|
|
198
|
+
} else {
|
|
199
|
+
const otherInfo = this._splitText(s, newSeparators);
|
|
200
|
+
finalChunks.push(...otherInfo);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (goodSplits.length) {
|
|
206
|
+
const mergedText = this.mergeSplits(goodSplits, _separator);
|
|
207
|
+
finalChunks.push(...mergedText);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return finalChunks;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private splitOnSeparator(text: string, separator: string): string[] {
|
|
214
|
+
let splits: string[];
|
|
215
|
+
|
|
216
|
+
if (separator) {
|
|
217
|
+
if (this.keepSeparator) {
|
|
218
|
+
const regexEscapedSeparator = separator.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
219
|
+
splits = text.split(new RegExp(`(?=${regexEscapedSeparator})`));
|
|
220
|
+
} else {
|
|
221
|
+
splits = text.split(separator);
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
splits = text.split('');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return splits.filter((s) => s !== '');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private lengthFunction(text: string): number {
|
|
231
|
+
return text.length;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private joinDocs(docs: string[], separator: string): string | null {
|
|
235
|
+
const text = docs.join(separator).trim();
|
|
236
|
+
return text === '' ? null : text;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private mergeSplits(splits: string[], separator: string): string[] {
|
|
240
|
+
const docs: string[] = [];
|
|
241
|
+
const currentDoc: string[] = [];
|
|
242
|
+
let total = 0;
|
|
243
|
+
|
|
244
|
+
for (const d of splits) {
|
|
245
|
+
const _len = this.lengthFunction(d);
|
|
246
|
+
|
|
247
|
+
if (total + _len + currentDoc.length * separator.length > this.chunkSize) {
|
|
248
|
+
if (total > this.chunkSize) {
|
|
249
|
+
console.warn(`Created a chunk of size ${total}, which is longer than the specified ${this.chunkSize}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (currentDoc.length > 0) {
|
|
253
|
+
const doc = this.joinDocs(currentDoc, separator);
|
|
254
|
+
if (doc !== null) {
|
|
255
|
+
docs.push(doc);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Keep popping if conditions are met
|
|
259
|
+
while (total > this.chunkOverlap || (total + _len + currentDoc.length * separator.length > this.chunkSize && total > 0)) {
|
|
260
|
+
total -= this.lengthFunction(currentDoc[0]);
|
|
261
|
+
currentDoc.shift();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
currentDoc.push(d);
|
|
267
|
+
total += _len;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const doc = this.joinDocs(currentDoc, separator);
|
|
271
|
+
if (doc !== null) {
|
|
272
|
+
docs.push(doc);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return docs;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -18,7 +18,7 @@ export class GoogleEmbeds extends BaseEmbedding {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async embedTexts(texts: string[], candidate: AccessCandidate): Promise<number[][]> {
|
|
21
|
-
const batches = this.chunkArr(this.processTexts(texts), this.
|
|
21
|
+
const batches = this.chunkArr(this.processTexts(texts), this.batchSize);
|
|
22
22
|
|
|
23
23
|
const batchRequests = batches.map((batch) => {
|
|
24
24
|
return this.embed(batch, candidate);
|
|
@@ -41,7 +41,7 @@ export class OpenAIEmbeds extends BaseEmbedding {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async embedTexts(texts: string[], candidate: AccessCandidate): Promise<number[][]> {
|
|
44
|
-
const batches = this.chunkArr(this.processTexts(texts), this.
|
|
44
|
+
const batches = this.chunkArr(this.processTexts(texts), this.batchSize);
|
|
45
45
|
|
|
46
46
|
const batchRequests = batches.map((batch) => {
|
|
47
47
|
const params: OpenAIClient.EmbeddingCreateParams = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenAIEmbeds } from './OpenAIEmbedding';
|
|
2
2
|
import { GoogleEmbeds } from './GoogleEmbedding';
|
|
3
3
|
import { TEmbeddings } from './BaseEmbedding';
|
|
4
|
+
import { TLLMModel } from '@sre/types/LLM.types';
|
|
4
5
|
|
|
5
6
|
// a factory to get the correct embedding provider based on the provider name
|
|
6
7
|
const supportedProviders = {
|
|
@@ -20,7 +21,16 @@ export type SupportedModels = {
|
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
export class EmbeddingsFactory {
|
|
23
|
-
public static create(provider
|
|
24
|
-
|
|
24
|
+
public static create(provider?: SupportedProviders, config?: TEmbeddings & { model?: SupportedModels[SupportedProviders] | TLLMModel }) {
|
|
25
|
+
if (!provider) provider = 'OpenAI';
|
|
26
|
+
if (!config) config = { provider: 'OpenAI', model: 'text-embedding-3-large', dimensions: 1024 };
|
|
27
|
+
|
|
28
|
+
//if the model is a TLLMModel, we need to convert it to a SupportedModels[SupportedProviders]
|
|
29
|
+
if (config.model && typeof config.model === 'object') {
|
|
30
|
+
provider = (config.model as TLLMModel).provider as SupportedProviders;
|
|
31
|
+
config.model = (config.model as TLLMModel).modelId;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return new supportedProviders[provider as SupportedProviders].embedder(config);
|
|
25
35
|
}
|
|
26
36
|
}
|