@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.
Files changed (69) hide show
  1. package/dist/index.js +120 -82
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/DataSourceIndexer.class.d.ts +4 -12
  4. package/dist/types/Components/GenAILLM.class.d.ts +5 -5
  5. package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +37 -0
  6. package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +30 -0
  7. package/dist/types/Components/RAG/DataSourceIndexer.class.d.ts +14 -0
  8. package/dist/types/Components/RAG/DataSourceLookup.class.d.ts +36 -0
  9. package/dist/types/Components/index.d.ts +3 -3
  10. package/dist/types/helpers/Conversation.helper.d.ts +3 -0
  11. package/dist/types/index.d.ts +3 -3
  12. package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +1 -0
  13. package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +11 -4
  14. package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +5 -0
  15. package/dist/types/subsystems/LLMManager/LLM.inference.d.ts +10 -3
  16. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +4 -2
  17. package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.d.ts +35 -0
  18. package/dist/types/subsystems/Security/Account.service/AccountConnector.d.ts +2 -2
  19. package/dist/types/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.d.ts +10 -0
  20. package/dist/types/subsystems/Security/Vault.service/connectors/SecretsManager.class.d.ts +6 -2
  21. package/dist/types/types/LLM.types.d.ts +2 -0
  22. package/dist/types/types/VectorDB.types.d.ts +4 -0
  23. package/dist/types/utils/array.utils.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/Component.class.ts +14 -1
  28. package/src/Components/DataSourceIndexer.class.ts +148 -34
  29. package/src/Components/GenAILLM.class.ts +21 -11
  30. package/src/Components/RAG/DataSourceCleaner.class.ts +178 -0
  31. package/src/Components/RAG/DataSourceComponent.class.ts +111 -0
  32. package/src/Components/RAG/DataSourceIndexer.class.ts +254 -0
  33. package/src/Components/{DataSourceLookup.class.ts → RAG/DataSourceLookup.class.ts} +92 -3
  34. package/src/Components/ServerlessCode.class.ts +1 -4
  35. package/src/Components/index.ts +3 -3
  36. package/src/helpers/AWSLambdaCode.helper.ts +40 -45
  37. package/src/helpers/Conversation.helper.ts +14 -10
  38. package/src/helpers/S3Cache.helper.ts +2 -1
  39. package/src/index.ts +212 -212
  40. package/src/index.ts.bak +212 -212
  41. package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +3 -1
  42. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +145 -19
  43. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +56 -22
  44. package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -0
  45. package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +2 -1
  46. package/src/subsystems/IO/VectorDB.service/embed/index.ts +18 -0
  47. package/src/subsystems/LLMManager/LLM.inference.ts +63 -47
  48. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +35 -10
  49. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +12 -4
  50. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +4 -4
  51. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +105 -23
  52. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +17 -5
  53. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +18 -3
  54. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +14 -5
  55. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +6 -4
  56. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +5 -5
  57. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +8 -3
  58. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +9 -8
  59. package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +126 -28
  60. package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +38 -6
  61. package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -3
  62. package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +111 -48
  63. package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +41 -66
  64. package/src/types/LLM.types.ts +5 -0
  65. package/src/types/VectorDB.types.ts +4 -0
  66. package/src/utils/array.utils.ts +11 -0
  67. package/src/utils/base64.utils.ts +1 -1
  68. package/src/utils/string.utils.ts +3 -192
  69. package/src/Components/DataSourceCleaner.class.ts +0 -92
@@ -0,0 +1,254 @@
1
+ import { IAgent as Agent } from '@sre/types/Agent.types';
2
+ import { DataSourceComponent } from './DataSourceComponent.class';
3
+ import Joi from 'joi';
4
+ import { validateCharacterSet } from '@sre/utils/validation.utils';
5
+ import { TemplateString } from '@sre/helpers/TemplateString.helper';
6
+ import { isUrl, detectURLSourceType } from '../../utils';
7
+ import { SmythFS } from '@sre/IO/Storage.service/SmythFS.class';
8
+ import { ConnectorService } from '@sre/Core/ConnectorsService';
9
+
10
+ import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
11
+ import { TEmbeddings } from '@sre/IO/VectorDB.service/embed/BaseEmbedding';
12
+ import { VectorDBConnector } from '@sre/IO/VectorDB.service/VectorDBConnector';
13
+ import { JSONContentHelper } from '@sre/helpers/JsonContent.helper';
14
+
15
+ export class DataSourceIndexer extends DataSourceComponent {
16
+ private MAX_ALLOWED_URLS_PER_INPUT = 20;
17
+ protected configSchema = Joi.object({
18
+ namespace: Joi.string().max(50).allow(''),
19
+ id: Joi.string().custom(validateCharacterSet, 'id custom validation').allow('').label('source identifier'),
20
+ name: Joi.string().max(50).allow('').label('label'),
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'),
25
+ });
26
+ constructor() {
27
+ super();
28
+ }
29
+ init() {}
30
+ async process(input, config, agent: Agent) {
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) {
44
+ const teamId = agent.teamId;
45
+ const agentId = agent.id;
46
+ let debugOutput = agent.agentRuntime?.debug ? '== Source Indexer Log ==\n' : null;
47
+
48
+ try {
49
+ const _config = {
50
+ ...config.data,
51
+ name: TemplateString(config.data.name).parse(input).result,
52
+ id: TemplateString(config.data.id).parse(input).result,
53
+ metadata: TemplateString(config.data.metadata).parse(input).result,
54
+ };
55
+
56
+ const outputs = {};
57
+ for (let con of config.outputs) {
58
+ if (con.default) continue;
59
+ outputs[con.name] = con?.description ? `<${con?.description}>` : '';
60
+ }
61
+
62
+ const namespaceId = _config.namespace.split('_').slice(1).join('_') || _config.namespace;
63
+ debugOutput += `[Selected namespace id] \n${namespaceId}\n\n`;
64
+
65
+ const vectorDbConnector =
66
+ // (await vectorDBHelper.getTeamConnector(teamId)) ||
67
+ ConnectorService.getVectorDBConnector();
68
+ const nsExists = await vectorDbConnector.requester(AccessCandidate.team(teamId)).namespaceExists(namespaceId);
69
+
70
+ if (!nsExists) {
71
+ const newNs = await vectorDbConnector.requester(AccessCandidate.team(teamId)).createNamespace(namespaceId);
72
+ debugOutput += `[Created namespace] \n${newNs}\n\n`;
73
+ }
74
+
75
+ const inputSchema = this.validateInput(input);
76
+ if (inputSchema.error) {
77
+ throw new Error(`Input validation error: ${inputSchema.error}\n EXITING...`);
78
+ }
79
+
80
+ const providedId = _config.id;
81
+ // const isAutoId = _config.isAutoId;
82
+ const idRegex = /^[a-zA-Z0-9\-\_\.]+$/;
83
+
84
+ if (!providedId) {
85
+ // Assign a new ID if it's set to auto-generate or not provided
86
+ // _config.id = crypto.randomBytes(16).toString('hex');
87
+ throw new Error(`Id is required`);
88
+ } else if (!idRegex.test(providedId)) {
89
+ // Validate the provided ID if it's not auto-generated
90
+ throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
91
+ }
92
+
93
+ let indexRes: any = null;
94
+ let parsedUrlArray: string[] | null = null;
95
+ const dsId = DataSourceIndexer.normalizeDsId(providedId, teamId, namespaceId);
96
+
97
+ if (isUrl(inputSchema.value.Source)) {
98
+ debugOutput += `STEP: Parsing input as url\n\n`;
99
+ throw new Error('URLs are not supported yet');
100
+ } else {
101
+ debugOutput += `STEP: Parsing input as text\n\n`;
102
+ indexRes = await this.addDSFromText({
103
+ teamId,
104
+ namespaceId: namespaceId,
105
+ text: inputSchema.value.Source,
106
+ name: _config.name || 'Untitled',
107
+ metadata: _config.metadata || null,
108
+ sourceId: dsId,
109
+ });
110
+ }
111
+
112
+ debugOutput += `Created datasource successfully\n\n`;
113
+
114
+ return {
115
+ _debug: debugOutput,
116
+ Success: {
117
+ result: indexRes?.data?.dataSource || true,
118
+ id: _config.id,
119
+ },
120
+ // _error,
121
+ };
122
+ } catch (err: any) {
123
+ debugOutput += `Error: ${err?.message || "Couldn't index data source"}\n\n`;
124
+ return {
125
+ _debug: debugOutput,
126
+ _error: err?.message || "Couldn't index data source",
127
+ };
128
+ }
129
+ }
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 namespaceLabel = /^c[a-z0-9]{24}.+$/.test(_config.namespace) ? _config.namespace.split('_').slice(1).join('_') : _config.namespace;
153
+ const namespaceId = _config.namespace;
154
+ debugOutput += `[Selected namespace] \n${namespaceLabel}\n\n`;
155
+
156
+ let vecDbConnector: VectorDBConnector = null;
157
+ try {
158
+ vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
159
+ } catch (err: any) {
160
+ debugOutput += `Error: ${err?.message || "Couldn't get vector database connector"}\n\n`;
161
+ return {
162
+ _debug: debugOutput,
163
+ _error: err?.message || "Couldn't get vector database connector",
164
+ };
165
+ }
166
+ const vecDbClient = vecDbConnector.requester(AccessCandidate.team(teamId));
167
+
168
+ const inputSchema = this.validateInput(input);
169
+ if (inputSchema.error) {
170
+ throw new Error(`Input validation error: ${inputSchema.error}\n EXITING...`);
171
+ }
172
+
173
+ const providedId = _config.id;
174
+ // const isAutoId = _config.isAutoId;
175
+ const idRegex = /^[a-zA-Z0-9\-\_\.]+$/;
176
+
177
+ if (!providedId) {
178
+ // Assign a new ID if it's set to auto-generate or not provided
179
+ // _config.id = crypto.randomBytes(16).toString('hex');
180
+ throw new Error(`Id is required`);
181
+ } else if (!idRegex.test(providedId)) {
182
+ // Validate the provided ID if it's not auto-generated
183
+ throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
184
+ }
185
+
186
+ const dsId = DataSourceIndexer.normalizeDsId(providedId, teamId, namespaceLabel);
187
+
188
+ // check if the datasource already exists
189
+ const dsExists = await vecDbClient.getDatasource(namespaceLabel, dsId);
190
+ if (dsExists) {
191
+ debugOutput += `Datasource already exists\n\n`;
192
+ return {
193
+ _debug: debugOutput,
194
+ _error: `Datasource already exists`,
195
+ };
196
+ }
197
+
198
+ debugOutput += `STEP: Parsing input as text\n\n`;
199
+
200
+ const response = await vecDbClient.createDatasource(namespaceLabel, {
201
+ text: inputSchema.value.Source,
202
+ metadata: JSONContentHelper.create(_config.metadata).tryParse() || null,
203
+ id: dsId,
204
+ label: _config.name || 'Untitled',
205
+ chunkSize: _config.chunkSize ? parseInt(_config.chunkSize) : undefined,
206
+ chunkOverlap: _config.chunkOverlap ? parseInt(_config.chunkOverlap) : undefined,
207
+ });
208
+
209
+ debugOutput += `Created datasource successfully\n\n`;
210
+
211
+ return {
212
+ _debug: debugOutput,
213
+ Success: {
214
+ result: response || true,
215
+ id: _config.id,
216
+ },
217
+ // _error,
218
+ };
219
+ } catch (err: any) {
220
+ debugOutput += `Error: ${err?.message || "Couldn't index data source"}\n\n`;
221
+ return {
222
+ _debug: debugOutput,
223
+ _error: err?.message || "Couldn't index data source",
224
+ };
225
+ }
226
+ }
227
+
228
+ validateInput(input: any) {
229
+ return Joi.object({
230
+ Source: Joi.any().required(),
231
+ })
232
+ .unknown(true)
233
+ .validate(input);
234
+ }
235
+
236
+ private async addDSFromText({ teamId, sourceId, namespaceId, text, name, metadata }) {
237
+ let vectorDbConnector = ConnectorService.getVectorDBConnector();
238
+ // const isOnCustomStorage = await vectorDBHelper.isNamespaceOnCustomStorage(teamId, namespaceId);
239
+ // if (isOnCustomStorage) {
240
+ // const customTeamConnector = await vectorDBHelper.getTeamConnector(teamId);
241
+ // if (customTeamConnector) {
242
+ // vectorDbConnector = customTeamConnector;
243
+ // }
244
+ // }
245
+ const id = await vectorDbConnector.requester(AccessCandidate.team(teamId)).createDatasource(namespaceId, {
246
+ text,
247
+ metadata,
248
+ id: sourceId,
249
+ label: name,
250
+ });
251
+
252
+ return id;
253
+ }
254
+ }
@@ -1,12 +1,12 @@
1
1
  import Joi from 'joi';
2
- import { validateInteger } from '../utils';
2
+ import { validateInteger } from '../../utils';
3
3
  import { jsonrepair } from 'jsonrepair';
4
4
  import { TemplateString } from '@sre/helpers/TemplateString.helper';
5
5
  import { JSONContent } from '@sre/helpers/JsonContent.helper';
6
6
  import { ConnectorService } from '@sre/Core/ConnectorsService';
7
7
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
8
8
  import { IAgent as Agent } from '@sre/types/Agent.types';
9
- import { Component } from './Component.class';
9
+ import { DataSourceComponent } from './DataSourceComponent.class';
10
10
 
11
11
  // Note: LLMHelper renamed to LLMInference
12
12
  class LLMInference {
@@ -15,7 +15,7 @@ class LLMInference {
15
15
  }
16
16
  }
17
17
 
18
- export class DataSourceLookup extends Component {
18
+ export class DataSourceLookup extends DataSourceComponent {
19
19
  protected configSchema = Joi.object({
20
20
  topK: Joi.alternatives([Joi.string(), Joi.number()]) // Value is now a number; keep string fallback for backward compatibility.
21
21
 
@@ -31,13 +31,22 @@ export class DataSourceLookup extends Component {
31
31
  }),
32
32
  scoreThreshold: Joi.number().optional().label('Score Threshold'),
33
33
  includeScore: Joi.boolean().optional().label('Include Score'),
34
+ version: Joi.string().valid('v1', 'v2').default('v1'),
34
35
  });
35
36
  constructor() {
36
37
  super();
37
38
  }
38
39
  init() {}
40
+
39
41
  async process(input, config, agent: Agent) {
40
42
  await super.process(input, config, agent);
43
+ if (!config.data.version || config.data.version === 'v1') {
44
+ return await this.processV1(input, config, agent);
45
+ } else if (config.data.version === 'v2') {
46
+ return await this.processV2(input, config, agent);
47
+ }
48
+ }
49
+ async processV1(input, config, agent: Agent) {
41
50
  const componentId = config.id;
42
51
  const component = agent.components[componentId];
43
52
  const teamId = agent.teamId;
@@ -139,6 +148,86 @@ export class DataSourceLookup extends Component {
139
148
  };
140
149
  }
141
150
 
151
+ async processV2(input, config, agent: Agent) {
152
+ const teamId = agent.teamId;
153
+ let debugOutput = agent.agentRuntime?.debug ? '== Data Source Lookup Log ==\n' : null;
154
+
155
+ const outputs = {};
156
+ for (let con of config.outputs) {
157
+ if (con.default) continue;
158
+ outputs[con.name] = '';
159
+ }
160
+
161
+ const namespaceLabel = /^c[a-z0-9]{24}.+$/.test(config.data.namespace)
162
+ ? config.data.namespace.split('_').slice(1).join('_')
163
+ : config.data.namespace;
164
+ const namespaceId = config.data.namespace;
165
+ const model = config.data?.model || 'gpt-4o-mini';
166
+ const includeMetadata = config.data?.includeMetadata || false;
167
+
168
+ const scoreThreshold = config.data?.scoreThreshold || 0.001; // Use low score (0.001) to return most results for backward compatibility
169
+ const includeScore = config.data?.includeScore || false;
170
+
171
+ const _input = typeof input.Query === 'string' ? input.Query : JSON.stringify(input.Query);
172
+
173
+ const topK = Math.max(config.data?.topK || 50, 50);
174
+
175
+ // let vectorDbConnector = ConnectorService.getVectorDBConnector();
176
+ // let existingNs = await vectorDbConnector.requester(AccessCandidate.team(teamId)).namespaceExists(namespaceLabel);
177
+
178
+ const vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
179
+
180
+ let results: string[] | { content: string; metadata: any; score?: number }[];
181
+ let _error;
182
+ try {
183
+ const response = await vecDbConnector
184
+ .requester(AccessCandidate.team(teamId))
185
+ .search(namespaceLabel, _input, { topK, includeMetadata: true });
186
+
187
+ results = response.slice(0, config.data.topK).map((result) => ({
188
+ content: result.text,
189
+ metadata: result.metadata,
190
+ score: result.score, // use a very low score to return
191
+ }));
192
+
193
+ results = results.filter((result) => result.score >= scoreThreshold);
194
+
195
+ // Transform results based on inclusion flags
196
+ results = results.map((result) => {
197
+ const transformedResult: any = {
198
+ content: result.content,
199
+ };
200
+
201
+ if (includeMetadata) {
202
+ // legacy user-specific metadata key [result.metadata?.metadata]
203
+ transformedResult.metadata = this.parseMetadata(result.metadata || result.metadata?.metadata);
204
+ }
205
+
206
+ if (includeScore) {
207
+ transformedResult.score = result.score;
208
+ }
209
+
210
+ // If neither metadata nor score is included, return just the content string
211
+ return includeMetadata || includeScore ? transformedResult : result.content;
212
+ });
213
+
214
+ debugOutput += `[Results] \nLoaded ${results.length} results from namespace: ${namespaceLabel}\n\n`;
215
+ } catch (error) {
216
+ debugOutput += `Error: ${error instanceof Error ? error.message : error.toString()}\n\n`;
217
+ _error = error instanceof Error ? error.message : error.toString();
218
+ }
219
+
220
+ const totalLength = JSON.stringify(results).length;
221
+ debugOutput += `[Total Length] \n${totalLength}\n\n`;
222
+
223
+ return {
224
+ Results: results,
225
+ _error,
226
+ _debug: debugOutput,
227
+ //_debug: `Query: ${_input}. \nTotal Length = ${totalLength} \nResults: ${JSON.stringify(results)}`,
228
+ };
229
+ }
230
+
142
231
  // private async checkIfTeamOwnsNamespace(teamId: string, namespaceId: string, token: string) {
143
232
  // try {
144
233
  // const res = await SmythAPIHelper.fromAuth({ token }).mwSysAPI.get(`/vectors/namespaces/${namespaceId}`);
@@ -41,10 +41,7 @@ export class ServerlessCode extends Component {
41
41
  let codeInputs = {};
42
42
 
43
43
  for (let field of componentInputs) {
44
- // Parse using input values first, then agent variables.
45
- // This correctly resolves cases where input values reference agent variables with the same name.
46
- // Example: agent variables { user_id: "123" }, input { user_id: "{{user_id}}" }.
47
- const inputValue = TemplateString(input[field.name]).parse(input).parse(agent.agentVariables).result;
44
+ const inputValue = TemplateString(input[field.name]).parse(input).result;
48
45
 
49
46
  const _type = typeof inputValue;
50
47
 
@@ -8,9 +8,9 @@ import { FSleep } from './FSleep.class';
8
8
  import { FHash } from './FHash.class';
9
9
  import { FEncDec } from './FEncDec.class';
10
10
  import { FTimestamp } from './FTimestamp.class';
11
- import { DataSourceLookup } from './DataSourceLookup.class';
12
- import { DataSourceIndexer } from './DataSourceIndexer.class';
13
- import { DataSourceCleaner } from './DataSourceCleaner.class';
11
+ import { DataSourceLookup } from './RAG/DataSourceLookup.class';
12
+ import { DataSourceIndexer } from './RAG/DataSourceIndexer.class';
13
+ import { DataSourceCleaner } from './RAG/DataSourceCleaner.class';
14
14
  import { JSONFilter } from './JSONFilter.class';
15
15
  import { LogicAND } from './LogicAND.class';
16
16
  import { LogicOR } from './LogicOR.class';
@@ -134,13 +134,7 @@ export async function zipCode(directory: string) {
134
134
  }
135
135
 
136
136
  export async function createOrUpdateLambdaFunction(functionName, zipFilePath, awsConfigs, envVariables: Record<string, string>) {
137
- const client = new LambdaClient({
138
- region: awsConfigs.region,
139
- credentials: {
140
- accessKeyId: awsConfigs.accessKeyId,
141
- secretAccessKey: awsConfigs.secretAccessKey,
142
- },
143
- });
137
+ const client = getAWSLambdaClient(awsConfigs.region, awsConfigs.accessKeyId, awsConfigs.secretAccessKey)
144
138
  const functionContent = fs.readFileSync(zipFilePath);
145
139
 
146
140
  try {
@@ -168,26 +162,19 @@ export async function createOrUpdateLambdaFunction(functionName, zipFilePath, aw
168
162
  let roleArn = '';
169
163
  // check if the role exists
170
164
  try {
171
- const iamClient = new IAMClient({
172
- region: awsConfigs.region,
173
- credentials: { accessKeyId: awsConfigs.accessKeyId, secretAccessKey: awsConfigs.secretAccessKey },
174
- });
165
+ const iamClient = getAWSIAMClient(awsConfigs.region, awsConfigs.accessKeyId, awsConfigs.secretAccessKey);
175
166
  const getRoleCommand = new GetRoleCommand({ RoleName: `smyth-${functionName}-role` });
176
167
  const roleResponse: GetRoleCommandOutput = await iamClient.send(getRoleCommand);
177
168
  roleArn = roleResponse.Role.Arn;
178
169
  } catch (error) {
179
170
  if (error.name === 'NoSuchEntityException') {
180
171
  // create role
181
- const iamClient = new IAMClient({
182
- region: awsConfigs.region,
183
- credentials: { accessKeyId: awsConfigs.accessKeyId, secretAccessKey: awsConfigs.secretAccessKey },
184
- });
172
+ const iamClient = getAWSIAMClient(awsConfigs.region, awsConfigs.accessKeyId, awsConfigs.secretAccessKey);
185
173
  const createRoleCommand = new CreateRoleCommand({
186
174
  RoleName: `smyth-${functionName}-role`,
187
175
  AssumeRolePolicyDocument: getLambdaRolePolicy(),
188
176
  });
189
177
  const roleResponse: CreateRoleCommandOutput = await iamClient.send(createRoleCommand);
190
-
191
178
  await waitForRoleDeploymentStatus(`smyth-${functionName}-role`, iamClient);
192
179
  roleArn = roleResponse.Role.Arn;
193
180
  } else {
@@ -231,7 +218,7 @@ function updateLambdaFunctionConfiguration(client: LambdaClient, functionName: s
231
218
 
232
219
  async function createLambdaFunction(client: LambdaClient, functionParams: any, maxRetries: number = 5): Promise<void> {
233
220
  let lastError: Error | null = null;
234
-
221
+
235
222
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
236
223
  try {
237
224
  const functionCreateCommand = new CreateFunctionCommand(functionParams);
@@ -241,7 +228,6 @@ async function createLambdaFunction(client: LambdaClient, functionParams: any, m
241
228
  lastError = error;
242
229
  // Check if this is a role trust policy error
243
230
  if (error?.message?.includes('cannot be assumed by Lambda')) {
244
-
245
231
  if (attempt < maxRetries) {
246
232
  // Exponential backoff: 2^attempt seconds (2, 4, 8, 16, 32 seconds)
247
233
  const waitTime = Math.pow(2, attempt) * 1000;
@@ -249,12 +235,11 @@ async function createLambdaFunction(client: LambdaClient, functionParams: any, m
249
235
  continue;
250
236
  }
251
237
  }
252
-
253
238
  // For other errors or if we've exhausted retries, throw immediately
254
239
  throw error;
255
240
  }
256
241
  }
257
-
242
+
258
243
  // If we get here, all retries failed
259
244
  throw lastError || new Error('Lambda function creation failed after all retry attempts');
260
245
  }
@@ -262,15 +247,15 @@ async function createLambdaFunction(client: LambdaClient, functionParams: any, m
262
247
  export async function waitForRoleDeploymentStatus(roleName, client): Promise<boolean> {
263
248
  return new Promise((resolve, reject) => {
264
249
  const interval = setInterval(async () => {
265
- const getRoleCommand = new GetRoleCommand({ RoleName: roleName });
266
- const roleResponse = await client.send(getRoleCommand);
267
-
268
- // Check if role exists and has assume role policy document
269
- if (roleResponse.Role && roleResponse.Role.AssumeRolePolicyDocument) {
270
- clearInterval(interval);
271
- setTimeout(() => resolve(true), 2000);
272
- return;
273
- }
250
+ const getRoleCommand = new GetRoleCommand({ RoleName: roleName });
251
+ const roleResponse = await client.send(getRoleCommand);
252
+
253
+ // Check if role exists and has assume role policy document
254
+ if (roleResponse.Role && roleResponse.Role.AssumeRolePolicyDocument) {
255
+ clearInterval(interval);
256
+ setTimeout(() => resolve(true), 2000);
257
+ return;
258
+ }
274
259
  }, 2000); // Check every 2 seconds
275
260
  });
276
261
  }
@@ -319,15 +304,7 @@ export async function invokeLambdaFunction(
319
304
  awsCredentials: AWSCredentials & AWSRegionConfig
320
305
  ): Promise<any> {
321
306
  try {
322
- const client = new LambdaClient({
323
- region: awsCredentials.region as string,
324
- ...(awsCredentials.accessKeyId && {
325
- credentials: {
326
- accessKeyId: awsCredentials.accessKeyId as string,
327
- secretAccessKey: awsCredentials.secretAccessKey as string,
328
- },
329
- }),
330
- });
307
+ const client = getAWSLambdaClient(awsCredentials.region, awsCredentials.accessKeyId, awsCredentials.secretAccessKey);
331
308
 
332
309
  const invokeCommand = new InvokeCommand({
333
310
  FunctionName: functionName,
@@ -347,13 +324,7 @@ export async function invokeLambdaFunction(
347
324
 
348
325
  export async function getDeployedFunction(functionName: string, awsConfigs: AWSCredentials & AWSRegionConfig) {
349
326
  try {
350
- const client = new LambdaClient({
351
- region: awsConfigs.region as string,
352
- credentials: {
353
- accessKeyId: awsConfigs.accessKeyId as string,
354
- secretAccessKey: awsConfigs.secretAccessKey as string,
355
- },
356
- });
327
+ const client = getAWSLambdaClient(awsConfigs.region as string, awsConfigs.accessKeyId as string, awsConfigs.secretAccessKey as string);
357
328
  const getFunctionCommand = new GetFunctionCommand({ FunctionName: functionName });
358
329
  const lambdaResponse: GetFunctionCommandOutput = await client.send(getFunctionCommand);
359
330
  return {
@@ -392,6 +363,30 @@ export async function getLambdaCredentials(agent: IAgent, config: any): Promise<
392
363
  return awsCredentials;
393
364
  }
394
365
 
366
+ function getAWSLambdaClient(region: string, accessKeyId?: string, secretAccessKey?: string) {
367
+ return new LambdaClient({
368
+ region,
369
+ ...(accessKeyId && secretAccessKey && {
370
+ credentials: {
371
+ accessKeyId,
372
+ secretAccessKey,
373
+ },
374
+ }),
375
+ });
376
+ }
377
+
378
+ function getAWSIAMClient(region: string, accessKeyId?: string, secretAccessKey?: string) {
379
+ return new IAMClient({
380
+ region,
381
+ ...(accessKeyId && secretAccessKey && {
382
+ credentials: {
383
+ accessKeyId,
384
+ secretAccessKey,
385
+ },
386
+ }),
387
+ });
388
+ }
389
+
395
390
  export function calculateExecutionCost(executionTime: number) {
396
391
  // executionTime in milliseconds
397
392
  const cost = (executionTime / 1000) * Number(PER_SECOND_COST);
@@ -117,6 +117,11 @@ export class Conversation extends EventEmitter {
117
117
  return this._model;
118
118
  }
119
119
 
120
+ private _llmInference: LLMInference;
121
+ public get llmInference() {
122
+ return this._llmInference;
123
+ }
124
+
120
125
  constructor(
121
126
  private _model: string | TLLMModel,
122
127
  private _specSource?: string | Record<string, any>,
@@ -308,7 +313,6 @@ export class Conversation extends EventEmitter {
308
313
  // toolsConfig,
309
314
  // });
310
315
  /* ==================== STEP ENTRY ==================== */
311
- const llmInference: LLMInference = await LLMInference.getInstance(this.model, AccessCandidate.team(this._teamId));
312
316
 
313
317
  if (message) this._context.addUserMessage(message, message_id);
314
318
 
@@ -329,7 +333,7 @@ export class Conversation extends EventEmitter {
329
333
  requestId: llmReqUid,
330
334
  });
331
335
 
332
- const eventEmitter: any = await llmInference
336
+ const eventEmitter: any = await this.llmInference
333
337
  .promptStream({
334
338
  contextWindow,
335
339
  files,
@@ -818,6 +822,7 @@ export class Conversation extends EventEmitter {
818
822
  handler: (args: Record<string, any>) => Promise<any>;
819
823
  inputs?: any[];
820
824
  }) {
825
+ await this.ready;
821
826
  if (!tool.arguments) {
822
827
  //if no arguments are provided, we need to extract them from the function
823
828
  const toolFunction = tool.handler as Function;
@@ -879,11 +884,10 @@ export class Conversation extends EventEmitter {
879
884
 
880
885
  //deduplicate tools
881
886
 
882
- const llmInference: LLMInference = await LLMInference.getInstance(this.model, AccessCandidate.team(this._teamId));
883
887
  this._customToolsDeclarations = this._customToolsDeclarations.filter(
884
888
  (tool, index, self) => self.findIndex((t) => t.name === tool.name) === index
885
889
  );
886
- const toolsConfig: any = llmInference.connector.formatToolsConfig({
890
+ const toolsConfig: any = this.llmInference.connector.formatToolsConfig({
887
891
  type: 'function',
888
892
  toolDefinitions: this._customToolsDeclarations,
889
893
  toolChoice: this.toolChoice,
@@ -896,11 +900,11 @@ export class Conversation extends EventEmitter {
896
900
  }
897
901
 
898
902
  async removeTool(toolName: string) {
903
+ await this.ready;
899
904
  this._customToolsDeclarations = this._customToolsDeclarations.filter((tool) => tool.name !== toolName);
900
905
  delete this._customToolsHandlers[toolName];
901
- const llmInference: LLMInference = await LLMInference.getInstance(this.model, AccessCandidate.team(this._teamId));
902
906
 
903
- const toolsConfig: any = llmInference.connector.formatToolsConfig({
907
+ const toolsConfig: any = this.llmInference.connector.formatToolsConfig({
904
908
  type: 'function',
905
909
  toolDefinitions: this._customToolsDeclarations,
906
910
  toolChoice: this.toolChoice,
@@ -928,15 +932,15 @@ export class Conversation extends EventEmitter {
928
932
  const functionDeclarations = this.getFunctionDeclarations(this._spec);
929
933
  //functionDeclarations.push(...this._customToolsDeclarations);
930
934
  this._customToolsDeclarations.push(...functionDeclarations);
931
- const llmInference: LLMInference = await LLMInference.getInstance(this._model, AccessCandidate.team(this._teamId));
932
- if (!llmInference.connector) {
935
+ this._llmInference = await LLMInference.getInstance(this._model, AccessCandidate.team(this._teamId));
936
+ if (!this._llmInference.connector) {
933
937
  this.emit('error', 'No connector found for model: ' + this._model);
934
938
  return;
935
939
  }
936
940
  this._customToolsDeclarations = this._customToolsDeclarations.filter(
937
941
  (tool, index, self) => self.findIndex((t) => t.name === tool.name) === index
938
942
  );
939
- this._toolsConfig = llmInference.connector.formatToolsConfig({
943
+ this._toolsConfig = this.llmInference.connector.formatToolsConfig({
940
944
  type: 'function',
941
945
  toolDefinitions: this._customToolsDeclarations,
942
946
  toolChoice: this.toolChoice,
@@ -945,7 +949,7 @@ export class Conversation extends EventEmitter {
945
949
  let messages = [];
946
950
  if (this._context) messages = this._context.messages; // preserve messages
947
951
 
948
- this._context = new LLMContext(llmInference, this.systemPrompt, this._llmContextStore);
952
+ this._context = new LLMContext(this.llmInference, this.systemPrompt, this._llmContextStore);
949
953
  } else {
950
954
  this._toolsConfig = null;
951
955
  this._reqMethods = null;