@smythos/sre 1.5.0 → 1.5.2

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