@smythos/sre 1.5.45 → 1.5.50
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/CHANGELOG +98 -90
- package/LICENSE +18 -18
- package/README.md +135 -135
- package/dist/bundle-analysis-lazy.html +4949 -0
- package/dist/bundle-analysis.html +4949 -0
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/types/Components/MCPClient.class.d.ts +1 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +1 -6
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.d.ts +2 -2
- package/dist/types/utils/package-manager.utils.d.ts +26 -0
- package/package.json +1 -1
- package/src/Components/APICall/APICall.class.ts +156 -156
- package/src/Components/APICall/AccessTokenManager.ts +130 -130
- package/src/Components/APICall/ArrayBufferResponse.helper.ts +58 -58
- package/src/Components/APICall/OAuth.helper.ts +294 -294
- package/src/Components/APICall/mimeTypeCategories.ts +46 -46
- package/src/Components/APICall/parseData.ts +167 -167
- package/src/Components/APICall/parseHeaders.ts +41 -41
- package/src/Components/APICall/parseProxy.ts +68 -68
- package/src/Components/APICall/parseUrl.ts +91 -91
- package/src/Components/APIEndpoint.class.ts +234 -234
- package/src/Components/APIOutput.class.ts +58 -58
- package/src/Components/AgentPlugin.class.ts +102 -102
- package/src/Components/Async.class.ts +155 -155
- package/src/Components/Await.class.ts +90 -90
- package/src/Components/Classifier.class.ts +158 -158
- package/src/Components/Component.class.ts +132 -132
- package/src/Components/ComponentHost.class.ts +38 -38
- package/src/Components/DataSourceCleaner.class.ts +92 -92
- package/src/Components/DataSourceIndexer.class.ts +181 -181
- package/src/Components/DataSourceLookup.class.ts +161 -161
- package/src/Components/ECMASandbox.class.ts +71 -71
- package/src/Components/FEncDec.class.ts +29 -29
- package/src/Components/FHash.class.ts +33 -33
- package/src/Components/FSign.class.ts +80 -80
- package/src/Components/FSleep.class.ts +25 -25
- package/src/Components/FTimestamp.class.ts +25 -25
- package/src/Components/FileStore.class.ts +78 -78
- package/src/Components/ForEach.class.ts +97 -97
- package/src/Components/GPTPlugin.class.ts +70 -70
- package/src/Components/GenAILLM.class.ts +586 -586
- package/src/Components/HuggingFace.class.ts +314 -314
- package/src/Components/Image/imageSettings.config.ts +70 -70
- package/src/Components/ImageGenerator.class.ts +502 -502
- package/src/Components/JSONFilter.class.ts +54 -54
- package/src/Components/LLMAssistant.class.ts +213 -213
- package/src/Components/LogicAND.class.ts +28 -28
- package/src/Components/LogicAtLeast.class.ts +85 -85
- package/src/Components/LogicAtMost.class.ts +86 -86
- package/src/Components/LogicOR.class.ts +29 -29
- package/src/Components/LogicXOR.class.ts +34 -34
- package/src/Components/MCPClient.class.ts +138 -112
- package/src/Components/MemoryDeleteKeyVal.class.ts +70 -70
- package/src/Components/MemoryReadKeyVal.class.ts +66 -66
- package/src/Components/MemoryWriteKeyVal.class.ts +62 -62
- package/src/Components/MemoryWriteObject.class.ts +97 -97
- package/src/Components/MultimodalLLM.class.ts +128 -128
- package/src/Components/OpenAPI.class.ts +72 -72
- package/src/Components/PromptGenerator.class.ts +122 -122
- package/src/Components/ScrapflyWebScrape.class.ts +159 -159
- package/src/Components/ServerlessCode.class.ts +123 -123
- package/src/Components/TavilyWebSearch.class.ts +98 -98
- package/src/Components/VisionLLM.class.ts +104 -104
- package/src/Components/ZapierAction.class.ts +127 -127
- package/src/Components/index.ts +97 -97
- package/src/Core/AgentProcess.helper.ts +240 -240
- package/src/Core/Connector.class.ts +123 -123
- package/src/Core/ConnectorsService.ts +197 -197
- package/src/Core/DummyConnector.ts +49 -49
- package/src/Core/HookService.ts +105 -105
- package/src/Core/SmythRuntime.class.ts +235 -235
- package/src/Core/SystemEvents.ts +16 -16
- package/src/Core/boot.ts +56 -56
- package/src/config.ts +15 -15
- package/src/constants.ts +126 -126
- package/src/data/hugging-face.params.json +579 -579
- package/src/helpers/AWSLambdaCode.helper.ts +587 -587
- package/src/helpers/BinaryInput.helper.ts +331 -331
- package/src/helpers/Conversation.helper.ts +1119 -1119
- package/src/helpers/ECMASandbox.helper.ts +54 -54
- package/src/helpers/JsonContent.helper.ts +97 -97
- package/src/helpers/LocalCache.helper.ts +97 -97
- package/src/helpers/Log.helper.ts +274 -274
- package/src/helpers/OpenApiParser.helper.ts +150 -150
- package/src/helpers/S3Cache.helper.ts +147 -147
- package/src/helpers/SmythURI.helper.ts +5 -5
- package/src/helpers/Sysconfig.helper.ts +77 -77
- package/src/helpers/TemplateString.helper.ts +243 -243
- package/src/helpers/TypeChecker.helper.ts +329 -329
- package/src/index.ts +3 -3
- package/src/index.ts.bak +3 -3
- package/src/subsystems/AgentManager/Agent.class.ts +1114 -1114
- package/src/subsystems/AgentManager/Agent.helper.ts +3 -3
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +230 -230
- package/src/subsystems/AgentManager/AgentData.service/connectors/CLIAgentDataConnector.class.ts +66 -66
- package/src/subsystems/AgentManager/AgentData.service/connectors/LocalAgentDataConnector.class.ts +142 -142
- package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +39 -39
- package/src/subsystems/AgentManager/AgentData.service/index.ts +18 -18
- package/src/subsystems/AgentManager/AgentLogger.class.ts +297 -297
- package/src/subsystems/AgentManager/AgentRequest.class.ts +51 -51
- package/src/subsystems/AgentManager/AgentRuntime.class.ts +559 -559
- package/src/subsystems/AgentManager/AgentSSE.class.ts +101 -101
- package/src/subsystems/AgentManager/AgentSettings.class.ts +52 -52
- package/src/subsystems/AgentManager/Component.service/ComponentConnector.ts +32 -32
- package/src/subsystems/AgentManager/Component.service/connectors/LocalComponentConnector.class.ts +60 -60
- package/src/subsystems/AgentManager/Component.service/index.ts +11 -11
- package/src/subsystems/AgentManager/EmbodimentSettings.class.ts +47 -47
- package/src/subsystems/AgentManager/ForkedAgent.class.ts +154 -154
- package/src/subsystems/AgentManager/OSResourceMonitor.ts +77 -77
- package/src/subsystems/ComputeManager/Code.service/CodeConnector.ts +98 -98
- package/src/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.ts +172 -172
- package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -131
- package/src/subsystems/ComputeManager/Code.service/index.ts +13 -13
- package/src/subsystems/IO/CLI.service/CLIConnector.ts +47 -47
- package/src/subsystems/IO/CLI.service/index.ts +9 -9
- package/src/subsystems/IO/Log.service/LogConnector.ts +32 -32
- package/src/subsystems/IO/Log.service/connectors/ConsoleLog.class.ts +28 -28
- package/src/subsystems/IO/Log.service/index.ts +13 -13
- package/src/subsystems/IO/NKV.service/NKVConnector.ts +43 -43
- package/src/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.ts +234 -234
- package/src/subsystems/IO/NKV.service/connectors/NKVRAM.class.ts +204 -204
- package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +182 -182
- package/src/subsystems/IO/NKV.service/index.ts +14 -14
- package/src/subsystems/IO/Router.service/RouterConnector.ts +21 -21
- package/src/subsystems/IO/Router.service/connectors/ExpressRouter.class.ts +48 -48
- package/src/subsystems/IO/Router.service/connectors/NullRouter.class.ts +40 -40
- package/src/subsystems/IO/Router.service/index.ts +11 -11
- package/src/subsystems/IO/Storage.service/SmythFS.class.ts +489 -489
- package/src/subsystems/IO/Storage.service/StorageConnector.ts +66 -66
- package/src/subsystems/IO/Storage.service/connectors/LocalStorage.class.ts +327 -327
- package/src/subsystems/IO/Storage.service/connectors/S3Storage.class.ts +482 -482
- package/src/subsystems/IO/Storage.service/index.ts +13 -13
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +108 -108
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +454 -454
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +384 -384
- package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +421 -421
- package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +107 -107
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +109 -109
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +21 -21
- package/src/subsystems/IO/VectorDB.service/index.ts +14 -14
- package/src/subsystems/LLMManager/LLM.helper.ts +251 -251
- package/src/subsystems/LLMManager/LLM.inference.ts +339 -339
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +489 -489
- package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +171 -171
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +659 -659
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +400 -400
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +77 -77
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +757 -757
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +304 -304
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +250 -250
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +423 -423
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +488 -488
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +524 -528
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -100
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -81
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +1145 -1168
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +13 -13
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +11 -11
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +32 -32
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +471 -471
- package/src/subsystems/LLMManager/LLM.service/index.ts +44 -44
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +300 -300
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +252 -252
- package/src/subsystems/LLMManager/ModelsProvider.service/index.ts +11 -11
- package/src/subsystems/LLMManager/custom-models.ts +854 -854
- package/src/subsystems/LLMManager/models.ts +2540 -2540
- package/src/subsystems/LLMManager/paramMappings.ts +69 -69
- package/src/subsystems/MemoryManager/Cache.service/CacheConnector.ts +86 -86
- package/src/subsystems/MemoryManager/Cache.service/connectors/LocalStorageCache.class.ts +297 -297
- package/src/subsystems/MemoryManager/Cache.service/connectors/RAMCache.class.ts +201 -201
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +252 -252
- package/src/subsystems/MemoryManager/Cache.service/connectors/S3Cache.class.ts +373 -373
- package/src/subsystems/MemoryManager/Cache.service/index.ts +15 -15
- package/src/subsystems/MemoryManager/LLMCache.ts +72 -72
- package/src/subsystems/MemoryManager/LLMContext.ts +124 -124
- package/src/subsystems/MemoryManager/LLMMemory.service/LLMMemoryConnector.ts +26 -26
- package/src/subsystems/MemoryManager/RuntimeContext.ts +266 -266
- package/src/subsystems/Security/AccessControl/ACL.class.ts +208 -208
- package/src/subsystems/Security/AccessControl/AccessCandidate.class.ts +82 -82
- package/src/subsystems/Security/AccessControl/AccessRequest.class.ts +52 -52
- package/src/subsystems/Security/Account.service/AccountConnector.ts +44 -44
- package/src/subsystems/Security/Account.service/connectors/AWSAccount.class.ts +76 -76
- package/src/subsystems/Security/Account.service/connectors/DummyAccount.class.ts +130 -130
- package/src/subsystems/Security/Account.service/connectors/JSONFileAccount.class.ts +159 -159
- package/src/subsystems/Security/Account.service/index.ts +14 -14
- package/src/subsystems/Security/Credentials.helper.ts +62 -62
- package/src/subsystems/Security/ManagedVault.service/ManagedVaultConnector.ts +38 -38
- package/src/subsystems/Security/ManagedVault.service/connectors/NullManagedVault.class.ts +53 -53
- package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +154 -154
- package/src/subsystems/Security/ManagedVault.service/index.ts +12 -12
- package/src/subsystems/Security/SecureConnector.class.ts +110 -110
- package/src/subsystems/Security/Vault.service/Vault.helper.ts +30 -30
- package/src/subsystems/Security/Vault.service/VaultConnector.ts +29 -29
- package/src/subsystems/Security/Vault.service/connectors/HashicorpVault.class.ts +46 -46
- package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +221 -221
- package/src/subsystems/Security/Vault.service/connectors/NullVault.class.ts +54 -54
- package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +140 -140
- package/src/subsystems/Security/Vault.service/index.ts +12 -12
- package/src/types/ACL.types.ts +104 -104
- package/src/types/AWS.types.ts +10 -10
- package/src/types/Agent.types.ts +61 -61
- package/src/types/AgentLogger.types.ts +17 -17
- package/src/types/Cache.types.ts +1 -1
- package/src/types/Common.types.ts +2 -2
- package/src/types/LLM.types.ts +496 -496
- package/src/types/Redis.types.ts +8 -8
- package/src/types/SRE.types.ts +64 -64
- package/src/types/Security.types.ts +14 -14
- package/src/types/Storage.types.ts +5 -5
- package/src/types/VectorDB.types.ts +86 -86
- package/src/utils/base64.utils.ts +275 -275
- package/src/utils/cli.utils.ts +68 -68
- package/src/utils/data.utils.ts +322 -322
- package/src/utils/date-time.utils.ts +22 -22
- package/src/utils/general.utils.ts +238 -238
- package/src/utils/index.ts +12 -12
- package/src/utils/lazy-client.ts +261 -261
- package/src/utils/numbers.utils.ts +13 -13
- package/src/utils/oauth.utils.ts +35 -35
- package/src/utils/string.utils.ts +414 -414
- package/src/utils/url.utils.ts +19 -19
- package/src/utils/validation.utils.ts +74 -74
- package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/SmythModelsProvider.class.d.ts +0 -39
|
@@ -1,1114 +1,1114 @@
|
|
|
1
|
-
import { Component } from '@sre/Components/Component.class';
|
|
2
|
-
import { AgentLogger } from './AgentLogger.class';
|
|
3
|
-
import { AgentRequest } from './AgentRequest.class';
|
|
4
|
-
import { AgentRuntime } from './AgentRuntime.class';
|
|
5
|
-
import { AgentSettings } from './AgentSettings.class';
|
|
6
|
-
import { OSResourceMonitor } from './OSResourceMonitor';
|
|
7
|
-
import config from '@sre/config';
|
|
8
|
-
import { ControlledPromise, delay, getCurrentFormattedDate, uid } from '@sre/utils/index';
|
|
9
|
-
|
|
10
|
-
import { Logger } from '@sre/helpers/Log.helper';
|
|
11
|
-
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
12
|
-
import { AgentSSE } from './AgentSSE.class';
|
|
13
|
-
import { IAgent } from '@sre/types/Agent.types';
|
|
14
|
-
import { IModelsProviderRequest, ModelsProviderConnector } from '@sre/LLMManager/ModelsProvider.service/ModelsProviderConnector';
|
|
15
|
-
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
16
|
-
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
17
|
-
|
|
18
|
-
const console = Logger('Agent');
|
|
19
|
-
const idPromise = (id) => id;
|
|
20
|
-
const MAX_LATENCY = 50;
|
|
21
|
-
|
|
22
|
-
export class Agent implements IAgent {
|
|
23
|
-
public name: any;
|
|
24
|
-
public data: any;
|
|
25
|
-
public teamId: any;
|
|
26
|
-
public components: any;
|
|
27
|
-
public connections: any;
|
|
28
|
-
public endpoints: any = {};
|
|
29
|
-
public sessionId;
|
|
30
|
-
public sessionTag = '';
|
|
31
|
-
public callerSessionId;
|
|
32
|
-
public apiBasePath = '/api';
|
|
33
|
-
public agentRuntime: AgentRuntime | any;
|
|
34
|
-
|
|
35
|
-
public usingTestDomain = false;
|
|
36
|
-
public domain = '';
|
|
37
|
-
public debugSessionEnabled = false;
|
|
38
|
-
public circularLimit = 100; //TODO : make it configurable from agent settings
|
|
39
|
-
public version = '';
|
|
40
|
-
|
|
41
|
-
//public baseUrl = '';
|
|
42
|
-
public agentVariables: any = {};
|
|
43
|
-
private _kill = false;
|
|
44
|
-
//public agentRequest: Request | AgentRequest | any;
|
|
45
|
-
public async = false;
|
|
46
|
-
public jobID = '';
|
|
47
|
-
public planInfo: any = {};
|
|
48
|
-
public callback: (data: any) => void;
|
|
49
|
-
|
|
50
|
-
public agentRequest: AgentRequest;
|
|
51
|
-
public sse: AgentSSE;
|
|
52
|
-
public modelsProvider: IModelsProviderRequest;
|
|
53
|
-
|
|
54
|
-
private _componentInstance = {};
|
|
55
|
-
|
|
56
|
-
public get ComponentInstances() {
|
|
57
|
-
return this._componentInstance;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
private _componentInstancesLoader = new ControlledPromise(() => {});
|
|
61
|
-
|
|
62
|
-
constructor(
|
|
63
|
-
public id,
|
|
64
|
-
agentData,
|
|
65
|
-
public agentSettings: AgentSettings,
|
|
66
|
-
agentRequest?: AgentRequest | any //private req: express.Request,
|
|
67
|
-
) {
|
|
68
|
-
//this.agentRequest = new AgentRequest(req);
|
|
69
|
-
const json = typeof agentData === 'string' ? JSON.parse(agentData) : agentData;
|
|
70
|
-
this.data = json.connections && json.components ? json : json.data;
|
|
71
|
-
if (!this.data) this.data = { name: '', connections: [], components: [] };
|
|
72
|
-
//this.agentVariables = json.data.variables || {};
|
|
73
|
-
|
|
74
|
-
this.name = this.data.name;
|
|
75
|
-
this.version = this.data.agentVersion || ''; //when version is not set we load the latest dev version
|
|
76
|
-
this.teamId = this.data.teamId;
|
|
77
|
-
this.connections = this.data.connections;
|
|
78
|
-
this.debugSessionEnabled = this.data.debugSessionEnabled;
|
|
79
|
-
this.usingTestDomain = this.data.usingTestDomain;
|
|
80
|
-
|
|
81
|
-
this.agentVariables = this.data.variables || {};
|
|
82
|
-
|
|
83
|
-
const endpoints = this.data.components.filter((c) => c.name == 'APIEndpoint');
|
|
84
|
-
for (let endpoint of endpoints) {
|
|
85
|
-
let method = endpoint.data.method || 'POST';
|
|
86
|
-
method = method.toUpperCase();
|
|
87
|
-
if (!this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`])
|
|
88
|
-
this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`] = {};
|
|
89
|
-
this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`][method] = endpoint;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
this.components = {};
|
|
93
|
-
for (let component of this.data.components) {
|
|
94
|
-
//FIXME : this does not persist in debug mode, it breaks key value mem logic
|
|
95
|
-
|
|
96
|
-
this.components[component.id] = component;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
for (let connection of this.data.connections) {
|
|
100
|
-
const sourceComponent = this.components[connection.sourceId];
|
|
101
|
-
const targetComponent = this.components[connection.targetId];
|
|
102
|
-
|
|
103
|
-
//if connections ids passed as names, we convert them to indexes
|
|
104
|
-
//TODO : harmonize connections formats
|
|
105
|
-
const sourceIndex =
|
|
106
|
-
typeof connection.sourceIndex === 'number'
|
|
107
|
-
? connection.sourceIndex
|
|
108
|
-
: sourceComponent.outputs.findIndex((o) => o.name == connection.sourceIndex);
|
|
109
|
-
const targetIndex =
|
|
110
|
-
typeof connection.targetIndex === 'number'
|
|
111
|
-
? connection.targetIndex
|
|
112
|
-
: targetComponent.inputs.findIndex((i) => i.name == connection.targetIndex);
|
|
113
|
-
|
|
114
|
-
connection.sourceIndex = sourceIndex;
|
|
115
|
-
connection.targetIndex = targetIndex;
|
|
116
|
-
|
|
117
|
-
const output = sourceComponent.outputs[sourceIndex];
|
|
118
|
-
output.index = sourceIndex; // legacy ids (numbers)
|
|
119
|
-
|
|
120
|
-
const input = targetComponent.inputs[targetIndex];
|
|
121
|
-
input.index = targetIndex;
|
|
122
|
-
|
|
123
|
-
if (!output.next) output.next = [];
|
|
124
|
-
output.next.push(targetComponent.id);
|
|
125
|
-
|
|
126
|
-
if (!input.prev) input.prev = [];
|
|
127
|
-
input.prev.push(sourceComponent.id);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
this.tagAsyncComponents();
|
|
131
|
-
|
|
132
|
-
if (agentRequest) {
|
|
133
|
-
this.setRequest(agentRequest);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.sse = new AgentSSE(this);
|
|
137
|
-
//this.settings = new AgentSettings(this.id);
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
//add hosted components to the available instances
|
|
141
|
-
//hosted components are custom components that are specific to some SRE instances implementations : e.g Shell component is only available to local SRE
|
|
142
|
-
const componentConnector = ConnectorService.getComponentConnector();
|
|
143
|
-
componentConnector
|
|
144
|
-
.requester(AccessCandidate.agent(id))
|
|
145
|
-
.getAll()
|
|
146
|
-
.then((customComponents) => {
|
|
147
|
-
this._componentInstance = { ...this._componentInstance, ...customComponents };
|
|
148
|
-
this._componentInstancesLoader.resolve(true);
|
|
149
|
-
});
|
|
150
|
-
} catch (error) {
|
|
151
|
-
console.warn('Could not load custom components', AccessCandidate.agent(this.id));
|
|
152
|
-
this._componentInstancesLoader.reject('Could not load custom components');
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const modelsProvider: ModelsProviderConnector = ConnectorService.getModelsProviderConnector();
|
|
156
|
-
if (modelsProvider.valid) {
|
|
157
|
-
this.modelsProvider = modelsProvider.agent(id);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Add a SSE connection to the agent
|
|
163
|
-
* @param sseSource - The SSE source to add
|
|
164
|
-
* @param monitorId - The monitor ID to add
|
|
165
|
-
*/
|
|
166
|
-
public addSSE(sseSource: Response | AgentSSE, monitorId?: string) {
|
|
167
|
-
if (sseSource instanceof AgentSSE) {
|
|
168
|
-
for (const [monitorId, res] of sseSource) {
|
|
169
|
-
this.sse.add(res, monitorId); // add each connection to the new sse
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
const id = monitorId || Math.random().toString(36).substring(2, 15);
|
|
173
|
-
this.sse.add(sseSource, id);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
public setRequest(agentRequest: AgentRequest | any) {
|
|
177
|
-
if (this.agentRequest) return;
|
|
178
|
-
this.agentRequest = agentRequest;
|
|
179
|
-
this.agentRequest = agentRequest;
|
|
180
|
-
const dateTime = getCurrentFormattedDate();
|
|
181
|
-
this.sessionId = 'rt-' + (this.agentRequest.sessionID || dateTime + '.' + uid());
|
|
182
|
-
|
|
183
|
-
const sessionTags = this?.agentRequest?.headers['x-session-tag'];
|
|
184
|
-
if (sessionTags) this.sessionTag += this.sessionTag ? `,${sessionTags}` : sessionTags;
|
|
185
|
-
|
|
186
|
-
var regex = new RegExp(`^\/v[0-9]+(\.[0-9]+)?${this.apiBasePath}\/(.*)`);
|
|
187
|
-
if (this.agentRequest?.path?.startsWith(`${this.apiBasePath}/`) || this.agentRequest?.path?.match(regex)) {
|
|
188
|
-
//we only need runtime context for API calls
|
|
189
|
-
this.agentRuntime = new AgentRuntime(this);
|
|
190
|
-
this.callerSessionId =
|
|
191
|
-
this?.agentRequest?.headers['x-caller-session-id']?.substring(0, 256) || this.agentRuntime.workflowReqId || this.sessionId;
|
|
192
|
-
} else {
|
|
193
|
-
this.agentRuntime = AgentRuntime.dummy;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
public setCallback(callback: (data: any) => void) {
|
|
197
|
-
this.callback = callback;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
public kill() {
|
|
201
|
-
this._kill = true;
|
|
202
|
-
}
|
|
203
|
-
public isKilled() {
|
|
204
|
-
return this._kill;
|
|
205
|
-
}
|
|
206
|
-
private async parseVariables() {
|
|
207
|
-
//parse vault agent variables
|
|
208
|
-
if (typeof this.agentVariables === 'object') {
|
|
209
|
-
for (let key in this.agentVariables) {
|
|
210
|
-
const value = this.agentVariables[key];
|
|
211
|
-
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
212
|
-
//this.agentVariables[key] = (await parseKey(value, this.teamId)) || '';
|
|
213
|
-
this.agentVariables[key] = await TemplateString(value).parseTeamKeysAsync(this.teamId).asyncResult;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async process(endpointPath, input) {
|
|
220
|
-
await this.agentRuntime.ready();
|
|
221
|
-
|
|
222
|
-
//TODO: replace endpointPath + input params with a single agentRequest object. (This will require intensive regression testing)
|
|
223
|
-
let result: any;
|
|
224
|
-
let dbgSession: any = null;
|
|
225
|
-
let sessionClosed = false;
|
|
226
|
-
|
|
227
|
-
const eventId = 'e-' + uid();
|
|
228
|
-
const startTime = Date.now();
|
|
229
|
-
|
|
230
|
-
this.sse.send('agent', {
|
|
231
|
-
eventId,
|
|
232
|
-
action: 'callStart',
|
|
233
|
-
endpointPath,
|
|
234
|
-
id: this.id,
|
|
235
|
-
name: this.name,
|
|
236
|
-
startTime,
|
|
237
|
-
input,
|
|
238
|
-
});
|
|
239
|
-
//this.agentRuntime.checkRuntimeContext();
|
|
240
|
-
//insert log
|
|
241
|
-
const logId = AgentLogger.log(this, null, {
|
|
242
|
-
sourceId: endpointPath,
|
|
243
|
-
componentId: `AGENT`,
|
|
244
|
-
domain: this.domain,
|
|
245
|
-
input,
|
|
246
|
-
workflowID: this.agentRuntime.workflowReqId,
|
|
247
|
-
processID: this.agentRuntime.processID,
|
|
248
|
-
inputTimestamp: new Date().toISOString(),
|
|
249
|
-
sessionID: this.callerSessionId,
|
|
250
|
-
tags: this.sessionTag,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const method = this.agentRequest.method.toUpperCase();
|
|
254
|
-
const endpoint = this.endpoints[endpointPath]?.[method];
|
|
255
|
-
|
|
256
|
-
//first check if this is a debug session, and return debug result if it's the case
|
|
257
|
-
if (this.agentRuntime.debug) {
|
|
258
|
-
if (!endpoint && this.agentRequest.path != '/api/') {
|
|
259
|
-
if (logId) AgentLogger.log(this, logId, { error: `Endpoint ${method} ${endpointPath} Not Found` });
|
|
260
|
-
|
|
261
|
-
throw new Error(`Endpoint ${method} ${endpointPath} Not Found`);
|
|
262
|
-
}
|
|
263
|
-
let dbgResult: any;
|
|
264
|
-
//let dbgResult: any = await this.agentRuntime.readState(true); //is this a debug read reqeust ?
|
|
265
|
-
|
|
266
|
-
if (!dbgResult) dbgResult = await this.agentRuntime.runCycle(); //no, is this a step over request ?
|
|
267
|
-
|
|
268
|
-
// result = dbgResult?.state;
|
|
269
|
-
// dbgSession = dbgResult?.dbgSession;
|
|
270
|
-
// sessionClosed = dbgResult?.sessionClosed;
|
|
271
|
-
if (dbgResult && typeof dbgResult?.state !== 'undefined') {
|
|
272
|
-
this.agentRuntime.sync();
|
|
273
|
-
if (dbgResult?.finalResult) {
|
|
274
|
-
dbgResult.finalResult = await this.postProcess(dbgResult.finalResult).catch((error) => ({ error }));
|
|
275
|
-
}
|
|
276
|
-
return dbgResult;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (!endpoint) {
|
|
281
|
-
if (logId) AgentLogger.log(this, logId, { error: `Endpoint ${method} ${endpointPath} Not Found` });
|
|
282
|
-
const endTime = Date.now();
|
|
283
|
-
const duration = endTime - startTime;
|
|
284
|
-
this.sse.send('agent', {
|
|
285
|
-
eventId,
|
|
286
|
-
action: 'callStop',
|
|
287
|
-
endpointPath,
|
|
288
|
-
id: this.id,
|
|
289
|
-
name: this.name,
|
|
290
|
-
startTime,
|
|
291
|
-
endTime,
|
|
292
|
-
duration,
|
|
293
|
-
input,
|
|
294
|
-
error: `Endpoint ${method} ${endpointPath} Not Found`,
|
|
295
|
-
});
|
|
296
|
-
throw new Error(`Endpoint ${method} ${endpointPath} Not Found`);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
this.agentRuntime.updateComponent(endpoint.id, { active: true, input, sourceId: null });
|
|
300
|
-
|
|
301
|
-
let step;
|
|
302
|
-
do {
|
|
303
|
-
step = await this.agentRuntime.runCycle();
|
|
304
|
-
|
|
305
|
-
//adjust latency based on cpu load
|
|
306
|
-
const qosLatency = Math.floor(OSResourceMonitor.cpu.load * MAX_LATENCY || 0);
|
|
307
|
-
|
|
308
|
-
await delay(10 + qosLatency);
|
|
309
|
-
} while (!step?.finalResult && !this._kill);
|
|
310
|
-
|
|
311
|
-
if (this._kill) {
|
|
312
|
-
const endTime = Date.now();
|
|
313
|
-
const duration = endTime - startTime;
|
|
314
|
-
this.sse.send('agent', {
|
|
315
|
-
eventId,
|
|
316
|
-
action: 'callStop',
|
|
317
|
-
endpointPath,
|
|
318
|
-
id: this.id,
|
|
319
|
-
name: this.name,
|
|
320
|
-
startTime,
|
|
321
|
-
endTime,
|
|
322
|
-
duration,
|
|
323
|
-
input,
|
|
324
|
-
error: 'Agent killed',
|
|
325
|
-
});
|
|
326
|
-
console.warn(`Agent ${this.id} was killed`, AccessCandidate.agent(this.id));
|
|
327
|
-
return { error: 'Agent killed' };
|
|
328
|
-
}
|
|
329
|
-
result = await this.postProcess(step?.finalResult).catch((error) => ({ error }));
|
|
330
|
-
|
|
331
|
-
//post process all results
|
|
332
|
-
if (this.agentRuntime.circularLimitReached) {
|
|
333
|
-
const circularLimitData = this.agentRuntime.circularLimitReached;
|
|
334
|
-
result = { error: `Circular Calls Limit Reached on ${circularLimitData}. Current circular limit is ${this.circularLimit}` };
|
|
335
|
-
|
|
336
|
-
const endTime = Date.now();
|
|
337
|
-
const duration = endTime - startTime;
|
|
338
|
-
this.sse.send('agent', {
|
|
339
|
-
eventId,
|
|
340
|
-
action: 'callStop',
|
|
341
|
-
endpointPath,
|
|
342
|
-
id: this.id,
|
|
343
|
-
name: this.name,
|
|
344
|
-
startTime,
|
|
345
|
-
endTime,
|
|
346
|
-
duration,
|
|
347
|
-
input,
|
|
348
|
-
error: result.error,
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
throw new Error(`Circular Calls Limit Reached on ${circularLimitData}. Current circular limit is ${this.circularLimit}`);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (logId) AgentLogger.log(this, logId, { outputTimestamp: '' + Date.now(), result });
|
|
355
|
-
|
|
356
|
-
this.updateTasksCount(); //Important, don't use await here, we need the call to be non blocking
|
|
357
|
-
|
|
358
|
-
const endTime = Date.now();
|
|
359
|
-
const duration = endTime - startTime;
|
|
360
|
-
this.sse.send('agent', {
|
|
361
|
-
eventId,
|
|
362
|
-
action: 'callStop',
|
|
363
|
-
endpointPath,
|
|
364
|
-
id: this.id,
|
|
365
|
-
name: this.name,
|
|
366
|
-
startTime,
|
|
367
|
-
endTime,
|
|
368
|
-
duration,
|
|
369
|
-
input,
|
|
370
|
-
result,
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
//FIXME: does debug call ever reach this point ?
|
|
374
|
-
return this.agentRuntime.debug ? { state: result, dbgSession, sessionClosed } : result;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
private async updateTasksCount() {
|
|
378
|
-
//tasks count update logic
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
public async postProcess(result) {
|
|
382
|
-
if (Array.isArray(result)) result = result.flat(Infinity);
|
|
383
|
-
if (!Array.isArray(result)) result = [result];
|
|
384
|
-
|
|
385
|
-
//filter out handled errors
|
|
386
|
-
result = result.filter((r) => !(r?.result?._error && r?.result?._error_handled));
|
|
387
|
-
|
|
388
|
-
for (let i = 0; i < result.length; i++) {
|
|
389
|
-
const _result = result[i];
|
|
390
|
-
if (!_result) continue;
|
|
391
|
-
if (_result._debug) delete _result._debug;
|
|
392
|
-
if (_result._debug_time) delete _result._debug_time;
|
|
393
|
-
const _componentData = this.components[_result.id];
|
|
394
|
-
if (!_componentData) continue;
|
|
395
|
-
const _component: Component = this._componentInstance[_componentData.name];
|
|
396
|
-
if (!_component) continue;
|
|
397
|
-
//if (_component.hasPostProcess) {
|
|
398
|
-
const postProcessResult = await _component.postProcess(_result, _componentData, this).catch((error) => ({ error }));
|
|
399
|
-
|
|
400
|
-
result[i] = postProcessResult;
|
|
401
|
-
//}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (result.length == 1) result = result[0];
|
|
405
|
-
return result;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// public saveRuntimeComponentData(componentId, data) {
|
|
409
|
-
// //let runtimeData = { ...this.agentRuntime.getRuntimeData(componentId), ...data };
|
|
410
|
-
// //this.agentRuntime.updateComponent(componentId, { runtimeData: data });
|
|
411
|
-
|
|
412
|
-
// this.agentRuntime.saveRuntimeComponentData(componentId, data);
|
|
413
|
-
// }
|
|
414
|
-
// private getRuntimeData(componentId) {
|
|
415
|
-
// // const componentData = this.agentRuntime.getComponentData(componentId);
|
|
416
|
-
// // if (!componentData) return {};
|
|
417
|
-
// // const rData = componentData.runtimeData || {};
|
|
418
|
-
|
|
419
|
-
// return this.agentRuntime.getRuntimeData(componentId);
|
|
420
|
-
// }
|
|
421
|
-
|
|
422
|
-
// private clearRuntimeComponentData(componentId) {
|
|
423
|
-
// this.agentRuntime.resetComponent(componentId);
|
|
424
|
-
// }
|
|
425
|
-
|
|
426
|
-
private hasLoopAncestor(inputEntry) {
|
|
427
|
-
if (!inputEntry.prev) return false;
|
|
428
|
-
for (let prevId of inputEntry.prev) {
|
|
429
|
-
const prevComponentData = this.components[prevId];
|
|
430
|
-
if (prevComponentData.name == 'ForEach') return true;
|
|
431
|
-
|
|
432
|
-
for (let inputEntry of prevComponentData.inputs) {
|
|
433
|
-
if (this.hasLoopAncestor(inputEntry)) return true;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
private clearChildLoopRuntimeComponentData(componentId) {
|
|
439
|
-
const componentData = this.components[componentId];
|
|
440
|
-
const runtimeData = this.agentRuntime.getRuntimeData(componentId);
|
|
441
|
-
if (runtimeData._ChildLoopData) {
|
|
442
|
-
for (let inputEntry of componentData.inputs) {
|
|
443
|
-
if (this.hasLoopAncestor(inputEntry)) {
|
|
444
|
-
delete runtimeData.input[inputEntry.name];
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
private getComponentMissingInputs(componentId, _input) {
|
|
450
|
-
let missingInputs: any = [];
|
|
451
|
-
const componentData = this.components[componentId];
|
|
452
|
-
const component: Component = this._componentInstance[componentData.name];
|
|
453
|
-
if (component.alwaysActive) return missingInputs;
|
|
454
|
-
|
|
455
|
-
const readablePredecessors = this.findReadablePredecessors(componentId);
|
|
456
|
-
const readableInputNames = {};
|
|
457
|
-
for (let pred of readablePredecessors) {
|
|
458
|
-
if (pred) {
|
|
459
|
-
readableInputNames[pred.input.name] = pred;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
//readablePredecessors.map((e) => e.input.name);
|
|
463
|
-
|
|
464
|
-
const allInputIndexes = this.connections.filter((c) => c.targetId == componentId).map((e) => e.targetIndex);
|
|
465
|
-
const allInputs = componentData.inputs.filter((r) => allInputIndexes.includes(r.index));
|
|
466
|
-
|
|
467
|
-
if (Array.isArray(allInputs) && allInputs.length > 0) {
|
|
468
|
-
//if the next component has named inputs
|
|
469
|
-
for (let input of allInputs) {
|
|
470
|
-
if (input.optional) continue;
|
|
471
|
-
if (readableInputNames[input.name]) {
|
|
472
|
-
const pred = readableInputNames[input.name];
|
|
473
|
-
const component: Component = pred.component;
|
|
474
|
-
const predComponentData = this.components[pred.id];
|
|
475
|
-
const foundOutput = component.hasOutput(pred.output.name, predComponentData, this);
|
|
476
|
-
if (foundOutput) continue; //if the input is readable, skip it, because we can read it's value when needed. Readable inputs are non blocking
|
|
477
|
-
}
|
|
478
|
-
if (typeof _input[input.name] == 'undefined' /* || _input[input.name] == null*/) {
|
|
479
|
-
missingInputs.push(input.name);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return missingInputs;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
public findReadablePredecessors(componentId) {
|
|
488
|
-
const componentData = this.components[componentId];
|
|
489
|
-
const component: Component = this._componentInstance[componentData.name];
|
|
490
|
-
|
|
491
|
-
const connections = this.connections.filter((c) => c.targetId == componentId);
|
|
492
|
-
const readablePredecessors = connections.map((c) => {
|
|
493
|
-
//this.components[c.sourceId])
|
|
494
|
-
const sourceComponentData = this.components[c.sourceId];
|
|
495
|
-
const sourceComponent: Component = this._componentInstance[sourceComponentData.name];
|
|
496
|
-
const output = sourceComponentData.outputs[c.sourceIndex];
|
|
497
|
-
const input = componentData.inputs[c.targetIndex];
|
|
498
|
-
if (sourceComponent.hasReadOutput) {
|
|
499
|
-
return { output, input, component: sourceComponent, id: c.sourceId };
|
|
500
|
-
}
|
|
501
|
-
return null;
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
return readablePredecessors.filter((e) => e != null);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
*
|
|
509
|
-
* @param sourceId
|
|
510
|
-
* @param componentId
|
|
511
|
-
*/
|
|
512
|
-
private updateStep(sourceId, componentId) {
|
|
513
|
-
const agentRuntime = this.agentRuntime;
|
|
514
|
-
const step = agentRuntime.curStep;
|
|
515
|
-
//const componentData = agentRuntime.getComponentData(componentId);
|
|
516
|
-
|
|
517
|
-
// if (!componentData.steps) componentData.steps = {};
|
|
518
|
-
// if (!componentData.steps[step]) componentData.steps[step] = { sources: [] };
|
|
519
|
-
// componentData.steps[step].sources.push(sourceId);
|
|
520
|
-
|
|
521
|
-
// if (!componentData.stepSources) componentData.stepSources = {};
|
|
522
|
-
// if (!componentData.stepSources[sourceId]) componentData.stepSources[sourceId] = [];
|
|
523
|
-
// componentData.stepSources[sourceId].push(step);
|
|
524
|
-
|
|
525
|
-
agentRuntime.updateComponent(componentId, { step });
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async callComponent(sourceId, componentId, input?) {
|
|
529
|
-
const startTime = Date.now();
|
|
530
|
-
const agentRuntime = this.agentRuntime;
|
|
531
|
-
const componentData = this.components[componentId];
|
|
532
|
-
const component: Component = this._componentInstance[componentData.name];
|
|
533
|
-
|
|
534
|
-
const eventId = 'e-' + uid();
|
|
535
|
-
|
|
536
|
-
this.sse.send('component', {
|
|
537
|
-
eventId,
|
|
538
|
-
action: 'callStart',
|
|
539
|
-
sourceId,
|
|
540
|
-
id: componentId,
|
|
541
|
-
name: componentData.displayName,
|
|
542
|
-
title: componentData.title,
|
|
543
|
-
startTime,
|
|
544
|
-
input,
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
if (this._kill) {
|
|
548
|
-
console.warn(`Agent ${this.id} was killed, skipping component ${componentData.name}`, AccessCandidate.agent(this.id));
|
|
549
|
-
|
|
550
|
-
const output = { id: componentData.id, name: componentData.displayName, result: null, error: 'Agent killed' };
|
|
551
|
-
|
|
552
|
-
const endTime = Date.now();
|
|
553
|
-
const duration = endTime - startTime;
|
|
554
|
-
this.sse.send('component', {
|
|
555
|
-
eventId,
|
|
556
|
-
action: 'callStop',
|
|
557
|
-
sourceId,
|
|
558
|
-
id: componentId,
|
|
559
|
-
name: componentData.displayName,
|
|
560
|
-
title: componentData.title,
|
|
561
|
-
startTime,
|
|
562
|
-
endTime,
|
|
563
|
-
duration,
|
|
564
|
-
output,
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
return output;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
if (!component) {
|
|
571
|
-
const endTime = Date.now();
|
|
572
|
-
const duration = endTime - startTime;
|
|
573
|
-
|
|
574
|
-
this.sse.send('component', {
|
|
575
|
-
eventId,
|
|
576
|
-
action: 'callStop',
|
|
577
|
-
sourceId,
|
|
578
|
-
id: componentId,
|
|
579
|
-
name: componentData.displayName,
|
|
580
|
-
title: componentData.title,
|
|
581
|
-
startTime,
|
|
582
|
-
endTime,
|
|
583
|
-
duration,
|
|
584
|
-
output: { error: 'Component not found' },
|
|
585
|
-
});
|
|
586
|
-
throw new Error(`Component ${componentData.name} not found`);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
this.agentRuntime.incTag(componentId);
|
|
590
|
-
this.agentRuntime.checkCircularLimit();
|
|
591
|
-
if (this.agentRuntime.circularLimitReached) {
|
|
592
|
-
const endTime = Date.now();
|
|
593
|
-
const duration = endTime - startTime;
|
|
594
|
-
this.sse.send('component', {
|
|
595
|
-
eventId,
|
|
596
|
-
action: 'callStop',
|
|
597
|
-
sourceId,
|
|
598
|
-
id: componentId,
|
|
599
|
-
name: componentData.displayName,
|
|
600
|
-
title: componentData.title,
|
|
601
|
-
startTime,
|
|
602
|
-
endTime,
|
|
603
|
-
duration,
|
|
604
|
-
output: { error: 'Circular Calls Reached' },
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
return { error: `Circular Calls Reached` };
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const data = agentRuntime.getComponentData(componentId);
|
|
611
|
-
if (data?.output?._missing_inputs) {
|
|
612
|
-
agentRuntime.updateComponent(componentId, { output: {} });
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const _input = this.prepareComponentInput(componentId, input);
|
|
616
|
-
|
|
617
|
-
//insert log
|
|
618
|
-
const logId = AgentLogger.log(this, null, {
|
|
619
|
-
sourceId: sourceId || 'AGENT',
|
|
620
|
-
componentId,
|
|
621
|
-
domain: this.domain,
|
|
622
|
-
workflowID: this.agentRuntime.workflowReqId,
|
|
623
|
-
processID: this.agentRuntime.processID,
|
|
624
|
-
input:
|
|
625
|
-
componentData.name == 'APIEndpoint' ? (this.agentRequest.method == 'GET' ? this.agentRequest.query : this.agentRequest.body) : _input,
|
|
626
|
-
inputTimestamp: new Date().toISOString(),
|
|
627
|
-
sessionID: this.callerSessionId,
|
|
628
|
-
tags: this.sessionTag,
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
let output: any = null;
|
|
632
|
-
let missingInputs: any = [];
|
|
633
|
-
|
|
634
|
-
//agentRuntime.updateComponent(componentId, { step: agentRuntime.curStep });
|
|
635
|
-
this.updateStep(sourceId, componentId);
|
|
636
|
-
|
|
637
|
-
//first we check if the debugger is injecting an output, if yes we skip the inputs check
|
|
638
|
-
if (agentRuntime.debug) {
|
|
639
|
-
output = await agentRuntime.injectDebugOutput(componentId);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (!output) {
|
|
643
|
-
missingInputs = this.getComponentMissingInputs(componentId, _input);
|
|
644
|
-
|
|
645
|
-
if (missingInputs.length > 0) {
|
|
646
|
-
agentRuntime.updateComponent(componentId, { active: true, status: 'waiting' });
|
|
647
|
-
//check if _error output is connected to a component
|
|
648
|
-
const connections = this.connections.filter((c) => c.sourceId == componentId) || [];
|
|
649
|
-
let hasErrorHandler = false;
|
|
650
|
-
for (let connection of connections) {
|
|
651
|
-
const outputEndpoint = componentData.outputs[connection.sourceIndex];
|
|
652
|
-
if (outputEndpoint.name == '_error') {
|
|
653
|
-
hasErrorHandler = true;
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
//if (hasErrorHandler) return { id: componentData.id, name: componentData.name, result: null };
|
|
658
|
-
|
|
659
|
-
output = { _error: 'Missing inputs : ' + JSON.stringify(missingInputs), _missing_inputs: missingInputs };
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
if (!output) {
|
|
663
|
-
//the following case happens when no debugger injection was performed
|
|
664
|
-
const validationResult = await component.validateConfig(componentData);
|
|
665
|
-
if (validationResult._error) {
|
|
666
|
-
output = validationResult;
|
|
667
|
-
} else {
|
|
668
|
-
try {
|
|
669
|
-
await this.parseVariables(); //make sure that any vault variable is loaded before processing the component
|
|
670
|
-
//TODO: apply type inference here instead of in the component .process method
|
|
671
|
-
output = await component.process({ ...this.agentVariables, ..._input }, { ...componentData, eventId }, this);
|
|
672
|
-
console.debug(output, AccessCandidate.agent(this.id));
|
|
673
|
-
} catch (error: any) {
|
|
674
|
-
//this are fatal errors requiring to cancel the execution of this component.
|
|
675
|
-
console.error(
|
|
676
|
-
'Error on component process: ',
|
|
677
|
-
{ componentId, name: componentData.name, input: _input },
|
|
678
|
-
error,
|
|
679
|
-
AccessCandidate.agent(this.id)
|
|
680
|
-
);
|
|
681
|
-
if (error?.message) output = { Response: undefined, _error: error.message, _debug: error.message };
|
|
682
|
-
else output = { Response: undefined, _error: error.toString(), _debug: error.toString() };
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
const runtimeData = this.agentRuntime.getRuntimeData(componentId);
|
|
688
|
-
agentRuntime.updateComponent(componentId, { output });
|
|
689
|
-
|
|
690
|
-
if (output._in_progress) {
|
|
691
|
-
agentRuntime.updateComponent(componentId, { active: true, status: 'in_progress' });
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (output.error || output._error) {
|
|
695
|
-
//TODO : check if we need to keep loop data while clearing runtime data here
|
|
696
|
-
//in fact, output._error might be connected to a next component, in which case we need to keep the loop data
|
|
697
|
-
if (!runtimeData?._ChildLoopData?._in_progress) {
|
|
698
|
-
//don't reset if we are inside a loop, otherwise ._error branches will break the loop
|
|
699
|
-
|
|
700
|
-
this.agentRuntime.resetComponent(componentId);
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
if (logId) {
|
|
704
|
-
//update log
|
|
705
|
-
AgentLogger.log(this, logId, { error: output.error || output._error });
|
|
706
|
-
}
|
|
707
|
-
if (output.error) {
|
|
708
|
-
const endTime = Date.now();
|
|
709
|
-
const duration = endTime - startTime;
|
|
710
|
-
this.sse.send('component', {
|
|
711
|
-
eventId,
|
|
712
|
-
action: 'callStop',
|
|
713
|
-
sourceId,
|
|
714
|
-
id: componentId,
|
|
715
|
-
name: componentData.displayName,
|
|
716
|
-
title: componentData.title,
|
|
717
|
-
startTime,
|
|
718
|
-
endTime,
|
|
719
|
-
duration,
|
|
720
|
-
output: { error: output.error || output._error },
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
return [
|
|
724
|
-
{
|
|
725
|
-
id: componentData.id,
|
|
726
|
-
name: componentData.displayName,
|
|
727
|
-
result: null,
|
|
728
|
-
error: output.error || output._error,
|
|
729
|
-
_debug: output.error || output._error,
|
|
730
|
-
},
|
|
731
|
-
];
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
let results: any = [];
|
|
736
|
-
if (output /*&& !component.hasReadOutput*/ && !output._missing_inputs) {
|
|
737
|
-
AgentLogger.logTask(this, 1); //log successful task (non blocking call)
|
|
738
|
-
|
|
739
|
-
//proceed with the next component(s)
|
|
740
|
-
results = await this.callNextComponents(componentId, output).catch((error) => ({
|
|
741
|
-
error,
|
|
742
|
-
id: componentData.id,
|
|
743
|
-
name: componentData.displayName,
|
|
744
|
-
}));
|
|
745
|
-
|
|
746
|
-
//TODO : maybe handle the number of branches inside ForEach component
|
|
747
|
-
if (runtimeData._LoopData && output._in_progress && runtimeData._LoopData.branches == undefined) {
|
|
748
|
-
//handle loop branching
|
|
749
|
-
const branches = Array.isArray(results) ? results.length : 1;
|
|
750
|
-
if (output._in_progress) {
|
|
751
|
-
runtimeData._LoopData.branches = branches;
|
|
752
|
-
agentRuntime.updateRuntimeData(componentId, { _LoopData: runtimeData._LoopData });
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (results._is_leaf) {
|
|
757
|
-
//we reached the end of the execution tree, we need to check if this branch is a loop
|
|
758
|
-
delete results._is_leaf;
|
|
759
|
-
const _ChildLoopData = runtimeData._ChildLoopData;
|
|
760
|
-
if (_ChildLoopData && _ChildLoopData.parentId) {
|
|
761
|
-
const parentId = _ChildLoopData.parentId;
|
|
762
|
-
const _LoopData = this.agentRuntime.getRuntimeData(parentId)._LoopData;
|
|
763
|
-
if (_LoopData) {
|
|
764
|
-
if (!_LoopData.result) _LoopData.result = [];
|
|
765
|
-
//we are in a loop, we need to update loop parent status in order to signal that we can run the next loop cycle
|
|
766
|
-
|
|
767
|
-
let resultsCopy = JSON.parse(JSON.stringify(results));
|
|
768
|
-
if (results.result) results.result._exclude = true;
|
|
769
|
-
|
|
770
|
-
resultsCopy = await component.postProcess(resultsCopy, componentData, this);
|
|
771
|
-
|
|
772
|
-
_LoopData.result.push(resultsCopy);
|
|
773
|
-
_LoopData.branches--;
|
|
774
|
-
|
|
775
|
-
if (_LoopData.branches <= 0) {
|
|
776
|
-
agentRuntime.updateComponent(parentId, { active: true, status: '' }); //remove _in_progress status after processing all branches
|
|
777
|
-
}
|
|
778
|
-
//save the last result so that the loop parent can read it
|
|
779
|
-
agentRuntime.updateRuntimeData(parentId, { _LoopData });
|
|
780
|
-
}
|
|
781
|
-
} else {
|
|
782
|
-
//leaf but no childLoopData, is this a loop component with no children ?
|
|
783
|
-
const _LoopData = this.agentRuntime.getRuntimeData(componentId)._LoopData;
|
|
784
|
-
if (_LoopData && _LoopData.loopIndex == 1) {
|
|
785
|
-
_LoopData._in_progress = false;
|
|
786
|
-
output._in_progress = false;
|
|
787
|
-
agentRuntime.updateComponent(componentId, { active: true, status: '' });
|
|
788
|
-
agentRuntime.updateRuntimeData(componentId, { _LoopData });
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
//check if the component context is potentially needed in next cycles
|
|
795
|
-
if (!output._missing_inputs && !output._in_progress) {
|
|
796
|
-
//we processed the current component, we can now reset the runtime data and active status
|
|
797
|
-
const inLoop =
|
|
798
|
-
runtimeData?._ChildLoopData?._in_progress && runtimeData._ChildLoopData?.loopIndex < runtimeData._ChildLoopData?.loopLength;
|
|
799
|
-
if (inLoop) {
|
|
800
|
-
// loop children require to keep external runtime data, we only clear the data that was set inside the loop
|
|
801
|
-
this.clearChildLoopRuntimeComponentData(componentId);
|
|
802
|
-
agentRuntime.updateComponent(componentId, { active: true, status: 'waiting' });
|
|
803
|
-
} else {
|
|
804
|
-
this.agentRuntime.resetComponent(componentId); //also sets active to false
|
|
805
|
-
}
|
|
806
|
-
} //if inputs were missing, the output contains error information, not actual component processing output, in this case we keep the runtime data
|
|
807
|
-
|
|
808
|
-
//filter out null results
|
|
809
|
-
if (Array.isArray(results)) results = results.flat(Infinity).filter((r) => r.result != null);
|
|
810
|
-
|
|
811
|
-
if (logId) {
|
|
812
|
-
//update log
|
|
813
|
-
AgentLogger.log(this, logId, { output, outputTimestamp: '' + Date.now() });
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
//return this.agentRuntime.debug ? [results, { id: componentData.id, name: componentData.name, result: output }] : results;
|
|
817
|
-
|
|
818
|
-
const endTime = Date.now();
|
|
819
|
-
const duration = endTime - startTime;
|
|
820
|
-
this.sse.send('component', {
|
|
821
|
-
eventId,
|
|
822
|
-
action: 'callStop',
|
|
823
|
-
sourceId,
|
|
824
|
-
id: componentId,
|
|
825
|
-
name: componentData.displayName,
|
|
826
|
-
title: componentData.title,
|
|
827
|
-
startTime,
|
|
828
|
-
endTime,
|
|
829
|
-
duration,
|
|
830
|
-
output,
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
return [results, { id: componentData.id, name: componentData.displayName, result: output }];
|
|
834
|
-
}
|
|
835
|
-
JSONExpression(obj, propertyString) {
|
|
836
|
-
const properties = propertyString.split(/\.|\[|\]\.|\]\[|\]/).filter(Boolean);
|
|
837
|
-
let currentProperty = obj;
|
|
838
|
-
|
|
839
|
-
for (let property of properties) {
|
|
840
|
-
if (currentProperty === undefined || currentProperty === null) {
|
|
841
|
-
return undefined;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
currentProperty = currentProperty[property];
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
return currentProperty;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
//
|
|
851
|
-
async callNextComponents(componentId, output) {
|
|
852
|
-
const agentRuntime = this.agentRuntime;
|
|
853
|
-
//agentRuntime.incStep();
|
|
854
|
-
|
|
855
|
-
const componentData = this.components[componentId];
|
|
856
|
-
const component: Component = this._componentInstance[componentData.name];
|
|
857
|
-
|
|
858
|
-
//if (component.hasReadOutput) return [];
|
|
859
|
-
|
|
860
|
-
//get the list of connections for the current component in order to determine the next component(s) to call
|
|
861
|
-
let connections = this.connections
|
|
862
|
-
.filter((c) => c.sourceId == componentId /*|| this.alwaysActiveComponents[c.sourceId]*/)
|
|
863
|
-
.map((c) => ({ ...c, output, componentData }));
|
|
864
|
-
|
|
865
|
-
//also find connections from always active components to components with status 'waiting'
|
|
866
|
-
|
|
867
|
-
const waitingComponents = agentRuntime.getWaitingComponents();
|
|
868
|
-
const waitingComponentIds = waitingComponents.map((e) => e.id);
|
|
869
|
-
const alwaysActiveIds = Object.keys(this.agentRuntime.alwaysActiveComponents);
|
|
870
|
-
const alwaysActiveConnections = this.connections
|
|
871
|
-
.filter((c) => alwaysActiveIds.includes(c.sourceId) && waitingComponentIds.includes(c.targetId))
|
|
872
|
-
.map((c) => {
|
|
873
|
-
const output = {};
|
|
874
|
-
const waitingComponent = waitingComponents.find((e) => e.id == c.targetId);
|
|
875
|
-
const prevComponentData = this.components[c.sourceId];
|
|
876
|
-
const prevComponent: Component = this._componentInstance[prevComponentData.name];
|
|
877
|
-
const outputEndpoint = prevComponentData.outputs[c.sourceIndex];
|
|
878
|
-
output[outputEndpoint.name] = prevComponent.readOutput(outputEndpoint.name, prevComponentData, this);
|
|
879
|
-
|
|
880
|
-
return { ...c, output, componentData: this.components[c.sourceId] };
|
|
881
|
-
});
|
|
882
|
-
connections = [...connections, ...alwaysActiveConnections];
|
|
883
|
-
|
|
884
|
-
//no more components to call, return the output
|
|
885
|
-
if (!Array.isArray(connections) || connections.length == 0) {
|
|
886
|
-
return { id: componentData.id, name: componentData.name, result: output, _is_leaf: true };
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
const targetComponents = //classify connections by objects
|
|
890
|
-
connections.reduce((acc, obj) => {
|
|
891
|
-
let key = obj.targetId;
|
|
892
|
-
if (!acc[key]) {
|
|
893
|
-
acc[key] = [];
|
|
894
|
-
}
|
|
895
|
-
acc[key].push(obj);
|
|
896
|
-
return acc;
|
|
897
|
-
}, {});
|
|
898
|
-
|
|
899
|
-
const promises: any = [];
|
|
900
|
-
for (let targetId in targetComponents) {
|
|
901
|
-
const targetComponentData = this.components[targetId];
|
|
902
|
-
|
|
903
|
-
//if we are not inside an async component, we skip async branches
|
|
904
|
-
//Note : we exclude Async component from this rule because it's the one that initiates the async job
|
|
905
|
-
if (!this.async && targetComponentData.async && targetComponentData.name !== 'Async') continue;
|
|
906
|
-
|
|
907
|
-
const targetComponent: Component = this._componentInstance[targetComponentData.name];
|
|
908
|
-
const connections = targetComponents[targetId];
|
|
909
|
-
|
|
910
|
-
let _isErrorHandler = false;
|
|
911
|
-
if (Array.isArray(connections) && connections.length > 0) {
|
|
912
|
-
const nextInput = {};
|
|
913
|
-
for (let connection of connections) {
|
|
914
|
-
const output = connection.output;
|
|
915
|
-
const componentData = connection.componentData;
|
|
916
|
-
const outputEndpoint = componentData.outputs[connection.sourceIndex]; //source
|
|
917
|
-
const inputEndpoint = targetComponentData.inputs[connection.targetIndex]; //target
|
|
918
|
-
|
|
919
|
-
//outputs can be named (e.g "user:email" or "Req:body:data") in which case they refer to a path in the output object
|
|
920
|
-
const outputExpression = outputEndpoint.expression || outputEndpoint.name;
|
|
921
|
-
const outputParts = outputExpression.split('.');
|
|
922
|
-
|
|
923
|
-
const defaultOutputs = componentData.outputs.find((c) => c.default);
|
|
924
|
-
let value: any = undefined;
|
|
925
|
-
|
|
926
|
-
if (outputEndpoint.name == '_error') _isErrorHandler = true;
|
|
927
|
-
|
|
928
|
-
if (outputEndpoint.default) value = output[outputEndpoint.name] /* || null*/;
|
|
929
|
-
else {
|
|
930
|
-
if (defaultOutputs /* && output[defaultOutputs.name]?.[outputEndpoint.name]*/) {
|
|
931
|
-
value = output[defaultOutputs.name]?.[outputEndpoint.name];
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
if (/*value === null || */ value === undefined && outputParts.length >= 1) {
|
|
935
|
-
let val = this.JSONExpression(output, outputExpression);
|
|
936
|
-
if (val !== undefined) value = val;
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// if (/*value !== null && */ value !== undefined) {
|
|
940
|
-
// nextInput[inputEndpoint.name] = [...new Set([[nextInput[inputEndpoint.name], value]].flat(Infinity))].filter(
|
|
941
|
-
// (e) => e !== undefined /*&& e !== null*/,
|
|
942
|
-
// );
|
|
943
|
-
|
|
944
|
-
// if (nextInput[inputEndpoint.name].length == 1) nextInput[inputEndpoint.name] = nextInput[inputEndpoint.name][0];
|
|
945
|
-
// }
|
|
946
|
-
|
|
947
|
-
//Fix suggested by Sentinel Agent
|
|
948
|
-
if (/*value !== null && */ value !== undefined) {
|
|
949
|
-
// let combinedInput = [...[nextInput[inputEndpoint.name]].flat(), ...[value].flat()].filter(
|
|
950
|
-
// (e) => e !== undefined /*&& e !== null*/,
|
|
951
|
-
// ); // ! Deprecated
|
|
952
|
-
|
|
953
|
-
let combinedInput = _mergeInputs(nextInput[inputEndpoint.name], value).filter((e) => e !== undefined /*&& e !== null*/);
|
|
954
|
-
|
|
955
|
-
nextInput[inputEndpoint.name] = combinedInput.length === 1 ? combinedInput[0] : combinedInput;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
if (!nextInput || JSON.stringify(nextInput) == '{}') continue;
|
|
959
|
-
|
|
960
|
-
const input = this.prepareComponentInput(targetId, nextInput);
|
|
961
|
-
|
|
962
|
-
const targetComponent = this.components[targetId];
|
|
963
|
-
|
|
964
|
-
if (_isErrorHandler && targetComponent) {
|
|
965
|
-
output._error_handled = true;
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
const missingInputs = this.getComponentMissingInputs(targetId, input);
|
|
969
|
-
const status = missingInputs.length > 0 ? 'waiting' : undefined;
|
|
970
|
-
|
|
971
|
-
const sourceRuntimeData = this.agentRuntime.getRuntimeData(componentId); //We read the previous component runtime data
|
|
972
|
-
|
|
973
|
-
let _ChildLoopData = sourceRuntimeData._LoopData; //is the source a loop component ?
|
|
974
|
-
|
|
975
|
-
if (!_ChildLoopData || !_ChildLoopData._in_progress) {
|
|
976
|
-
//if it's a loop component we need to check if the loop is still in progress
|
|
977
|
-
|
|
978
|
-
_ChildLoopData = sourceRuntimeData._ChildLoopData; // if the loop is completed, check if the loop component is a nested loop, in which case we pass the parent context to the following component
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
agentRuntime.updateComponent(targetId, { active: true, input: nextInput, sourceId: componentId, status });
|
|
982
|
-
agentRuntime.updateRuntimeData(targetId, { _ChildLoopData, _LoopData: null });
|
|
983
|
-
promises.push(idPromise({ id: targetId, name: targetComponent.name, inputs: nextInput }));
|
|
984
|
-
|
|
985
|
-
if (status) {
|
|
986
|
-
//if status is set, track the component status update
|
|
987
|
-
//if not set, it means that the component is active and will be logged upon execution
|
|
988
|
-
//this can be considered as a fake log step that help us keep track of the execution tree
|
|
989
|
-
const logId = AgentLogger.log(this, null, {
|
|
990
|
-
sourceId: componentId,
|
|
991
|
-
componentId: targetId,
|
|
992
|
-
step: this.agentRuntime.curStep + 1, //we force to next step because the current step order is updated in the next runCycle()
|
|
993
|
-
domain: this.domain,
|
|
994
|
-
workflowID: this.agentRuntime.workflowReqId,
|
|
995
|
-
processID: this.agentRuntime.processID,
|
|
996
|
-
input: { __action: 'status_update', __status: status, data: nextInput },
|
|
997
|
-
inputTimestamp: new Date().toISOString(),
|
|
998
|
-
sessionID: this.callerSessionId,
|
|
999
|
-
tags: this.sessionTag,
|
|
1000
|
-
});
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
if (promises.length == 0) {
|
|
1006
|
-
return { id: componentData.id, name: componentData.name, result: output, _is_leaf: true };
|
|
1007
|
-
}
|
|
1008
|
-
const results = await Promise.all(promises);
|
|
1009
|
-
|
|
1010
|
-
//TODO : exclusive components handling
|
|
1011
|
-
//in order to run exclusive components first, we need to run them in current cycle
|
|
1012
|
-
//then we signal to the caller component that one more run cycle is needed
|
|
1013
|
-
return results.length == 1 ? results[0] : results;
|
|
1014
|
-
}
|
|
1015
|
-
private prepareComponentInput(targetId, inputs) {
|
|
1016
|
-
const rData: any = this.agentRuntime.getRuntimeData(targetId);
|
|
1017
|
-
const componentData = this.components[targetId];
|
|
1018
|
-
const rDataInput = rData?.input || {};
|
|
1019
|
-
|
|
1020
|
-
let _input = { ...rDataInput };
|
|
1021
|
-
if (inputs) {
|
|
1022
|
-
// for (let key in inputs) {
|
|
1023
|
-
// let value = inputs[key];
|
|
1024
|
-
// //_input[key] = mergeJsonData(_input[key], value);
|
|
1025
|
-
|
|
1026
|
-
// _input[key] = [...new Set([[rDataInput[key]], [value]].flat(Infinity))].filter((e) => e !== undefined /* && e !== null*/);
|
|
1027
|
-
// if (_input[key].length == 1) _input[key] = _input[key][0];
|
|
1028
|
-
// }
|
|
1029
|
-
|
|
1030
|
-
//Fix suggested by Sentinel Agent
|
|
1031
|
-
for (let key in inputs) {
|
|
1032
|
-
let value = inputs[key];
|
|
1033
|
-
// Concatenate the existing value with the new input, without using Set to preserve duplicates
|
|
1034
|
-
// _input[key] = [rDataInput[key], value].flat(Infinity).filter((e) => e !== undefined); // ! Deprecated
|
|
1035
|
-
_input[key] = _mergeInputs(rDataInput[key], value).filter((e) => e !== undefined);
|
|
1036
|
-
|
|
1037
|
-
// Simplify the array to a single value if there is only one element after flattening
|
|
1038
|
-
if (_input[key].length == 1) _input[key] = _input[key][0];
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
const readablePredecessors = this.findReadablePredecessors(targetId);
|
|
1043
|
-
for (let c of readablePredecessors) {
|
|
1044
|
-
if (c) {
|
|
1045
|
-
const predComponentData = this.components[c.id];
|
|
1046
|
-
const value = c.component.readOutput(c.output.name, predComponentData, this);
|
|
1047
|
-
if (value && c.input?.name) {
|
|
1048
|
-
if (!_input) _input = {};
|
|
1049
|
-
_input[c.input.name] = value;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
//this.saveRuntimeComponentData(targetId, { input: _input }); //TODO : check if we can use this.agentRuntime.updateRuntimeData instead (need to be carefully tested)
|
|
1055
|
-
this.agentRuntime.updateRuntimeData(targetId, { input: _input });
|
|
1056
|
-
|
|
1057
|
-
for (let input of componentData.inputs) {
|
|
1058
|
-
if (input.defaultVal && _input[input.name] === undefined) {
|
|
1059
|
-
_input[input.name] = TemplateString(input.defaultVal).parse(this.agentVariables).result;
|
|
1060
|
-
//parseTemplate(input.defaultVal, this.agentVariables, { escapeString: false, processUnmatched: false });
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
return _input;
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
public getConnectionSource(connection) {
|
|
1067
|
-
return this.components[connection.sourceId].inputs.find((e) => e.index === connection.sourceIndex);
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
public getConnectionTarget(connection) {
|
|
1071
|
-
return this.components[connection.targetId].inputs.find((e) => e.index === connection.targetIndex);
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
private recursiveTagAsyncComponents(component) {
|
|
1075
|
-
const agent = this;
|
|
1076
|
-
for (let output of component.outputs) {
|
|
1077
|
-
if (component.name == 'Async' && output.name === 'JobID') continue; //'JobID' is a special output
|
|
1078
|
-
const connected = agent.connections.filter((c) => c.sourceId === component.id && c.sourceIndex === output.index);
|
|
1079
|
-
if (!connected) continue;
|
|
1080
|
-
for (let con of connected) {
|
|
1081
|
-
const targetComponent = agent.components[con.targetId];
|
|
1082
|
-
if (!targetComponent) continue;
|
|
1083
|
-
targetComponent.async = true;
|
|
1084
|
-
this.recursiveTagAsyncComponents(targetComponent);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
private tagAsyncComponents() {
|
|
1089
|
-
const agent = this;
|
|
1090
|
-
const componentsList: any[] = Object.values(agent.components);
|
|
1091
|
-
const AsyncComponents: any[] = componentsList.filter((c) => c.name === 'Async');
|
|
1092
|
-
if (!AsyncComponents || AsyncComponents.length == 0) return;
|
|
1093
|
-
for (let AsyncComponent of AsyncComponents) {
|
|
1094
|
-
AsyncComponent.async = true;
|
|
1095
|
-
this.recursiveTagAsyncComponents(AsyncComponent);
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
//AsyncComponents.async = true;
|
|
1099
|
-
|
|
1100
|
-
//this.recursiveTagAsyncComponents(AsyncComponent, agent);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
function _mergeInputs(existing, newValue) {
|
|
1105
|
-
if (existing === undefined) {
|
|
1106
|
-
return [newValue];
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
if (!Array.isArray(existing)) {
|
|
1110
|
-
existing = [existing];
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
return [...existing, newValue];
|
|
1114
|
-
}
|
|
1
|
+
import { Component } from '@sre/Components/Component.class';
|
|
2
|
+
import { AgentLogger } from './AgentLogger.class';
|
|
3
|
+
import { AgentRequest } from './AgentRequest.class';
|
|
4
|
+
import { AgentRuntime } from './AgentRuntime.class';
|
|
5
|
+
import { AgentSettings } from './AgentSettings.class';
|
|
6
|
+
import { OSResourceMonitor } from './OSResourceMonitor';
|
|
7
|
+
import config from '@sre/config';
|
|
8
|
+
import { ControlledPromise, delay, getCurrentFormattedDate, uid } from '@sre/utils/index';
|
|
9
|
+
|
|
10
|
+
import { Logger } from '@sre/helpers/Log.helper';
|
|
11
|
+
import { TemplateString } from '@sre/helpers/TemplateString.helper';
|
|
12
|
+
import { AgentSSE } from './AgentSSE.class';
|
|
13
|
+
import { IAgent } from '@sre/types/Agent.types';
|
|
14
|
+
import { IModelsProviderRequest, ModelsProviderConnector } from '@sre/LLMManager/ModelsProvider.service/ModelsProviderConnector';
|
|
15
|
+
import { ConnectorService } from '@sre/Core/ConnectorsService';
|
|
16
|
+
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
17
|
+
|
|
18
|
+
const console = Logger('Agent');
|
|
19
|
+
const idPromise = (id) => id;
|
|
20
|
+
const MAX_LATENCY = 50;
|
|
21
|
+
|
|
22
|
+
export class Agent implements IAgent {
|
|
23
|
+
public name: any;
|
|
24
|
+
public data: any;
|
|
25
|
+
public teamId: any;
|
|
26
|
+
public components: any;
|
|
27
|
+
public connections: any;
|
|
28
|
+
public endpoints: any = {};
|
|
29
|
+
public sessionId;
|
|
30
|
+
public sessionTag = '';
|
|
31
|
+
public callerSessionId;
|
|
32
|
+
public apiBasePath = '/api';
|
|
33
|
+
public agentRuntime: AgentRuntime | any;
|
|
34
|
+
|
|
35
|
+
public usingTestDomain = false;
|
|
36
|
+
public domain = '';
|
|
37
|
+
public debugSessionEnabled = false;
|
|
38
|
+
public circularLimit = 100; //TODO : make it configurable from agent settings
|
|
39
|
+
public version = '';
|
|
40
|
+
|
|
41
|
+
//public baseUrl = '';
|
|
42
|
+
public agentVariables: any = {};
|
|
43
|
+
private _kill = false;
|
|
44
|
+
//public agentRequest: Request | AgentRequest | any;
|
|
45
|
+
public async = false;
|
|
46
|
+
public jobID = '';
|
|
47
|
+
public planInfo: any = {};
|
|
48
|
+
public callback: (data: any) => void;
|
|
49
|
+
|
|
50
|
+
public agentRequest: AgentRequest;
|
|
51
|
+
public sse: AgentSSE;
|
|
52
|
+
public modelsProvider: IModelsProviderRequest;
|
|
53
|
+
|
|
54
|
+
private _componentInstance = {};
|
|
55
|
+
|
|
56
|
+
public get ComponentInstances() {
|
|
57
|
+
return this._componentInstance;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private _componentInstancesLoader = new ControlledPromise(() => {});
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
public id,
|
|
64
|
+
agentData,
|
|
65
|
+
public agentSettings: AgentSettings,
|
|
66
|
+
agentRequest?: AgentRequest | any //private req: express.Request,
|
|
67
|
+
) {
|
|
68
|
+
//this.agentRequest = new AgentRequest(req);
|
|
69
|
+
const json = typeof agentData === 'string' ? JSON.parse(agentData) : agentData;
|
|
70
|
+
this.data = json.connections && json.components ? json : json.data;
|
|
71
|
+
if (!this.data) this.data = { name: '', connections: [], components: [] };
|
|
72
|
+
//this.agentVariables = json.data.variables || {};
|
|
73
|
+
|
|
74
|
+
this.name = this.data.name;
|
|
75
|
+
this.version = this.data.agentVersion || ''; //when version is not set we load the latest dev version
|
|
76
|
+
this.teamId = this.data.teamId;
|
|
77
|
+
this.connections = this.data.connections;
|
|
78
|
+
this.debugSessionEnabled = this.data.debugSessionEnabled;
|
|
79
|
+
this.usingTestDomain = this.data.usingTestDomain;
|
|
80
|
+
|
|
81
|
+
this.agentVariables = this.data.variables || {};
|
|
82
|
+
|
|
83
|
+
const endpoints = this.data.components.filter((c) => c.name == 'APIEndpoint');
|
|
84
|
+
for (let endpoint of endpoints) {
|
|
85
|
+
let method = endpoint.data.method || 'POST';
|
|
86
|
+
method = method.toUpperCase();
|
|
87
|
+
if (!this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`])
|
|
88
|
+
this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`] = {};
|
|
89
|
+
this.endpoints[`${this.apiBasePath}/${endpoint.data.endpoint}`][method] = endpoint;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.components = {};
|
|
93
|
+
for (let component of this.data.components) {
|
|
94
|
+
//FIXME : this does not persist in debug mode, it breaks key value mem logic
|
|
95
|
+
|
|
96
|
+
this.components[component.id] = component;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (let connection of this.data.connections) {
|
|
100
|
+
const sourceComponent = this.components[connection.sourceId];
|
|
101
|
+
const targetComponent = this.components[connection.targetId];
|
|
102
|
+
|
|
103
|
+
//if connections ids passed as names, we convert them to indexes
|
|
104
|
+
//TODO : harmonize connections formats
|
|
105
|
+
const sourceIndex =
|
|
106
|
+
typeof connection.sourceIndex === 'number'
|
|
107
|
+
? connection.sourceIndex
|
|
108
|
+
: sourceComponent.outputs.findIndex((o) => o.name == connection.sourceIndex);
|
|
109
|
+
const targetIndex =
|
|
110
|
+
typeof connection.targetIndex === 'number'
|
|
111
|
+
? connection.targetIndex
|
|
112
|
+
: targetComponent.inputs.findIndex((i) => i.name == connection.targetIndex);
|
|
113
|
+
|
|
114
|
+
connection.sourceIndex = sourceIndex;
|
|
115
|
+
connection.targetIndex = targetIndex;
|
|
116
|
+
|
|
117
|
+
const output = sourceComponent.outputs[sourceIndex];
|
|
118
|
+
output.index = sourceIndex; // legacy ids (numbers)
|
|
119
|
+
|
|
120
|
+
const input = targetComponent.inputs[targetIndex];
|
|
121
|
+
input.index = targetIndex;
|
|
122
|
+
|
|
123
|
+
if (!output.next) output.next = [];
|
|
124
|
+
output.next.push(targetComponent.id);
|
|
125
|
+
|
|
126
|
+
if (!input.prev) input.prev = [];
|
|
127
|
+
input.prev.push(sourceComponent.id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.tagAsyncComponents();
|
|
131
|
+
|
|
132
|
+
if (agentRequest) {
|
|
133
|
+
this.setRequest(agentRequest);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.sse = new AgentSSE(this);
|
|
137
|
+
//this.settings = new AgentSettings(this.id);
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
//add hosted components to the available instances
|
|
141
|
+
//hosted components are custom components that are specific to some SRE instances implementations : e.g Shell component is only available to local SRE
|
|
142
|
+
const componentConnector = ConnectorService.getComponentConnector();
|
|
143
|
+
componentConnector
|
|
144
|
+
.requester(AccessCandidate.agent(id))
|
|
145
|
+
.getAll()
|
|
146
|
+
.then((customComponents) => {
|
|
147
|
+
this._componentInstance = { ...this._componentInstance, ...customComponents };
|
|
148
|
+
this._componentInstancesLoader.resolve(true);
|
|
149
|
+
});
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn('Could not load custom components', AccessCandidate.agent(this.id));
|
|
152
|
+
this._componentInstancesLoader.reject('Could not load custom components');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const modelsProvider: ModelsProviderConnector = ConnectorService.getModelsProviderConnector();
|
|
156
|
+
if (modelsProvider.valid) {
|
|
157
|
+
this.modelsProvider = modelsProvider.agent(id);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Add a SSE connection to the agent
|
|
163
|
+
* @param sseSource - The SSE source to add
|
|
164
|
+
* @param monitorId - The monitor ID to add
|
|
165
|
+
*/
|
|
166
|
+
public addSSE(sseSource: Response | AgentSSE, monitorId?: string) {
|
|
167
|
+
if (sseSource instanceof AgentSSE) {
|
|
168
|
+
for (const [monitorId, res] of sseSource) {
|
|
169
|
+
this.sse.add(res, monitorId); // add each connection to the new sse
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
const id = monitorId || Math.random().toString(36).substring(2, 15);
|
|
173
|
+
this.sse.add(sseSource, id);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
public setRequest(agentRequest: AgentRequest | any) {
|
|
177
|
+
if (this.agentRequest) return;
|
|
178
|
+
this.agentRequest = agentRequest;
|
|
179
|
+
this.agentRequest = agentRequest;
|
|
180
|
+
const dateTime = getCurrentFormattedDate();
|
|
181
|
+
this.sessionId = 'rt-' + (this.agentRequest.sessionID || dateTime + '.' + uid());
|
|
182
|
+
|
|
183
|
+
const sessionTags = this?.agentRequest?.headers['x-session-tag'];
|
|
184
|
+
if (sessionTags) this.sessionTag += this.sessionTag ? `,${sessionTags}` : sessionTags;
|
|
185
|
+
|
|
186
|
+
var regex = new RegExp(`^\/v[0-9]+(\.[0-9]+)?${this.apiBasePath}\/(.*)`);
|
|
187
|
+
if (this.agentRequest?.path?.startsWith(`${this.apiBasePath}/`) || this.agentRequest?.path?.match(regex)) {
|
|
188
|
+
//we only need runtime context for API calls
|
|
189
|
+
this.agentRuntime = new AgentRuntime(this);
|
|
190
|
+
this.callerSessionId =
|
|
191
|
+
this?.agentRequest?.headers['x-caller-session-id']?.substring(0, 256) || this.agentRuntime.workflowReqId || this.sessionId;
|
|
192
|
+
} else {
|
|
193
|
+
this.agentRuntime = AgentRuntime.dummy;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
public setCallback(callback: (data: any) => void) {
|
|
197
|
+
this.callback = callback;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public kill() {
|
|
201
|
+
this._kill = true;
|
|
202
|
+
}
|
|
203
|
+
public isKilled() {
|
|
204
|
+
return this._kill;
|
|
205
|
+
}
|
|
206
|
+
private async parseVariables() {
|
|
207
|
+
//parse vault agent variables
|
|
208
|
+
if (typeof this.agentVariables === 'object') {
|
|
209
|
+
for (let key in this.agentVariables) {
|
|
210
|
+
const value = this.agentVariables[key];
|
|
211
|
+
if (value.startsWith('{{') && value.endsWith('}}')) {
|
|
212
|
+
//this.agentVariables[key] = (await parseKey(value, this.teamId)) || '';
|
|
213
|
+
this.agentVariables[key] = await TemplateString(value).parseTeamKeysAsync(this.teamId).asyncResult;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async process(endpointPath, input) {
|
|
220
|
+
await this.agentRuntime.ready();
|
|
221
|
+
|
|
222
|
+
//TODO: replace endpointPath + input params with a single agentRequest object. (This will require intensive regression testing)
|
|
223
|
+
let result: any;
|
|
224
|
+
let dbgSession: any = null;
|
|
225
|
+
let sessionClosed = false;
|
|
226
|
+
|
|
227
|
+
const eventId = 'e-' + uid();
|
|
228
|
+
const startTime = Date.now();
|
|
229
|
+
|
|
230
|
+
this.sse.send('agent', {
|
|
231
|
+
eventId,
|
|
232
|
+
action: 'callStart',
|
|
233
|
+
endpointPath,
|
|
234
|
+
id: this.id,
|
|
235
|
+
name: this.name,
|
|
236
|
+
startTime,
|
|
237
|
+
input,
|
|
238
|
+
});
|
|
239
|
+
//this.agentRuntime.checkRuntimeContext();
|
|
240
|
+
//insert log
|
|
241
|
+
const logId = AgentLogger.log(this, null, {
|
|
242
|
+
sourceId: endpointPath,
|
|
243
|
+
componentId: `AGENT`,
|
|
244
|
+
domain: this.domain,
|
|
245
|
+
input,
|
|
246
|
+
workflowID: this.agentRuntime.workflowReqId,
|
|
247
|
+
processID: this.agentRuntime.processID,
|
|
248
|
+
inputTimestamp: new Date().toISOString(),
|
|
249
|
+
sessionID: this.callerSessionId,
|
|
250
|
+
tags: this.sessionTag,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const method = this.agentRequest.method.toUpperCase();
|
|
254
|
+
const endpoint = this.endpoints[endpointPath]?.[method];
|
|
255
|
+
|
|
256
|
+
//first check if this is a debug session, and return debug result if it's the case
|
|
257
|
+
if (this.agentRuntime.debug) {
|
|
258
|
+
if (!endpoint && this.agentRequest.path != '/api/') {
|
|
259
|
+
if (logId) AgentLogger.log(this, logId, { error: `Endpoint ${method} ${endpointPath} Not Found` });
|
|
260
|
+
|
|
261
|
+
throw new Error(`Endpoint ${method} ${endpointPath} Not Found`);
|
|
262
|
+
}
|
|
263
|
+
let dbgResult: any;
|
|
264
|
+
//let dbgResult: any = await this.agentRuntime.readState(true); //is this a debug read reqeust ?
|
|
265
|
+
|
|
266
|
+
if (!dbgResult) dbgResult = await this.agentRuntime.runCycle(); //no, is this a step over request ?
|
|
267
|
+
|
|
268
|
+
// result = dbgResult?.state;
|
|
269
|
+
// dbgSession = dbgResult?.dbgSession;
|
|
270
|
+
// sessionClosed = dbgResult?.sessionClosed;
|
|
271
|
+
if (dbgResult && typeof dbgResult?.state !== 'undefined') {
|
|
272
|
+
this.agentRuntime.sync();
|
|
273
|
+
if (dbgResult?.finalResult) {
|
|
274
|
+
dbgResult.finalResult = await this.postProcess(dbgResult.finalResult).catch((error) => ({ error }));
|
|
275
|
+
}
|
|
276
|
+
return dbgResult;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!endpoint) {
|
|
281
|
+
if (logId) AgentLogger.log(this, logId, { error: `Endpoint ${method} ${endpointPath} Not Found` });
|
|
282
|
+
const endTime = Date.now();
|
|
283
|
+
const duration = endTime - startTime;
|
|
284
|
+
this.sse.send('agent', {
|
|
285
|
+
eventId,
|
|
286
|
+
action: 'callStop',
|
|
287
|
+
endpointPath,
|
|
288
|
+
id: this.id,
|
|
289
|
+
name: this.name,
|
|
290
|
+
startTime,
|
|
291
|
+
endTime,
|
|
292
|
+
duration,
|
|
293
|
+
input,
|
|
294
|
+
error: `Endpoint ${method} ${endpointPath} Not Found`,
|
|
295
|
+
});
|
|
296
|
+
throw new Error(`Endpoint ${method} ${endpointPath} Not Found`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
this.agentRuntime.updateComponent(endpoint.id, { active: true, input, sourceId: null });
|
|
300
|
+
|
|
301
|
+
let step;
|
|
302
|
+
do {
|
|
303
|
+
step = await this.agentRuntime.runCycle();
|
|
304
|
+
|
|
305
|
+
//adjust latency based on cpu load
|
|
306
|
+
const qosLatency = Math.floor(OSResourceMonitor.cpu.load * MAX_LATENCY || 0);
|
|
307
|
+
|
|
308
|
+
await delay(10 + qosLatency);
|
|
309
|
+
} while (!step?.finalResult && !this._kill);
|
|
310
|
+
|
|
311
|
+
if (this._kill) {
|
|
312
|
+
const endTime = Date.now();
|
|
313
|
+
const duration = endTime - startTime;
|
|
314
|
+
this.sse.send('agent', {
|
|
315
|
+
eventId,
|
|
316
|
+
action: 'callStop',
|
|
317
|
+
endpointPath,
|
|
318
|
+
id: this.id,
|
|
319
|
+
name: this.name,
|
|
320
|
+
startTime,
|
|
321
|
+
endTime,
|
|
322
|
+
duration,
|
|
323
|
+
input,
|
|
324
|
+
error: 'Agent killed',
|
|
325
|
+
});
|
|
326
|
+
console.warn(`Agent ${this.id} was killed`, AccessCandidate.agent(this.id));
|
|
327
|
+
return { error: 'Agent killed' };
|
|
328
|
+
}
|
|
329
|
+
result = await this.postProcess(step?.finalResult).catch((error) => ({ error }));
|
|
330
|
+
|
|
331
|
+
//post process all results
|
|
332
|
+
if (this.agentRuntime.circularLimitReached) {
|
|
333
|
+
const circularLimitData = this.agentRuntime.circularLimitReached;
|
|
334
|
+
result = { error: `Circular Calls Limit Reached on ${circularLimitData}. Current circular limit is ${this.circularLimit}` };
|
|
335
|
+
|
|
336
|
+
const endTime = Date.now();
|
|
337
|
+
const duration = endTime - startTime;
|
|
338
|
+
this.sse.send('agent', {
|
|
339
|
+
eventId,
|
|
340
|
+
action: 'callStop',
|
|
341
|
+
endpointPath,
|
|
342
|
+
id: this.id,
|
|
343
|
+
name: this.name,
|
|
344
|
+
startTime,
|
|
345
|
+
endTime,
|
|
346
|
+
duration,
|
|
347
|
+
input,
|
|
348
|
+
error: result.error,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
throw new Error(`Circular Calls Limit Reached on ${circularLimitData}. Current circular limit is ${this.circularLimit}`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (logId) AgentLogger.log(this, logId, { outputTimestamp: '' + Date.now(), result });
|
|
355
|
+
|
|
356
|
+
this.updateTasksCount(); //Important, don't use await here, we need the call to be non blocking
|
|
357
|
+
|
|
358
|
+
const endTime = Date.now();
|
|
359
|
+
const duration = endTime - startTime;
|
|
360
|
+
this.sse.send('agent', {
|
|
361
|
+
eventId,
|
|
362
|
+
action: 'callStop',
|
|
363
|
+
endpointPath,
|
|
364
|
+
id: this.id,
|
|
365
|
+
name: this.name,
|
|
366
|
+
startTime,
|
|
367
|
+
endTime,
|
|
368
|
+
duration,
|
|
369
|
+
input,
|
|
370
|
+
result,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
//FIXME: does debug call ever reach this point ?
|
|
374
|
+
return this.agentRuntime.debug ? { state: result, dbgSession, sessionClosed } : result;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private async updateTasksCount() {
|
|
378
|
+
//tasks count update logic
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
public async postProcess(result) {
|
|
382
|
+
if (Array.isArray(result)) result = result.flat(Infinity);
|
|
383
|
+
if (!Array.isArray(result)) result = [result];
|
|
384
|
+
|
|
385
|
+
//filter out handled errors
|
|
386
|
+
result = result.filter((r) => !(r?.result?._error && r?.result?._error_handled));
|
|
387
|
+
|
|
388
|
+
for (let i = 0; i < result.length; i++) {
|
|
389
|
+
const _result = result[i];
|
|
390
|
+
if (!_result) continue;
|
|
391
|
+
if (_result._debug) delete _result._debug;
|
|
392
|
+
if (_result._debug_time) delete _result._debug_time;
|
|
393
|
+
const _componentData = this.components[_result.id];
|
|
394
|
+
if (!_componentData) continue;
|
|
395
|
+
const _component: Component = this._componentInstance[_componentData.name];
|
|
396
|
+
if (!_component) continue;
|
|
397
|
+
//if (_component.hasPostProcess) {
|
|
398
|
+
const postProcessResult = await _component.postProcess(_result, _componentData, this).catch((error) => ({ error }));
|
|
399
|
+
|
|
400
|
+
result[i] = postProcessResult;
|
|
401
|
+
//}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (result.length == 1) result = result[0];
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// public saveRuntimeComponentData(componentId, data) {
|
|
409
|
+
// //let runtimeData = { ...this.agentRuntime.getRuntimeData(componentId), ...data };
|
|
410
|
+
// //this.agentRuntime.updateComponent(componentId, { runtimeData: data });
|
|
411
|
+
|
|
412
|
+
// this.agentRuntime.saveRuntimeComponentData(componentId, data);
|
|
413
|
+
// }
|
|
414
|
+
// private getRuntimeData(componentId) {
|
|
415
|
+
// // const componentData = this.agentRuntime.getComponentData(componentId);
|
|
416
|
+
// // if (!componentData) return {};
|
|
417
|
+
// // const rData = componentData.runtimeData || {};
|
|
418
|
+
|
|
419
|
+
// return this.agentRuntime.getRuntimeData(componentId);
|
|
420
|
+
// }
|
|
421
|
+
|
|
422
|
+
// private clearRuntimeComponentData(componentId) {
|
|
423
|
+
// this.agentRuntime.resetComponent(componentId);
|
|
424
|
+
// }
|
|
425
|
+
|
|
426
|
+
private hasLoopAncestor(inputEntry) {
|
|
427
|
+
if (!inputEntry.prev) return false;
|
|
428
|
+
for (let prevId of inputEntry.prev) {
|
|
429
|
+
const prevComponentData = this.components[prevId];
|
|
430
|
+
if (prevComponentData.name == 'ForEach') return true;
|
|
431
|
+
|
|
432
|
+
for (let inputEntry of prevComponentData.inputs) {
|
|
433
|
+
if (this.hasLoopAncestor(inputEntry)) return true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private clearChildLoopRuntimeComponentData(componentId) {
|
|
439
|
+
const componentData = this.components[componentId];
|
|
440
|
+
const runtimeData = this.agentRuntime.getRuntimeData(componentId);
|
|
441
|
+
if (runtimeData._ChildLoopData) {
|
|
442
|
+
for (let inputEntry of componentData.inputs) {
|
|
443
|
+
if (this.hasLoopAncestor(inputEntry)) {
|
|
444
|
+
delete runtimeData.input[inputEntry.name];
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
private getComponentMissingInputs(componentId, _input) {
|
|
450
|
+
let missingInputs: any = [];
|
|
451
|
+
const componentData = this.components[componentId];
|
|
452
|
+
const component: Component = this._componentInstance[componentData.name];
|
|
453
|
+
if (component.alwaysActive) return missingInputs;
|
|
454
|
+
|
|
455
|
+
const readablePredecessors = this.findReadablePredecessors(componentId);
|
|
456
|
+
const readableInputNames = {};
|
|
457
|
+
for (let pred of readablePredecessors) {
|
|
458
|
+
if (pred) {
|
|
459
|
+
readableInputNames[pred.input.name] = pred;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
//readablePredecessors.map((e) => e.input.name);
|
|
463
|
+
|
|
464
|
+
const allInputIndexes = this.connections.filter((c) => c.targetId == componentId).map((e) => e.targetIndex);
|
|
465
|
+
const allInputs = componentData.inputs.filter((r) => allInputIndexes.includes(r.index));
|
|
466
|
+
|
|
467
|
+
if (Array.isArray(allInputs) && allInputs.length > 0) {
|
|
468
|
+
//if the next component has named inputs
|
|
469
|
+
for (let input of allInputs) {
|
|
470
|
+
if (input.optional) continue;
|
|
471
|
+
if (readableInputNames[input.name]) {
|
|
472
|
+
const pred = readableInputNames[input.name];
|
|
473
|
+
const component: Component = pred.component;
|
|
474
|
+
const predComponentData = this.components[pred.id];
|
|
475
|
+
const foundOutput = component.hasOutput(pred.output.name, predComponentData, this);
|
|
476
|
+
if (foundOutput) continue; //if the input is readable, skip it, because we can read it's value when needed. Readable inputs are non blocking
|
|
477
|
+
}
|
|
478
|
+
if (typeof _input[input.name] == 'undefined' /* || _input[input.name] == null*/) {
|
|
479
|
+
missingInputs.push(input.name);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return missingInputs;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
public findReadablePredecessors(componentId) {
|
|
488
|
+
const componentData = this.components[componentId];
|
|
489
|
+
const component: Component = this._componentInstance[componentData.name];
|
|
490
|
+
|
|
491
|
+
const connections = this.connections.filter((c) => c.targetId == componentId);
|
|
492
|
+
const readablePredecessors = connections.map((c) => {
|
|
493
|
+
//this.components[c.sourceId])
|
|
494
|
+
const sourceComponentData = this.components[c.sourceId];
|
|
495
|
+
const sourceComponent: Component = this._componentInstance[sourceComponentData.name];
|
|
496
|
+
const output = sourceComponentData.outputs[c.sourceIndex];
|
|
497
|
+
const input = componentData.inputs[c.targetIndex];
|
|
498
|
+
if (sourceComponent.hasReadOutput) {
|
|
499
|
+
return { output, input, component: sourceComponent, id: c.sourceId };
|
|
500
|
+
}
|
|
501
|
+
return null;
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
return readablePredecessors.filter((e) => e != null);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
*
|
|
509
|
+
* @param sourceId
|
|
510
|
+
* @param componentId
|
|
511
|
+
*/
|
|
512
|
+
private updateStep(sourceId, componentId) {
|
|
513
|
+
const agentRuntime = this.agentRuntime;
|
|
514
|
+
const step = agentRuntime.curStep;
|
|
515
|
+
//const componentData = agentRuntime.getComponentData(componentId);
|
|
516
|
+
|
|
517
|
+
// if (!componentData.steps) componentData.steps = {};
|
|
518
|
+
// if (!componentData.steps[step]) componentData.steps[step] = { sources: [] };
|
|
519
|
+
// componentData.steps[step].sources.push(sourceId);
|
|
520
|
+
|
|
521
|
+
// if (!componentData.stepSources) componentData.stepSources = {};
|
|
522
|
+
// if (!componentData.stepSources[sourceId]) componentData.stepSources[sourceId] = [];
|
|
523
|
+
// componentData.stepSources[sourceId].push(step);
|
|
524
|
+
|
|
525
|
+
agentRuntime.updateComponent(componentId, { step });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
async callComponent(sourceId, componentId, input?) {
|
|
529
|
+
const startTime = Date.now();
|
|
530
|
+
const agentRuntime = this.agentRuntime;
|
|
531
|
+
const componentData = this.components[componentId];
|
|
532
|
+
const component: Component = this._componentInstance[componentData.name];
|
|
533
|
+
|
|
534
|
+
const eventId = 'e-' + uid();
|
|
535
|
+
|
|
536
|
+
this.sse.send('component', {
|
|
537
|
+
eventId,
|
|
538
|
+
action: 'callStart',
|
|
539
|
+
sourceId,
|
|
540
|
+
id: componentId,
|
|
541
|
+
name: componentData.displayName,
|
|
542
|
+
title: componentData.title,
|
|
543
|
+
startTime,
|
|
544
|
+
input,
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
if (this._kill) {
|
|
548
|
+
console.warn(`Agent ${this.id} was killed, skipping component ${componentData.name}`, AccessCandidate.agent(this.id));
|
|
549
|
+
|
|
550
|
+
const output = { id: componentData.id, name: componentData.displayName, result: null, error: 'Agent killed' };
|
|
551
|
+
|
|
552
|
+
const endTime = Date.now();
|
|
553
|
+
const duration = endTime - startTime;
|
|
554
|
+
this.sse.send('component', {
|
|
555
|
+
eventId,
|
|
556
|
+
action: 'callStop',
|
|
557
|
+
sourceId,
|
|
558
|
+
id: componentId,
|
|
559
|
+
name: componentData.displayName,
|
|
560
|
+
title: componentData.title,
|
|
561
|
+
startTime,
|
|
562
|
+
endTime,
|
|
563
|
+
duration,
|
|
564
|
+
output,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
return output;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (!component) {
|
|
571
|
+
const endTime = Date.now();
|
|
572
|
+
const duration = endTime - startTime;
|
|
573
|
+
|
|
574
|
+
this.sse.send('component', {
|
|
575
|
+
eventId,
|
|
576
|
+
action: 'callStop',
|
|
577
|
+
sourceId,
|
|
578
|
+
id: componentId,
|
|
579
|
+
name: componentData.displayName,
|
|
580
|
+
title: componentData.title,
|
|
581
|
+
startTime,
|
|
582
|
+
endTime,
|
|
583
|
+
duration,
|
|
584
|
+
output: { error: 'Component not found' },
|
|
585
|
+
});
|
|
586
|
+
throw new Error(`Component ${componentData.name} not found`);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
this.agentRuntime.incTag(componentId);
|
|
590
|
+
this.agentRuntime.checkCircularLimit();
|
|
591
|
+
if (this.agentRuntime.circularLimitReached) {
|
|
592
|
+
const endTime = Date.now();
|
|
593
|
+
const duration = endTime - startTime;
|
|
594
|
+
this.sse.send('component', {
|
|
595
|
+
eventId,
|
|
596
|
+
action: 'callStop',
|
|
597
|
+
sourceId,
|
|
598
|
+
id: componentId,
|
|
599
|
+
name: componentData.displayName,
|
|
600
|
+
title: componentData.title,
|
|
601
|
+
startTime,
|
|
602
|
+
endTime,
|
|
603
|
+
duration,
|
|
604
|
+
output: { error: 'Circular Calls Reached' },
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
return { error: `Circular Calls Reached` };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const data = agentRuntime.getComponentData(componentId);
|
|
611
|
+
if (data?.output?._missing_inputs) {
|
|
612
|
+
agentRuntime.updateComponent(componentId, { output: {} });
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const _input = this.prepareComponentInput(componentId, input);
|
|
616
|
+
|
|
617
|
+
//insert log
|
|
618
|
+
const logId = AgentLogger.log(this, null, {
|
|
619
|
+
sourceId: sourceId || 'AGENT',
|
|
620
|
+
componentId,
|
|
621
|
+
domain: this.domain,
|
|
622
|
+
workflowID: this.agentRuntime.workflowReqId,
|
|
623
|
+
processID: this.agentRuntime.processID,
|
|
624
|
+
input:
|
|
625
|
+
componentData.name == 'APIEndpoint' ? (this.agentRequest.method == 'GET' ? this.agentRequest.query : this.agentRequest.body) : _input,
|
|
626
|
+
inputTimestamp: new Date().toISOString(),
|
|
627
|
+
sessionID: this.callerSessionId,
|
|
628
|
+
tags: this.sessionTag,
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
let output: any = null;
|
|
632
|
+
let missingInputs: any = [];
|
|
633
|
+
|
|
634
|
+
//agentRuntime.updateComponent(componentId, { step: agentRuntime.curStep });
|
|
635
|
+
this.updateStep(sourceId, componentId);
|
|
636
|
+
|
|
637
|
+
//first we check if the debugger is injecting an output, if yes we skip the inputs check
|
|
638
|
+
if (agentRuntime.debug) {
|
|
639
|
+
output = await agentRuntime.injectDebugOutput(componentId);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (!output) {
|
|
643
|
+
missingInputs = this.getComponentMissingInputs(componentId, _input);
|
|
644
|
+
|
|
645
|
+
if (missingInputs.length > 0) {
|
|
646
|
+
agentRuntime.updateComponent(componentId, { active: true, status: 'waiting' });
|
|
647
|
+
//check if _error output is connected to a component
|
|
648
|
+
const connections = this.connections.filter((c) => c.sourceId == componentId) || [];
|
|
649
|
+
let hasErrorHandler = false;
|
|
650
|
+
for (let connection of connections) {
|
|
651
|
+
const outputEndpoint = componentData.outputs[connection.sourceIndex];
|
|
652
|
+
if (outputEndpoint.name == '_error') {
|
|
653
|
+
hasErrorHandler = true;
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
//if (hasErrorHandler) return { id: componentData.id, name: componentData.name, result: null };
|
|
658
|
+
|
|
659
|
+
output = { _error: 'Missing inputs : ' + JSON.stringify(missingInputs), _missing_inputs: missingInputs };
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (!output) {
|
|
663
|
+
//the following case happens when no debugger injection was performed
|
|
664
|
+
const validationResult = await component.validateConfig(componentData);
|
|
665
|
+
if (validationResult._error) {
|
|
666
|
+
output = validationResult;
|
|
667
|
+
} else {
|
|
668
|
+
try {
|
|
669
|
+
await this.parseVariables(); //make sure that any vault variable is loaded before processing the component
|
|
670
|
+
//TODO: apply type inference here instead of in the component .process method
|
|
671
|
+
output = await component.process({ ...this.agentVariables, ..._input }, { ...componentData, eventId }, this);
|
|
672
|
+
console.debug(output, AccessCandidate.agent(this.id));
|
|
673
|
+
} catch (error: any) {
|
|
674
|
+
//this are fatal errors requiring to cancel the execution of this component.
|
|
675
|
+
console.error(
|
|
676
|
+
'Error on component process: ',
|
|
677
|
+
{ componentId, name: componentData.name, input: _input },
|
|
678
|
+
error,
|
|
679
|
+
AccessCandidate.agent(this.id)
|
|
680
|
+
);
|
|
681
|
+
if (error?.message) output = { Response: undefined, _error: error.message, _debug: error.message };
|
|
682
|
+
else output = { Response: undefined, _error: error.toString(), _debug: error.toString() };
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const runtimeData = this.agentRuntime.getRuntimeData(componentId);
|
|
688
|
+
agentRuntime.updateComponent(componentId, { output });
|
|
689
|
+
|
|
690
|
+
if (output._in_progress) {
|
|
691
|
+
agentRuntime.updateComponent(componentId, { active: true, status: 'in_progress' });
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (output.error || output._error) {
|
|
695
|
+
//TODO : check if we need to keep loop data while clearing runtime data here
|
|
696
|
+
//in fact, output._error might be connected to a next component, in which case we need to keep the loop data
|
|
697
|
+
if (!runtimeData?._ChildLoopData?._in_progress) {
|
|
698
|
+
//don't reset if we are inside a loop, otherwise ._error branches will break the loop
|
|
699
|
+
|
|
700
|
+
this.agentRuntime.resetComponent(componentId);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (logId) {
|
|
704
|
+
//update log
|
|
705
|
+
AgentLogger.log(this, logId, { error: output.error || output._error });
|
|
706
|
+
}
|
|
707
|
+
if (output.error) {
|
|
708
|
+
const endTime = Date.now();
|
|
709
|
+
const duration = endTime - startTime;
|
|
710
|
+
this.sse.send('component', {
|
|
711
|
+
eventId,
|
|
712
|
+
action: 'callStop',
|
|
713
|
+
sourceId,
|
|
714
|
+
id: componentId,
|
|
715
|
+
name: componentData.displayName,
|
|
716
|
+
title: componentData.title,
|
|
717
|
+
startTime,
|
|
718
|
+
endTime,
|
|
719
|
+
duration,
|
|
720
|
+
output: { error: output.error || output._error },
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
return [
|
|
724
|
+
{
|
|
725
|
+
id: componentData.id,
|
|
726
|
+
name: componentData.displayName,
|
|
727
|
+
result: null,
|
|
728
|
+
error: output.error || output._error,
|
|
729
|
+
_debug: output.error || output._error,
|
|
730
|
+
},
|
|
731
|
+
];
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
let results: any = [];
|
|
736
|
+
if (output /*&& !component.hasReadOutput*/ && !output._missing_inputs) {
|
|
737
|
+
AgentLogger.logTask(this, 1); //log successful task (non blocking call)
|
|
738
|
+
|
|
739
|
+
//proceed with the next component(s)
|
|
740
|
+
results = await this.callNextComponents(componentId, output).catch((error) => ({
|
|
741
|
+
error,
|
|
742
|
+
id: componentData.id,
|
|
743
|
+
name: componentData.displayName,
|
|
744
|
+
}));
|
|
745
|
+
|
|
746
|
+
//TODO : maybe handle the number of branches inside ForEach component
|
|
747
|
+
if (runtimeData._LoopData && output._in_progress && runtimeData._LoopData.branches == undefined) {
|
|
748
|
+
//handle loop branching
|
|
749
|
+
const branches = Array.isArray(results) ? results.length : 1;
|
|
750
|
+
if (output._in_progress) {
|
|
751
|
+
runtimeData._LoopData.branches = branches;
|
|
752
|
+
agentRuntime.updateRuntimeData(componentId, { _LoopData: runtimeData._LoopData });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (results._is_leaf) {
|
|
757
|
+
//we reached the end of the execution tree, we need to check if this branch is a loop
|
|
758
|
+
delete results._is_leaf;
|
|
759
|
+
const _ChildLoopData = runtimeData._ChildLoopData;
|
|
760
|
+
if (_ChildLoopData && _ChildLoopData.parentId) {
|
|
761
|
+
const parentId = _ChildLoopData.parentId;
|
|
762
|
+
const _LoopData = this.agentRuntime.getRuntimeData(parentId)._LoopData;
|
|
763
|
+
if (_LoopData) {
|
|
764
|
+
if (!_LoopData.result) _LoopData.result = [];
|
|
765
|
+
//we are in a loop, we need to update loop parent status in order to signal that we can run the next loop cycle
|
|
766
|
+
|
|
767
|
+
let resultsCopy = JSON.parse(JSON.stringify(results));
|
|
768
|
+
if (results.result) results.result._exclude = true;
|
|
769
|
+
|
|
770
|
+
resultsCopy = await component.postProcess(resultsCopy, componentData, this);
|
|
771
|
+
|
|
772
|
+
_LoopData.result.push(resultsCopy);
|
|
773
|
+
_LoopData.branches--;
|
|
774
|
+
|
|
775
|
+
if (_LoopData.branches <= 0) {
|
|
776
|
+
agentRuntime.updateComponent(parentId, { active: true, status: '' }); //remove _in_progress status after processing all branches
|
|
777
|
+
}
|
|
778
|
+
//save the last result so that the loop parent can read it
|
|
779
|
+
agentRuntime.updateRuntimeData(parentId, { _LoopData });
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
//leaf but no childLoopData, is this a loop component with no children ?
|
|
783
|
+
const _LoopData = this.agentRuntime.getRuntimeData(componentId)._LoopData;
|
|
784
|
+
if (_LoopData && _LoopData.loopIndex == 1) {
|
|
785
|
+
_LoopData._in_progress = false;
|
|
786
|
+
output._in_progress = false;
|
|
787
|
+
agentRuntime.updateComponent(componentId, { active: true, status: '' });
|
|
788
|
+
agentRuntime.updateRuntimeData(componentId, { _LoopData });
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
//check if the component context is potentially needed in next cycles
|
|
795
|
+
if (!output._missing_inputs && !output._in_progress) {
|
|
796
|
+
//we processed the current component, we can now reset the runtime data and active status
|
|
797
|
+
const inLoop =
|
|
798
|
+
runtimeData?._ChildLoopData?._in_progress && runtimeData._ChildLoopData?.loopIndex < runtimeData._ChildLoopData?.loopLength;
|
|
799
|
+
if (inLoop) {
|
|
800
|
+
// loop children require to keep external runtime data, we only clear the data that was set inside the loop
|
|
801
|
+
this.clearChildLoopRuntimeComponentData(componentId);
|
|
802
|
+
agentRuntime.updateComponent(componentId, { active: true, status: 'waiting' });
|
|
803
|
+
} else {
|
|
804
|
+
this.agentRuntime.resetComponent(componentId); //also sets active to false
|
|
805
|
+
}
|
|
806
|
+
} //if inputs were missing, the output contains error information, not actual component processing output, in this case we keep the runtime data
|
|
807
|
+
|
|
808
|
+
//filter out null results
|
|
809
|
+
if (Array.isArray(results)) results = results.flat(Infinity).filter((r) => r.result != null);
|
|
810
|
+
|
|
811
|
+
if (logId) {
|
|
812
|
+
//update log
|
|
813
|
+
AgentLogger.log(this, logId, { output, outputTimestamp: '' + Date.now() });
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
//return this.agentRuntime.debug ? [results, { id: componentData.id, name: componentData.name, result: output }] : results;
|
|
817
|
+
|
|
818
|
+
const endTime = Date.now();
|
|
819
|
+
const duration = endTime - startTime;
|
|
820
|
+
this.sse.send('component', {
|
|
821
|
+
eventId,
|
|
822
|
+
action: 'callStop',
|
|
823
|
+
sourceId,
|
|
824
|
+
id: componentId,
|
|
825
|
+
name: componentData.displayName,
|
|
826
|
+
title: componentData.title,
|
|
827
|
+
startTime,
|
|
828
|
+
endTime,
|
|
829
|
+
duration,
|
|
830
|
+
output,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
return [results, { id: componentData.id, name: componentData.displayName, result: output }];
|
|
834
|
+
}
|
|
835
|
+
JSONExpression(obj, propertyString) {
|
|
836
|
+
const properties = propertyString.split(/\.|\[|\]\.|\]\[|\]/).filter(Boolean);
|
|
837
|
+
let currentProperty = obj;
|
|
838
|
+
|
|
839
|
+
for (let property of properties) {
|
|
840
|
+
if (currentProperty === undefined || currentProperty === null) {
|
|
841
|
+
return undefined;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
currentProperty = currentProperty[property];
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
return currentProperty;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
//
|
|
851
|
+
async callNextComponents(componentId, output) {
|
|
852
|
+
const agentRuntime = this.agentRuntime;
|
|
853
|
+
//agentRuntime.incStep();
|
|
854
|
+
|
|
855
|
+
const componentData = this.components[componentId];
|
|
856
|
+
const component: Component = this._componentInstance[componentData.name];
|
|
857
|
+
|
|
858
|
+
//if (component.hasReadOutput) return [];
|
|
859
|
+
|
|
860
|
+
//get the list of connections for the current component in order to determine the next component(s) to call
|
|
861
|
+
let connections = this.connections
|
|
862
|
+
.filter((c) => c.sourceId == componentId /*|| this.alwaysActiveComponents[c.sourceId]*/)
|
|
863
|
+
.map((c) => ({ ...c, output, componentData }));
|
|
864
|
+
|
|
865
|
+
//also find connections from always active components to components with status 'waiting'
|
|
866
|
+
|
|
867
|
+
const waitingComponents = agentRuntime.getWaitingComponents();
|
|
868
|
+
const waitingComponentIds = waitingComponents.map((e) => e.id);
|
|
869
|
+
const alwaysActiveIds = Object.keys(this.agentRuntime.alwaysActiveComponents);
|
|
870
|
+
const alwaysActiveConnections = this.connections
|
|
871
|
+
.filter((c) => alwaysActiveIds.includes(c.sourceId) && waitingComponentIds.includes(c.targetId))
|
|
872
|
+
.map((c) => {
|
|
873
|
+
const output = {};
|
|
874
|
+
const waitingComponent = waitingComponents.find((e) => e.id == c.targetId);
|
|
875
|
+
const prevComponentData = this.components[c.sourceId];
|
|
876
|
+
const prevComponent: Component = this._componentInstance[prevComponentData.name];
|
|
877
|
+
const outputEndpoint = prevComponentData.outputs[c.sourceIndex];
|
|
878
|
+
output[outputEndpoint.name] = prevComponent.readOutput(outputEndpoint.name, prevComponentData, this);
|
|
879
|
+
|
|
880
|
+
return { ...c, output, componentData: this.components[c.sourceId] };
|
|
881
|
+
});
|
|
882
|
+
connections = [...connections, ...alwaysActiveConnections];
|
|
883
|
+
|
|
884
|
+
//no more components to call, return the output
|
|
885
|
+
if (!Array.isArray(connections) || connections.length == 0) {
|
|
886
|
+
return { id: componentData.id, name: componentData.name, result: output, _is_leaf: true };
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const targetComponents = //classify connections by objects
|
|
890
|
+
connections.reduce((acc, obj) => {
|
|
891
|
+
let key = obj.targetId;
|
|
892
|
+
if (!acc[key]) {
|
|
893
|
+
acc[key] = [];
|
|
894
|
+
}
|
|
895
|
+
acc[key].push(obj);
|
|
896
|
+
return acc;
|
|
897
|
+
}, {});
|
|
898
|
+
|
|
899
|
+
const promises: any = [];
|
|
900
|
+
for (let targetId in targetComponents) {
|
|
901
|
+
const targetComponentData = this.components[targetId];
|
|
902
|
+
|
|
903
|
+
//if we are not inside an async component, we skip async branches
|
|
904
|
+
//Note : we exclude Async component from this rule because it's the one that initiates the async job
|
|
905
|
+
if (!this.async && targetComponentData.async && targetComponentData.name !== 'Async') continue;
|
|
906
|
+
|
|
907
|
+
const targetComponent: Component = this._componentInstance[targetComponentData.name];
|
|
908
|
+
const connections = targetComponents[targetId];
|
|
909
|
+
|
|
910
|
+
let _isErrorHandler = false;
|
|
911
|
+
if (Array.isArray(connections) && connections.length > 0) {
|
|
912
|
+
const nextInput = {};
|
|
913
|
+
for (let connection of connections) {
|
|
914
|
+
const output = connection.output;
|
|
915
|
+
const componentData = connection.componentData;
|
|
916
|
+
const outputEndpoint = componentData.outputs[connection.sourceIndex]; //source
|
|
917
|
+
const inputEndpoint = targetComponentData.inputs[connection.targetIndex]; //target
|
|
918
|
+
|
|
919
|
+
//outputs can be named (e.g "user:email" or "Req:body:data") in which case they refer to a path in the output object
|
|
920
|
+
const outputExpression = outputEndpoint.expression || outputEndpoint.name;
|
|
921
|
+
const outputParts = outputExpression.split('.');
|
|
922
|
+
|
|
923
|
+
const defaultOutputs = componentData.outputs.find((c) => c.default);
|
|
924
|
+
let value: any = undefined;
|
|
925
|
+
|
|
926
|
+
if (outputEndpoint.name == '_error') _isErrorHandler = true;
|
|
927
|
+
|
|
928
|
+
if (outputEndpoint.default) value = output[outputEndpoint.name] /* || null*/;
|
|
929
|
+
else {
|
|
930
|
+
if (defaultOutputs /* && output[defaultOutputs.name]?.[outputEndpoint.name]*/) {
|
|
931
|
+
value = output[defaultOutputs.name]?.[outputEndpoint.name];
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (/*value === null || */ value === undefined && outputParts.length >= 1) {
|
|
935
|
+
let val = this.JSONExpression(output, outputExpression);
|
|
936
|
+
if (val !== undefined) value = val;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// if (/*value !== null && */ value !== undefined) {
|
|
940
|
+
// nextInput[inputEndpoint.name] = [...new Set([[nextInput[inputEndpoint.name], value]].flat(Infinity))].filter(
|
|
941
|
+
// (e) => e !== undefined /*&& e !== null*/,
|
|
942
|
+
// );
|
|
943
|
+
|
|
944
|
+
// if (nextInput[inputEndpoint.name].length == 1) nextInput[inputEndpoint.name] = nextInput[inputEndpoint.name][0];
|
|
945
|
+
// }
|
|
946
|
+
|
|
947
|
+
//Fix suggested by Sentinel Agent
|
|
948
|
+
if (/*value !== null && */ value !== undefined) {
|
|
949
|
+
// let combinedInput = [...[nextInput[inputEndpoint.name]].flat(), ...[value].flat()].filter(
|
|
950
|
+
// (e) => e !== undefined /*&& e !== null*/,
|
|
951
|
+
// ); // ! Deprecated
|
|
952
|
+
|
|
953
|
+
let combinedInput = _mergeInputs(nextInput[inputEndpoint.name], value).filter((e) => e !== undefined /*&& e !== null*/);
|
|
954
|
+
|
|
955
|
+
nextInput[inputEndpoint.name] = combinedInput.length === 1 ? combinedInput[0] : combinedInput;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (!nextInput || JSON.stringify(nextInput) == '{}') continue;
|
|
959
|
+
|
|
960
|
+
const input = this.prepareComponentInput(targetId, nextInput);
|
|
961
|
+
|
|
962
|
+
const targetComponent = this.components[targetId];
|
|
963
|
+
|
|
964
|
+
if (_isErrorHandler && targetComponent) {
|
|
965
|
+
output._error_handled = true;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const missingInputs = this.getComponentMissingInputs(targetId, input);
|
|
969
|
+
const status = missingInputs.length > 0 ? 'waiting' : undefined;
|
|
970
|
+
|
|
971
|
+
const sourceRuntimeData = this.agentRuntime.getRuntimeData(componentId); //We read the previous component runtime data
|
|
972
|
+
|
|
973
|
+
let _ChildLoopData = sourceRuntimeData._LoopData; //is the source a loop component ?
|
|
974
|
+
|
|
975
|
+
if (!_ChildLoopData || !_ChildLoopData._in_progress) {
|
|
976
|
+
//if it's a loop component we need to check if the loop is still in progress
|
|
977
|
+
|
|
978
|
+
_ChildLoopData = sourceRuntimeData._ChildLoopData; // if the loop is completed, check if the loop component is a nested loop, in which case we pass the parent context to the following component
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
agentRuntime.updateComponent(targetId, { active: true, input: nextInput, sourceId: componentId, status });
|
|
982
|
+
agentRuntime.updateRuntimeData(targetId, { _ChildLoopData, _LoopData: null });
|
|
983
|
+
promises.push(idPromise({ id: targetId, name: targetComponent.name, inputs: nextInput }));
|
|
984
|
+
|
|
985
|
+
if (status) {
|
|
986
|
+
//if status is set, track the component status update
|
|
987
|
+
//if not set, it means that the component is active and will be logged upon execution
|
|
988
|
+
//this can be considered as a fake log step that help us keep track of the execution tree
|
|
989
|
+
const logId = AgentLogger.log(this, null, {
|
|
990
|
+
sourceId: componentId,
|
|
991
|
+
componentId: targetId,
|
|
992
|
+
step: this.agentRuntime.curStep + 1, //we force to next step because the current step order is updated in the next runCycle()
|
|
993
|
+
domain: this.domain,
|
|
994
|
+
workflowID: this.agentRuntime.workflowReqId,
|
|
995
|
+
processID: this.agentRuntime.processID,
|
|
996
|
+
input: { __action: 'status_update', __status: status, data: nextInput },
|
|
997
|
+
inputTimestamp: new Date().toISOString(),
|
|
998
|
+
sessionID: this.callerSessionId,
|
|
999
|
+
tags: this.sessionTag,
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
if (promises.length == 0) {
|
|
1006
|
+
return { id: componentData.id, name: componentData.name, result: output, _is_leaf: true };
|
|
1007
|
+
}
|
|
1008
|
+
const results = await Promise.all(promises);
|
|
1009
|
+
|
|
1010
|
+
//TODO : exclusive components handling
|
|
1011
|
+
//in order to run exclusive components first, we need to run them in current cycle
|
|
1012
|
+
//then we signal to the caller component that one more run cycle is needed
|
|
1013
|
+
return results.length == 1 ? results[0] : results;
|
|
1014
|
+
}
|
|
1015
|
+
private prepareComponentInput(targetId, inputs) {
|
|
1016
|
+
const rData: any = this.agentRuntime.getRuntimeData(targetId);
|
|
1017
|
+
const componentData = this.components[targetId];
|
|
1018
|
+
const rDataInput = rData?.input || {};
|
|
1019
|
+
|
|
1020
|
+
let _input = { ...rDataInput };
|
|
1021
|
+
if (inputs) {
|
|
1022
|
+
// for (let key in inputs) {
|
|
1023
|
+
// let value = inputs[key];
|
|
1024
|
+
// //_input[key] = mergeJsonData(_input[key], value);
|
|
1025
|
+
|
|
1026
|
+
// _input[key] = [...new Set([[rDataInput[key]], [value]].flat(Infinity))].filter((e) => e !== undefined /* && e !== null*/);
|
|
1027
|
+
// if (_input[key].length == 1) _input[key] = _input[key][0];
|
|
1028
|
+
// }
|
|
1029
|
+
|
|
1030
|
+
//Fix suggested by Sentinel Agent
|
|
1031
|
+
for (let key in inputs) {
|
|
1032
|
+
let value = inputs[key];
|
|
1033
|
+
// Concatenate the existing value with the new input, without using Set to preserve duplicates
|
|
1034
|
+
// _input[key] = [rDataInput[key], value].flat(Infinity).filter((e) => e !== undefined); // ! Deprecated
|
|
1035
|
+
_input[key] = _mergeInputs(rDataInput[key], value).filter((e) => e !== undefined);
|
|
1036
|
+
|
|
1037
|
+
// Simplify the array to a single value if there is only one element after flattening
|
|
1038
|
+
if (_input[key].length == 1) _input[key] = _input[key][0];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const readablePredecessors = this.findReadablePredecessors(targetId);
|
|
1043
|
+
for (let c of readablePredecessors) {
|
|
1044
|
+
if (c) {
|
|
1045
|
+
const predComponentData = this.components[c.id];
|
|
1046
|
+
const value = c.component.readOutput(c.output.name, predComponentData, this);
|
|
1047
|
+
if (value && c.input?.name) {
|
|
1048
|
+
if (!_input) _input = {};
|
|
1049
|
+
_input[c.input.name] = value;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
//this.saveRuntimeComponentData(targetId, { input: _input }); //TODO : check if we can use this.agentRuntime.updateRuntimeData instead (need to be carefully tested)
|
|
1055
|
+
this.agentRuntime.updateRuntimeData(targetId, { input: _input });
|
|
1056
|
+
|
|
1057
|
+
for (let input of componentData.inputs) {
|
|
1058
|
+
if (input.defaultVal && _input[input.name] === undefined) {
|
|
1059
|
+
_input[input.name] = TemplateString(input.defaultVal).parse(this.agentVariables).result;
|
|
1060
|
+
//parseTemplate(input.defaultVal, this.agentVariables, { escapeString: false, processUnmatched: false });
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return _input;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
public getConnectionSource(connection) {
|
|
1067
|
+
return this.components[connection.sourceId].inputs.find((e) => e.index === connection.sourceIndex);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
public getConnectionTarget(connection) {
|
|
1071
|
+
return this.components[connection.targetId].inputs.find((e) => e.index === connection.targetIndex);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
private recursiveTagAsyncComponents(component) {
|
|
1075
|
+
const agent = this;
|
|
1076
|
+
for (let output of component.outputs) {
|
|
1077
|
+
if (component.name == 'Async' && output.name === 'JobID') continue; //'JobID' is a special output
|
|
1078
|
+
const connected = agent.connections.filter((c) => c.sourceId === component.id && c.sourceIndex === output.index);
|
|
1079
|
+
if (!connected) continue;
|
|
1080
|
+
for (let con of connected) {
|
|
1081
|
+
const targetComponent = agent.components[con.targetId];
|
|
1082
|
+
if (!targetComponent) continue;
|
|
1083
|
+
targetComponent.async = true;
|
|
1084
|
+
this.recursiveTagAsyncComponents(targetComponent);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
private tagAsyncComponents() {
|
|
1089
|
+
const agent = this;
|
|
1090
|
+
const componentsList: any[] = Object.values(agent.components);
|
|
1091
|
+
const AsyncComponents: any[] = componentsList.filter((c) => c.name === 'Async');
|
|
1092
|
+
if (!AsyncComponents || AsyncComponents.length == 0) return;
|
|
1093
|
+
for (let AsyncComponent of AsyncComponents) {
|
|
1094
|
+
AsyncComponent.async = true;
|
|
1095
|
+
this.recursiveTagAsyncComponents(AsyncComponent);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
//AsyncComponents.async = true;
|
|
1099
|
+
|
|
1100
|
+
//this.recursiveTagAsyncComponents(AsyncComponent, agent);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
function _mergeInputs(existing, newValue) {
|
|
1105
|
+
if (existing === undefined) {
|
|
1106
|
+
return [newValue];
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (!Array.isArray(existing)) {
|
|
1110
|
+
existing = [existing];
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
return [...existing, newValue];
|
|
1114
|
+
}
|