@smythos/sre 1.5.53 → 1.5.54

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