@smythos/sre 1.6.1 → 1.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/CHANGELOG +111 -111
  2. package/LICENSE +18 -18
  3. package/README.md +135 -135
  4. package/dist/index.js +2 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/SmythModelsProvider.class.d.ts +39 -0
  7. package/package.json +1 -1
  8. package/src/Components/APICall/APICall.class.ts +161 -161
  9. package/src/Components/APICall/AccessTokenManager.ts +166 -166
  10. package/src/Components/APICall/ArrayBufferResponse.helper.ts +58 -58
  11. package/src/Components/APICall/OAuth.helper.ts +447 -447
  12. package/src/Components/APICall/mimeTypeCategories.ts +46 -46
  13. package/src/Components/APICall/parseData.ts +167 -167
  14. package/src/Components/APICall/parseHeaders.ts +41 -41
  15. package/src/Components/APICall/parseProxy.ts +68 -68
  16. package/src/Components/APICall/parseUrl.ts +91 -91
  17. package/src/Components/APIEndpoint.class.ts +234 -234
  18. package/src/Components/APIOutput.class.ts +58 -58
  19. package/src/Components/AgentPlugin.class.ts +102 -102
  20. package/src/Components/Async.class.ts +155 -155
  21. package/src/Components/Await.class.ts +90 -90
  22. package/src/Components/Classifier.class.ts +158 -158
  23. package/src/Components/Component.class.ts +147 -147
  24. package/src/Components/ComponentHost.class.ts +38 -38
  25. package/src/Components/DataSourceCleaner.class.ts +92 -92
  26. package/src/Components/DataSourceIndexer.class.ts +181 -181
  27. package/src/Components/DataSourceLookup.class.ts +161 -161
  28. package/src/Components/ECMASandbox.class.ts +72 -72
  29. package/src/Components/FEncDec.class.ts +29 -29
  30. package/src/Components/FHash.class.ts +33 -33
  31. package/src/Components/FSign.class.ts +80 -80
  32. package/src/Components/FSleep.class.ts +25 -25
  33. package/src/Components/FTimestamp.class.ts +66 -66
  34. package/src/Components/FileStore.class.ts +78 -78
  35. package/src/Components/ForEach.class.ts +97 -97
  36. package/src/Components/GPTPlugin.class.ts +70 -70
  37. package/src/Components/GenAILLM.class.ts +586 -586
  38. package/src/Components/HuggingFace.class.ts +313 -313
  39. package/src/Components/Image/imageSettings.config.ts +70 -70
  40. package/src/Components/ImageGenerator.class.ts +483 -483
  41. package/src/Components/JSONFilter.class.ts +54 -54
  42. package/src/Components/LLMAssistant.class.ts +213 -213
  43. package/src/Components/LogicAND.class.ts +28 -28
  44. package/src/Components/LogicAtLeast.class.ts +85 -85
  45. package/src/Components/LogicAtMost.class.ts +86 -86
  46. package/src/Components/LogicOR.class.ts +29 -29
  47. package/src/Components/LogicXOR.class.ts +34 -34
  48. package/src/Components/MCPClient.class.ts +137 -137
  49. package/src/Components/MemoryDeleteKeyVal.class.ts +70 -70
  50. package/src/Components/MemoryReadKeyVal.class.ts +67 -67
  51. package/src/Components/MemoryWriteKeyVal.class.ts +62 -62
  52. package/src/Components/MemoryWriteObject.class.ts +97 -97
  53. package/src/Components/MultimodalLLM.class.ts +128 -128
  54. package/src/Components/OpenAPI.class.ts +72 -72
  55. package/src/Components/PromptGenerator.class.ts +122 -122
  56. package/src/Components/ScrapflyWebScrape.class.ts +183 -183
  57. package/src/Components/ServerlessCode.class.ts +123 -123
  58. package/src/Components/TavilyWebSearch.class.ts +103 -103
  59. package/src/Components/VisionLLM.class.ts +104 -104
  60. package/src/Components/ZapierAction.class.ts +127 -127
  61. package/src/Components/index.ts +97 -97
  62. package/src/Core/AgentProcess.helper.ts +240 -240
  63. package/src/Core/Connector.class.ts +123 -123
  64. package/src/Core/ConnectorsService.ts +197 -197
  65. package/src/Core/DummyConnector.ts +49 -49
  66. package/src/Core/HookService.ts +105 -105
  67. package/src/Core/SmythRuntime.class.ts +241 -241
  68. package/src/Core/SystemEvents.ts +16 -16
  69. package/src/Core/boot.ts +56 -56
  70. package/src/config.ts +15 -15
  71. package/src/constants.ts +126 -126
  72. package/src/data/hugging-face.params.json +579 -579
  73. package/src/helpers/AWSLambdaCode.helper.ts +624 -599
  74. package/src/helpers/BinaryInput.helper.ts +331 -331
  75. package/src/helpers/Conversation.helper.ts +1157 -1157
  76. package/src/helpers/ECMASandbox.helper.ts +64 -64
  77. package/src/helpers/JsonContent.helper.ts +97 -97
  78. package/src/helpers/LocalCache.helper.ts +97 -97
  79. package/src/helpers/Log.helper.ts +274 -274
  80. package/src/helpers/OpenApiParser.helper.ts +150 -150
  81. package/src/helpers/S3Cache.helper.ts +147 -147
  82. package/src/helpers/SmythURI.helper.ts +5 -5
  83. package/src/helpers/Sysconfig.helper.ts +95 -95
  84. package/src/helpers/TemplateString.helper.ts +243 -243
  85. package/src/helpers/TypeChecker.helper.ts +329 -329
  86. package/src/index.ts +198 -198
  87. package/src/index.ts.bak +198 -198
  88. package/src/subsystems/AgentManager/Agent.class.ts +1114 -1114
  89. package/src/subsystems/AgentManager/Agent.helper.ts +3 -3
  90. package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +230 -230
  91. package/src/subsystems/AgentManager/AgentData.service/connectors/CLIAgentDataConnector.class.ts +66 -66
  92. package/src/subsystems/AgentManager/AgentData.service/connectors/LocalAgentDataConnector.class.ts +145 -145
  93. package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +39 -39
  94. package/src/subsystems/AgentManager/AgentData.service/index.ts +18 -18
  95. package/src/subsystems/AgentManager/AgentLogger.class.ts +301 -301
  96. package/src/subsystems/AgentManager/AgentRequest.class.ts +51 -51
  97. package/src/subsystems/AgentManager/AgentRuntime.class.ts +557 -557
  98. package/src/subsystems/AgentManager/AgentSSE.class.ts +101 -101
  99. package/src/subsystems/AgentManager/AgentSettings.class.ts +52 -52
  100. package/src/subsystems/AgentManager/Component.service/ComponentConnector.ts +32 -32
  101. package/src/subsystems/AgentManager/Component.service/connectors/LocalComponentConnector.class.ts +60 -60
  102. package/src/subsystems/AgentManager/Component.service/index.ts +11 -11
  103. package/src/subsystems/AgentManager/EmbodimentSettings.class.ts +47 -47
  104. package/src/subsystems/AgentManager/ForkedAgent.class.ts +154 -154
  105. package/src/subsystems/AgentManager/OSResourceMonitor.ts +77 -77
  106. package/src/subsystems/ComputeManager/Code.service/CodeConnector.ts +98 -98
  107. package/src/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.ts +171 -172
  108. package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -131
  109. package/src/subsystems/ComputeManager/Code.service/index.ts +13 -13
  110. package/src/subsystems/IO/CLI.service/CLIConnector.ts +47 -47
  111. package/src/subsystems/IO/CLI.service/index.ts +9 -9
  112. package/src/subsystems/IO/Log.service/LogConnector.ts +32 -32
  113. package/src/subsystems/IO/Log.service/connectors/ConsoleLog.class.ts +28 -28
  114. package/src/subsystems/IO/Log.service/index.ts +13 -13
  115. package/src/subsystems/IO/NKV.service/NKVConnector.ts +43 -43
  116. package/src/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.ts +234 -234
  117. package/src/subsystems/IO/NKV.service/connectors/NKVRAM.class.ts +204 -204
  118. package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +182 -182
  119. package/src/subsystems/IO/NKV.service/index.ts +14 -14
  120. package/src/subsystems/IO/Router.service/RouterConnector.ts +21 -21
  121. package/src/subsystems/IO/Router.service/connectors/ExpressRouter.class.ts +48 -48
  122. package/src/subsystems/IO/Router.service/connectors/NullRouter.class.ts +40 -40
  123. package/src/subsystems/IO/Router.service/index.ts +11 -11
  124. package/src/subsystems/IO/Storage.service/SmythFS.class.ts +488 -488
  125. package/src/subsystems/IO/Storage.service/StorageConnector.ts +66 -66
  126. package/src/subsystems/IO/Storage.service/connectors/LocalStorage.class.ts +327 -327
  127. package/src/subsystems/IO/Storage.service/connectors/S3Storage.class.ts +482 -482
  128. package/src/subsystems/IO/Storage.service/index.ts +13 -13
  129. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +108 -108
  130. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +465 -465
  131. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +387 -387
  132. package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +408 -408
  133. package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +107 -107
  134. package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +118 -118
  135. package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +109 -109
  136. package/src/subsystems/IO/VectorDB.service/embed/index.ts +26 -26
  137. package/src/subsystems/IO/VectorDB.service/index.ts +14 -14
  138. package/src/subsystems/LLMManager/LLM.helper.ts +251 -251
  139. package/src/subsystems/LLMManager/LLM.inference.ts +345 -345
  140. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +492 -492
  141. package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +171 -171
  142. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +666 -666
  143. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +407 -407
  144. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +92 -92
  145. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +983 -983
  146. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +319 -319
  147. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +361 -361
  148. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +257 -257
  149. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +430 -430
  150. package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +503 -503
  151. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +524 -524
  152. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -100
  153. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -81
  154. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +1145 -1145
  155. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +13 -13
  156. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -4
  157. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +11 -11
  158. package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +32 -32
  159. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +478 -478
  160. package/src/subsystems/LLMManager/LLM.service/index.ts +47 -47
  161. package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +303 -303
  162. package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +271 -271
  163. package/src/subsystems/LLMManager/ModelsProvider.service/index.ts +11 -11
  164. package/src/subsystems/LLMManager/custom-models.ts +854 -854
  165. package/src/subsystems/LLMManager/models.ts +2540 -2540
  166. package/src/subsystems/LLMManager/paramMappings.ts +69 -69
  167. package/src/subsystems/MemoryManager/Cache.service/CacheConnector.ts +86 -86
  168. package/src/subsystems/MemoryManager/Cache.service/connectors/LocalStorageCache.class.ts +297 -297
  169. package/src/subsystems/MemoryManager/Cache.service/connectors/RAMCache.class.ts +214 -214
  170. package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +252 -252
  171. package/src/subsystems/MemoryManager/Cache.service/connectors/S3Cache.class.ts +373 -373
  172. package/src/subsystems/MemoryManager/Cache.service/index.ts +15 -15
  173. package/src/subsystems/MemoryManager/LLMCache.ts +72 -72
  174. package/src/subsystems/MemoryManager/LLMContext.ts +124 -124
  175. package/src/subsystems/MemoryManager/LLMMemory.service/LLMMemoryConnector.ts +26 -26
  176. package/src/subsystems/MemoryManager/RuntimeContext.ts +277 -277
  177. package/src/subsystems/Security/AccessControl/ACL.class.ts +208 -208
  178. package/src/subsystems/Security/AccessControl/AccessCandidate.class.ts +82 -82
  179. package/src/subsystems/Security/AccessControl/AccessRequest.class.ts +52 -52
  180. package/src/subsystems/Security/Account.service/AccountConnector.ts +44 -44
  181. package/src/subsystems/Security/Account.service/connectors/DummyAccount.class.ts +130 -130
  182. package/src/subsystems/Security/Account.service/connectors/JSONFileAccount.class.ts +170 -170
  183. package/src/subsystems/Security/Account.service/connectors/MySQLAccount.class.ts +76 -76
  184. package/src/subsystems/Security/Account.service/index.ts +14 -14
  185. package/src/subsystems/Security/Credentials.helper.ts +62 -62
  186. package/src/subsystems/Security/ManagedVault.service/ManagedVaultConnector.ts +38 -38
  187. package/src/subsystems/Security/ManagedVault.service/connectors/NullManagedVault.class.ts +53 -53
  188. package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +154 -154
  189. package/src/subsystems/Security/ManagedVault.service/index.ts +12 -12
  190. package/src/subsystems/Security/SecureConnector.class.ts +110 -110
  191. package/src/subsystems/Security/Vault.service/Vault.helper.ts +30 -30
  192. package/src/subsystems/Security/Vault.service/VaultConnector.ts +29 -29
  193. package/src/subsystems/Security/Vault.service/connectors/HashicorpVault.class.ts +46 -46
  194. package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +221 -221
  195. package/src/subsystems/Security/Vault.service/connectors/NullVault.class.ts +54 -54
  196. package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +140 -140
  197. package/src/subsystems/Security/Vault.service/index.ts +12 -12
  198. package/src/types/ACL.types.ts +104 -104
  199. package/src/types/AWS.types.ts +10 -10
  200. package/src/types/Agent.types.ts +61 -61
  201. package/src/types/AgentLogger.types.ts +17 -17
  202. package/src/types/Cache.types.ts +1 -1
  203. package/src/types/Common.types.ts +2 -2
  204. package/src/types/LLM.types.ts +520 -520
  205. package/src/types/Redis.types.ts +8 -8
  206. package/src/types/SRE.types.ts +64 -64
  207. package/src/types/Security.types.ts +14 -14
  208. package/src/types/Storage.types.ts +5 -5
  209. package/src/types/VectorDB.types.ts +86 -86
  210. package/src/utils/base64.utils.ts +275 -275
  211. package/src/utils/cli.utils.ts +68 -68
  212. package/src/utils/data.utils.ts +322 -322
  213. package/src/utils/date-time.utils.ts +22 -22
  214. package/src/utils/general.utils.ts +238 -238
  215. package/src/utils/index.ts +12 -12
  216. package/src/utils/lazy-client.ts +261 -261
  217. package/src/utils/numbers.utils.ts +13 -13
  218. package/src/utils/oauth.utils.ts +35 -35
  219. package/src/utils/string.utils.ts +414 -414
  220. package/src/utils/url.utils.ts +19 -19
  221. package/src/utils/validation.utils.ts +74 -74
  222. package/dist/bundle-analysis-lazy.html +0 -4949
  223. package/dist/bundle-analysis.html +0 -4949
  224. package/dist/types/Components/Triggers/GmailTrigger.class.d.ts +0 -13
  225. package/dist/types/Components/Triggers/Trigger.class.d.ts +0 -3
  226. package/dist/types/helpers/AIPerformanceAnalyzer.helper.d.ts +0 -45
  227. package/dist/types/helpers/AIPerformanceCollector.helper.d.ts +0 -111
  228. package/dist/types/subsystems/IO/Storage.service/connectors/AzureBlobStorage.class.d.ts +0 -211
  229. package/dist/types/subsystems/IO/VectorDB.service/connectors/WeaviateVectorDB.class.d.ts +0 -187
  230. package/dist/types/subsystems/PerformanceManager/Performance.service/PerformanceConnector.d.ts +0 -102
  231. package/dist/types/subsystems/PerformanceManager/Performance.service/connectors/LocalPerformanceConnector.class.d.ts +0 -100
  232. package/dist/types/subsystems/PerformanceManager/Performance.service/index.d.ts +0 -22
  233. package/dist/types/types/Performance.types.d.ts +0 -468
  234. package/dist/types/utils/package-manager.utils.d.ts +0 -26
@@ -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
+ }