@smythos/sre 1.5.16 → 1.5.20
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 +68 -34
- package/dist/index.js.map +1 -1
- package/dist/types/Components/Component.class.d.ts +4 -0
- package/dist/types/Components/ServerlessCode.class.d.ts +13 -0
- package/dist/types/Components/index.d.ts +2 -0
- package/dist/types/Core/ConnectorsService.d.ts +1 -0
- package/dist/types/Core/SystemEvents.d.ts +2 -1
- package/dist/types/helpers/AWSLambdaCode.helper.d.ts +30 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/subsystems/ComputeManager/Code.service/CodeConnector.d.ts +16 -15
- package/dist/types/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.d.ts +13 -7
- package/dist/types/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.d.ts +1 -0
- package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +3 -3
- package/dist/types/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.d.ts +1 -0
- package/dist/types/subsystems/Security/Account.service/AccountConnector.d.ts +2 -0
- package/dist/types/subsystems/Security/ManagedVault.service/ManagedVaultConnector.d.ts +2 -0
- package/dist/types/subsystems/Security/Vault.service/VaultConnector.d.ts +2 -0
- package/dist/types/types/AWS.types.d.ts +1 -0
- package/dist/types/types/Security.types.d.ts +0 -3
- package/package.json +7 -3
- package/src/Components/Component.class.ts +40 -2
- package/src/Components/GenAILLM.class.ts +3 -2
- package/src/Components/ServerlessCode.class.ts +100 -0
- package/src/Components/index.ts +2 -0
- package/src/Core/ConnectorsService.ts +5 -0
- package/src/Core/SmythRuntime.class.ts +6 -3
- package/src/Core/SystemEvents.ts +2 -1
- package/src/Core/boot.ts +1 -0
- package/src/helpers/AWSLambdaCode.helper.ts +347 -0
- package/src/helpers/Conversation.helper.ts +15 -6
- package/src/helpers/Sysconfig.helper.ts +11 -1
- package/src/index.ts +2 -0
- package/src/index.ts.bak +2 -0
- package/src/subsystems/AgentManager/ForkedAgent.class.ts +1 -0
- package/src/subsystems/ComputeManager/Code.service/CodeConnector.ts +30 -31
- package/src/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.ts +127 -24
- package/src/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.ts +25 -2
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +13 -13
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +5 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/OpenAI.class.ts +1 -1
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +4 -0
- package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -0
- package/src/subsystems/Security/Account.service/connectors/DummyAccount.class.ts +1 -1
- package/src/subsystems/Security/ManagedVault.service/ManagedVaultConnector.ts +4 -0
- package/src/subsystems/Security/ManagedVault.service/connectors/NullManagedVault.class.ts +1 -5
- package/src/subsystems/Security/Vault.service/VaultConnector.ts +3 -0
- package/src/types/AWS.types.ts +2 -0
- package/src/types/Security.types.ts +0 -4
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
3
|
+
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
4
|
+
import zl from 'zip-lib';
|
|
5
|
+
import { InvokeCommand, Runtime, LambdaClient, UpdateFunctionCodeCommand, CreateFunctionCommand, GetFunctionCommand, GetFunctionCommandOutput, InvokeCommandOutput } from '@aws-sdk/client-lambda';
|
|
6
|
+
import { GetRoleCommand, CreateRoleCommand, IAMClient, GetRoleCommandOutput, CreateRoleCommandOutput } from '@aws-sdk/client-iam';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import { AWSCredentials, AWSRegionConfig } from '@sre/types/AWS.types';
|
|
9
|
+
export const cachePrefix = 'serverless_code';
|
|
10
|
+
export const cacheTTL = 60 * 60 * 24 * 16; // 16 days
|
|
11
|
+
|
|
12
|
+
export function getLambdaFunctionName(agentId: string, componentId: string) {
|
|
13
|
+
return `${agentId}-${componentId}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export function generateCodeHash(code_body: string, code_imports: string, codeInputs: string[]) {
|
|
18
|
+
const importsHash = getSanitizeCodeHash(code_imports);
|
|
19
|
+
const bodyHash = getSanitizeCodeHash(code_body);
|
|
20
|
+
const inputsHash = getSanitizeCodeHash(JSON.stringify(codeInputs));
|
|
21
|
+
return `imports-${importsHash}__body-${bodyHash}__inputs-${inputsHash}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getSanitizeCodeHash(code: string) {
|
|
25
|
+
let output = '';
|
|
26
|
+
let isSingleQuote = false;
|
|
27
|
+
let isDoubleQuote = false;
|
|
28
|
+
let isTemplateLiteral = false;
|
|
29
|
+
let isRegex = false;
|
|
30
|
+
let isComment = false;
|
|
31
|
+
let prevChar = '';
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < code.length; i++) {
|
|
34
|
+
let char = code[i];
|
|
35
|
+
let nextChar = code[i + 1];
|
|
36
|
+
|
|
37
|
+
// Toggle string flags
|
|
38
|
+
if (char === "'" && !isDoubleQuote && !isTemplateLiteral && prevChar !== '\\') isSingleQuote = !isSingleQuote;
|
|
39
|
+
if (char === '"' && !isSingleQuote && !isTemplateLiteral && prevChar !== '\\') isDoubleQuote = !isDoubleQuote;
|
|
40
|
+
if (char === '`' && !isSingleQuote && !isDoubleQuote && prevChar !== '\\') isTemplateLiteral = !isTemplateLiteral;
|
|
41
|
+
|
|
42
|
+
// Handle regex cases
|
|
43
|
+
if (char === '/' && nextChar === '/' && !isSingleQuote && !isDoubleQuote && !isTemplateLiteral && !isRegex) {
|
|
44
|
+
isComment = true; // Single-line comment
|
|
45
|
+
}
|
|
46
|
+
if (char === '/' && nextChar === '*' && !isSingleQuote && !isDoubleQuote && !isTemplateLiteral) {
|
|
47
|
+
isComment = true; // Multi-line comment start
|
|
48
|
+
}
|
|
49
|
+
if (char === '*' && nextChar === '/' && isComment) {
|
|
50
|
+
isComment = false; // Multi-line comment end
|
|
51
|
+
i++; // Skip ending slash
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (char === '\n' && isComment) {
|
|
55
|
+
isComment = false; // End single-line comment
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!isComment) {
|
|
59
|
+
output += char;
|
|
60
|
+
}
|
|
61
|
+
prevChar = char;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return crypto.createHash('md5').update(output.replace(/\s+/g, ' ').trim()).digest('hex');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function getDeployedCodeHash(agentId: string, componentId: string) {
|
|
68
|
+
const redisCache = ConnectorService.getCacheConnector();
|
|
69
|
+
const cachedCodeHash = await redisCache.user(AccessCandidate.agent(agentId)).get(`${cachePrefix}_${agentId}-${componentId}`);
|
|
70
|
+
return cachedCodeHash;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function setDeployedCodeHash(agentId: string, componentId: string, codeHash: string) {
|
|
74
|
+
const redisCache = ConnectorService.getCacheConnector();
|
|
75
|
+
await redisCache
|
|
76
|
+
.user(AccessCandidate.agent(agentId))
|
|
77
|
+
.set(`${cachePrefix}_${agentId}-${componentId}`, codeHash, null, null, cacheTTL);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
export function extractNpmImports(code: string) {
|
|
82
|
+
const importRegex = /import\s+(?:[\w*\s{},]*\s+from\s+)?['"]([^'"]+)['"]/g;
|
|
83
|
+
const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
|
|
84
|
+
const dynamicImportRegex = /import\(['"]([^'"]+)['"]\)/g;
|
|
85
|
+
|
|
86
|
+
let libraries = new Set();
|
|
87
|
+
let match;
|
|
88
|
+
|
|
89
|
+
// Function to extract the main package name
|
|
90
|
+
function extractPackageName(modulePath: string) {
|
|
91
|
+
if (modulePath.startsWith('@')) {
|
|
92
|
+
// Handle scoped packages (e.g., @babel/core)
|
|
93
|
+
return modulePath.split('/').slice(0, 2).join('/');
|
|
94
|
+
}
|
|
95
|
+
return modulePath.split('/')[0]; // Extract the first part (main package)
|
|
96
|
+
}
|
|
97
|
+
// Match static ESM imports
|
|
98
|
+
while ((match = importRegex.exec(code)) !== null) {
|
|
99
|
+
libraries.add(extractPackageName(match[1]));
|
|
100
|
+
}
|
|
101
|
+
// Match CommonJS require() calls
|
|
102
|
+
while ((match = requireRegex.exec(code)) !== null) {
|
|
103
|
+
libraries.add(extractPackageName(match[1]));
|
|
104
|
+
}
|
|
105
|
+
// Match dynamic import() calls
|
|
106
|
+
while ((match = dynamicImportRegex.exec(code)) !== null) {
|
|
107
|
+
libraries.add(extractPackageName(match[1]));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Array.from(libraries);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
export function generateLambdaCode(code_imports: string, code_body: string, input_variables: string[]) {
|
|
115
|
+
const lambdaCode = `${code_imports}\nexport const handler = async (event, context) => {
|
|
116
|
+
try {
|
|
117
|
+
context.callbackWaitsForEmptyEventLoop = false;
|
|
118
|
+
let startTime = Date.now();
|
|
119
|
+
const result = await (async () => {
|
|
120
|
+
${input_variables && input_variables.length ? input_variables.map((variable) => `const ${variable} = event.${variable};`).join('\n') : ''}
|
|
121
|
+
${code_body}
|
|
122
|
+
})();
|
|
123
|
+
let endTime = Date.now();
|
|
124
|
+
return {
|
|
125
|
+
result,
|
|
126
|
+
executionTime: endTime - startTime
|
|
127
|
+
}
|
|
128
|
+
} catch (e) {
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
};`;
|
|
132
|
+
return lambdaCode;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function zipCode(directory: string) {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
zl.archiveFolder(directory, `${directory}.zip`).then(
|
|
138
|
+
function () {
|
|
139
|
+
resolve(`${directory}.zip`);
|
|
140
|
+
},
|
|
141
|
+
function (err) {
|
|
142
|
+
reject(err);
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export async function createOrUpdateLambdaFunction(functionName, zipFilePath, awsConfigs) {
|
|
149
|
+
const client = new LambdaClient({
|
|
150
|
+
region: awsConfigs.region,
|
|
151
|
+
credentials: {
|
|
152
|
+
accessKeyId: awsConfigs.accessKeyId,
|
|
153
|
+
secretAccessKey: awsConfigs.secretAccessKey,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
const functionContent = fs.readFileSync(zipFilePath);
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
// Check if the function exists
|
|
160
|
+
const exisitingFunction = await getDeployedFunction(functionName, awsConfigs);
|
|
161
|
+
if (exisitingFunction) {
|
|
162
|
+
if (exisitingFunction.status === 'InProgress') {
|
|
163
|
+
await verifyFunctionDeploymentStatus(functionName, client);
|
|
164
|
+
}
|
|
165
|
+
// Update function code if it exists
|
|
166
|
+
const updateCodeParams = {
|
|
167
|
+
FunctionName: functionName,
|
|
168
|
+
ZipFile: functionContent,
|
|
169
|
+
};
|
|
170
|
+
const updateFunctionCodeCommand = new UpdateFunctionCodeCommand(updateCodeParams);
|
|
171
|
+
await client.send(updateFunctionCodeCommand);
|
|
172
|
+
// Update function configuration to attach layer
|
|
173
|
+
await verifyFunctionDeploymentStatus(functionName, client);
|
|
174
|
+
// console.log('Lambda function code and configuration updated successfully!');
|
|
175
|
+
} else {
|
|
176
|
+
// Create function if it does not exist
|
|
177
|
+
let roleArn = '';
|
|
178
|
+
// check if the role exists
|
|
179
|
+
try {
|
|
180
|
+
const iamClient = new IAMClient({
|
|
181
|
+
region: awsConfigs.region,
|
|
182
|
+
credentials: { accessKeyId: awsConfigs.accessKeyId, secretAccessKey: awsConfigs.secretAccessKey },
|
|
183
|
+
});
|
|
184
|
+
const getRoleCommand = new GetRoleCommand({ RoleName: `smyth-${functionName}-role` });
|
|
185
|
+
const roleResponse: GetRoleCommandOutput = await iamClient.send(getRoleCommand);
|
|
186
|
+
roleArn = roleResponse.Role.Arn;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error.name === 'NoSuchEntityException') {
|
|
189
|
+
// create role
|
|
190
|
+
const iamClient = new IAMClient({
|
|
191
|
+
region: awsConfigs.region,
|
|
192
|
+
credentials: { accessKeyId: awsConfigs.accessKeyId, secretAccessKey: awsConfigs.secretAccessKey },
|
|
193
|
+
});
|
|
194
|
+
const createRoleCommand = new CreateRoleCommand({
|
|
195
|
+
RoleName: `smyth-${functionName}-role`,
|
|
196
|
+
AssumeRolePolicyDocument: getLambdaRolePolicy(),
|
|
197
|
+
});
|
|
198
|
+
const roleResponse: CreateRoleCommandOutput = await iamClient.send(createRoleCommand);
|
|
199
|
+
await waitForRoleDeploymentStatus(`smyth-${functionName}-role`, iamClient);
|
|
200
|
+
roleArn = roleResponse.Role.Arn;
|
|
201
|
+
} else {
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const functionParams = {
|
|
207
|
+
Code: { ZipFile: functionContent },
|
|
208
|
+
FunctionName: functionName,
|
|
209
|
+
Handler: 'index.handler',
|
|
210
|
+
Role: roleArn,
|
|
211
|
+
Runtime: Runtime.nodejs18x,
|
|
212
|
+
Layers: [],
|
|
213
|
+
Timeout: 900,
|
|
214
|
+
Tags: {
|
|
215
|
+
'auto-delete': 'true',
|
|
216
|
+
},
|
|
217
|
+
MemorySize: 256,
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const functionCreateCommand = new CreateFunctionCommand(functionParams);
|
|
221
|
+
const functionResponse = await client.send(functionCreateCommand);
|
|
222
|
+
// console.log('Function ARN:', functionResponse.FunctionArn);
|
|
223
|
+
await verifyFunctionDeploymentStatus(functionName, client);
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function waitForRoleDeploymentStatus(roleName, client): Promise<boolean> {
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
try {
|
|
233
|
+
let interval = setInterval(async () => {
|
|
234
|
+
const getRoleCommand = new GetRoleCommand({ RoleName: roleName });
|
|
235
|
+
const roleResponse = await client.send(getRoleCommand);
|
|
236
|
+
if (roleResponse.Role.AssumeRolePolicyDocument) {
|
|
237
|
+
clearInterval(interval);
|
|
238
|
+
return resolve(true);
|
|
239
|
+
}
|
|
240
|
+
}, 7000);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function verifyFunctionDeploymentStatus(functionName, client): Promise<boolean> {
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
try {
|
|
250
|
+
let interval = setInterval(async () => {
|
|
251
|
+
const getFunctionCommand = new GetFunctionCommand({ FunctionName: functionName });
|
|
252
|
+
const lambdaResponse = await client.send(getFunctionCommand);
|
|
253
|
+
|
|
254
|
+
if (lambdaResponse.Configuration.LastUpdateStatus === 'Successful') {
|
|
255
|
+
clearInterval(interval);
|
|
256
|
+
return resolve(true);
|
|
257
|
+
}
|
|
258
|
+
}, 5000);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function getLambdaRolePolicy() {
|
|
266
|
+
return JSON.stringify({
|
|
267
|
+
Version: '2012-10-17',
|
|
268
|
+
Statement: [
|
|
269
|
+
{
|
|
270
|
+
Effect: 'Allow',
|
|
271
|
+
Principal: {
|
|
272
|
+
Service: 'lambda.amazonaws.com',
|
|
273
|
+
},
|
|
274
|
+
Action: 'sts:AssumeRole',
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
export async function updateDeployedCodeTTL(agentId: string, componentId: string, ttl: number) {
|
|
282
|
+
const redisCache = ConnectorService.getCacheConnector();
|
|
283
|
+
await redisCache.user(AccessCandidate.agent(agentId)).updateTTL(`${cachePrefix}_${agentId}-${componentId}`, ttl);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export async function invokeLambdaFunction(
|
|
287
|
+
functionName: string,
|
|
288
|
+
inputs: { [key: string]: any },
|
|
289
|
+
awsCredentials: AWSCredentials & AWSRegionConfig,
|
|
290
|
+
): Promise<any> {
|
|
291
|
+
try {
|
|
292
|
+
const client = new LambdaClient({
|
|
293
|
+
region: awsCredentials.region as string,
|
|
294
|
+
...(awsCredentials.accessKeyId && {
|
|
295
|
+
credentials: {
|
|
296
|
+
accessKeyId: awsCredentials.accessKeyId as string,
|
|
297
|
+
secretAccessKey: awsCredentials.secretAccessKey as string,
|
|
298
|
+
},
|
|
299
|
+
}),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const invokeCommand = new InvokeCommand({
|
|
303
|
+
FunctionName: functionName,
|
|
304
|
+
Payload: new TextEncoder().encode(`${JSON.stringify(inputs)}`),
|
|
305
|
+
InvocationType: 'RequestResponse',
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const response: InvokeCommandOutput = await client.send(invokeCommand);
|
|
309
|
+
if (response.FunctionError) {
|
|
310
|
+
throw new Error(new TextDecoder().decode(response.Payload));
|
|
311
|
+
}
|
|
312
|
+
return new TextDecoder().decode(response.Payload);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export async function getDeployedFunction(functionName: string, awsConfigs: AWSCredentials & AWSRegionConfig) {
|
|
319
|
+
try {
|
|
320
|
+
const client = new LambdaClient({
|
|
321
|
+
region: awsConfigs.region as string,
|
|
322
|
+
credentials: {
|
|
323
|
+
accessKeyId: awsConfigs.accessKeyId as string,
|
|
324
|
+
secretAccessKey: awsConfigs.secretAccessKey as string,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
const getFunctionCommand = new GetFunctionCommand({ FunctionName: functionName });
|
|
328
|
+
const lambdaResponse: GetFunctionCommandOutput = await client.send(getFunctionCommand);
|
|
329
|
+
return {
|
|
330
|
+
status: lambdaResponse.Configuration.LastUpdateStatus,
|
|
331
|
+
functionName: lambdaResponse.Configuration.FunctionName,
|
|
332
|
+
functionVersion: lambdaResponse.Configuration.Version,
|
|
333
|
+
updatedAt: lambdaResponse.Configuration.LastModified,
|
|
334
|
+
role: lambdaResponse.Configuration.Role,
|
|
335
|
+
runtime: lambdaResponse.Configuration.Runtime,
|
|
336
|
+
version: lambdaResponse.Configuration.Version,
|
|
337
|
+
};
|
|
338
|
+
} catch (error) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function extractKeyFromTemplateVar(input: string) {
|
|
344
|
+
const regex = /\{\{KEY\((.*?)\)\}\}/;
|
|
345
|
+
const match = input.match(regex);
|
|
346
|
+
return match ? match[1] : input;
|
|
347
|
+
}
|
|
@@ -13,10 +13,8 @@ import { JSONContent } from './JsonContent.helper';
|
|
|
13
13
|
import { OpenAPIParser } from './OpenApiParser.helper';
|
|
14
14
|
import { Match, TemplateString } from './TemplateString.helper';
|
|
15
15
|
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
16
|
-
import { delay } from '@sre/utils/date-time.utils';
|
|
17
16
|
import { EventSource, FetchLike } from 'eventsource';
|
|
18
17
|
import { hookAsyncWithContext } from '@sre/Core/HookService';
|
|
19
|
-
import { DEFAULT_TEAM_ID } from '@sre/types/ACL.types';
|
|
20
18
|
import { randomUUID } from 'crypto';
|
|
21
19
|
import * as acorn from 'acorn';
|
|
22
20
|
|
|
@@ -159,7 +157,7 @@ export class Conversation extends EventEmitter {
|
|
|
159
157
|
this._spec = spec;
|
|
160
158
|
|
|
161
159
|
if (!this._agentId && _settings?.agentId) this._agentId = _settings.agentId;
|
|
162
|
-
if (!this._agentId) this._agentId = '
|
|
160
|
+
if (!this._agentId) this._agentId = 'FAKE-AGENT-ID'; //We use a fake agent ID to avoid ACL check errors
|
|
163
161
|
|
|
164
162
|
// teamId is required to load custom LLMs, we must assign it before updateModel()
|
|
165
163
|
await this.assignTeamIdFromAgentId(this._agentId);
|
|
@@ -217,8 +215,19 @@ export class Conversation extends EventEmitter {
|
|
|
217
215
|
model: instance._model,
|
|
218
216
|
};
|
|
219
217
|
})
|
|
220
|
-
public async prompt(message?: string|any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
218
|
+
public async prompt(message?: string | any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
219
|
+
// if an error occured while streaming, we need to propagate it so for this, we register a one time error listener
|
|
220
|
+
let error = null;
|
|
221
|
+
const errListener = (err) => (error = err);
|
|
222
|
+
this.once('error', errListener);
|
|
221
223
|
const result = await this.streamPrompt(message, toolHeaders, concurrentToolCalls, abortSignal);
|
|
224
|
+
|
|
225
|
+
// if an error event occured, throw the error
|
|
226
|
+
if (error) {
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
this.removeListener('error', errListener);
|
|
222
231
|
return result;
|
|
223
232
|
}
|
|
224
233
|
|
|
@@ -232,8 +241,8 @@ export class Conversation extends EventEmitter {
|
|
|
232
241
|
model: instance._model,
|
|
233
242
|
};
|
|
234
243
|
})
|
|
235
|
-
public async streamPrompt(message?: string|any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
236
|
-
let options = typeof message === 'object' ? message : {message};
|
|
244
|
+
public async streamPrompt(message?: string | any, toolHeaders = {}, concurrentToolCalls = 4, abortSignal?: AbortSignal) {
|
|
245
|
+
let options = typeof message === 'object' ? message : { message };
|
|
237
246
|
message = options?.message;
|
|
238
247
|
const files = options?.files;
|
|
239
248
|
|
|
@@ -5,6 +5,12 @@ import os from 'os';
|
|
|
5
5
|
export function findSmythPath(_path: string = '', callback?: (smythDir: string, success?: boolean, nextDir?: string) => void) {
|
|
6
6
|
//TODO : also search for a local sre configuration file indicating .smyth folder explicitly
|
|
7
7
|
|
|
8
|
+
let _smythDir = '';
|
|
9
|
+
|
|
10
|
+
if (_path) {
|
|
11
|
+
_smythDir = findSmythPath('');
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
const searchDirectories = [];
|
|
9
15
|
|
|
10
16
|
// 1. Try to find in local directory (the directory from which the program was run)
|
|
@@ -49,7 +55,11 @@ export function findSmythPath(_path: string = '', callback?: (smythDir: string,
|
|
|
49
55
|
return dir;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
if (_smythDir && _path) {
|
|
59
|
+
return path.resolve(_smythDir, _path);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return homeDir;
|
|
53
63
|
}
|
|
54
64
|
|
|
55
65
|
function findPackageRoot(startDir = process.cwd()) {
|
package/src/index.ts
CHANGED
|
@@ -40,6 +40,7 @@ export * from './Components/LogicXOR.class';
|
|
|
40
40
|
export * from './Components/MCPClient.class';
|
|
41
41
|
export * from './Components/PromptGenerator.class';
|
|
42
42
|
export * from './Components/ScrapflyWebScrape.class';
|
|
43
|
+
export * from './Components/ServerlessCode.class';
|
|
43
44
|
export * from './Components/TavilyWebSearch.class';
|
|
44
45
|
export * from './Core/AgentProcess.helper';
|
|
45
46
|
export * from './Core/boot';
|
|
@@ -49,6 +50,7 @@ export * from './Core/DummyConnector';
|
|
|
49
50
|
export * from './Core/HookService';
|
|
50
51
|
export * from './Core/SmythRuntime.class';
|
|
51
52
|
export * from './Core/SystemEvents';
|
|
53
|
+
export * from './helpers/AWSLambdaCode.helper';
|
|
52
54
|
export * from './helpers/BinaryInput.helper';
|
|
53
55
|
export * from './helpers/Conversation.helper';
|
|
54
56
|
export * from './helpers/JsonContent.helper';
|
package/src/index.ts.bak
CHANGED
|
@@ -40,6 +40,7 @@ export * from './Components/LogicXOR.class';
|
|
|
40
40
|
export * from './Components/MCPClient.class';
|
|
41
41
|
export * from './Components/PromptGenerator.class';
|
|
42
42
|
export * from './Components/ScrapflyWebScrape.class';
|
|
43
|
+
export * from './Components/ServerlessCode.class';
|
|
43
44
|
export * from './Components/TavilyWebSearch.class';
|
|
44
45
|
export * from './Core/AgentProcess.helper';
|
|
45
46
|
export * from './Core/boot';
|
|
@@ -49,6 +50,7 @@ export * from './Core/DummyConnector';
|
|
|
49
50
|
export * from './Core/HookService';
|
|
50
51
|
export * from './Core/SmythRuntime.class';
|
|
51
52
|
export * from './Core/SystemEvents';
|
|
53
|
+
export * from './helpers/AWSLambdaCode.helper';
|
|
52
54
|
export * from './helpers/BinaryInput.helper';
|
|
53
55
|
export * from './helpers/Conversation.helper';
|
|
54
56
|
export * from './helpers/JsonContent.helper';
|
|
@@ -31,6 +31,7 @@ export class ForkedAgent {
|
|
|
31
31
|
const data: any = fork(this.parent.data, componentId);
|
|
32
32
|
data.variables = JSON.parse(JSON.stringify(this.parent?.data?.variables || {})); //copy parent Agent variables to forked agent
|
|
33
33
|
data.teamId = this.parent.teamId;
|
|
34
|
+
data.planInfo = this.parent.data?.planInfo || {};
|
|
34
35
|
//TODO : we need to create a default APIEndpoint bound to the root component if root component is not an APIEndpoint
|
|
35
36
|
const content = { name: this.parent.name, data, teamId: this.parent.teamId, debugSessionEnabled: false, version: this.parent.version };
|
|
36
37
|
|
|
@@ -16,7 +16,6 @@ export interface CodeConfig {
|
|
|
16
16
|
timeout?: number; // Execution timeout in milliseconds
|
|
17
17
|
memoryLimit?: number; // Memory allocation in MB
|
|
18
18
|
environment?: Record<string, string>; // Environment variables
|
|
19
|
-
|
|
20
19
|
// Platform-specific settings
|
|
21
20
|
platformConfig?: Record<string, any>;
|
|
22
21
|
}
|
|
@@ -38,61 +37,61 @@ export interface CodePreparationResult {
|
|
|
38
37
|
|
|
39
38
|
export interface CodeDeployment {
|
|
40
39
|
id: string; // Deployment identifier
|
|
41
|
-
status:
|
|
40
|
+
status: string;
|
|
42
41
|
runtime: string;
|
|
43
42
|
createdAt: Date;
|
|
44
43
|
lastUsed?: Date;
|
|
44
|
+
lastUpdated?: Date;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export interface ICodeRequest {
|
|
48
48
|
// Core workflow
|
|
49
|
-
prepare(input: CodeInput, config: CodeConfig): Promise<CodePreparationResult>;
|
|
50
|
-
deploy(
|
|
51
|
-
execute(
|
|
49
|
+
prepare(codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodePreparationResult>;
|
|
50
|
+
deploy(codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodeDeployment>;
|
|
51
|
+
execute(codeUID: string, inputs: Record<string, any>, config: CodeConfig): Promise<CodeExecutionResult>;
|
|
52
52
|
|
|
53
53
|
// Execute with existing deployment (for platforms that support it)
|
|
54
|
-
executeDeployment(deploymentId: string, inputs: Record<string, any
|
|
54
|
+
executeDeployment(codeUID: string, deploymentId: string, inputs: Record<string, any>, config: CodeConfig): Promise<CodeExecutionResult>;
|
|
55
55
|
|
|
56
56
|
// Deployment management
|
|
57
|
-
listDeployments(): Promise<CodeDeployment[]>;
|
|
58
|
-
getDeployment(deploymentId: string): Promise<CodeDeployment | null>;
|
|
59
|
-
deleteDeployment(deploymentId: string): Promise<void>;
|
|
57
|
+
listDeployments(codeUID: string, config: CodeConfig): Promise<CodeDeployment[]>;
|
|
58
|
+
getDeployment(codeUID: string, deploymentId: string, config: CodeConfig): Promise<CodeDeployment | null>;
|
|
59
|
+
deleteDeployment(codeUID: string, deploymentId: string, config: CodeConfig): Promise<void>;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
export abstract class CodeConnector extends SecureConnector {
|
|
63
63
|
public abstract getResourceACL(resourceId: string, candidate: IAccessCandidate): Promise<ACL>;
|
|
64
|
-
|
|
65
64
|
// Abstract methods that concrete connectors must implement
|
|
66
|
-
protected abstract prepare(acRequest: AccessRequest, input: CodeInput, config: CodeConfig): Promise<CodePreparationResult>;
|
|
67
|
-
protected abstract deploy(acRequest: AccessRequest,
|
|
68
|
-
protected abstract execute(acRequest: AccessRequest,
|
|
69
|
-
protected abstract executeDeployment(acRequest: AccessRequest, deploymentId: string, inputs: Record<string, any
|
|
70
|
-
protected abstract listDeployments(acRequest: AccessRequest): Promise<CodeDeployment[]>;
|
|
71
|
-
protected abstract getDeployment(acRequest: AccessRequest, deploymentId: string): Promise<CodeDeployment | null>;
|
|
72
|
-
protected abstract deleteDeployment(acRequest: AccessRequest, deploymentId: string): Promise<void>;
|
|
65
|
+
protected abstract prepare(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodePreparationResult>;
|
|
66
|
+
protected abstract deploy(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodeDeployment>;
|
|
67
|
+
protected abstract execute(acRequest: AccessRequest, codeUID: string, inputs: Record<string, any>, config: CodeConfig): Promise<CodeExecutionResult>;
|
|
68
|
+
protected abstract executeDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, inputs: Record<string, any>, config: CodeConfig): Promise<CodeExecutionResult>;
|
|
69
|
+
protected abstract listDeployments(acRequest: AccessRequest, codeUID: string, config: CodeConfig): Promise<CodeDeployment[]>;
|
|
70
|
+
protected abstract getDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, config: CodeConfig): Promise<CodeDeployment | null>;
|
|
71
|
+
protected abstract deleteDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, config: CodeConfig): Promise<void>;
|
|
73
72
|
|
|
74
73
|
public requester(candidate: AccessCandidate): ICodeRequest {
|
|
75
74
|
return {
|
|
76
|
-
prepare: async (input: CodeInput, config: CodeConfig) => {
|
|
77
|
-
return await this.prepare(candidate.readRequest, input, config);
|
|
75
|
+
prepare: async (codeUID: string, input: CodeInput, config: CodeConfig) => {
|
|
76
|
+
return await this.prepare(candidate.readRequest, codeUID, input, config);
|
|
78
77
|
},
|
|
79
|
-
deploy: async (
|
|
80
|
-
return await this.deploy(candidate.writeRequest,
|
|
78
|
+
deploy: async (codeUID: string, input: CodeInput, config: CodeConfig) => {
|
|
79
|
+
return await this.deploy(candidate.writeRequest, codeUID, input, config);
|
|
81
80
|
},
|
|
82
|
-
execute: async (
|
|
83
|
-
return await this.execute(candidate.readRequest,
|
|
81
|
+
execute: async (codeUID: string, inputs: Record<string, any>, config: CodeConfig) => {
|
|
82
|
+
return await this.execute(candidate.readRequest, codeUID, inputs, config);
|
|
84
83
|
},
|
|
85
|
-
executeDeployment: async (deploymentId: string, inputs: Record<string, any
|
|
86
|
-
return await this.executeDeployment(candidate.readRequest, deploymentId, inputs);
|
|
84
|
+
executeDeployment: async (codeUID: string, deploymentId: string, inputs: Record<string, any>, config: CodeConfig) => {
|
|
85
|
+
return await this.executeDeployment(candidate.readRequest, codeUID, deploymentId, inputs, config);
|
|
87
86
|
},
|
|
88
|
-
listDeployments: async () => {
|
|
89
|
-
return await this.listDeployments(candidate.readRequest);
|
|
87
|
+
listDeployments: async (codeUID: string, config: CodeConfig) => {
|
|
88
|
+
return await this.listDeployments(candidate.readRequest, codeUID, config);
|
|
90
89
|
},
|
|
91
|
-
getDeployment: async (deploymentId: string) => {
|
|
92
|
-
return await this.getDeployment(candidate.readRequest, deploymentId);
|
|
90
|
+
getDeployment: async (codeUID: string, deploymentId: string, config: CodeConfig) => {
|
|
91
|
+
return await this.getDeployment(candidate.readRequest, codeUID, deploymentId, config);
|
|
93
92
|
},
|
|
94
|
-
deleteDeployment: async (deploymentId: string) => {
|
|
95
|
-
await this.deleteDeployment(candidate.writeRequest, deploymentId);
|
|
93
|
+
deleteDeployment: async (codeUID: string, deploymentId: string, config: CodeConfig) => {
|
|
94
|
+
await this.deleteDeployment(candidate.writeRequest, codeUID, deploymentId, config);
|
|
96
95
|
},
|
|
97
96
|
} as ICodeRequest;
|
|
98
97
|
}
|