@smythos/sre 1.7.18 → 1.7.40
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 +120 -82
- package/dist/index.js.map +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 +37 -0
- package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +30 -0
- package/dist/types/Components/RAG/DataSourceIndexer.class.d.ts +14 -0
- package/dist/types/Components/RAG/DataSourceLookup.class.d.ts +36 -0
- package/dist/types/Components/index.d.ts +3 -3
- package/dist/types/helpers/Conversation.helper.d.ts +3 -0
- package/dist/types/index.d.ts +3 -3
- 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.inference.d.ts +10 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +4 -2
- 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/ManagedVault.service/connectors/SecretManagerManagedVault.d.ts +10 -0
- package/dist/types/subsystems/Security/Vault.service/connectors/SecretsManager.class.d.ts +6 -2
- package/dist/types/types/LLM.types.d.ts +2 -0
- package/dist/types/types/VectorDB.types.d.ts +4 -0
- package/dist/types/utils/array.utils.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/Component.class.ts +14 -1
- package/src/Components/DataSourceIndexer.class.ts +148 -34
- package/src/Components/GenAILLM.class.ts +21 -11
- package/src/Components/RAG/DataSourceCleaner.class.ts +178 -0
- package/src/Components/RAG/DataSourceComponent.class.ts +111 -0
- package/src/Components/RAG/DataSourceIndexer.class.ts +254 -0
- package/src/Components/{DataSourceLookup.class.ts → RAG/DataSourceLookup.class.ts} +92 -3
- package/src/Components/ServerlessCode.class.ts +1 -4
- package/src/Components/index.ts +3 -3
- package/src/helpers/AWSLambdaCode.helper.ts +40 -45
- package/src/helpers/Conversation.helper.ts +14 -10
- package/src/helpers/S3Cache.helper.ts +2 -1
- package/src/index.ts +212 -212
- package/src/index.ts.bak +212 -212
- package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +3 -1
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +145 -19
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +56 -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 +18 -0
- package/src/subsystems/LLMManager/LLM.inference.ts +63 -47
- 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 +105 -23
- 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/xAI.class.ts +9 -8
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +126 -28
- package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +38 -6
- package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -3
- package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +111 -48
- package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +41 -66
- package/src/types/LLM.types.ts +5 -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/string.utils.ts +3 -192
- package/src/Components/DataSourceCleaner.class.ts +0 -92
|
@@ -8,6 +8,9 @@ import { SmythFS } from '@sre/IO/Storage.service/SmythFS.class';
|
|
|
8
8
|
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
9
9
|
|
|
10
10
|
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
11
|
+
import { TEmbeddings } from '@sre/IO/VectorDB.service/embed/BaseEmbedding';
|
|
12
|
+
import { EmbeddingsFactory, SupportedModels } from '@sre/IO/VectorDB.service/embed';
|
|
13
|
+
import { getLLMCredentials } from '@sre/LLMManager/LLM.service/LLMCredentials.helper';
|
|
11
14
|
|
|
12
15
|
export class DataSourceIndexer extends Component {
|
|
13
16
|
private MAX_ALLOWED_URLS_PER_INPUT = 20;
|
|
@@ -16,6 +19,9 @@ export class DataSourceIndexer extends Component {
|
|
|
16
19
|
id: Joi.string().custom(validateCharacterSet, 'id custom validation').allow('').label('source identifier'),
|
|
17
20
|
name: Joi.string().max(50).allow('').label('label'),
|
|
18
21
|
metadata: Joi.string().allow(null).allow('').max(10000).label('metadata'),
|
|
22
|
+
chunkSize: Joi.number().optional(),
|
|
23
|
+
chunkOverlap: Joi.number().optional(),
|
|
24
|
+
version: Joi.string().valid('v1', 'v2').default('v1'),
|
|
19
25
|
});
|
|
20
26
|
constructor() {
|
|
21
27
|
super();
|
|
@@ -23,6 +29,18 @@ export class DataSourceIndexer extends Component {
|
|
|
23
29
|
init() {}
|
|
24
30
|
async process(input, config, agent: Agent) {
|
|
25
31
|
await super.process(input, config, agent);
|
|
32
|
+
|
|
33
|
+
let response: any = null;
|
|
34
|
+
if (!config.data.version || config.data.version === 'v1') {
|
|
35
|
+
response = await this.processV1(input, config, agent);
|
|
36
|
+
} else if (config.data.version === 'v2') {
|
|
37
|
+
response = await this.processV2(input, config, agent);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async processV1(input, config, agent: Agent) {
|
|
26
44
|
const teamId = agent.teamId;
|
|
27
45
|
const agentId = agent.id;
|
|
28
46
|
let debugOutput = agent.agentRuntime?.debug ? '== Source Indexer Log ==\n' : null;
|
|
@@ -72,47 +90,13 @@ export class DataSourceIndexer extends Component {
|
|
|
72
90
|
throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
|
|
73
91
|
}
|
|
74
92
|
|
|
75
|
-
// check if the datasource already exists with the same id
|
|
76
|
-
// await this.checkForRecordDuplicate(dsId, token);
|
|
77
|
-
|
|
78
93
|
let indexRes: any = null;
|
|
79
94
|
let parsedUrlArray: string[] | null = null;
|
|
80
|
-
|
|
81
|
-
//! DISABLE URL ARRAY PARSING FOR NOW UNTIL WE HAVE A GOOD WAY TO HANDLE BULK INDEXING
|
|
82
|
-
// if ((parsedUrlArray = parseUrlArray(inputSchema.value.Source))) {
|
|
83
|
-
// debugOutput += `STEP: Parsing input as url array\n\n`;
|
|
84
|
-
// if (parsedUrlArray.length > this.MAX_ALLOWED_URLS_PER_INPUT) {
|
|
85
|
-
// throw new Error(`Too many urls in input. Max allowed: ${this.MAX_ALLOWED_URLS_PER_INPUT}`);
|
|
86
|
-
// }
|
|
87
|
-
|
|
88
|
-
// for (let url of parsedUrlArray) {
|
|
89
|
-
// indexRes = await this.addDSFromUrl({
|
|
90
|
-
// teamId,
|
|
91
|
-
// namespaceId,
|
|
92
|
-
// dsId, // WILL OVERRIDE EACH OTHER
|
|
93
|
-
// type: detectURLSourceType(url),
|
|
94
|
-
// url,
|
|
95
|
-
// name: _config.name || 'Untitled',
|
|
96
|
-
// });
|
|
97
|
-
|
|
98
|
-
// debugOutput += `STEP: Created datasource for url: ${url}\n\n`;
|
|
99
|
-
// }
|
|
100
|
-
// } else
|
|
101
|
-
|
|
102
95
|
const dsId = DataSourceIndexer.genDsId(providedId, teamId, namespaceId);
|
|
103
96
|
|
|
104
97
|
if (isUrl(inputSchema.value.Source)) {
|
|
105
98
|
debugOutput += `STEP: Parsing input as url\n\n`;
|
|
106
99
|
throw new Error('URLs are not supported yet');
|
|
107
|
-
// indexRes = await this.addDSFromUrl({
|
|
108
|
-
// teamId,
|
|
109
|
-
// namespaceId,
|
|
110
|
-
// dsId,
|
|
111
|
-
// type: detectURLSourceType(inputSchema.value.Source),
|
|
112
|
-
// url: inputSchema.value.Source,
|
|
113
|
-
// name: _config.name || 'Untitled',
|
|
114
|
-
// metadata: _config.metadata || null,
|
|
115
|
-
// });
|
|
116
100
|
} else {
|
|
117
101
|
debugOutput += `STEP: Parsing input as text\n\n`;
|
|
118
102
|
indexRes = await this.addDSFromText({
|
|
@@ -144,6 +128,136 @@ export class DataSourceIndexer extends Component {
|
|
|
144
128
|
}
|
|
145
129
|
}
|
|
146
130
|
|
|
131
|
+
private async processV2(input, config, agent: Agent) {
|
|
132
|
+
const teamId = agent.teamId;
|
|
133
|
+
const agentId = agent.id;
|
|
134
|
+
let debugOutput = agent.agentRuntime?.debug ? '== Source Indexer Log ==\n' : null;
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const _config = {
|
|
138
|
+
...config.data,
|
|
139
|
+
name: TemplateString(config.data.name).parse(input).result,
|
|
140
|
+
id: TemplateString(config.data.id).parse(input).result,
|
|
141
|
+
metadata: TemplateString(config.data.metadata).parse(input).result,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const outputs = {};
|
|
145
|
+
for (let con of config.outputs) {
|
|
146
|
+
if (con.default) continue;
|
|
147
|
+
outputs[con.name] = con?.description ? `<${con?.description}>` : '';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// we try to get the namespace without the prefix teamId, if not exist, we use the full namespace id
|
|
151
|
+
const namespaceLabel = _config.namespace.split('_').slice(1).join('_') || _config.namespace;
|
|
152
|
+
const namespaceId = _config.namespace;
|
|
153
|
+
debugOutput += `[Selected namespace] \n${namespaceLabel}\n\n`;
|
|
154
|
+
|
|
155
|
+
// resolve the ns record, if not exist, throw an error (new in v2)
|
|
156
|
+
// then we also need to resolve the credentials
|
|
157
|
+
const nkvConnector = ConnectorService.getNKVConnector();
|
|
158
|
+
const nkvClient = nkvConnector.requester(AccessCandidate.team(teamId));
|
|
159
|
+
const rawNsRecord = await nkvClient.get(`vectorDB:namespaces`, namespaceId);
|
|
160
|
+
|
|
161
|
+
if (!rawNsRecord) {
|
|
162
|
+
return {
|
|
163
|
+
_debug: debugOutput,
|
|
164
|
+
_error: `Namespace ${namespaceLabel} does not exist`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// const { credentialId, embeddings: embeddingsOptions } = JSON.parse(rawNsRecord.toString());
|
|
169
|
+
const namespaceRecord = JSON.parse(rawNsRecord.toString());
|
|
170
|
+
const accountConnector = ConnectorService.getAccountConnector();
|
|
171
|
+
const accountClient = accountConnector.requester(AccessCandidate.team(teamId));
|
|
172
|
+
const rawCredRecord = await accountClient.getTeamSetting(namespaceRecord.credentialId, 'vector_db_creds');
|
|
173
|
+
if (!rawCredRecord) {
|
|
174
|
+
throw new Error(`Credential ${namespaceRecord.credentialId} does not exist`);
|
|
175
|
+
}
|
|
176
|
+
const credRecord = JSON.parse(rawCredRecord);
|
|
177
|
+
await Promise.all(
|
|
178
|
+
Object.keys(credRecord.credentials).map(async (key) => {
|
|
179
|
+
if (typeof credRecord.credentials[key] !== 'string') return;
|
|
180
|
+
credRecord.credentials[key] = await TemplateString(credRecord.credentials[key]).parseTeamKeysAsync(teamId).asyncResult;
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const vecDbConnector = ConnectorService.getVectorDBConnector(credRecord.provider).instance({
|
|
185
|
+
credentials: credRecord.credentials,
|
|
186
|
+
embeddings: await this.transformEmbedding(namespaceRecord.embeddings, config.data, teamId),
|
|
187
|
+
});
|
|
188
|
+
const vecDbClient = vecDbConnector.requester(AccessCandidate.team(teamId));
|
|
189
|
+
|
|
190
|
+
const inputSchema = this.validateInput(input);
|
|
191
|
+
if (inputSchema.error) {
|
|
192
|
+
throw new Error(`Input validation error: ${inputSchema.error}\n EXITING...`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const providedId = _config.id;
|
|
196
|
+
// const isAutoId = _config.isAutoId;
|
|
197
|
+
const idRegex = /^[a-zA-Z0-9\-\_\.]+$/;
|
|
198
|
+
|
|
199
|
+
if (!providedId) {
|
|
200
|
+
// Assign a new ID if it's set to auto-generate or not provided
|
|
201
|
+
// _config.id = crypto.randomBytes(16).toString('hex');
|
|
202
|
+
throw new Error(`Id is required`);
|
|
203
|
+
} else if (!idRegex.test(providedId)) {
|
|
204
|
+
// Validate the provided ID if it's not auto-generated
|
|
205
|
+
throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const dsId = DataSourceIndexer.genDsId(providedId, teamId, namespaceLabel);
|
|
209
|
+
|
|
210
|
+
debugOutput += `STEP: Parsing input as text\n\n`;
|
|
211
|
+
|
|
212
|
+
const response = await vecDbClient.createDatasource(namespaceLabel, {
|
|
213
|
+
text: inputSchema.value.Source,
|
|
214
|
+
metadata: _config.metadata || null,
|
|
215
|
+
id: dsId,
|
|
216
|
+
label: _config.name || 'Untitled',
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
debugOutput += `Created datasource successfully\n\n`;
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
_debug: debugOutput,
|
|
223
|
+
Success: {
|
|
224
|
+
result: response || true,
|
|
225
|
+
id: _config.id,
|
|
226
|
+
},
|
|
227
|
+
// _error,
|
|
228
|
+
};
|
|
229
|
+
} catch (err: any) {
|
|
230
|
+
debugOutput += `Error: ${err?.message || "Couldn't index data source"}\n\n`;
|
|
231
|
+
return {
|
|
232
|
+
_debug: debugOutput,
|
|
233
|
+
_error: err?.message || "Couldn't index data source",
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async transformEmbedding(embedding: { dimensions: string; modelId: string }, data: any, teamId: string): Promise<TEmbeddings> {
|
|
239
|
+
// we need to take this and return a proper TEmbeddings object
|
|
240
|
+
|
|
241
|
+
const provider = EmbeddingsFactory.getProviderByModel(embedding.modelId as any);
|
|
242
|
+
|
|
243
|
+
// based on the provider, we should be able to retreive the correct credentials
|
|
244
|
+
const modelsProvider = ConnectorService.getModelsProviderConnector();
|
|
245
|
+
const modelProviderCandidate = modelsProvider.requester(AccessCandidate.team(teamId));
|
|
246
|
+
const modelInfo = await modelProviderCandidate.getModelInfo(embedding.modelId);
|
|
247
|
+
|
|
248
|
+
const llmCreds = await getLLMCredentials(AccessCandidate.team(teamId), modelInfo);
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
provider,
|
|
252
|
+
model: embedding.modelId,
|
|
253
|
+
credentials: llmCreds,
|
|
254
|
+
params: {
|
|
255
|
+
dimensions: parseInt(embedding.dimensions),
|
|
256
|
+
chunkSize: data.chunkSize,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
147
261
|
validateInput(input: any) {
|
|
148
262
|
return Joi.object({
|
|
149
263
|
Source: Joi.any().required(),
|
|
@@ -528,26 +528,36 @@ export class GenAILLM extends Component {
|
|
|
528
528
|
response = await contentPromise.catch((error) => {
|
|
529
529
|
return { error: error.message || error };
|
|
530
530
|
});
|
|
531
|
-
|
|
531
|
+
|
|
532
|
+
// #region Handle Response Errors
|
|
533
|
+
if (response?.error) {
|
|
534
|
+
const error = response?.error + ' ' + (response?.details || '');
|
|
535
|
+
logger.error(` LLM Error=`, error);
|
|
536
|
+
|
|
537
|
+
return { Output: response?.data, _error: error, _debug: logger.output };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const emptyResponseErrorMsg =
|
|
541
|
+
"Empty response. This is usually due to output token limit reached. Please try again with a higher 'Maximum Output Tokens'.";
|
|
542
|
+
|
|
543
|
+
// If the finish reason is not "stop", it means the model stopped before completing the response.
|
|
532
544
|
if (finishReason !== 'stop') {
|
|
545
|
+
let errMsg = `The model stopped before completing the response.
|
|
546
|
+
\nReason: ${finishReason}.
|
|
547
|
+
\n${!response ? emptyResponseErrorMsg : ''}`;
|
|
548
|
+
|
|
533
549
|
return {
|
|
534
550
|
Reply: response,
|
|
535
|
-
_error:
|
|
551
|
+
_error: errMsg,
|
|
536
552
|
_debug: logger.output,
|
|
537
553
|
};
|
|
538
554
|
}
|
|
539
555
|
|
|
540
|
-
//
|
|
556
|
+
// If the finish reason is "stop" but there is still no response, it is usually caused by reaching the output token limit.
|
|
541
557
|
if (!response) {
|
|
542
|
-
return { _error:
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
if (response?.error) {
|
|
546
|
-
const error = response?.error + ' ' + (response?.details || '');
|
|
547
|
-
logger.error(` LLM Error=`, error);
|
|
548
|
-
|
|
549
|
-
return { Output: response?.data, _error: error, _debug: logger.output };
|
|
558
|
+
return { _error: emptyResponseErrorMsg, _debug: logger.output };
|
|
550
559
|
}
|
|
560
|
+
// #endregion
|
|
551
561
|
|
|
552
562
|
const Reply = llmInference.connector.postProcess(response);
|
|
553
563
|
if (Reply.error) {
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { IAgent as Agent } from '@sre/types/Agent.types';
|
|
2
|
+
import { Component } from '../Component.class';
|
|
3
|
+
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
4
|
+
import Joi from 'joi';
|
|
5
|
+
import { validateCharacterSet } from '../../utils';
|
|
6
|
+
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
7
|
+
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
8
|
+
|
|
9
|
+
import { DataSourceIndexer } from './DataSourceIndexer.class';
|
|
10
|
+
import { DataSourceComponent } from './DataSourceComponent.class';
|
|
11
|
+
import { VectorDBConnector } from '@sre/IO/VectorDB.service/VectorDBConnector';
|
|
12
|
+
|
|
13
|
+
export class DataSourceCleaner extends DataSourceComponent {
|
|
14
|
+
protected configSchema = Joi.object({
|
|
15
|
+
namespaceId: Joi.string().max(50).allow('').label('namespace'),
|
|
16
|
+
id: Joi.string().custom(validateCharacterSet, 'custom validation characterSet').allow('').label('source identifier'),
|
|
17
|
+
version: Joi.string().valid('v1', 'v2').default('v1'),
|
|
18
|
+
});
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
}
|
|
22
|
+
init() {}
|
|
23
|
+
|
|
24
|
+
async process(input, config, agent: Agent) {
|
|
25
|
+
await super.process(input, config, agent);
|
|
26
|
+
if (!config.data.version || config.data.version === 'v1') {
|
|
27
|
+
return await this.processV1(input, config, agent);
|
|
28
|
+
} else if (config.data.version === 'v2') {
|
|
29
|
+
return await this.processV2(input, config, agent);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async processV1(input, config, agent: Agent) {
|
|
33
|
+
const teamId = agent.teamId;
|
|
34
|
+
const agentId = agent.id;
|
|
35
|
+
let debugOutput = agent.agentRuntime?.debug ? '== Source Indexer Log ==\n' : null;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const configSchema = this.validateConfigData(config.data);
|
|
39
|
+
if (configSchema.error) {
|
|
40
|
+
throw new Error(`Config data validation error: ${configSchema.error}\n EXITING...`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const outputs = {};
|
|
44
|
+
for (let con of config.outputs) {
|
|
45
|
+
if (con.default) continue;
|
|
46
|
+
outputs[con.name] = con?.description ? `<${con?.description}>` : '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const inputSchema = this.validateInput(input);
|
|
50
|
+
if (inputSchema.error) {
|
|
51
|
+
throw new Error(`Input validation error: ${inputSchema.error}\n EXITING...`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// const namespaceId = configSchema.value.namespaceId.split('_')?.slice(1).join('_') || configSchema.value.namespaceId;
|
|
55
|
+
const namespaceId = /^c[a-z0-9]{24}.+$/.test(configSchema.value.namespaceId)
|
|
56
|
+
? configSchema.value.namespaceId.split('_').slice(1).join('_')
|
|
57
|
+
: configSchema.value.namespaceId;
|
|
58
|
+
|
|
59
|
+
let vectorDbConnector = ConnectorService.getVectorDBConnector();
|
|
60
|
+
|
|
61
|
+
let existingnamespace = await vectorDbConnector.requester(AccessCandidate.team(teamId)).namespaceExists(namespaceId);
|
|
62
|
+
if (!existingnamespace) {
|
|
63
|
+
throw new Error(`Namespace ${namespaceId} does not exist`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const providedId = TemplateString(config.data.id).parse(input).result;
|
|
67
|
+
const idRegex = /^[a-zA-Z0-9\-\_\.]+$/;
|
|
68
|
+
if (!idRegex.test(providedId)) {
|
|
69
|
+
throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
|
|
70
|
+
}
|
|
71
|
+
debugOutput += `Searching for data source with id: ${providedId}\n`;
|
|
72
|
+
|
|
73
|
+
const dsId = DataSourceIndexer.normalizeDsId(providedId, teamId, namespaceId);
|
|
74
|
+
|
|
75
|
+
await vectorDbConnector.requester(AccessCandidate.team(teamId)).deleteDatasource(namespaceId, dsId);
|
|
76
|
+
|
|
77
|
+
debugOutput += `Deleted data source with id: ${providedId}\n`;
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
_debug: debugOutput,
|
|
81
|
+
Success: true,
|
|
82
|
+
// _error,
|
|
83
|
+
};
|
|
84
|
+
} catch (err: any) {
|
|
85
|
+
debugOutput += `Failed to delete data source: \n Error: ${err?.message}\n`;
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
_debug: debugOutput,
|
|
89
|
+
_error: err?.message || "Couldn't delete data source",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async processV2(input, config, agent: Agent) {
|
|
95
|
+
const teamId = agent.teamId;
|
|
96
|
+
const agentId = agent.id;
|
|
97
|
+
let debugOutput = agent.agentRuntime?.debug ? '== Source Indexer Log ==\n' : null;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const configSchema = this.validateConfigData(config.data);
|
|
101
|
+
if (configSchema.error) {
|
|
102
|
+
throw new Error(`Config data validation error: ${configSchema.error}\n EXITING...`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const outputs = {};
|
|
106
|
+
for (let con of config.outputs) {
|
|
107
|
+
if (con.default) continue;
|
|
108
|
+
outputs[con.name] = con?.description ? `<${con?.description}>` : '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const inputSchema = this.validateInput(input);
|
|
112
|
+
if (inputSchema.error) {
|
|
113
|
+
throw new Error(`Input validation error: ${inputSchema.error}\n EXITING...`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// const namespaceId = configSchema.value.namespaceId.split('_')?.slice(1).join('_') || configSchema.value.namespaceId;
|
|
117
|
+
const namespaceLabel = /^c[a-z0-9]{24}.+$/.test(configSchema.value.namespaceId)
|
|
118
|
+
? configSchema.value.namespaceId.split('_').slice(1).join('_')
|
|
119
|
+
: configSchema.value.namespaceId;
|
|
120
|
+
const namespaceId = configSchema.value.namespaceId;
|
|
121
|
+
|
|
122
|
+
let vecDbConnector: VectorDBConnector = null;
|
|
123
|
+
try {
|
|
124
|
+
vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
|
|
125
|
+
} catch (err: any) {
|
|
126
|
+
debugOutput += `Error: ${err?.message || "Couldn't get vector database connector"}\n\n`;
|
|
127
|
+
return {
|
|
128
|
+
_debug: debugOutput,
|
|
129
|
+
_error: err?.message || "Couldn't get vector database connector",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// let existingnamespace = await vecDbConnector.requester(AccessCandidate.team(teamId)).namespaceExists(namespaceId);
|
|
134
|
+
// if (!existingnamespace) {
|
|
135
|
+
// throw new Error(`Namespace ${namespaceId} does not exist`);
|
|
136
|
+
// }
|
|
137
|
+
|
|
138
|
+
const providedId = TemplateString(config.data.id).parse(input).result;
|
|
139
|
+
const idRegex = /^[a-zA-Z0-9\-\_\.]+$/;
|
|
140
|
+
if (!idRegex.test(providedId)) {
|
|
141
|
+
throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
|
|
142
|
+
}
|
|
143
|
+
debugOutput += `Searching for data source with id: ${providedId}\n`;
|
|
144
|
+
|
|
145
|
+
const dsId = DataSourceComponent.normalizeDsId(providedId, teamId, namespaceLabel);
|
|
146
|
+
|
|
147
|
+
await vecDbConnector.requester(AccessCandidate.team(teamId)).deleteDatasource(namespaceLabel, dsId);
|
|
148
|
+
|
|
149
|
+
debugOutput += `Deleted data source with id: ${providedId}\n`;
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
_debug: debugOutput,
|
|
153
|
+
Success: true,
|
|
154
|
+
// _error,
|
|
155
|
+
};
|
|
156
|
+
} catch (err: any) {
|
|
157
|
+
debugOutput += `Failed to delete data source: \n Error: ${err?.message}\n`;
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
_debug: debugOutput,
|
|
161
|
+
_error: err?.message || "Couldn't delete data source",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
validateInput(input: any) {
|
|
167
|
+
return Joi.object({}).unknown(true).validate(input);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
validateConfigData(data: any) {
|
|
171
|
+
return Joi.object({
|
|
172
|
+
namespaceId: Joi.string().required(),
|
|
173
|
+
id: Joi.string().optional().allow('').allow(null),
|
|
174
|
+
})
|
|
175
|
+
.unknown(true)
|
|
176
|
+
.validate(data);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Base class for the RAG components that handle the shared logic for the RAG components
|
|
2
|
+
import { EmbeddingsFactory } from '@sre/IO/VectorDB.service/embed';
|
|
3
|
+
import { Component } from '../Component.class';
|
|
4
|
+
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
5
|
+
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
6
|
+
import { getLLMCredentials } from '@sre/LLMManager/LLM.service/LLMCredentials.helper';
|
|
7
|
+
import { TEmbeddings } from '@sre/IO/VectorDB.service/embed/BaseEmbedding';
|
|
8
|
+
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
9
|
+
import { VectorDBConnector } from '@sre/IO/VectorDB.service/VectorDBConnector';
|
|
10
|
+
import { TLLMCredentials } from '@sre/types/LLM.types';
|
|
11
|
+
|
|
12
|
+
export type NsRecord = {
|
|
13
|
+
credentialId: string;
|
|
14
|
+
embeddings: { dimensions: string; modelId: string };
|
|
15
|
+
label: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export enum TDataSourceCompErrorCodes {
|
|
20
|
+
NAMESPACE_NOT_FOUND = 1,
|
|
21
|
+
CREDENTIAL_NOT_FOUND = 2,
|
|
22
|
+
EMBEDDING_CONFIG_ERROR = 3,
|
|
23
|
+
}
|
|
24
|
+
export class DataSourceCompError extends Error {
|
|
25
|
+
public code: TDataSourceCompErrorCodes;
|
|
26
|
+
constructor(message: string, code: TDataSourceCompErrorCodes) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'DataSourceCompError';
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class DataSourceComponent extends Component {
|
|
33
|
+
constructor() {
|
|
34
|
+
super();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public async resolveVectorDbConnector(namespace: string | NsRecord, teamId: string): Promise<VectorDBConnector> {
|
|
38
|
+
// resolve the ns record, if not exist, throw an error (new in v2)
|
|
39
|
+
// then we also need to resolve the credentials
|
|
40
|
+
let namespaceRecord = namespace as NsRecord;
|
|
41
|
+
|
|
42
|
+
if (typeof namespace === 'string') {
|
|
43
|
+
// if it's a string, we need to get the namespace record from the NKV
|
|
44
|
+
const nkvConnector = ConnectorService.getNKVConnector();
|
|
45
|
+
const nkvClient = nkvConnector.requester(AccessCandidate.team(teamId));
|
|
46
|
+
const rawNsRecord = await nkvClient.get(`vectorDB:namespaces`, namespace);
|
|
47
|
+
|
|
48
|
+
if (!rawNsRecord) {
|
|
49
|
+
throw new DataSourceCompError(`Namespace ${namespace} does not exist`, TDataSourceCompErrorCodes.NAMESPACE_NOT_FOUND);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// const { credentialId, embeddings: embeddingsOptions } = JSON.parse(rawNsRecord.toString());
|
|
53
|
+
namespaceRecord = JSON.parse(rawNsRecord.toString()) as NsRecord;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const accountConnector = ConnectorService.getAccountConnector();
|
|
57
|
+
const accountClient = accountConnector.requester(AccessCandidate.team(teamId));
|
|
58
|
+
const rawCredRecord = await accountClient.getTeamSetting(namespaceRecord.credentialId, 'vector_db_creds');
|
|
59
|
+
if (!rawCredRecord) {
|
|
60
|
+
throw new DataSourceCompError(
|
|
61
|
+
`Credential ${namespaceRecord.credentialId} does not exist`,
|
|
62
|
+
TDataSourceCompErrorCodes.CREDENTIAL_NOT_FOUND
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const credRecord = JSON.parse(rawCredRecord);
|
|
66
|
+
await Promise.all(
|
|
67
|
+
Object.keys(credRecord.credentials).map(async (key) => {
|
|
68
|
+
if (typeof credRecord.credentials[key] !== 'string') return;
|
|
69
|
+
credRecord.credentials[key] = await TemplateString(credRecord.credentials[key]).parseTeamKeysAsync(teamId).asyncResult;
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const vecDbConnector = ConnectorService.getVectorDBConnector(credRecord.provider).instance({
|
|
74
|
+
credentials: credRecord.credentials,
|
|
75
|
+
embeddings: await this.buildEmbeddingConfig(namespaceRecord.embeddings, teamId),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return vecDbConnector;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public async buildEmbeddingConfig(embedding: { dimensions: string; modelId: string }, teamId: string): Promise<TEmbeddings> {
|
|
82
|
+
// we need to take this and return a proper TEmbeddings object
|
|
83
|
+
|
|
84
|
+
const provider = EmbeddingsFactory.getProviderByModel(embedding.modelId as any);
|
|
85
|
+
|
|
86
|
+
// based on the provider, we should be able to retreive the correct credentials
|
|
87
|
+
const modelsProvider = ConnectorService.getModelsProviderConnector();
|
|
88
|
+
const modelProviderCandidate = modelsProvider.requester(AccessCandidate.team(teamId));
|
|
89
|
+
// const modelInfo = await modelProviderCandidate.getModelInfo(embedding.modelId);
|
|
90
|
+
|
|
91
|
+
const llmCreds = await getLLMCredentials(AccessCandidate.team(teamId), {
|
|
92
|
+
provider,
|
|
93
|
+
modelId: embedding.modelId,
|
|
94
|
+
credentials: [TLLMCredentials.Vault, TLLMCredentials.Internal],
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
provider,
|
|
99
|
+
model: embedding.modelId,
|
|
100
|
+
credentials: llmCreds,
|
|
101
|
+
dimensions: parseInt(embedding.dimensions), // pass both for backwards compatibility
|
|
102
|
+
params: {
|
|
103
|
+
dimensions: parseInt(embedding.dimensions), // pass both for backwards compatibility
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public static normalizeDsId(providedId: string, teamId: string, namespaceId: string) {
|
|
109
|
+
return `${teamId}::${namespaceId}::${providedId}`;
|
|
110
|
+
}
|
|
111
|
+
}
|