@smythos/sre 1.5.45 → 1.5.50

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