@smythos/sre 1.6.8 → 1.6.10

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 (239) hide show
  1. package/CHANGELOG +111 -111
  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 +2 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/types/Components/Triggers/Gmail.trigger.d.ts +58 -0
  9. package/dist/types/Components/Triggers/GmailTrigger.class.d.ts +44 -0
  10. package/dist/types/Components/Triggers/Trigger.class.d.ts +21 -0
  11. package/dist/types/Components/Triggers/WhatsApp.trigger.d.ts +22 -0
  12. package/dist/types/helpers/AIPerformanceAnalyzer.helper.d.ts +45 -0
  13. package/dist/types/helpers/AIPerformanceCollector.helper.d.ts +111 -0
  14. package/dist/types/subsystems/IO/Storage.service/connectors/AzureBlobStorage.class.d.ts +211 -0
  15. package/dist/types/subsystems/IO/VectorDB.service/connectors/WeaviateVectorDB.class.d.ts +187 -0
  16. package/dist/types/subsystems/PerformanceManager/Performance.service/PerformanceConnector.d.ts +102 -0
  17. package/dist/types/subsystems/PerformanceManager/Performance.service/connectors/LocalPerformanceConnector.class.d.ts +100 -0
  18. package/dist/types/subsystems/PerformanceManager/Performance.service/index.d.ts +22 -0
  19. package/dist/types/subsystems/Security/Credentials/Credentials.class.d.ts +2 -0
  20. package/dist/types/subsystems/Security/Credentials/ManagedOAuth2Credentials.class.d.ts +18 -0
  21. package/dist/types/subsystems/Security/Credentials/OAuth2Credentials.class.d.ts +14 -0
  22. package/dist/types/types/Performance.types.d.ts +468 -0
  23. package/dist/types/utils/package-manager.utils.d.ts +26 -0
  24. package/package.json +1 -1
  25. package/src/Components/APICall/APICall.class.ts +161 -161
  26. package/src/Components/APICall/AccessTokenManager.ts +166 -166
  27. package/src/Components/APICall/ArrayBufferResponse.helper.ts +58 -58
  28. package/src/Components/APICall/OAuth.helper.ts +447 -447
  29. package/src/Components/APICall/mimeTypeCategories.ts +46 -46
  30. package/src/Components/APICall/parseData.ts +167 -167
  31. package/src/Components/APICall/parseHeaders.ts +41 -41
  32. package/src/Components/APICall/parseProxy.ts +68 -68
  33. package/src/Components/APICall/parseUrl.ts +91 -91
  34. package/src/Components/APIEndpoint.class.ts +234 -234
  35. package/src/Components/APIOutput.class.ts +58 -58
  36. package/src/Components/AgentPlugin.class.ts +102 -102
  37. package/src/Components/Async.class.ts +155 -155
  38. package/src/Components/Await.class.ts +90 -90
  39. package/src/Components/Classifier.class.ts +158 -158
  40. package/src/Components/Component.class.ts +147 -147
  41. package/src/Components/ComponentHost.class.ts +38 -38
  42. package/src/Components/DataSourceCleaner.class.ts +92 -92
  43. package/src/Components/DataSourceIndexer.class.ts +181 -181
  44. package/src/Components/DataSourceLookup.class.ts +161 -161
  45. package/src/Components/ECMASandbox.class.ts +72 -72
  46. package/src/Components/FEncDec.class.ts +29 -29
  47. package/src/Components/FHash.class.ts +33 -33
  48. package/src/Components/FSign.class.ts +80 -80
  49. package/src/Components/FSleep.class.ts +25 -25
  50. package/src/Components/FTimestamp.class.ts +66 -66
  51. package/src/Components/FileStore.class.ts +78 -78
  52. package/src/Components/ForEach.class.ts +97 -97
  53. package/src/Components/GPTPlugin.class.ts +70 -70
  54. package/src/Components/GenAILLM.class.ts +586 -586
  55. package/src/Components/HuggingFace.class.ts +313 -313
  56. package/src/Components/Image/imageSettings.config.ts +70 -70
  57. package/src/Components/ImageGenerator.class.ts +483 -483
  58. package/src/Components/JSONFilter.class.ts +54 -54
  59. package/src/Components/LLMAssistant.class.ts +213 -213
  60. package/src/Components/LogicAND.class.ts +28 -28
  61. package/src/Components/LogicAtLeast.class.ts +85 -85
  62. package/src/Components/LogicAtMost.class.ts +86 -86
  63. package/src/Components/LogicOR.class.ts +29 -29
  64. package/src/Components/LogicXOR.class.ts +34 -34
  65. package/src/Components/MCPClient.class.ts +137 -137
  66. package/src/Components/MemoryDeleteKeyVal.class.ts +70 -70
  67. package/src/Components/MemoryReadKeyVal.class.ts +67 -67
  68. package/src/Components/MemoryWriteKeyVal.class.ts +62 -62
  69. package/src/Components/MemoryWriteObject.class.ts +97 -97
  70. package/src/Components/MultimodalLLM.class.ts +128 -128
  71. package/src/Components/OpenAPI.class.ts +72 -72
  72. package/src/Components/PromptGenerator.class.ts +122 -122
  73. package/src/Components/ScrapflyWebScrape.class.ts +183 -183
  74. package/src/Components/ServerlessCode.class.ts +123 -123
  75. package/src/Components/TavilyWebSearch.class.ts +103 -103
  76. package/src/Components/VisionLLM.class.ts +104 -104
  77. package/src/Components/ZapierAction.class.ts +127 -127
  78. package/src/Components/index.ts +97 -97
  79. package/src/Core/AgentProcess.helper.ts +240 -240
  80. package/src/Core/Connector.class.ts +123 -123
  81. package/src/Core/ConnectorsService.ts +197 -197
  82. package/src/Core/DummyConnector.ts +49 -49
  83. package/src/Core/HookService.ts +105 -105
  84. package/src/Core/SmythRuntime.class.ts +241 -241
  85. package/src/Core/SystemEvents.ts +16 -16
  86. package/src/Core/boot.ts +56 -56
  87. package/src/config.ts +15 -15
  88. package/src/constants.ts +126 -126
  89. package/src/data/hugging-face.params.json +579 -579
  90. package/src/helpers/AWSLambdaCode.helper.ts +624 -624
  91. package/src/helpers/BinaryInput.helper.ts +331 -331
  92. package/src/helpers/Conversation.helper.ts +1157 -1157
  93. package/src/helpers/ECMASandbox.helper.ts +64 -64
  94. package/src/helpers/JsonContent.helper.ts +97 -97
  95. package/src/helpers/LocalCache.helper.ts +97 -97
  96. package/src/helpers/Log.helper.ts +274 -274
  97. package/src/helpers/OpenApiParser.helper.ts +150 -150
  98. package/src/helpers/S3Cache.helper.ts +147 -147
  99. package/src/helpers/SmythURI.helper.ts +5 -5
  100. package/src/helpers/Sysconfig.helper.ts +95 -95
  101. package/src/helpers/TemplateString.helper.ts +243 -243
  102. package/src/helpers/TypeChecker.helper.ts +329 -329
  103. package/src/index.ts +3 -3
  104. package/src/index.ts.bak +3 -3
  105. package/src/subsystems/AgentManager/Agent.class.ts +1114 -1114
  106. package/src/subsystems/AgentManager/Agent.helper.ts +3 -3
  107. package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +230 -230
  108. package/src/subsystems/AgentManager/AgentData.service/connectors/CLIAgentDataConnector.class.ts +66 -66
  109. package/src/subsystems/AgentManager/AgentData.service/connectors/LocalAgentDataConnector.class.ts +145 -145
  110. package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +39 -39
  111. package/src/subsystems/AgentManager/AgentData.service/index.ts +18 -18
  112. package/src/subsystems/AgentManager/AgentLogger.class.ts +301 -301
  113. package/src/subsystems/AgentManager/AgentRequest.class.ts +51 -51
  114. package/src/subsystems/AgentManager/AgentRuntime.class.ts +557 -557
  115. package/src/subsystems/AgentManager/AgentSSE.class.ts +101 -101
  116. package/src/subsystems/AgentManager/AgentSettings.class.ts +52 -52
  117. package/src/subsystems/AgentManager/Component.service/ComponentConnector.ts +32 -32
  118. package/src/subsystems/AgentManager/Component.service/connectors/LocalComponentConnector.class.ts +60 -60
  119. package/src/subsystems/AgentManager/Component.service/index.ts +11 -11
  120. package/src/subsystems/AgentManager/EmbodimentSettings.class.ts +47 -47
  121. package/src/subsystems/AgentManager/ForkedAgent.class.ts +154 -154
  122. package/src/subsystems/AgentManager/OSResourceMonitor.ts +77 -77
  123. package/src/subsystems/ComputeManager/Code.service/CodeConnector.ts +98 -98
  124. package/src/subsystems/ComputeManager/Code.service/connectors/AWSLambdaCode.class.ts +171 -171
  125. package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -131
  126. package/src/subsystems/ComputeManager/Code.service/index.ts +13 -13
  127. package/src/subsystems/IO/CLI.service/CLIConnector.ts +47 -47
  128. package/src/subsystems/IO/CLI.service/index.ts +9 -9
  129. package/src/subsystems/IO/Log.service/LogConnector.ts +32 -32
  130. package/src/subsystems/IO/Log.service/connectors/ConsoleLog.class.ts +28 -28
  131. package/src/subsystems/IO/Log.service/index.ts +13 -13
  132. package/src/subsystems/IO/NKV.service/NKVConnector.ts +43 -43
  133. package/src/subsystems/IO/NKV.service/connectors/NKVLocalStorage.class.ts +234 -234
  134. package/src/subsystems/IO/NKV.service/connectors/NKVRAM.class.ts +204 -204
  135. package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +182 -182
  136. package/src/subsystems/IO/NKV.service/index.ts +14 -14
  137. package/src/subsystems/IO/Router.service/RouterConnector.ts +21 -21
  138. package/src/subsystems/IO/Router.service/connectors/ExpressRouter.class.ts +48 -48
  139. package/src/subsystems/IO/Router.service/connectors/NullRouter.class.ts +40 -40
  140. package/src/subsystems/IO/Router.service/index.ts +11 -11
  141. package/src/subsystems/IO/Storage.service/SmythFS.class.ts +488 -488
  142. package/src/subsystems/IO/Storage.service/StorageConnector.ts +66 -66
  143. package/src/subsystems/IO/Storage.service/connectors/LocalStorage.class.ts +327 -327
  144. package/src/subsystems/IO/Storage.service/connectors/S3Storage.class.ts +482 -482
  145. package/src/subsystems/IO/Storage.service/index.ts +13 -13
  146. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +108 -108
  147. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +465 -465
  148. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +387 -387
  149. package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +408 -408
  150. package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +107 -107
  151. package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +118 -118
  152. package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +109 -109
  153. package/src/subsystems/IO/VectorDB.service/embed/index.ts +26 -26
  154. package/src/subsystems/IO/VectorDB.service/index.ts +14 -14
  155. package/src/subsystems/LLMManager/LLM.helper.ts +251 -251
  156. package/src/subsystems/LLMManager/LLM.inference.ts +345 -345
  157. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +492 -492
  158. package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +171 -171
  159. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +666 -666
  160. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +407 -407
  161. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +92 -92
  162. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +983 -983
  163. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +319 -319
  164. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +361 -361
  165. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +257 -257
  166. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +430 -430
  167. package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +503 -503
  168. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +524 -524
  169. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -100
  170. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -81
  171. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +1145 -1145
  172. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +13 -13
  173. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -4
  174. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +11 -11
  175. package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +32 -32
  176. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +478 -478
  177. package/src/subsystems/LLMManager/LLM.service/index.ts +47 -47
  178. package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +303 -303
  179. package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +280 -271
  180. package/src/subsystems/LLMManager/ModelsProvider.service/index.ts +11 -11
  181. package/src/subsystems/LLMManager/custom-models.ts +854 -854
  182. package/src/subsystems/LLMManager/models.ts +2540 -2540
  183. package/src/subsystems/LLMManager/paramMappings.ts +69 -69
  184. package/src/subsystems/MemoryManager/Cache.service/CacheConnector.ts +86 -86
  185. package/src/subsystems/MemoryManager/Cache.service/connectors/LocalStorageCache.class.ts +297 -297
  186. package/src/subsystems/MemoryManager/Cache.service/connectors/RAMCache.class.ts +214 -214
  187. package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +252 -252
  188. package/src/subsystems/MemoryManager/Cache.service/connectors/S3Cache.class.ts +373 -373
  189. package/src/subsystems/MemoryManager/Cache.service/index.ts +15 -15
  190. package/src/subsystems/MemoryManager/LLMCache.ts +72 -72
  191. package/src/subsystems/MemoryManager/LLMContext.ts +124 -124
  192. package/src/subsystems/MemoryManager/LLMMemory.service/LLMMemoryConnector.ts +26 -26
  193. package/src/subsystems/MemoryManager/RuntimeContext.ts +277 -277
  194. package/src/subsystems/Security/AccessControl/ACL.class.ts +208 -208
  195. package/src/subsystems/Security/AccessControl/AccessCandidate.class.ts +82 -82
  196. package/src/subsystems/Security/AccessControl/AccessRequest.class.ts +52 -52
  197. package/src/subsystems/Security/Account.service/AccountConnector.ts +44 -44
  198. package/src/subsystems/Security/Account.service/connectors/DummyAccount.class.ts +130 -130
  199. package/src/subsystems/Security/Account.service/connectors/JSONFileAccount.class.ts +170 -170
  200. package/src/subsystems/Security/Account.service/connectors/MySQLAccount.class.ts +76 -76
  201. package/src/subsystems/Security/Account.service/index.ts +14 -14
  202. package/src/subsystems/Security/Credentials.helper.ts +62 -62
  203. package/src/subsystems/Security/ManagedVault.service/ManagedVaultConnector.ts +38 -38
  204. package/src/subsystems/Security/ManagedVault.service/connectors/NullManagedVault.class.ts +53 -53
  205. package/src/subsystems/Security/ManagedVault.service/connectors/SecretManagerManagedVault.ts +154 -154
  206. package/src/subsystems/Security/ManagedVault.service/index.ts +12 -12
  207. package/src/subsystems/Security/SecureConnector.class.ts +110 -110
  208. package/src/subsystems/Security/Vault.service/Vault.helper.ts +30 -30
  209. package/src/subsystems/Security/Vault.service/VaultConnector.ts +29 -29
  210. package/src/subsystems/Security/Vault.service/connectors/HashicorpVault.class.ts +46 -46
  211. package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +221 -221
  212. package/src/subsystems/Security/Vault.service/connectors/NullVault.class.ts +54 -54
  213. package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +140 -140
  214. package/src/subsystems/Security/Vault.service/index.ts +12 -12
  215. package/src/types/ACL.types.ts +104 -104
  216. package/src/types/AWS.types.ts +10 -10
  217. package/src/types/Agent.types.ts +61 -61
  218. package/src/types/AgentLogger.types.ts +17 -17
  219. package/src/types/Cache.types.ts +1 -1
  220. package/src/types/Common.types.ts +2 -2
  221. package/src/types/LLM.types.ts +520 -520
  222. package/src/types/Redis.types.ts +8 -8
  223. package/src/types/SRE.types.ts +64 -64
  224. package/src/types/Security.types.ts +14 -14
  225. package/src/types/Storage.types.ts +5 -5
  226. package/src/types/VectorDB.types.ts +86 -86
  227. package/src/utils/base64.utils.ts +275 -275
  228. package/src/utils/cli.utils.ts +68 -68
  229. package/src/utils/data.utils.ts +322 -322
  230. package/src/utils/date-time.utils.ts +22 -22
  231. package/src/utils/general.utils.ts +238 -238
  232. package/src/utils/index.ts +12 -12
  233. package/src/utils/lazy-client.ts +261 -261
  234. package/src/utils/numbers.utils.ts +13 -13
  235. package/src/utils/oauth.utils.ts +35 -35
  236. package/src/utils/string.utils.ts +414 -414
  237. package/src/utils/url.utils.ts +19 -19
  238. package/src/utils/validation.utils.ts +74 -74
  239. package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/SmythModelsProvider.class.d.ts +0 -39
@@ -1,483 +1,483 @@
1
- //TODO: this component need to be fully refactored to use the same approach as GenAI LLM
2
-
3
- import { IRequestImage, Runware } from '@runware/sdk-js';
4
- import { OpenAI } from 'openai';
5
-
6
- import { TemplateString } from '@sre/helpers/TemplateString.helper';
7
- import { LLMInference } from '@sre/LLMManager/LLM.inference';
8
- import { IAgent } from '@sre/types/Agent.types';
9
- import { APIKeySource, GenerateImageConfig } from '@sre/types/LLM.types';
10
- import Joi from 'joi';
11
- import { Component } from './Component.class';
12
-
13
- import { SystemEvents } from '@sre/Core/SystemEvents';
14
- import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
15
-
16
- import { BUILT_IN_MODEL_PREFIX, SUPPORTED_MIME_TYPES_MAP } from '@sre/constants';
17
- import { BinaryInput } from '@sre/helpers/BinaryInput.helper';
18
- import { normalizeImageInput } from '@sre/utils/data.utils';
19
- import { ImageSettingsConfig } from './Image/imageSettings.config';
20
- import { getCredentials } from '../subsystems/Security/Credentials.helper';
21
-
22
- enum DALL_E_MODELS {
23
- DALL_E_2 = 'dall-e-2',
24
- DALL_E_3 = 'dall-e-3',
25
- }
26
-
27
- const IMAGE_GEN_COST_MAP = {
28
- [DALL_E_MODELS.DALL_E_3]: {
29
- standard: {
30
- '1024x1024': 0.04,
31
- '1024x1792': 0.08,
32
- '1792x1024': 0.08,
33
- },
34
- hd: {
35
- '1024x1024': 0.08,
36
- '1024x1792': 0.12,
37
- '1792x1024': 0.12,
38
- },
39
- },
40
- [DALL_E_MODELS.DALL_E_2]: {
41
- '256x256': 0.016,
42
- '512x512': 0.018,
43
- '1024x1024': 0.02,
44
- },
45
- };
46
-
47
- export class ImageGenerator extends Component {
48
- protected configSchema = Joi.object({
49
- model: Joi.string().max(100).required(),
50
- prompt: Joi.string().optional().min(2).max(2000).label('Prompt'),
51
-
52
- // #region OpenAI (DALL·E)
53
- sizeDalle2: Joi.string().valid('256x256', '512x512', '1024x1024').optional(),
54
- sizeDalle3: Joi.string().valid('1024x1024', '1792x1024', '1024x1792').optional(),
55
- quality: Joi.string().valid('standard', 'hd', 'auto', 'high', 'medium', 'low').allow('').optional(),
56
- style: Joi.string().valid('vivid', 'natural').optional(),
57
- isRawInputPrompt: Joi.boolean().strict().optional(),
58
- // #endregion
59
-
60
- // #region Runware
61
- negativePrompt: Joi.string().optional().allow('').min(2).max(2000).label('Negative Prompt'),
62
- width: Joi.number().min(128).max(2048).multiple(64).optional().messages({
63
- 'number.multiple': '{{#label}} must be divisible by 64 (eg: 128...512, 576, 640...2048). Provided value: {{#value}}',
64
- }),
65
- height: Joi.number().min(128).max(2048).multiple(64).optional().messages({
66
- 'number.multiple': '{{#label}} must be divisible by 64 (eg: 128...512, 576, 640...2048). Provided value: {{#value}}',
67
- }),
68
- outputFormat: Joi.string().valid('PNG', 'JPEG', 'WEBP', 'auto', 'jpeg', 'png', 'webp').optional(),
69
- strength: ImageSettingsConfig.strength,
70
- // #endregion
71
-
72
- // #region GPT model
73
- size: Joi.string().optional().allow('').max(100).label('Size'),
74
- // #endregion
75
-
76
- // #region Google AI model
77
- aspectRatio: Joi.string().valid('1:1', '3:4', '4:3', '9:16', '16:9').optional().allow('').label('Aspect Ratio'),
78
- personGeneration: Joi.string().valid('dont_allow', 'allow_adult', 'allow_all').optional().allow('').label('Person Generation'),
79
- // #endregion
80
- });
81
- constructor() {
82
- super();
83
- }
84
- init() {}
85
- async process(input, config, agent: IAgent) {
86
- await super.process(input, config, agent);
87
-
88
- const logger = this.createComponentLogger(agent, config);
89
-
90
- logger.debug(`=== Image Generator Log ===`);
91
-
92
- let model = config?.data?.model;
93
-
94
- if (!model) {
95
- return { _error: 'Model Not Found: ', _debug: logger.output };
96
- }
97
-
98
- logger.debug(`Model: ${model}`);
99
-
100
- let prompt = config.data?.prompt || input?.Prompt;
101
- prompt = typeof prompt === 'string' ? prompt : JSON.stringify(prompt);
102
- prompt = TemplateString(prompt).parse(input).result;
103
-
104
- if (!prompt) {
105
- return { _error: 'Please provide a prompt or Image', _debug: logger.output };
106
- }
107
-
108
- logger.debug(`Prompt: \n`, prompt);
109
-
110
- const modelFamily = await getModelFamily(model, agent);
111
-
112
- if (typeof imageGenerator[modelFamily] !== 'function') {
113
- return { _error: `The model '${model}' is not available. Please try a different one.`, _debug: logger.output };
114
- }
115
-
116
- try {
117
- const { output } = await imageGenerator[modelFamily]({ model, config, input, logger, agent, prompt });
118
-
119
- logger.debug(`Output: `, output);
120
-
121
- return { Output: output, _debug: logger.output };
122
- } catch (error: any) {
123
- return { _error: `Generating Image(s)\n${error?.message || JSON.stringify(error)}`, _debug: logger.output };
124
- }
125
- }
126
- }
127
-
128
- // TODO: Create a separate service for image generation, similar to LLM.service.
129
-
130
- // TODO: Hopefully we will have the proper type with new OpenAI SDK, then we can use their type
131
- type TokenUsage = OpenAI.Completions.CompletionUsage & {
132
- prompt_tokens_details?: { cached_tokens?: number };
133
- input_tokens_details: { image_tokens?: number; text_tokens?: number };
134
- output_tokens: number;
135
- };
136
-
137
- enum MODEL_FAMILY {
138
- GPT = 'gpt',
139
- RUNWARE = 'runware',
140
- DALL_E = 'dall-e',
141
- IMAGEN = 'imagen',
142
- }
143
-
144
- const imageGenerator = {
145
- [MODEL_FAMILY.GPT]: async ({ model, prompt, config, logger, agent, input }) => {
146
- let args: GenerateImageConfig & { files?: BinaryInput[] } = {
147
- model,
148
- size: config?.data?.size || 'auto',
149
- quality: config?.data?.quality || 'auto',
150
- };
151
-
152
- try {
153
- const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
154
-
155
- // if the llm is undefined, then it means we removed the model from our system
156
- if (!llmInference.connector) {
157
- return {
158
- _error: `The model '${model}' is not available. Please try a different one.`,
159
- _debug: logger.output,
160
- };
161
- }
162
-
163
- const provider = await agent.modelsProvider.getProvider(model);
164
-
165
- const files: any[] = parseFiles(input, config);
166
- const validFiles = files.filter((file) => imageGenerator.isValidImageFile(provider, file.mimetype));
167
-
168
- if (files.length > 0 && validFiles.length === 0) {
169
- throw new Error('Supported image file types are: ' + SUPPORTED_MIME_TYPES_MAP[provider]?.imageGen?.join(', '));
170
- }
171
-
172
- let response;
173
-
174
- if (validFiles.length > 0) {
175
- response = await llmInference.imageEditRequest({ query: prompt, files: validFiles, params: { ...args, agentId: agent.id } });
176
- } else {
177
- response = await llmInference.imageGenRequest({ query: prompt, params: { ...args, agentId: agent.id } });
178
- }
179
-
180
- if (response?.usage) {
181
- imageGenerator.reportTokenUsage(response.usage, {
182
- modelEntryName: model,
183
- keySource: model.startsWith(BUILT_IN_MODEL_PREFIX) ? APIKeySource.Smyth : APIKeySource.User,
184
- agentId: agent.id,
185
- teamId: agent.teamId,
186
- });
187
- }
188
-
189
- let output = response?.data?.[0]?.b64_json;
190
-
191
- const binaryInput = BinaryInput.from(output);
192
- const agentId = typeof agent == 'object' && agent.id ? agent.id : agent;
193
- const smythFile = await binaryInput.getJsonData(AccessCandidate.agent(agentId));
194
-
195
- return { output: smythFile };
196
- } catch (error: any) {
197
- throw new Error(`OpenAI Image Generation Error: ${error?.message || JSON.stringify(error)}`);
198
- }
199
- },
200
- [MODEL_FAMILY.DALL_E]: async ({ model, prompt, config, logger, agent, input }) => {
201
- let _finalPrompt = prompt;
202
-
203
- const files: any[] = parseFiles(input, config);
204
-
205
- if (files.length > 0) {
206
- throw new Error('OpenAI Image Generation Error: DALL-E models do not support image editing or variations. Please use a different model.');
207
- }
208
-
209
- const responseFormat = config?.data?.responseFormat || 'url';
210
-
211
- let args: GenerateImageConfig & { responseFormat: 'url' | 'b64_json' } = {
212
- responseFormat,
213
- model,
214
- };
215
-
216
- let cost = 0;
217
-
218
- if (model === DALL_E_MODELS.DALL_E_3) {
219
- const size = config?.data?.sizeDalle3 || '1024x1024';
220
- const quality = config?.data?.quality || 'standard';
221
- const style = config?.data?.style || 'vivid';
222
- args.size = size;
223
- args.quality = quality;
224
- args.style = style;
225
-
226
- const isRawInputPrompt = config?.data?.isRawInputPrompt || false;
227
-
228
- if (isRawInputPrompt) {
229
- _finalPrompt = `I NEED to test how the tool works with extremely simple prompts. DO NOT add any detail, just use it AS-IS: ${prompt}`;
230
- }
231
-
232
- cost = IMAGE_GEN_COST_MAP[model][quality][size];
233
- } else if (model === DALL_E_MODELS.DALL_E_2) {
234
- const size = config?.data?.sizeDalle2 || '256x256';
235
- const numberOfImages = parseInt(config?.data?.numberOfImages) || 1;
236
- args.size = size;
237
- args.n = numberOfImages;
238
-
239
- cost = IMAGE_GEN_COST_MAP[model][size];
240
- }
241
-
242
- const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
243
-
244
- // if the llm is undefined, then it means we removed the model from our system
245
- if (!llmInference.connector) {
246
- return {
247
- _error: `The model '${model}' is not available. Please try a different one.`,
248
- _debug: logger.output,
249
- };
250
- }
251
-
252
- const response: any = await llmInference.imageGenRequest({ query: _finalPrompt, params: { ...args, agentId: agent.id } });
253
-
254
- let output = response?.data?.[0]?.[responseFormat];
255
- const revised_prompt = response?.data?.[0]?.revised_prompt;
256
-
257
- if (revised_prompt && prompt !== revised_prompt) {
258
- logger.debug(`Revised Prompt:\n${revised_prompt}`);
259
- }
260
-
261
- imageGenerator.reportUsage({ cost }, { modelEntryName: model, keySource: APIKeySource.Smyth, agentId: agent.id, teamId: agent.teamId });
262
-
263
- return { output };
264
- },
265
- [MODEL_FAMILY.RUNWARE]: async ({ model, prompt, config, agent, input }) => {
266
- // Initialize Runware client
267
- const teamId = agent.teamId;
268
- const apiKey = (await getCredentials(AccessCandidate.team(teamId), 'runware')) as string;
269
-
270
- if (!apiKey) {
271
- throw new Error('Runware API key is missing. Please provide a valid key to continue.');
272
- }
273
-
274
- const runware = new Runware({ apiKey });
275
- await runware.ensureConnection();
276
-
277
- const negativePrompt = config?.data?.negativePrompt || '';
278
-
279
- const files: any[] = parseFiles(input, config);
280
- let seedImage = Array.isArray(files) ? files[0] : files;
281
- seedImage = await normalizeImageInput(seedImage);
282
-
283
- const modelId = await agent.modelsProvider.getModelId(model);
284
- const imageRequestArgs: IRequestImage = {
285
- model: modelId,
286
- positivePrompt: prompt,
287
- width: +config?.data?.width || 1024,
288
- height: +config?.data?.height || 1024,
289
- numberResults: 1, // For Image Generation we only need 1 image
290
- outputType: 'URL', // For Image Generation we only need the URL
291
- outputFormat: config?.data?.outputFormat || 'JPEG',
292
- includeCost: true,
293
- };
294
-
295
- if (seedImage) {
296
- imageRequestArgs.seedImage = seedImage;
297
- imageRequestArgs.strength = +config?.data?.strength || 0.5;
298
- }
299
-
300
- // If a negative prompt is provided, add it to the request args
301
- if (negativePrompt) {
302
- imageRequestArgs.negativePrompt = negativePrompt;
303
- }
304
-
305
- try {
306
- const response = await runware.requestImages(imageRequestArgs);
307
-
308
- // Get first image from response array
309
- const firstImage = response[0];
310
-
311
- // Map response to match expected format
312
- let output = firstImage.imageURL;
313
-
314
- imageGenerator.reportUsage(
315
- { cost: firstImage.cost },
316
- { modelEntryName: model, keySource: APIKeySource.Smyth, agentId: agent.id, teamId: agent.teamId }
317
- );
318
-
319
- return { output };
320
- } catch (error: any) {
321
- throw new Error(`Runware Image Generation Error: ${error?.message || JSON.stringify(error)}`);
322
- } finally {
323
- // Clean up connection
324
- await runware.disconnect();
325
- }
326
- },
327
- [MODEL_FAMILY.IMAGEN]: async ({ model, prompt, config, logger, agent, input }) => {
328
- try {
329
- const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
330
-
331
- // if the llm is undefined, then it means we removed the model from our system
332
- if (!llmInference.connector) {
333
- return {
334
- _error: `The model '${model}' is not available. Please try a different one.`,
335
- _debug: logger.output,
336
- };
337
- }
338
-
339
- const files: any[] = parseFiles(input, config);
340
-
341
- let args: GenerateImageConfig & {
342
- aspectRatio?: string;
343
- numberOfImages?: number;
344
- personGeneration?: string;
345
- } = {
346
- model,
347
- aspectRatio: config?.data?.aspectRatio || config?.data?.size || '1:1',
348
- numberOfImages: config?.data?.numberOfImages || 1,
349
- personGeneration: config?.data?.personGeneration || 'allow_adult',
350
- };
351
-
352
- let response;
353
-
354
- // Check if files are provided for image editing
355
- if (files.length > 0) {
356
- const validFiles = files.filter((file) => imageGenerator.isValidImageFile('GoogleAI', file.mimetype));
357
- if (validFiles.length === 0) {
358
- throw new Error('Supported image file types are: ' + SUPPORTED_MIME_TYPES_MAP.GoogleAI?.image?.join(', '));
359
- }
360
- response = await llmInference.imageEditRequest({ query: prompt, files: validFiles, params: { ...args, agentId: agent.id } });
361
- } else {
362
- response = await llmInference.imageGenRequest({ query: prompt, params: { ...args, agentId: agent.id } });
363
- }
364
-
365
- // Usage reporting is now handled in the GoogleAI connector
366
-
367
- let output = response?.data?.[0]?.b64_json;
368
-
369
- if (output) {
370
- const binaryInput = BinaryInput.from(output);
371
- const agentId = typeof agent == 'object' && agent.id ? agent.id : agent;
372
- const smythFile = await binaryInput.getJsonData(AccessCandidate.agent(agentId));
373
- return { output: smythFile };
374
- } else {
375
- // Handle URL response format
376
- output = response?.data?.[0]?.url;
377
- return { output };
378
- }
379
- } catch (error: any) {
380
- throw new Error(`Google AI Image Generation Error: ${error?.message || JSON.stringify(error)}`);
381
- }
382
- },
383
- reportTokenUsage(usage: TokenUsage, metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }) {
384
- // SmythOS (built-in) models have a prefix, so we need to remove it to get the model name
385
- const modelName = metadata.modelEntryName.replace(BUILT_IN_MODEL_PREFIX, '');
386
-
387
- const usageData = {
388
- sourceId: `api:imagegen.${modelName}`,
389
- keySource: metadata.keySource,
390
-
391
- input_tokens_txt: usage?.input_tokens_details?.text_tokens || 0,
392
- input_tokens_img: usage?.input_tokens_details?.image_tokens || 0,
393
- output_tokens: usage?.output_tokens,
394
- input_tokens_cache_read: usage?.prompt_tokens_details?.cached_tokens || 0,
395
-
396
- agentId: metadata.agentId,
397
- teamId: metadata.teamId,
398
- };
399
- SystemEvents.emit('USAGE:API', usageData);
400
-
401
- return usageData;
402
- },
403
- reportUsage(usage: { cost: number }, metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }) {
404
- const usageData = {
405
- sourceId: `api:imagegen.smyth`,
406
- keySource: metadata.keySource,
407
-
408
- cost: usage?.cost,
409
-
410
- agentId: metadata.agentId,
411
- teamId: metadata.teamId,
412
- };
413
- SystemEvents.emit('USAGE:API', usageData);
414
-
415
- return usageData;
416
- },
417
- isValidImageFile(provider: string, mimetype: string) {
418
- return SUPPORTED_MIME_TYPES_MAP[provider]?.imageGen?.includes(mimetype);
419
- },
420
- };
421
-
422
- enum PROVIDERS {
423
- OPENAI = 'OpenAI',
424
- RUNWARE = 'Runware',
425
- GOOGLEAI = 'GoogleAI',
426
- }
427
-
428
- /**
429
- * Gets the model family from a model identifier
430
- * @param model The model identifier
431
- * @returns The model family or null if not recognized
432
- */
433
- async function getModelFamily(model: string, agent: IAgent): Promise<string | null> {
434
- if (await isGPTModel(model)) return MODEL_FAMILY.GPT;
435
- if (await isRunwareModel(model, agent)) return MODEL_FAMILY.RUNWARE;
436
- if (await isDallEModel(model)) return MODEL_FAMILY.DALL_E;
437
- if (await isGoogleAIModel(model, agent)) return MODEL_FAMILY.IMAGEN;
438
-
439
- return null;
440
- }
441
-
442
- function isGPTModel(model: string) {
443
- return model?.replace(BUILT_IN_MODEL_PREFIX, '')?.startsWith(MODEL_FAMILY.GPT);
444
- }
445
-
446
- async function isRunwareModel(model: string, agent: IAgent): Promise<boolean> {
447
- const provider = await agent.modelsProvider.getProvider(model);
448
- return provider === PROVIDERS.RUNWARE || provider?.toLowerCase() === PROVIDERS.RUNWARE.toLowerCase();
449
- }
450
-
451
- function isDallEModel(model: string) {
452
- return model?.replace(BUILT_IN_MODEL_PREFIX, '')?.startsWith(MODEL_FAMILY.DALL_E);
453
- }
454
-
455
- async function isGoogleAIModel(model: string, agent: IAgent): Promise<boolean> {
456
- const provider = await agent.modelsProvider.getProvider(model);
457
- return (
458
- provider === PROVIDERS.GOOGLEAI ||
459
- provider?.toLowerCase() === PROVIDERS.GOOGLEAI.toLowerCase() ||
460
- model?.replace(BUILT_IN_MODEL_PREFIX, '')?.includes('imagen')
461
- );
462
- }
463
-
464
- function parseFiles(input: any, config: any) {
465
- const mediaTypes = ['Image', 'Audio', 'Video', 'Binary'];
466
-
467
- // Parse media inputs from config
468
- const inputFiles =
469
- config.inputs
470
- ?.filter((_input) => mediaTypes.includes(_input.type))
471
- ?.flatMap((_input) => {
472
- const value = input[_input.name];
473
-
474
- if (Array.isArray(value)) {
475
- return value.map((item) => TemplateString(item).parseRaw(input).result);
476
- } else {
477
- return TemplateString(value).parseRaw(input).result;
478
- }
479
- })
480
- ?.filter((file) => file) || [];
481
-
482
- return inputFiles;
483
- }
1
+ //TODO: this component need to be fully refactored to use the same approach as GenAI LLM
2
+
3
+ import { IRequestImage, Runware } from '@runware/sdk-js';
4
+ import { OpenAI } from 'openai';
5
+
6
+ import { TemplateString } from '@sre/helpers/TemplateString.helper';
7
+ import { LLMInference } from '@sre/LLMManager/LLM.inference';
8
+ import { IAgent } from '@sre/types/Agent.types';
9
+ import { APIKeySource, GenerateImageConfig } from '@sre/types/LLM.types';
10
+ import Joi from 'joi';
11
+ import { Component } from './Component.class';
12
+
13
+ import { SystemEvents } from '@sre/Core/SystemEvents';
14
+ import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
15
+
16
+ import { BUILT_IN_MODEL_PREFIX, SUPPORTED_MIME_TYPES_MAP } from '@sre/constants';
17
+ import { BinaryInput } from '@sre/helpers/BinaryInput.helper';
18
+ import { normalizeImageInput } from '@sre/utils/data.utils';
19
+ import { ImageSettingsConfig } from './Image/imageSettings.config';
20
+ import { getCredentials } from '../subsystems/Security/Credentials.helper';
21
+
22
+ enum DALL_E_MODELS {
23
+ DALL_E_2 = 'dall-e-2',
24
+ DALL_E_3 = 'dall-e-3',
25
+ }
26
+
27
+ const IMAGE_GEN_COST_MAP = {
28
+ [DALL_E_MODELS.DALL_E_3]: {
29
+ standard: {
30
+ '1024x1024': 0.04,
31
+ '1024x1792': 0.08,
32
+ '1792x1024': 0.08,
33
+ },
34
+ hd: {
35
+ '1024x1024': 0.08,
36
+ '1024x1792': 0.12,
37
+ '1792x1024': 0.12,
38
+ },
39
+ },
40
+ [DALL_E_MODELS.DALL_E_2]: {
41
+ '256x256': 0.016,
42
+ '512x512': 0.018,
43
+ '1024x1024': 0.02,
44
+ },
45
+ };
46
+
47
+ export class ImageGenerator extends Component {
48
+ protected configSchema = Joi.object({
49
+ model: Joi.string().max(100).required(),
50
+ prompt: Joi.string().optional().min(2).max(2000).label('Prompt'),
51
+
52
+ // #region OpenAI (DALL·E)
53
+ sizeDalle2: Joi.string().valid('256x256', '512x512', '1024x1024').optional(),
54
+ sizeDalle3: Joi.string().valid('1024x1024', '1792x1024', '1024x1792').optional(),
55
+ quality: Joi.string().valid('standard', 'hd', 'auto', 'high', 'medium', 'low').allow('').optional(),
56
+ style: Joi.string().valid('vivid', 'natural').optional(),
57
+ isRawInputPrompt: Joi.boolean().strict().optional(),
58
+ // #endregion
59
+
60
+ // #region Runware
61
+ negativePrompt: Joi.string().optional().allow('').min(2).max(2000).label('Negative Prompt'),
62
+ width: Joi.number().min(128).max(2048).multiple(64).optional().messages({
63
+ 'number.multiple': '{{#label}} must be divisible by 64 (eg: 128...512, 576, 640...2048). Provided value: {{#value}}',
64
+ }),
65
+ height: Joi.number().min(128).max(2048).multiple(64).optional().messages({
66
+ 'number.multiple': '{{#label}} must be divisible by 64 (eg: 128...512, 576, 640...2048). Provided value: {{#value}}',
67
+ }),
68
+ outputFormat: Joi.string().valid('PNG', 'JPEG', 'WEBP', 'auto', 'jpeg', 'png', 'webp').optional(),
69
+ strength: ImageSettingsConfig.strength,
70
+ // #endregion
71
+
72
+ // #region GPT model
73
+ size: Joi.string().optional().allow('').max(100).label('Size'),
74
+ // #endregion
75
+
76
+ // #region Google AI model
77
+ aspectRatio: Joi.string().valid('1:1', '3:4', '4:3', '9:16', '16:9').optional().allow('').label('Aspect Ratio'),
78
+ personGeneration: Joi.string().valid('dont_allow', 'allow_adult', 'allow_all').optional().allow('').label('Person Generation'),
79
+ // #endregion
80
+ });
81
+ constructor() {
82
+ super();
83
+ }
84
+ init() {}
85
+ async process(input, config, agent: IAgent) {
86
+ await super.process(input, config, agent);
87
+
88
+ const logger = this.createComponentLogger(agent, config);
89
+
90
+ logger.debug(`=== Image Generator Log ===`);
91
+
92
+ let model = config?.data?.model;
93
+
94
+ if (!model) {
95
+ return { _error: 'Model Not Found: ', _debug: logger.output };
96
+ }
97
+
98
+ logger.debug(`Model: ${model}`);
99
+
100
+ let prompt = config.data?.prompt || input?.Prompt;
101
+ prompt = typeof prompt === 'string' ? prompt : JSON.stringify(prompt);
102
+ prompt = TemplateString(prompt).parse(input).result;
103
+
104
+ if (!prompt) {
105
+ return { _error: 'Please provide a prompt or Image', _debug: logger.output };
106
+ }
107
+
108
+ logger.debug(`Prompt: \n`, prompt);
109
+
110
+ const modelFamily = await getModelFamily(model, agent);
111
+
112
+ if (typeof imageGenerator[modelFamily] !== 'function') {
113
+ return { _error: `The model '${model}' is not available. Please try a different one.`, _debug: logger.output };
114
+ }
115
+
116
+ try {
117
+ const { output } = await imageGenerator[modelFamily]({ model, config, input, logger, agent, prompt });
118
+
119
+ logger.debug(`Output: `, output);
120
+
121
+ return { Output: output, _debug: logger.output };
122
+ } catch (error: any) {
123
+ return { _error: `Generating Image(s)\n${error?.message || JSON.stringify(error)}`, _debug: logger.output };
124
+ }
125
+ }
126
+ }
127
+
128
+ // TODO: Create a separate service for image generation, similar to LLM.service.
129
+
130
+ // TODO: Hopefully we will have the proper type with new OpenAI SDK, then we can use their type
131
+ type TokenUsage = OpenAI.Completions.CompletionUsage & {
132
+ prompt_tokens_details?: { cached_tokens?: number };
133
+ input_tokens_details: { image_tokens?: number; text_tokens?: number };
134
+ output_tokens: number;
135
+ };
136
+
137
+ enum MODEL_FAMILY {
138
+ GPT = 'gpt',
139
+ RUNWARE = 'runware',
140
+ DALL_E = 'dall-e',
141
+ IMAGEN = 'imagen',
142
+ }
143
+
144
+ const imageGenerator = {
145
+ [MODEL_FAMILY.GPT]: async ({ model, prompt, config, logger, agent, input }) => {
146
+ let args: GenerateImageConfig & { files?: BinaryInput[] } = {
147
+ model,
148
+ size: config?.data?.size || 'auto',
149
+ quality: config?.data?.quality || 'auto',
150
+ };
151
+
152
+ try {
153
+ const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
154
+
155
+ // if the llm is undefined, then it means we removed the model from our system
156
+ if (!llmInference.connector) {
157
+ return {
158
+ _error: `The model '${model}' is not available. Please try a different one.`,
159
+ _debug: logger.output,
160
+ };
161
+ }
162
+
163
+ const provider = await agent.modelsProvider.getProvider(model);
164
+
165
+ const files: any[] = parseFiles(input, config);
166
+ const validFiles = files.filter((file) => imageGenerator.isValidImageFile(provider, file.mimetype));
167
+
168
+ if (files.length > 0 && validFiles.length === 0) {
169
+ throw new Error('Supported image file types are: ' + SUPPORTED_MIME_TYPES_MAP[provider]?.imageGen?.join(', '));
170
+ }
171
+
172
+ let response;
173
+
174
+ if (validFiles.length > 0) {
175
+ response = await llmInference.imageEditRequest({ query: prompt, files: validFiles, params: { ...args, agentId: agent.id } });
176
+ } else {
177
+ response = await llmInference.imageGenRequest({ query: prompt, params: { ...args, agentId: agent.id } });
178
+ }
179
+
180
+ if (response?.usage) {
181
+ imageGenerator.reportTokenUsage(response.usage, {
182
+ modelEntryName: model,
183
+ keySource: model.startsWith(BUILT_IN_MODEL_PREFIX) ? APIKeySource.Smyth : APIKeySource.User,
184
+ agentId: agent.id,
185
+ teamId: agent.teamId,
186
+ });
187
+ }
188
+
189
+ let output = response?.data?.[0]?.b64_json;
190
+
191
+ const binaryInput = BinaryInput.from(output);
192
+ const agentId = typeof agent == 'object' && agent.id ? agent.id : agent;
193
+ const smythFile = await binaryInput.getJsonData(AccessCandidate.agent(agentId));
194
+
195
+ return { output: smythFile };
196
+ } catch (error: any) {
197
+ throw new Error(`OpenAI Image Generation Error: ${error?.message || JSON.stringify(error)}`);
198
+ }
199
+ },
200
+ [MODEL_FAMILY.DALL_E]: async ({ model, prompt, config, logger, agent, input }) => {
201
+ let _finalPrompt = prompt;
202
+
203
+ const files: any[] = parseFiles(input, config);
204
+
205
+ if (files.length > 0) {
206
+ throw new Error('OpenAI Image Generation Error: DALL-E models do not support image editing or variations. Please use a different model.');
207
+ }
208
+
209
+ const responseFormat = config?.data?.responseFormat || 'url';
210
+
211
+ let args: GenerateImageConfig & { responseFormat: 'url' | 'b64_json' } = {
212
+ responseFormat,
213
+ model,
214
+ };
215
+
216
+ let cost = 0;
217
+
218
+ if (model === DALL_E_MODELS.DALL_E_3) {
219
+ const size = config?.data?.sizeDalle3 || '1024x1024';
220
+ const quality = config?.data?.quality || 'standard';
221
+ const style = config?.data?.style || 'vivid';
222
+ args.size = size;
223
+ args.quality = quality;
224
+ args.style = style;
225
+
226
+ const isRawInputPrompt = config?.data?.isRawInputPrompt || false;
227
+
228
+ if (isRawInputPrompt) {
229
+ _finalPrompt = `I NEED to test how the tool works with extremely simple prompts. DO NOT add any detail, just use it AS-IS: ${prompt}`;
230
+ }
231
+
232
+ cost = IMAGE_GEN_COST_MAP[model][quality][size];
233
+ } else if (model === DALL_E_MODELS.DALL_E_2) {
234
+ const size = config?.data?.sizeDalle2 || '256x256';
235
+ const numberOfImages = parseInt(config?.data?.numberOfImages) || 1;
236
+ args.size = size;
237
+ args.n = numberOfImages;
238
+
239
+ cost = IMAGE_GEN_COST_MAP[model][size];
240
+ }
241
+
242
+ const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
243
+
244
+ // if the llm is undefined, then it means we removed the model from our system
245
+ if (!llmInference.connector) {
246
+ return {
247
+ _error: `The model '${model}' is not available. Please try a different one.`,
248
+ _debug: logger.output,
249
+ };
250
+ }
251
+
252
+ const response: any = await llmInference.imageGenRequest({ query: _finalPrompt, params: { ...args, agentId: agent.id } });
253
+
254
+ let output = response?.data?.[0]?.[responseFormat];
255
+ const revised_prompt = response?.data?.[0]?.revised_prompt;
256
+
257
+ if (revised_prompt && prompt !== revised_prompt) {
258
+ logger.debug(`Revised Prompt:\n${revised_prompt}`);
259
+ }
260
+
261
+ imageGenerator.reportUsage({ cost }, { modelEntryName: model, keySource: APIKeySource.Smyth, agentId: agent.id, teamId: agent.teamId });
262
+
263
+ return { output };
264
+ },
265
+ [MODEL_FAMILY.RUNWARE]: async ({ model, prompt, config, agent, input }) => {
266
+ // Initialize Runware client
267
+ const teamId = agent.teamId;
268
+ const apiKey = (await getCredentials(AccessCandidate.team(teamId), 'runware')) as string;
269
+
270
+ if (!apiKey) {
271
+ throw new Error('Runware API key is missing. Please provide a valid key to continue.');
272
+ }
273
+
274
+ const runware = new Runware({ apiKey });
275
+ await runware.ensureConnection();
276
+
277
+ const negativePrompt = config?.data?.negativePrompt || '';
278
+
279
+ const files: any[] = parseFiles(input, config);
280
+ let seedImage = Array.isArray(files) ? files[0] : files;
281
+ seedImage = await normalizeImageInput(seedImage);
282
+
283
+ const modelId = await agent.modelsProvider.getModelId(model);
284
+ const imageRequestArgs: IRequestImage = {
285
+ model: modelId,
286
+ positivePrompt: prompt,
287
+ width: +config?.data?.width || 1024,
288
+ height: +config?.data?.height || 1024,
289
+ numberResults: 1, // For Image Generation we only need 1 image
290
+ outputType: 'URL', // For Image Generation we only need the URL
291
+ outputFormat: config?.data?.outputFormat || 'JPEG',
292
+ includeCost: true,
293
+ };
294
+
295
+ if (seedImage) {
296
+ imageRequestArgs.seedImage = seedImage;
297
+ imageRequestArgs.strength = +config?.data?.strength || 0.5;
298
+ }
299
+
300
+ // If a negative prompt is provided, add it to the request args
301
+ if (negativePrompt) {
302
+ imageRequestArgs.negativePrompt = negativePrompt;
303
+ }
304
+
305
+ try {
306
+ const response = await runware.requestImages(imageRequestArgs);
307
+
308
+ // Get first image from response array
309
+ const firstImage = response[0];
310
+
311
+ // Map response to match expected format
312
+ let output = firstImage.imageURL;
313
+
314
+ imageGenerator.reportUsage(
315
+ { cost: firstImage.cost },
316
+ { modelEntryName: model, keySource: APIKeySource.Smyth, agentId: agent.id, teamId: agent.teamId }
317
+ );
318
+
319
+ return { output };
320
+ } catch (error: any) {
321
+ throw new Error(`Runware Image Generation Error: ${error?.message || JSON.stringify(error)}`);
322
+ } finally {
323
+ // Clean up connection
324
+ await runware.disconnect();
325
+ }
326
+ },
327
+ [MODEL_FAMILY.IMAGEN]: async ({ model, prompt, config, logger, agent, input }) => {
328
+ try {
329
+ const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
330
+
331
+ // if the llm is undefined, then it means we removed the model from our system
332
+ if (!llmInference.connector) {
333
+ return {
334
+ _error: `The model '${model}' is not available. Please try a different one.`,
335
+ _debug: logger.output,
336
+ };
337
+ }
338
+
339
+ const files: any[] = parseFiles(input, config);
340
+
341
+ let args: GenerateImageConfig & {
342
+ aspectRatio?: string;
343
+ numberOfImages?: number;
344
+ personGeneration?: string;
345
+ } = {
346
+ model,
347
+ aspectRatio: config?.data?.aspectRatio || config?.data?.size || '1:1',
348
+ numberOfImages: config?.data?.numberOfImages || 1,
349
+ personGeneration: config?.data?.personGeneration || 'allow_adult',
350
+ };
351
+
352
+ let response;
353
+
354
+ // Check if files are provided for image editing
355
+ if (files.length > 0) {
356
+ const validFiles = files.filter((file) => imageGenerator.isValidImageFile('GoogleAI', file.mimetype));
357
+ if (validFiles.length === 0) {
358
+ throw new Error('Supported image file types are: ' + SUPPORTED_MIME_TYPES_MAP.GoogleAI?.image?.join(', '));
359
+ }
360
+ response = await llmInference.imageEditRequest({ query: prompt, files: validFiles, params: { ...args, agentId: agent.id } });
361
+ } else {
362
+ response = await llmInference.imageGenRequest({ query: prompt, params: { ...args, agentId: agent.id } });
363
+ }
364
+
365
+ // Usage reporting is now handled in the GoogleAI connector
366
+
367
+ let output = response?.data?.[0]?.b64_json;
368
+
369
+ if (output) {
370
+ const binaryInput = BinaryInput.from(output);
371
+ const agentId = typeof agent == 'object' && agent.id ? agent.id : agent;
372
+ const smythFile = await binaryInput.getJsonData(AccessCandidate.agent(agentId));
373
+ return { output: smythFile };
374
+ } else {
375
+ // Handle URL response format
376
+ output = response?.data?.[0]?.url;
377
+ return { output };
378
+ }
379
+ } catch (error: any) {
380
+ throw new Error(`Google AI Image Generation Error: ${error?.message || JSON.stringify(error)}`);
381
+ }
382
+ },
383
+ reportTokenUsage(usage: TokenUsage, metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }) {
384
+ // SmythOS (built-in) models have a prefix, so we need to remove it to get the model name
385
+ const modelName = metadata.modelEntryName.replace(BUILT_IN_MODEL_PREFIX, '');
386
+
387
+ const usageData = {
388
+ sourceId: `api:imagegen.${modelName}`,
389
+ keySource: metadata.keySource,
390
+
391
+ input_tokens_txt: usage?.input_tokens_details?.text_tokens || 0,
392
+ input_tokens_img: usage?.input_tokens_details?.image_tokens || 0,
393
+ output_tokens: usage?.output_tokens,
394
+ input_tokens_cache_read: usage?.prompt_tokens_details?.cached_tokens || 0,
395
+
396
+ agentId: metadata.agentId,
397
+ teamId: metadata.teamId,
398
+ };
399
+ SystemEvents.emit('USAGE:API', usageData);
400
+
401
+ return usageData;
402
+ },
403
+ reportUsage(usage: { cost: number }, metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }) {
404
+ const usageData = {
405
+ sourceId: `api:imagegen.smyth`,
406
+ keySource: metadata.keySource,
407
+
408
+ cost: usage?.cost,
409
+
410
+ agentId: metadata.agentId,
411
+ teamId: metadata.teamId,
412
+ };
413
+ SystemEvents.emit('USAGE:API', usageData);
414
+
415
+ return usageData;
416
+ },
417
+ isValidImageFile(provider: string, mimetype: string) {
418
+ return SUPPORTED_MIME_TYPES_MAP[provider]?.imageGen?.includes(mimetype);
419
+ },
420
+ };
421
+
422
+ enum PROVIDERS {
423
+ OPENAI = 'OpenAI',
424
+ RUNWARE = 'Runware',
425
+ GOOGLEAI = 'GoogleAI',
426
+ }
427
+
428
+ /**
429
+ * Gets the model family from a model identifier
430
+ * @param model The model identifier
431
+ * @returns The model family or null if not recognized
432
+ */
433
+ async function getModelFamily(model: string, agent: IAgent): Promise<string | null> {
434
+ if (await isGPTModel(model)) return MODEL_FAMILY.GPT;
435
+ if (await isRunwareModel(model, agent)) return MODEL_FAMILY.RUNWARE;
436
+ if (await isDallEModel(model)) return MODEL_FAMILY.DALL_E;
437
+ if (await isGoogleAIModel(model, agent)) return MODEL_FAMILY.IMAGEN;
438
+
439
+ return null;
440
+ }
441
+
442
+ function isGPTModel(model: string) {
443
+ return model?.replace(BUILT_IN_MODEL_PREFIX, '')?.startsWith(MODEL_FAMILY.GPT);
444
+ }
445
+
446
+ async function isRunwareModel(model: string, agent: IAgent): Promise<boolean> {
447
+ const provider = await agent.modelsProvider.getProvider(model);
448
+ return provider === PROVIDERS.RUNWARE || provider?.toLowerCase() === PROVIDERS.RUNWARE.toLowerCase();
449
+ }
450
+
451
+ function isDallEModel(model: string) {
452
+ return model?.replace(BUILT_IN_MODEL_PREFIX, '')?.startsWith(MODEL_FAMILY.DALL_E);
453
+ }
454
+
455
+ async function isGoogleAIModel(model: string, agent: IAgent): Promise<boolean> {
456
+ const provider = await agent.modelsProvider.getProvider(model);
457
+ return (
458
+ provider === PROVIDERS.GOOGLEAI ||
459
+ provider?.toLowerCase() === PROVIDERS.GOOGLEAI.toLowerCase() ||
460
+ model?.replace(BUILT_IN_MODEL_PREFIX, '')?.includes('imagen')
461
+ );
462
+ }
463
+
464
+ function parseFiles(input: any, config: any) {
465
+ const mediaTypes = ['Image', 'Audio', 'Video', 'Binary'];
466
+
467
+ // Parse media inputs from config
468
+ const inputFiles =
469
+ config.inputs
470
+ ?.filter((_input) => mediaTypes.includes(_input.type))
471
+ ?.flatMap((_input) => {
472
+ const value = input[_input.name];
473
+
474
+ if (Array.isArray(value)) {
475
+ return value.map((item) => TemplateString(item).parseRaw(input).result);
476
+ } else {
477
+ return TemplateString(value).parseRaw(input).result;
478
+ }
479
+ })
480
+ ?.filter((file) => file) || [];
481
+
482
+ return inputFiles;
483
+ }