@webiny/ai-powerups 6.6.0-alpha.4 → 6.6.0-alpha.6

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 (211) hide show
  1. package/AiPowerups.d.ts +12 -0
  2. package/AiPowerups.js +1 -2
  3. package/AiPowerups.js.map +1 -1
  4. package/admin/Extension.d.ts +1 -1
  5. package/admin/Extension.js +6 -2
  6. package/admin/Extension.js.map +1 -1
  7. package/admin/domain/modelRoles.d.ts +17 -0
  8. package/admin/domain/modelRoles.js +8 -0
  9. package/admin/domain/modelRoles.js.map +1 -0
  10. package/admin/features/feature.js +2 -0
  11. package/admin/features/feature.js.map +1 -1
  12. package/admin/features/listCapabilities/ListCapabilitiesGateway.d.ts +12 -0
  13. package/admin/features/listCapabilities/ListCapabilitiesGateway.js +34 -0
  14. package/admin/features/listCapabilities/ListCapabilitiesGateway.js.map +1 -0
  15. package/admin/features/listCapabilities/ListCapabilitiesRepository.d.ts +13 -0
  16. package/admin/features/listCapabilities/ListCapabilitiesRepository.js +27 -0
  17. package/admin/features/listCapabilities/ListCapabilitiesRepository.js.map +1 -0
  18. package/admin/features/listCapabilities/ListCapabilitiesUseCase.d.ts +10 -0
  19. package/admin/features/listCapabilities/ListCapabilitiesUseCase.js +18 -0
  20. package/admin/features/listCapabilities/ListCapabilitiesUseCase.js.map +1 -0
  21. package/admin/features/listCapabilities/abstractions.d.ts +28 -0
  22. package/admin/features/listCapabilities/abstractions.js +7 -0
  23. package/admin/features/listCapabilities/abstractions.js.map +1 -0
  24. package/admin/features/listCapabilities/feature.d.ts +3 -0
  25. package/admin/features/listCapabilities/feature.js +21 -0
  26. package/admin/features/listCapabilities/feature.js.map +1 -0
  27. package/admin/features/listCapabilities/index.d.ts +3 -0
  28. package/admin/features/listCapabilities/index.js +2 -0
  29. package/admin/features/settings/shared/abstractions.d.ts +58 -25
  30. package/admin/features/settings/shared/abstractions.js.map +1 -1
  31. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPresenter.js +13 -1
  32. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPresenter.js.map +1 -1
  33. package/admin/presentation/AiPowerUpsSettings/feature.js +6 -2
  34. package/admin/presentation/AiPowerUpsSettings/feature.js.map +1 -1
  35. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.d.ts +13 -0
  36. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.js +9 -0
  37. package/admin/presentation/AiPowerUpsSettings/hasUsableModel.js.map +1 -0
  38. package/admin/presentation/AiPowerUpsSettings/index.d.ts +1 -0
  39. package/admin/presentation/AiPowerUpsSettings/index.js +1 -0
  40. package/admin/presentation/AiPowerUpsSettings/settingsGroup.d.ts +9 -0
  41. package/admin/presentation/AiPowerUpsSettings/settingsGroup.js.map +1 -1
  42. package/admin/presentation/CapabilitiesSettings.d.ts +39 -0
  43. package/admin/presentation/CapabilitiesSettings.js +104 -0
  44. package/admin/presentation/CapabilitiesSettings.js.map +1 -0
  45. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js +2 -2
  46. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js.map +1 -1
  47. package/admin/presentation/ConnectionsSettings.d.ts +25 -0
  48. package/admin/presentation/ConnectionsSettings.js +52 -0
  49. package/admin/presentation/ConnectionsSettings.js.map +1 -0
  50. package/admin/presentation/ModelRolesSettings.d.ts +29 -0
  51. package/admin/presentation/ModelRolesSettings.js +67 -0
  52. package/admin/presentation/ModelRolesSettings.js.map +1 -0
  53. package/admin/presentation/WbContentGeneration/GenerateContentButton.js +2 -2
  54. package/admin/presentation/WbContentGeneration/GenerateContentButton.js.map +1 -1
  55. package/admin/presentation/modelRoles.d.ts +10 -0
  56. package/admin/presentation/modelRoles.js +23 -0
  57. package/admin/presentation/modelRoles.js.map +1 -0
  58. package/api/Extension.js +10 -2
  59. package/api/Extension.js.map +1 -1
  60. package/api/features/AdminAssistant/AdminAssistantStreamRoute.d.ts +34 -0
  61. package/api/features/AdminAssistant/AdminAssistantStreamRoute.js +42 -0
  62. package/api/features/AdminAssistant/AdminAssistantStreamRoute.js.map +1 -0
  63. package/api/features/AdminAssistant/AdminAssistantUseCase.d.ts +42 -0
  64. package/api/features/AdminAssistant/AdminAssistantUseCase.js +170 -0
  65. package/api/features/AdminAssistant/AdminAssistantUseCase.js.map +1 -0
  66. package/api/features/AdminAssistant/abstractions.d.ts +38 -0
  67. package/api/features/AdminAssistant/abstractions.js +6 -0
  68. package/api/features/AdminAssistant/abstractions.js.map +1 -0
  69. package/api/features/AdminAssistant/approvals.d.ts +29 -0
  70. package/api/features/AdminAssistant/approvals.js +30 -0
  71. package/api/features/AdminAssistant/approvals.js.map +1 -0
  72. package/api/features/AdminAssistant/capability.d.ts +31 -0
  73. package/api/features/AdminAssistant/capability.js +19 -0
  74. package/api/features/AdminAssistant/capability.js.map +1 -0
  75. package/api/features/AdminAssistant/events.d.ts +54 -0
  76. package/api/features/AdminAssistant/events.js +0 -0
  77. package/api/features/AdminAssistant/feature.d.ts +12 -0
  78. package/api/features/AdminAssistant/feature.js +23 -0
  79. package/api/features/AdminAssistant/feature.js.map +1 -0
  80. package/api/features/AdminAssistant/index.d.ts +5 -0
  81. package/api/features/AdminAssistant/index.js +3 -0
  82. package/api/features/AdminAssistant/parseChatBody.d.ts +14 -0
  83. package/api/features/AdminAssistant/parseChatBody.js +33 -0
  84. package/api/features/AdminAssistant/parseChatBody.js.map +1 -0
  85. package/api/features/AdminAssistant/systemPrompt.d.ts +7 -0
  86. package/api/features/AdminAssistant/systemPrompt.js +24 -0
  87. package/api/features/AdminAssistant/systemPrompt.js.map +1 -0
  88. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.d.ts +3 -5
  89. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js +11 -16
  90. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js.map +1 -1
  91. package/api/features/AiImageEnrichment/abstractions.d.ts +6 -0
  92. package/api/features/AiImageEnrichment/abstractions.js.map +1 -1
  93. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js +2 -2
  94. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js.map +1 -1
  95. package/api/features/AiImageEnrichment/capability.d.ts +18 -0
  96. package/api/features/AiImageEnrichment/capability.js +19 -0
  97. package/api/features/AiImageEnrichment/capability.js.map +1 -0
  98. package/api/features/AiImageEnrichment/errors.d.ts +6 -1
  99. package/api/features/AiImageEnrichment/errors.js +2 -2
  100. package/api/features/AiImageEnrichment/errors.js.map +1 -1
  101. package/api/features/AiImageEnrichment/feature.js +2 -0
  102. package/api/features/AiImageEnrichment/feature.js.map +1 -1
  103. package/api/features/Capabilities/CapabilitiesHandler.d.ts +28 -0
  104. package/api/features/Capabilities/CapabilitiesHandler.js +56 -0
  105. package/api/features/Capabilities/CapabilitiesHandler.js.map +1 -0
  106. package/api/features/Capabilities/ListAiCapabilitiesUseCase.d.ts +18 -0
  107. package/api/features/Capabilities/ListAiCapabilitiesUseCase.js +28 -0
  108. package/api/features/Capabilities/ListAiCapabilitiesUseCase.js.map +1 -0
  109. package/api/features/Capabilities/ResolveAiCapabilityUseCase.d.ts +30 -0
  110. package/api/features/Capabilities/ResolveAiCapabilityUseCase.js +88 -0
  111. package/api/features/Capabilities/ResolveAiCapabilityUseCase.js.map +1 -0
  112. package/api/features/Capabilities/abstractions.d.ts +77 -0
  113. package/api/features/Capabilities/abstractions.js +7 -0
  114. package/api/features/Capabilities/abstractions.js.map +1 -0
  115. package/api/features/Capabilities/composeSystemPrompt.d.ts +14 -0
  116. package/api/features/Capabilities/composeSystemPrompt.js +13 -0
  117. package/api/features/Capabilities/composeSystemPrompt.js.map +1 -0
  118. package/api/features/Capabilities/feature.d.ts +4 -0
  119. package/api/features/Capabilities/feature.js +15 -0
  120. package/api/features/Capabilities/feature.js.map +1 -0
  121. package/api/features/Capabilities/index.d.ts +5 -0
  122. package/api/features/Capabilities/index.js +3 -0
  123. package/api/features/Capabilities/types.d.ts +86 -0
  124. package/api/features/Capabilities/types.js +4 -0
  125. package/api/features/Capabilities/types.js.map +1 -0
  126. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.d.ts +3 -5
  127. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js +11 -65
  128. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js.map +1 -1
  129. package/api/features/CmsCompareEntryRevisions/capability.d.ts +13 -0
  130. package/api/features/CmsCompareEntryRevisions/capability.js +64 -0
  131. package/api/features/CmsCompareEntryRevisions/capability.js.map +1 -0
  132. package/api/features/CmsCompareEntryRevisions/feature.js +2 -0
  133. package/api/features/CmsCompareEntryRevisions/feature.js.map +1 -1
  134. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.d.ts +3 -5
  135. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js +11 -19
  136. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js.map +1 -1
  137. package/api/features/CmsGenerateEntryContent/capability.d.ts +17 -0
  138. package/api/features/CmsGenerateEntryContent/capability.js +17 -0
  139. package/api/features/CmsGenerateEntryContent/capability.js.map +1 -0
  140. package/api/features/CmsGenerateEntryContent/feature.js +2 -0
  141. package/api/features/CmsGenerateEntryContent/feature.js.map +1 -1
  142. package/api/features/Connections/ConnectionsGraphQLMapper.d.ts +20 -0
  143. package/api/features/Connections/ConnectionsGraphQLMapper.js +37 -0
  144. package/api/features/Connections/ConnectionsGraphQLMapper.js.map +1 -0
  145. package/api/features/Connections/ConnectionsHandler.d.ts +25 -0
  146. package/api/features/Connections/ConnectionsHandler.js +84 -0
  147. package/api/features/Connections/ConnectionsHandler.js.map +1 -0
  148. package/api/features/Connections/feature.d.ts +4 -0
  149. package/api/features/Connections/feature.js +13 -0
  150. package/api/features/Connections/feature.js.map +1 -0
  151. package/api/features/Connections/index.d.ts +2 -0
  152. package/api/features/Connections/index.js +1 -0
  153. package/api/features/Connections/legacyProviders.d.ts +26 -0
  154. package/api/features/Connections/legacyProviders.js +9 -0
  155. package/api/features/Connections/legacyProviders.js.map +1 -0
  156. package/api/features/Connections/types.d.ts +36 -0
  157. package/api/features/Connections/types.js +0 -0
  158. package/api/features/GetSettings/GetSettingsRepository.js +1 -1
  159. package/api/features/GetSettings/GetSettingsRepository.js.map +1 -1
  160. package/api/features/ModelRoles/ModelRolesHandler.d.ts +15 -0
  161. package/api/features/ModelRoles/ModelRolesHandler.js +68 -0
  162. package/api/features/ModelRoles/ModelRolesHandler.js.map +1 -0
  163. package/api/features/ModelRoles/feature.d.ts +4 -0
  164. package/api/features/ModelRoles/feature.js +11 -0
  165. package/api/features/ModelRoles/feature.js.map +1 -0
  166. package/api/features/ModelRoles/index.d.ts +4 -0
  167. package/api/features/ModelRoles/index.js +2 -0
  168. package/api/features/ModelRoles/roles.d.ts +16 -0
  169. package/api/features/ModelRoles/roles.js +9 -0
  170. package/api/features/ModelRoles/roles.js.map +1 -0
  171. package/api/features/ModelRoles/types.d.ts +21 -0
  172. package/api/features/ModelRoles/types.js +7 -0
  173. package/api/features/ModelRoles/types.js.map +1 -0
  174. package/api/features/UpdateSettings/UpdateSettingsRepository.js +2 -2
  175. package/api/features/UpdateSettings/UpdateSettingsRepository.js.map +1 -1
  176. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.d.ts +3 -5
  177. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js +11 -19
  178. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js.map +1 -1
  179. package/api/features/WbGeneratePageContent/capability.d.ts +16 -0
  180. package/api/features/WbGeneratePageContent/capability.js +17 -0
  181. package/api/features/WbGeneratePageContent/capability.js.map +1 -0
  182. package/api/features/WbGeneratePageContent/feature.js +2 -0
  183. package/api/features/WbGeneratePageContent/feature.js.map +1 -1
  184. package/api/features/WbTranslatePage/WbTranslatePageDecorator.d.ts +5 -5
  185. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js +19 -19
  186. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js.map +1 -1
  187. package/api/features/WbTranslatePage/capability.d.ts +13 -0
  188. package/api/features/WbTranslatePage/capability.js +19 -0
  189. package/api/features/WbTranslatePage/capability.js.map +1 -0
  190. package/api/features/WbTranslatePage/feature.js +2 -0
  191. package/api/features/WbTranslatePage/feature.js.map +1 -1
  192. package/api/features/shared/abstractions.d.ts +6 -1
  193. package/api/features/shared/abstractions.js.map +1 -1
  194. package/api/features/shared/modelId.d.ts +11 -0
  195. package/api/features/shared/modelId.js +4 -0
  196. package/api/features/shared/modelId.js.map +1 -0
  197. package/api/graphql/BaseGraphQLSchema.js +16 -0
  198. package/api/graphql/BaseGraphQLSchema.js.map +1 -1
  199. package/api/types.d.ts +3 -0
  200. package/api/types.js +3 -0
  201. package/exports/api/ai-powerups.d.ts +5 -0
  202. package/exports/api/ai-powerups.js +3 -0
  203. package/package.json +24 -26
  204. package/api/features/AiChatProvider/PowerUpsAiChatProvider.d.ts +0 -24
  205. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js +0 -31
  206. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js.map +0 -1
  207. package/api/features/AiChatProvider/feature.d.ts +0 -9
  208. package/api/features/AiChatProvider/feature.js +0 -11
  209. package/api/features/AiChatProvider/feature.js.map +0 -1
  210. package/api/features/AiChatProvider/index.d.ts +0 -2
  211. package/api/features/AiChatProvider/index.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"admin/presentation/AiPowerUpsSettings/feature.js","sources":["../../../../src/admin/presentation/AiPowerUpsSettings/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { AiPowerUpsSettingsPresenter as PresenterAbstraction } from \"./abstractions.js\";\nimport { AiPowerUpsSettingsPresenter } from \"./AiPowerUpsSettingsPresenter.js\";\nimport { ProviderSettings } from \"../ProvidersSettings.js\";\nimport { ReaderPersonasSettings } from \"../ReaderPersonasSettings.js\";\nimport { WriterPersonasSettings } from \"../WriterPersonasSettings.js\";\nimport { ProjectsSettings } from \"../ProjectsSettings.js\";\n\nexport const AiPowerUpsSettingsFeature = createFeature({\n name: \"AiPowerUps/Settings/Presenter\",\n register(container) {\n container.register(ProviderSettings);\n container.register(ReaderPersonasSettings);\n container.register(WriterPersonasSettings);\n container.register(ProjectsSettings);\n container.register(AiPowerUpsSettingsPresenter);\n },\n resolve(container) {\n return {\n presenter: container.resolve(PresenterAbstraction)\n };\n }\n});\n"],"names":["AiPowerUpsSettingsFeature","createFeature","container","ProviderSettings","ReaderPersonasSettings","WriterPersonasSettings","ProjectsSettings","AiPowerUpsSettingsPresenter","PresenterAbstraction"],"mappings":";;;;;;;AAQO,MAAMA,4BAA4BC,cAAc;IACnD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QACnBJ,UAAU,QAAQ,CAACK;IACvB;IACA,SAAQL,SAAS;QACb,OAAO;YACH,WAAWA,UAAU,OAAO,CAACM;QACjC;IACJ;AACJ"}
1
+ {"version":3,"file":"admin/presentation/AiPowerUpsSettings/feature.js","sources":["../../../../src/admin/presentation/AiPowerUpsSettings/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { AiPowerUpsSettingsPresenter as PresenterAbstraction } from \"./abstractions.js\";\nimport { AiPowerUpsSettingsPresenter } from \"./AiPowerUpsSettingsPresenter.js\";\nimport { ConnectionsSettings } from \"../ConnectionsSettings.js\";\nimport { ModelRolesSettings } from \"../ModelRolesSettings.js\";\nimport { CapabilitiesSettings } from \"../CapabilitiesSettings.js\";\nimport { ReaderPersonasSettings } from \"../ReaderPersonasSettings.js\";\nimport { WriterPersonasSettings } from \"../WriterPersonasSettings.js\";\nimport { ProjectsSettings } from \"../ProjectsSettings.js\";\n\nexport const AiPowerUpsSettingsFeature = createFeature({\n name: \"AiPowerUps/Settings/Presenter\",\n register(container) {\n /*\n * Registration order is tab order. Configuration first (a key, then what each role runs,\n * then per-feature exceptions), then the prompt content: personas and projects.\n *\n * `ProvidersSettings` is gone from the UI. Its stored section still round-trips on the api\n * so `Connections` and `ModelRoles` can seed themselves from it once.\n */\n container.register(ConnectionsSettings);\n container.register(ModelRolesSettings);\n container.register(CapabilitiesSettings);\n container.register(ReaderPersonasSettings);\n container.register(WriterPersonasSettings);\n container.register(ProjectsSettings);\n container.register(AiPowerUpsSettingsPresenter);\n },\n resolve(container) {\n return {\n presenter: container.resolve(PresenterAbstraction)\n };\n }\n});\n"],"names":["AiPowerUpsSettingsFeature","createFeature","container","ConnectionsSettings","ModelRolesSettings","CapabilitiesSettings","ReaderPersonasSettings","WriterPersonasSettings","ProjectsSettings","AiPowerUpsSettingsPresenter","PresenterAbstraction"],"mappings":";;;;;;;;;AAUO,MAAMA,4BAA4BC,cAAc;IACnD,MAAM;IACN,UAASC,SAAS;QAQdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QACnBJ,UAAU,QAAQ,CAACK;QACnBL,UAAU,QAAQ,CAACM;QACnBN,UAAU,QAAQ,CAACO;IACvB;IACA,SAAQP,SAAS;QACb,OAAO;YACH,WAAWA,UAAU,OAAO,CAACQ;QACjC;IACJ;AACJ"}
@@ -0,0 +1,13 @@
1
+ import type { IAiPowerUpsSettings } from "../../../admin/features/settings/shared/abstractions.js";
2
+ /**
3
+ * Whether a feature that runs on the Standard role has something to run on.
4
+ *
5
+ * The AI entry points hide themselves when the project has no model configured, and they used to
6
+ * test `providers.presets.length > 0` for it. That test passed for a row with no key in it, so the
7
+ * button appeared and the request failed. This one checks the whole chain the api will walk: the
8
+ * role is filled, the connection it names still exists, and that connection has a key.
9
+ *
10
+ * Admin only ever sees the masked key, and the mask is non-empty exactly when a key is stored, so
11
+ * it answers the question without the plaintext leaving the api.
12
+ */
13
+ export declare const hasUsableModel: (settings: IAiPowerUpsSettings | null) => boolean;
@@ -0,0 +1,9 @@
1
+ const hasUsableModel = (settings)=>{
2
+ const standard = settings?.modelRoles?.roles?.["standard"];
3
+ if (!standard?.model || !standard.connectionId) return false;
4
+ const connection = settings?.connections?.presets?.find((c)=>c.id === standard.connectionId);
5
+ return Boolean(connection?.apiKey);
6
+ };
7
+ export { hasUsableModel };
8
+
9
+ //# sourceMappingURL=hasUsableModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/presentation/AiPowerUpsSettings/hasUsableModel.js","sources":["../../../../src/admin/presentation/AiPowerUpsSettings/hasUsableModel.ts"],"sourcesContent":["import type { IAiPowerUpsSettings } from \"~/admin/features/settings/shared/abstractions.js\";\n\n/**\n * Whether a feature that runs on the Standard role has something to run on.\n *\n * The AI entry points hide themselves when the project has no model configured, and they used to\n * test `providers.presets.length > 0` for it. That test passed for a row with no key in it, so the\n * button appeared and the request failed. This one checks the whole chain the api will walk: the\n * role is filled, the connection it names still exists, and that connection has a key.\n *\n * Admin only ever sees the masked key, and the mask is non-empty exactly when a key is stored, so\n * it answers the question without the plaintext leaving the api.\n */\nexport const hasUsableModel = (settings: IAiPowerUpsSettings | null): boolean => {\n const standard = settings?.modelRoles?.roles?.[\"standard\"];\n\n if (!standard?.model || !standard.connectionId) {\n return false;\n }\n\n const connection = settings?.connections?.presets?.find(c => c.id === standard.connectionId);\n\n return Boolean(connection?.apiKey);\n};\n"],"names":["hasUsableModel","settings","standard","connection","c","Boolean"],"mappings":"AAaO,MAAMA,iBAAiB,CAACC;IAC3B,MAAMC,WAAWD,UAAU,YAAY,OAAO,CAAC,WAAW;IAE1D,IAAI,CAACC,UAAU,SAAS,CAACA,SAAS,YAAY,EAC1C,OAAO;IAGX,MAAMC,aAAaF,UAAU,aAAa,SAAS,KAAKG,CAAAA,IAAKA,EAAE,EAAE,KAAKF,SAAS,YAAY;IAE3F,OAAOG,QAAQF,YAAY;AAC/B"}
@@ -1,3 +1,4 @@
1
1
  export { AiPowerUpsSettingsFeature } from "./feature.js";
2
2
  export { useAiPowerUpsSettings } from "./useAiPowerUpsSettings.js";
3
+ export { hasUsableModel } from "./hasUsableModel.js";
3
4
  export { AiPowerUpsSettingsGroup } from "./settingsGroup.js";
@@ -1,3 +1,4 @@
1
1
  export { AiPowerUpsSettingsFeature } from "./feature.js";
2
2
  export { useAiPowerUpsSettings } from "./useAiPowerUpsSettings.js";
3
+ export { hasUsableModel } from "./hasUsableModel.js";
3
4
  export { AiPowerUpsSettingsGroup } from "./settingsGroup.js";
@@ -8,6 +8,15 @@ export interface IAiPowerUpsSettingsGroup {
8
8
  label: string;
9
9
  description?: string;
10
10
  icon?: Icon;
11
+ /**
12
+ * Awaited before `buildForm` runs.
13
+ *
14
+ * Field *options* are evaluated lazily on render, so a group that only needs server data for a
15
+ * select can fetch it in the background. Field *structure* is not: the Capabilities group
16
+ * builds one field per capability registered on the api, so that list has to be in hand before
17
+ * the form is built or the tab comes up empty.
18
+ */
19
+ init?(): Promise<void>;
11
20
  buildForm(formBuilder: IAiPowerUpsSettingsGroupFormBuilder): void;
12
21
  }
13
22
  export declare const AiPowerUpsSettingsGroup: import("@webiny/di").Abstraction<IAiPowerUpsSettingsGroup>;
@@ -1 +1 @@
1
- {"version":3,"file":"admin/presentation/AiPowerUpsSettings/settingsGroup.js","sources":["../../../../src/admin/presentation/AiPowerUpsSettings/settingsGroup.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport { FormModelFactory, FormModel, Icon } from \"@webiny/app-admin\";\n\nexport interface IAiPowerUpsSettingsGroupFormBuilder {\n fields(\n fn: (\n fields: FormModelFactory.FieldBuilderRegistry\n ) => Record<string, FormModelFactory.FieldBuilder>\n ): void;\n layout(fn: (layout: FormModelFactory.LayoutBuilder) => FormModel.LayoutNodeBuilder[]): void;\n}\n\nexport interface IAiPowerUpsSettingsGroup {\n name: string;\n label: string;\n description?: string;\n icon?: Icon;\n buildForm(formBuilder: IAiPowerUpsSettingsGroupFormBuilder): void;\n}\n\nexport const AiPowerUpsSettingsGroup = createAbstraction<IAiPowerUpsSettingsGroup>(\n \"AiPowerUps/SettingsGroup\"\n);\n\nexport namespace AiPowerUpsSettingsGroup {\n export type Interface = IAiPowerUpsSettingsGroup;\n export type FormBuilder = IAiPowerUpsSettingsGroupFormBuilder;\n}\n"],"names":["AiPowerUpsSettingsGroup","createAbstraction"],"mappings":";AAoBO,MAAMA,0BAA0BC,kBACnC"}
1
+ {"version":3,"file":"admin/presentation/AiPowerUpsSettings/settingsGroup.js","sources":["../../../../src/admin/presentation/AiPowerUpsSettings/settingsGroup.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport { FormModelFactory, FormModel, Icon } from \"@webiny/app-admin\";\n\nexport interface IAiPowerUpsSettingsGroupFormBuilder {\n fields(\n fn: (\n fields: FormModelFactory.FieldBuilderRegistry\n ) => Record<string, FormModelFactory.FieldBuilder>\n ): void;\n layout(fn: (layout: FormModelFactory.LayoutBuilder) => FormModel.LayoutNodeBuilder[]): void;\n}\n\nexport interface IAiPowerUpsSettingsGroup {\n name: string;\n label: string;\n description?: string;\n icon?: Icon;\n /**\n * Awaited before `buildForm` runs.\n *\n * Field *options* are evaluated lazily on render, so a group that only needs server data for a\n * select can fetch it in the background. Field *structure* is not: the Capabilities group\n * builds one field per capability registered on the api, so that list has to be in hand before\n * the form is built or the tab comes up empty.\n */\n init?(): Promise<void>;\n buildForm(formBuilder: IAiPowerUpsSettingsGroupFormBuilder): void;\n}\n\nexport const AiPowerUpsSettingsGroup = createAbstraction<IAiPowerUpsSettingsGroup>(\n \"AiPowerUps/SettingsGroup\"\n);\n\nexport namespace AiPowerUpsSettingsGroup {\n export type Interface = IAiPowerUpsSettingsGroup;\n export type FormBuilder = IAiPowerUpsSettingsGroupFormBuilder;\n}\n"],"names":["AiPowerUpsSettingsGroup","createAbstraction"],"mappings":";AA6BO,MAAMA,0BAA0BC,kBACnC"}
@@ -0,0 +1,39 @@
1
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
2
+ import { ListCapabilitiesUseCase, ListCapabilitiesRepository } from "../../admin/features/listCapabilities/abstractions.js";
3
+ import { ListModelsUseCase, ListModelsRepository } from "../../admin/features/listModels/abstractions.js";
4
+ /**
5
+ * One row per AI feature, all of them empty by default.
6
+ *
7
+ * The rows come from the api's registered capabilities, so a feature that declares one appears here
8
+ * without a change to this file. That is the point: the previous plan was a hand-maintained tab per
9
+ * feature, which is the kind of list that stops matching reality on the second addition.
10
+ */
11
+ declare class CapabilitiesSettingsImpl implements AiPowerUpsSettingsGroup.Interface {
12
+ private listCapabilities;
13
+ private capabilitiesRepository;
14
+ private listModels;
15
+ private modelsRepository;
16
+ name: string;
17
+ label: string;
18
+ description: string;
19
+ constructor(listCapabilities: ListCapabilitiesUseCase.Interface, capabilitiesRepository: ListCapabilitiesRepository.Interface, listModels: ListModelsUseCase.Interface, modelsRepository: ListModelsRepository.Interface);
20
+ init(): Promise<void>;
21
+ /**
22
+ * Alphabetical by the label people actually read, not by id or registration order.
23
+ *
24
+ * Registration order is whatever the container happened to do, which puts "Page generation"
25
+ * above "CMS entry generation" for no reason a reader could infer. `localeCompare` rather than
26
+ * `<` so accented labels from an extension sort where a person would expect.
27
+ */
28
+ private sortedCapabilities;
29
+ buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void;
30
+ private buildCapabilityField;
31
+ private getConnections;
32
+ private getOverride;
33
+ private getPinnedConnection;
34
+ private getModelOptions;
35
+ }
36
+ export declare const CapabilitiesSettings: typeof CapabilitiesSettingsImpl & {
37
+ __abstraction: import("@webiny/di").Abstraction<import("./AiPowerUpsSettings/settingsGroup.js").IAiPowerUpsSettingsGroup>;
38
+ };
39
+ export {};
@@ -0,0 +1,104 @@
1
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
2
+ import { ListCapabilitiesRepository, ListCapabilitiesUseCase } from "../features/listCapabilities/abstractions.js";
3
+ import { ListModelsRepository, ListModelsUseCase } from "../features/listModels/abstractions.js";
4
+ import { AI_MODEL_ROLE_DISPLAY, roleLabel } from "./modelRoles.js";
5
+ function isEnabled(form, capabilityId) {
6
+ const data = form.getData();
7
+ return data.capabilities?.items?.[capabilityId]?.enabled !== false;
8
+ }
9
+ class CapabilitiesSettingsImpl {
10
+ constructor(listCapabilities, capabilitiesRepository, listModels, modelsRepository){
11
+ this.listCapabilities = listCapabilities;
12
+ this.capabilitiesRepository = capabilitiesRepository;
13
+ this.listModels = listModels;
14
+ this.modelsRepository = modelsRepository;
15
+ this.name = "capabilities";
16
+ this.label = "Capabilities";
17
+ this.description = "Per-feature model and prompt overrides. Empty means inherit.";
18
+ }
19
+ async init() {
20
+ await Promise.all([
21
+ this.listCapabilities.execute(),
22
+ this.listModels.execute()
23
+ ]);
24
+ }
25
+ sortedCapabilities() {
26
+ return [
27
+ ...this.capabilitiesRepository.getCapabilities()
28
+ ].sort((a, b)=>a.label.localeCompare(b.label));
29
+ }
30
+ buildForm(form) {
31
+ form.fields((fields)=>({
32
+ items: fields.object().label("Capabilities").renderer("passthrough").fields((f)=>Object.fromEntries(this.sortedCapabilities().map((capability)=>[
33
+ capability.id,
34
+ this.buildCapabilityField(f, capability)
35
+ ])))
36
+ }));
37
+ form.layout((layout)=>[
38
+ layout.row("items")
39
+ ]);
40
+ }
41
+ buildCapabilityField(f, capability) {
42
+ const defaultRoleLabel = roleLabel(capability.defaultRole);
43
+ return f.object().label(capability.label).renderer("objectAccordionSingle", {
44
+ itemTitle: ()=>capability.label,
45
+ itemDescription: ()=>capability.description,
46
+ open: false
47
+ }).fields((cf)=>({
48
+ enabled: cf.boolean().label("Enabled").defaultValue(true).description("Turn this off to remove the feature from the app. The settings below are kept, but ignored, while it is off."),
49
+ overrides: cf.object().label("Overrides").renderer("passthrough").fields((of)=>({
50
+ roleId: of.text().label("Model role").disabledWhen(({ form })=>!isEnabled(form, capability.id)).description(`Which role supplies this feature's model. Left empty, it uses ${defaultRoleLabel}.`).options(()=>AI_MODEL_ROLE_DISPLAY.map((role)=>({
51
+ label: role.label,
52
+ value: role.id
53
+ }))),
54
+ connectionId: of.text().label("Pin a connection").disabledWhen(({ form })=>!isEnabled(form, capability.id)).description("Advanced. Overrides the role above for this feature only.").options(({ form })=>[
55
+ {
56
+ label: "Use the role's connection",
57
+ value: ""
58
+ },
59
+ ...this.getConnections(form).map((c)=>({
60
+ label: c.name,
61
+ value: c.id
62
+ }))
63
+ ]),
64
+ model: of.text().label("Pin a model").disabledWhen(({ form })=>!isEnabled(form, capability.id) || !this.getPinnedConnection(form, capability.id)).description("Both the connection and the model must be set for a pin to apply.").options(({ form })=>this.getModelOptions(form, capability.id)),
65
+ additionalInstructions: of.text().label("Additional instructions").renderer("textarea", {
66
+ rows: 5
67
+ }).disabledWhen(({ form })=>!isEnabled(form, capability.id)).description("Appended to this feature's prompt. Use this for house style and standing rules.").note("Appending keeps Webiny's prompt in place, including the output format the feature depends on.")
68
+ }))
69
+ }));
70
+ }
71
+ getConnections(form) {
72
+ const data = form.getData();
73
+ return (data.connections?.presets ?? []).filter((c)=>c.id && c.name);
74
+ }
75
+ getOverride(form, capabilityId) {
76
+ const data = form.getData();
77
+ return data.capabilities?.items?.[capabilityId]?.overrides ?? {};
78
+ }
79
+ getPinnedConnection(form, capabilityId) {
80
+ const connectionId = this.getOverride(form, capabilityId).connectionId;
81
+ if (!connectionId) return;
82
+ return this.getConnections(form).find((c)=>c.id === connectionId);
83
+ }
84
+ getModelOptions(form, capabilityId) {
85
+ const connection = this.getPinnedConnection(form, capabilityId);
86
+ if (!connection) return [];
87
+ return this.modelsRepository.getModels().filter((model)=>model.providerId === connection.sdkName).map((model)=>({
88
+ label: model.modelName,
89
+ value: `${model.providerId}/${model.modelId}`
90
+ }));
91
+ }
92
+ }
93
+ const CapabilitiesSettings = AiPowerUpsSettingsGroup.createImplementation({
94
+ implementation: CapabilitiesSettingsImpl,
95
+ dependencies: [
96
+ ListCapabilitiesUseCase,
97
+ ListCapabilitiesRepository,
98
+ ListModelsUseCase,
99
+ ListModelsRepository
100
+ ]
101
+ });
102
+ export { CapabilitiesSettings };
103
+
104
+ //# sourceMappingURL=CapabilitiesSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/presentation/CapabilitiesSettings.js","sources":["../../../src/admin/presentation/CapabilitiesSettings.ts"],"sourcesContent":["import type { IAiPowerUpsConnectionPreset } from \"~/admin/features/settings/shared/abstractions.js\";\nimport { AiPowerUpsSettingsGroup } from \"./AiPowerUpsSettings/settingsGroup.js\";\nimport {\n ListCapabilitiesUseCase,\n ListCapabilitiesRepository\n} from \"~/admin/features/listCapabilities/abstractions.js\";\nimport {\n ListModelsUseCase,\n ListModelsRepository\n} from \"~/admin/features/listModels/abstractions.js\";\nimport { AI_MODEL_ROLE_DISPLAY, roleLabel } from \"./modelRoles.js\";\nimport type { AiCapability } from \"~/admin/features/listCapabilities/abstractions.js\";\nimport type { FormModel, FormModelFactory } from \"@webiny/app-admin\";\n\n/**\n * Absent means enabled, exactly as it does in storage and in the resolver.\n *\n * The form seeds an untouched switch to `true`, so in practice this only reads `false` after someone\n * flips one. Spelled `!== false` anyway, so it keeps agreeing with the other two if that default\n * ever goes away.\n */\nfunction isEnabled(form: FormModel.Interface, capabilityId: string): boolean {\n const data = form.getData() as {\n capabilities?: { items?: Record<string, { enabled?: boolean }> };\n };\n return data.capabilities?.items?.[capabilityId]?.enabled !== false;\n}\n\n/**\n * One row per AI feature, all of them empty by default.\n *\n * The rows come from the api's registered capabilities, so a feature that declares one appears here\n * without a change to this file. That is the point: the previous plan was a hand-maintained tab per\n * feature, which is the kind of list that stops matching reality on the second addition.\n */\nclass CapabilitiesSettingsImpl implements AiPowerUpsSettingsGroup.Interface {\n name = \"capabilities\";\n label = \"Capabilities\";\n description = \"Per-feature model and prompt overrides. Empty means inherit.\";\n\n constructor(\n private listCapabilities: ListCapabilitiesUseCase.Interface,\n private capabilitiesRepository: ListCapabilitiesRepository.Interface,\n private listModels: ListModelsUseCase.Interface,\n private modelsRepository: ListModelsRepository.Interface\n ) {}\n\n async init(): Promise<void> {\n await Promise.all([this.listCapabilities.execute(), this.listModels.execute()]);\n }\n\n /**\n * Alphabetical by the label people actually read, not by id or registration order.\n *\n * Registration order is whatever the container happened to do, which puts \"Page generation\"\n * above \"CMS entry generation\" for no reason a reader could infer. `localeCompare` rather than\n * `<` so accented labels from an extension sort where a person would expect.\n */\n private sortedCapabilities(): AiCapability[] {\n return [...this.capabilitiesRepository.getCapabilities()].sort((a, b) =>\n a.label.localeCompare(b.label)\n );\n }\n\n buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void {\n form.fields(fields => ({\n items: fields\n .object()\n .label(\"Capabilities\")\n .renderer(\"passthrough\")\n .fields(f =>\n Object.fromEntries(\n this.sortedCapabilities().map(capability => [\n capability.id,\n this.buildCapabilityField(f, capability)\n ])\n )\n )\n }));\n\n form.layout(layout => [layout.row(\"items\")]);\n }\n\n private buildCapabilityField(\n f: FormModelFactory.FieldBuilderRegistry,\n capability: AiCapability\n ): FormModelFactory.FieldBuilder {\n const defaultRoleLabel = roleLabel(capability.defaultRole);\n\n return (\n f\n .object()\n .label(capability.label)\n /*\n * Callbacks, not strings: a string is read as a field name, not as literal text. The\n * title happened to survive that because the renderer falls back to the field label,\n * but the description silently vanished.\n */\n .renderer(\"objectAccordionSingle\", {\n itemTitle: () => capability.label,\n itemDescription: () => capability.description,\n // Closed by default. Every row is empty until someone changes something, so an\n // expanded list of five would be five screens of nothing.\n open: false\n })\n .fields(cf => ({\n /*\n * On unless someone turns it off. A licence that grants a capability enables it\n * immediately, so nobody has to come here and opt in; the only thing this\n * records is a decision to switch one off.\n */\n enabled: cf\n .boolean()\n .label(\"Enabled\")\n /*\n * On unless storage explicitly says otherwise. Nothing is written down for a\n * capability a licence enabled, so the stored value is absent, and a switch\n * renders absent as off. Without this default every untouched capability\n * shows as disabled while actually running, which is the same `undefined`\n * confusion the storage side guards against, seen from the front.\n */\n .defaultValue(true)\n .description(\n \"Turn this off to remove the feature from the app. The settings below are kept, but ignored, while it is off.\"\n ),\n\n overrides: cf\n .object()\n .label(\"Overrides\")\n .renderer(\"passthrough\")\n .fields(of => ({\n /*\n * The inherited role goes in the description, not in the option list. An option\n * with an empty value is dropped by the select renderer, so the \"Inherit (Fast)\"\n * entry never rendered and nothing said which role this feature falls back to.\n * Clearing the field is done with the select's own reset control.\n */\n roleId: of\n .text()\n .label(\"Model role\")\n .disabledWhen(({ form }) => !isEnabled(form, capability.id))\n .description(\n `Which role supplies this feature's model. Left empty, it uses ${defaultRoleLabel}.`\n )\n .options(() =>\n AI_MODEL_ROLE_DISPLAY.map(role => ({\n label: role.label,\n value: role.id\n }))\n ),\n\n /*\n * Pinning a model bypasses roles for this one feature. It is the escape hatch, not\n * the mechanism: a project that pins everything has thrown away the indirection\n * that makes \"switch our cheap model\" a single edit.\n */\n connectionId: of\n .text()\n .label(\"Pin a connection\")\n .disabledWhen(({ form }) => !isEnabled(form, capability.id))\n .description(\n \"Advanced. Overrides the role above for this feature only.\"\n )\n .options(({ form }) => [\n { label: \"Use the role's connection\", value: \"\" },\n ...this.getConnections(form).map(c => ({\n label: c.name,\n value: c.id\n }))\n ]),\n model: of\n .text()\n .label(\"Pin a model\")\n .disabledWhen(\n ({ form }) =>\n !isEnabled(form, capability.id) ||\n !this.getPinnedConnection(form, capability.id)\n )\n .description(\n \"Both the connection and the model must be set for a pin to apply.\"\n )\n .options(({ form }) => this.getModelOptions(form, capability.id)),\n\n additionalInstructions: of\n .text()\n .label(\"Additional instructions\")\n .renderer(\"textarea\", { rows: 5 })\n .disabledWhen(({ form }) => !isEnabled(form, capability.id))\n .description(\n \"Appended to this feature's prompt. Use this for house style and standing rules.\"\n )\n .note(\n \"Appending keeps Webiny's prompt in place, including the output format the feature depends on.\"\n )\n }))\n }))\n );\n }\n\n private getConnections(form: FormModel.Interface): IAiPowerUpsConnectionPreset[] {\n const data = form.getData() as {\n connections?: { presets?: IAiPowerUpsConnectionPreset[] };\n };\n return (data.connections?.presets ?? []).filter(c => c.id && c.name);\n }\n\n private getOverride(\n form: FormModel.Interface,\n capabilityId: string\n ): { connectionId?: string } {\n const data = form.getData() as {\n capabilities?: { items?: Record<string, { overrides?: { connectionId?: string } }> };\n };\n return data.capabilities?.items?.[capabilityId]?.overrides ?? {};\n }\n\n private getPinnedConnection(\n form: FormModel.Interface,\n capabilityId: string\n ): IAiPowerUpsConnectionPreset | undefined {\n const connectionId = this.getOverride(form, capabilityId).connectionId;\n\n if (!connectionId) {\n return undefined;\n }\n\n return this.getConnections(form).find(c => c.id === connectionId);\n }\n\n private getModelOptions(form: FormModel.Interface, capabilityId: string) {\n const connection = this.getPinnedConnection(form, capabilityId);\n\n if (!connection) {\n return [];\n }\n\n return this.modelsRepository\n .getModels()\n .filter(model => model.providerId === connection.sdkName)\n .map(model => ({\n label: model.modelName,\n value: `${model.providerId}/${model.modelId}`\n }));\n }\n}\n\nexport const CapabilitiesSettings = AiPowerUpsSettingsGroup.createImplementation({\n implementation: CapabilitiesSettingsImpl,\n dependencies: [\n ListCapabilitiesUseCase,\n ListCapabilitiesRepository,\n ListModelsUseCase,\n ListModelsRepository\n ]\n});\n"],"names":["isEnabled","form","capabilityId","data","CapabilitiesSettingsImpl","listCapabilities","capabilitiesRepository","listModels","modelsRepository","Promise","a","b","fields","f","Object","capability","layout","defaultRoleLabel","roleLabel","cf","of","AI_MODEL_ROLE_DISPLAY","role","c","connectionId","connection","model","CapabilitiesSettings","AiPowerUpsSettingsGroup","ListCapabilitiesUseCase","ListCapabilitiesRepository","ListModelsUseCase","ListModelsRepository"],"mappings":";;;;AAqBA,SAASA,UAAUC,IAAyB,EAAEC,YAAoB;IAC9D,MAAMC,OAAOF,KAAK,OAAO;IAGzB,OAAOE,KAAK,YAAY,EAAE,OAAO,CAACD,aAAa,EAAE,YAAY;AACjE;AASA,MAAME;IAKF,YACYC,gBAAmD,EACnDC,sBAA4D,EAC5DC,UAAuC,EACvCC,gBAAgD,CAC1D;aAJUH,gBAAgB,GAAhBA;aACAC,sBAAsB,GAAtBA;aACAC,UAAU,GAAVA;aACAC,gBAAgB,GAAhBA;aARZ,IAAI,GAAG;aACP,KAAK,GAAG;aACR,WAAW,GAAG;IAOX;IAEH,MAAM,OAAsB;QACxB,MAAMC,QAAQ,GAAG,CAAC;YAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO;YAAI,IAAI,CAAC,UAAU,CAAC,OAAO;SAAG;IAClF;IASQ,qBAAqC;QACzC,OAAO;eAAI,IAAI,CAAC,sBAAsB,CAAC,eAAe;SAAG,CAAC,IAAI,CAAC,CAACC,GAAGC,IAC/DD,EAAE,KAAK,CAAC,aAAa,CAACC,EAAE,KAAK;IAErC;IAEA,UAAUV,IAAyC,EAAQ;QACvDA,KAAK,MAAM,CAACW,CAAAA,SAAW;gBACnB,OAAOA,OACF,MAAM,GACN,KAAK,CAAC,gBACN,QAAQ,CAAC,eACT,MAAM,CAACC,CAAAA,IACJC,OAAO,WAAW,CACd,IAAI,CAAC,kBAAkB,GAAG,GAAG,CAACC,CAAAA,aAAc;4BACxCA,WAAW,EAAE;4BACb,IAAI,CAAC,oBAAoB,CAACF,GAAGE;yBAChC;YAGjB;QAEAd,KAAK,MAAM,CAACe,CAAAA,SAAU;gBAACA,OAAO,GAAG,CAAC;aAAS;IAC/C;IAEQ,qBACJH,CAAwC,EACxCE,UAAwB,EACK;QAC7B,MAAME,mBAAmBC,UAAUH,WAAW,WAAW;QAEzD,OACIF,EACK,MAAM,GACN,KAAK,CAACE,WAAW,KAAK,EAMtB,QAAQ,CAAC,yBAAyB;YAC/B,WAAW,IAAMA,WAAW,KAAK;YACjC,iBAAiB,IAAMA,WAAW,WAAW;YAG7C,MAAM;QACV,GACC,MAAM,CAACI,CAAAA,KAAO;gBAMX,SAASA,GACJ,OAAO,GACP,KAAK,CAAC,WAQN,YAAY,CAAC,MACb,WAAW,CACR;gBAGR,WAAWA,GACN,MAAM,GACN,KAAK,CAAC,aACN,QAAQ,CAAC,eACT,MAAM,CAACC,CAAAA,KAAO;wBAOX,QAAQA,GACH,IAAI,GACJ,KAAK,CAAC,cACN,YAAY,CAAC,CAAC,EAAEnB,IAAI,EAAE,GAAK,CAACD,UAAUC,MAAMc,WAAW,EAAE,GACzD,WAAW,CACR,CAAC,8DAA8D,EAAEE,iBAAiB,CAAC,CAAC,EAEvF,OAAO,CAAC,IACLI,sBAAsB,GAAG,CAACC,CAAAA,OAAS;oCAC/B,OAAOA,KAAK,KAAK;oCACjB,OAAOA,KAAK,EAAE;gCAClB;wBAQR,cAAcF,GACT,IAAI,GACJ,KAAK,CAAC,oBACN,YAAY,CAAC,CAAC,EAAEnB,IAAI,EAAE,GAAK,CAACD,UAAUC,MAAMc,WAAW,EAAE,GACzD,WAAW,CACR,6DAEH,OAAO,CAAC,CAAC,EAAEd,IAAI,EAAE,GAAK;gCACnB;oCAAE,OAAO;oCAA6B,OAAO;gCAAG;mCAC7C,IAAI,CAAC,cAAc,CAACA,MAAM,GAAG,CAACsB,CAAAA,IAAM;wCACnC,OAAOA,EAAE,IAAI;wCACb,OAAOA,EAAE,EAAE;oCACf;6BACH;wBACL,OAAOH,GACF,IAAI,GACJ,KAAK,CAAC,eACN,YAAY,CACT,CAAC,EAAEnB,IAAI,EAAE,GACL,CAACD,UAAUC,MAAMc,WAAW,EAAE,KAC9B,CAAC,IAAI,CAAC,mBAAmB,CAACd,MAAMc,WAAW,EAAE,GAEpD,WAAW,CACR,qEAEH,OAAO,CAAC,CAAC,EAAEd,IAAI,EAAE,GAAK,IAAI,CAAC,eAAe,CAACA,MAAMc,WAAW,EAAE;wBAEnE,wBAAwBK,GACnB,IAAI,GACJ,KAAK,CAAC,2BACN,QAAQ,CAAC,YAAY;4BAAE,MAAM;wBAAE,GAC/B,YAAY,CAAC,CAAC,EAAEnB,IAAI,EAAE,GAAK,CAACD,UAAUC,MAAMc,WAAW,EAAE,GACzD,WAAW,CACR,mFAEH,IAAI,CACD;oBAEZ;YACR;IAEZ;IAEQ,eAAed,IAAyB,EAAiC;QAC7E,MAAME,OAAOF,KAAK,OAAO;QAGzB,OAAQE,AAAAA,CAAAA,KAAK,WAAW,EAAE,WAAW,EAAC,EAAG,MAAM,CAACoB,CAAAA,IAAKA,EAAE,EAAE,IAAIA,EAAE,IAAI;IACvE;IAEQ,YACJtB,IAAyB,EACzBC,YAAoB,EACK;QACzB,MAAMC,OAAOF,KAAK,OAAO;QAGzB,OAAOE,KAAK,YAAY,EAAE,OAAO,CAACD,aAAa,EAAE,aAAa,CAAC;IACnE;IAEQ,oBACJD,IAAyB,EACzBC,YAAoB,EACmB;QACvC,MAAMsB,eAAe,IAAI,CAAC,WAAW,CAACvB,MAAMC,cAAc,YAAY;QAEtE,IAAI,CAACsB,cACD;QAGJ,OAAO,IAAI,CAAC,cAAc,CAACvB,MAAM,IAAI,CAACsB,CAAAA,IAAKA,EAAE,EAAE,KAAKC;IACxD;IAEQ,gBAAgBvB,IAAyB,EAAEC,YAAoB,EAAE;QACrE,MAAMuB,aAAa,IAAI,CAAC,mBAAmB,CAACxB,MAAMC;QAElD,IAAI,CAACuB,YACD,OAAO,EAAE;QAGb,OAAO,IAAI,CAAC,gBAAgB,CACvB,SAAS,GACT,MAAM,CAACC,CAAAA,QAASA,MAAM,UAAU,KAAKD,WAAW,OAAO,EACvD,GAAG,CAACC,CAAAA,QAAU;gBACX,OAAOA,MAAM,SAAS;gBACtB,OAAO,GAAGA,MAAM,UAAU,CAAC,CAAC,EAAEA,MAAM,OAAO,EAAE;YACjD;IACR;AACJ;AAEO,MAAMC,uBAAuBC,wBAAwB,oBAAoB,CAAC;IAC7E,gBAAgBxB;IAChB,cAAc;QACVyB;QACAC;QACAC;QACAC;KACH;AACL"}
@@ -5,7 +5,7 @@ import { ReactComponent } from "@webiny/icons/auto_fix_high.svg";
5
5
  import { useOpenDialog } from "@webiny/app-admin";
6
6
  import { useModel } from "@webiny/app-headless-cms/admin/components/ModelProvider/useModel.js";
7
7
  import { CMS_GENERATE_CONTENT_DIALOG } from "./CmsGenerateContentDialog.js";
8
- import { useAiPowerUpsSettings } from "../AiPowerUpsSettings/index.js";
8
+ import { hasUsableModel, useAiPowerUpsSettings } from "../AiPowerUpsSettings/index.js";
9
9
  import { useContentEntryFormPresenter } from "@webiny/app-headless-cms/exports/admin/cms/entry/editor.js";
10
10
  const CmsGenerateContentButton = observer(()=>{
11
11
  const { openDialog } = useOpenDialog();
@@ -13,7 +13,7 @@ const CmsGenerateContentButton = observer(()=>{
13
13
  const { model } = useModel();
14
14
  const presenter = useContentEntryFormPresenter();
15
15
  if (!presenter.vm.canSave) return null;
16
- if (!settings || 0 === settings.providers.presets.length) return null;
16
+ if (!hasUsableModel(settings)) return null;
17
17
  return /*#__PURE__*/ react.createElement(IconButton, {
18
18
  variant: "ghost",
19
19
  icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
@@ -1 +1 @@
1
- {"version":3,"file":"admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js","sources":["../../../../src/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as ChatIcon } from \"@webiny/icons/auto_fix_high.svg\";\nimport { useOpenDialog } from \"@webiny/app-admin\";\nimport { useModel } from \"@webiny/app-headless-cms/admin/components/ModelProvider/useModel.js\";\nimport { CMS_GENERATE_CONTENT_DIALOG } from \"./CmsGenerateContentDialog.js\";\nimport { useAiPowerUpsSettings } from \"~/admin/presentation/AiPowerUpsSettings/index.js\";\nimport { useContentEntryFormPresenter } from \"@webiny/app-headless-cms/exports/admin/cms/entry/editor.js\";\n\nexport const CmsGenerateContentButton = observer(() => {\n const { openDialog } = useOpenDialog();\n const { settings } = useAiPowerUpsSettings();\n const { model } = useModel();\n const presenter = useContentEntryFormPresenter();\n\n if (!presenter.vm.canSave) {\n return null;\n }\n\n if (!settings || settings.providers.presets.length === 0) {\n return null;\n }\n\n return (\n <IconButton\n variant=\"ghost\"\n icon={<ChatIcon />}\n onClick={() => openDialog(CMS_GENERATE_CONTENT_DIALOG, { modelId: model.modelId })}\n />\n );\n});\n"],"names":["CmsGenerateContentButton","observer","openDialog","useOpenDialog","settings","useAiPowerUpsSettings","model","useModel","presenter","useContentEntryFormPresenter","IconButton","ChatIcon","CMS_GENERATE_CONTENT_DIALOG"],"mappings":";;;;;;;;;AAUO,MAAMA,2BAA2BC,SAAS;IAC7C,MAAM,EAAEC,UAAU,EAAE,GAAGC;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC;IACrB,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClB,MAAMC,YAAYC;IAElB,IAAI,CAACD,UAAU,EAAE,CAAC,OAAO,EACrB,OAAO;IAGX,IAAI,CAACJ,YAAYA,AAAsC,MAAtCA,SAAS,SAAS,CAAC,OAAO,CAAC,MAAM,EAC9C,OAAO;IAGX,OAAO,WAAP,GACI,oBAACM,YAAUA;QACP,SAAQ;QACR,oBAAM,oBAACC,gBAAQA;QACf,SAAS,IAAMT,WAAWU,6BAA6B;gBAAE,SAASN,MAAM,OAAO;YAAC;;AAG5F"}
1
+ {"version":3,"file":"admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js","sources":["../../../../src/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport { IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as ChatIcon } from \"@webiny/icons/auto_fix_high.svg\";\nimport { useOpenDialog } from \"@webiny/app-admin\";\nimport { useModel } from \"@webiny/app-headless-cms/admin/components/ModelProvider/useModel.js\";\nimport { CMS_GENERATE_CONTENT_DIALOG } from \"./CmsGenerateContentDialog.js\";\nimport {\n hasUsableModel,\n useAiPowerUpsSettings\n} from \"~/admin/presentation/AiPowerUpsSettings/index.js\";\nimport { useContentEntryFormPresenter } from \"@webiny/app-headless-cms/exports/admin/cms/entry/editor.js\";\n\nexport const CmsGenerateContentButton = observer(() => {\n const { openDialog } = useOpenDialog();\n const { settings } = useAiPowerUpsSettings();\n const { model } = useModel();\n const presenter = useContentEntryFormPresenter();\n\n if (!presenter.vm.canSave) {\n return null;\n }\n\n if (!hasUsableModel(settings)) {\n return null;\n }\n\n return (\n <IconButton\n variant=\"ghost\"\n icon={<ChatIcon />}\n onClick={() => openDialog(CMS_GENERATE_CONTENT_DIALOG, { modelId: model.modelId })}\n />\n );\n});\n"],"names":["CmsGenerateContentButton","observer","openDialog","useOpenDialog","settings","useAiPowerUpsSettings","model","useModel","presenter","useContentEntryFormPresenter","hasUsableModel","IconButton","ChatIcon","CMS_GENERATE_CONTENT_DIALOG"],"mappings":";;;;;;;;;AAaO,MAAMA,2BAA2BC,SAAS;IAC7C,MAAM,EAAEC,UAAU,EAAE,GAAGC;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC;IACrB,MAAM,EAAEC,KAAK,EAAE,GAAGC;IAClB,MAAMC,YAAYC;IAElB,IAAI,CAACD,UAAU,EAAE,CAAC,OAAO,EACrB,OAAO;IAGX,IAAI,CAACE,eAAeN,WAChB,OAAO;IAGX,OAAO,WAAP,GACI,oBAACO,YAAUA;QACP,SAAQ;QACR,oBAAM,oBAACC,gBAAQA;QACf,SAAS,IAAMV,WAAWW,6BAA6B;gBAAE,SAASP,MAAM,OAAO;YAAC;;AAG5F"}
@@ -0,0 +1,25 @@
1
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
2
+ import { ListModelsUseCase, ListModelsRepository } from "../../admin/features/listModels/abstractions.js";
3
+ /**
4
+ * One row per API key. The model select moved to Model roles, so adding a second Anthropic model no
5
+ * longer means pasting the same key twice.
6
+ */
7
+ declare class ConnectionsSettingsImpl implements AiPowerUpsSettingsGroup.Interface {
8
+ private useCase;
9
+ private repository;
10
+ name: string;
11
+ label: string;
12
+ description: string;
13
+ constructor(useCase: ListModelsUseCase.Interface, repository: ListModelsRepository.Interface);
14
+ init(): Promise<void>;
15
+ buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void;
16
+ /**
17
+ * Derived from the registered SDK factories rather than hardcoded, so a project that registers
18
+ * its own `AiSdkFactory` shows up here without a change to this file.
19
+ */
20
+ private getVendorOptions;
21
+ }
22
+ export declare const ConnectionsSettings: typeof ConnectionsSettingsImpl & {
23
+ __abstraction: import("@webiny/di").Abstraction<import("./AiPowerUpsSettings/settingsGroup.js").IAiPowerUpsSettingsGroup>;
24
+ };
25
+ export {};
@@ -0,0 +1,52 @@
1
+ import { generateAlphaNumericId } from "@webiny/utils";
2
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
3
+ import { ListModelsRepository, ListModelsUseCase } from "../features/listModels/abstractions.js";
4
+ class ConnectionsSettingsImpl {
5
+ constructor(useCase, repository){
6
+ this.useCase = useCase;
7
+ this.repository = repository;
8
+ this.name = "connections";
9
+ this.label = "Connections";
10
+ this.description = "API keys for the AI vendors this project uses.";
11
+ }
12
+ async init() {
13
+ await this.useCase.execute();
14
+ }
15
+ buildForm(form) {
16
+ form.fields((fields)=>({
17
+ presets: fields.object().label("Connections").renderer("objectAccordionMultiple", {
18
+ container: false,
19
+ addItemLabel: "Add connection",
20
+ itemTitle: (data, index)=>String(data.name || `Connection #${index + 1}`)
21
+ }).fields((f)=>({
22
+ id: f.text().hidden().defaultValue(()=>generateAlphaNumericId(10)),
23
+ name: f.text().label("Name").required("Name is required").description("How this connection appears when picking a model. Name it after the key, not the vendor, so two keys for the same vendor stay distinguishable."),
24
+ sdkName: f.text().label("Vendor").required("Vendor is required").description("Which provider this key belongs to.").options(()=>this.getVendorOptions()).renderer("select"),
25
+ apiKey: f.text().label("API Key").required("API Key is required").description("Encrypted at rest. Only the last few characters are shown after saving.")
26
+ })).list()
27
+ }));
28
+ form.layout((layout)=>[
29
+ layout.row("presets")
30
+ ]);
31
+ }
32
+ getVendorOptions() {
33
+ const seen = new Map();
34
+ for (const model of this.repository.getModels())seen.set(model.providerId, model.providerName);
35
+ return [
36
+ ...seen.entries()
37
+ ].map(([value, label])=>({
38
+ label,
39
+ value
40
+ }));
41
+ }
42
+ }
43
+ const ConnectionsSettings = AiPowerUpsSettingsGroup.createImplementation({
44
+ implementation: ConnectionsSettingsImpl,
45
+ dependencies: [
46
+ ListModelsUseCase,
47
+ ListModelsRepository
48
+ ]
49
+ });
50
+ export { ConnectionsSettings };
51
+
52
+ //# sourceMappingURL=ConnectionsSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/presentation/ConnectionsSettings.js","sources":["../../../src/admin/presentation/ConnectionsSettings.ts"],"sourcesContent":["import { generateAlphaNumericId } from \"@webiny/utils\";\nimport { AiPowerUpsSettingsGroup } from \"./AiPowerUpsSettings/settingsGroup.js\";\nimport {\n ListModelsUseCase,\n ListModelsRepository\n} from \"~/admin/features/listModels/abstractions.js\";\n\n/**\n * One row per API key. The model select moved to Model roles, so adding a second Anthropic model no\n * longer means pasting the same key twice.\n */\nclass ConnectionsSettingsImpl implements AiPowerUpsSettingsGroup.Interface {\n name = \"connections\";\n label = \"Connections\";\n description = \"API keys for the AI vendors this project uses.\";\n\n constructor(\n private useCase: ListModelsUseCase.Interface,\n private repository: ListModelsRepository.Interface\n ) {}\n\n async init(): Promise<void> {\n await this.useCase.execute();\n }\n\n buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void {\n form.fields(fields => ({\n presets: fields\n .object()\n .label(\"Connections\")\n .renderer(\"objectAccordionMultiple\", {\n container: false,\n addItemLabel: \"Add connection\",\n itemTitle: (data, index) => String(data.name || `Connection #${index + 1}`)\n })\n .fields(f => ({\n id: f\n .text()\n .hidden()\n .defaultValue(() => generateAlphaNumericId(10)),\n name: f\n .text()\n .label(\"Name\")\n .required(\"Name is required\")\n .description(\n \"How this connection appears when picking a model. Name it after the key, not the vendor, so two keys for the same vendor stay distinguishable.\"\n ),\n sdkName: f\n .text()\n .label(\"Vendor\")\n .required(\"Vendor is required\")\n .description(\"Which provider this key belongs to.\")\n .options(() => this.getVendorOptions())\n .renderer(\"select\"),\n apiKey: f\n .text()\n .label(\"API Key\")\n .required(\"API Key is required\")\n .description(\n \"Encrypted at rest. Only the last few characters are shown after saving.\"\n )\n }))\n .list()\n }));\n\n form.layout(layout => [layout.row(\"presets\")]);\n }\n\n /**\n * Derived from the registered SDK factories rather than hardcoded, so a project that registers\n * its own `AiSdkFactory` shows up here without a change to this file.\n */\n private getVendorOptions() {\n const seen = new Map<string, string>();\n\n for (const model of this.repository.getModels()) {\n seen.set(model.providerId, model.providerName);\n }\n\n return [...seen.entries()].map(([value, label]) => ({ label, value }));\n }\n}\n\nexport const ConnectionsSettings = AiPowerUpsSettingsGroup.createImplementation({\n implementation: ConnectionsSettingsImpl,\n dependencies: [ListModelsUseCase, ListModelsRepository]\n});\n"],"names":["ConnectionsSettingsImpl","useCase","repository","form","fields","data","index","String","f","generateAlphaNumericId","layout","seen","Map","model","value","label","ConnectionsSettings","AiPowerUpsSettingsGroup","ListModelsUseCase","ListModelsRepository"],"mappings":";;;AAWA,MAAMA;IAKF,YACYC,OAAoC,EACpCC,UAA0C,CACpD;aAFUD,OAAO,GAAPA;aACAC,UAAU,GAAVA;aANZ,IAAI,GAAG;aACP,KAAK,GAAG;aACR,WAAW,GAAG;IAKX;IAEH,MAAM,OAAsB;QACxB,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO;IAC9B;IAEA,UAAUC,IAAyC,EAAQ;QACvDA,KAAK,MAAM,CAACC,CAAAA,SAAW;gBACnB,SAASA,OACJ,MAAM,GACN,KAAK,CAAC,eACN,QAAQ,CAAC,2BAA2B;oBACjC,WAAW;oBACX,cAAc;oBACd,WAAW,CAACC,MAAMC,QAAUC,OAAOF,KAAK,IAAI,IAAI,CAAC,YAAY,EAAEC,QAAQ,GAAG;gBAC9E,GACC,MAAM,CAACE,CAAAA,IAAM;wBACV,IAAIA,EACC,IAAI,GACJ,MAAM,GACN,YAAY,CAAC,IAAMC,uBAAuB;wBAC/C,MAAMD,EACD,IAAI,GACJ,KAAK,CAAC,QACN,QAAQ,CAAC,oBACT,WAAW,CACR;wBAER,SAASA,EACJ,IAAI,GACJ,KAAK,CAAC,UACN,QAAQ,CAAC,sBACT,WAAW,CAAC,uCACZ,OAAO,CAAC,IAAM,IAAI,CAAC,gBAAgB,IACnC,QAAQ,CAAC;wBACd,QAAQA,EACH,IAAI,GACJ,KAAK,CAAC,WACN,QAAQ,CAAC,uBACT,WAAW,CACR;oBAEZ,IACC,IAAI;YACb;QAEAL,KAAK,MAAM,CAACO,CAAAA,SAAU;gBAACA,OAAO,GAAG,CAAC;aAAW;IACjD;IAMQ,mBAAmB;QACvB,MAAMC,OAAO,IAAIC;QAEjB,KAAK,MAAMC,SAAS,IAAI,CAAC,UAAU,CAAC,SAAS,GACzCF,KAAK,GAAG,CAACE,MAAM,UAAU,EAAEA,MAAM,YAAY;QAGjD,OAAO;eAAIF,KAAK,OAAO;SAAG,CAAC,GAAG,CAAC,CAAC,CAACG,OAAOC,MAAM,GAAM;gBAAEA;gBAAOD;YAAM;IACvE;AACJ;AAEO,MAAME,sBAAsBC,wBAAwB,oBAAoB,CAAC;IAC5E,gBAAgBjB;IAChB,cAAc;QAACkB;QAAmBC;KAAqB;AAC3D"}
@@ -0,0 +1,29 @@
1
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
2
+ import { ListModelsUseCase, ListModelsRepository } from "../../admin/features/listModels/abstractions.js";
3
+ /**
4
+ * Three selects, and for most projects this is the whole AI configuration: one connection, one
5
+ * model per role. Everything else in these settings is opt-in.
6
+ */
7
+ declare class ModelRolesSettingsImpl implements AiPowerUpsSettingsGroup.Interface {
8
+ private useCase;
9
+ private repository;
10
+ name: string;
11
+ label: string;
12
+ description: string;
13
+ constructor(useCase: ListModelsUseCase.Interface, repository: ListModelsRepository.Interface);
14
+ init(): Promise<void>;
15
+ buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void;
16
+ private getConnections;
17
+ private getConnectionOptions;
18
+ private getSelectedConnection;
19
+ /**
20
+ * Filtered by the chosen connection's vendor. Listing every model regardless would let someone
21
+ * pair an OpenAI model with an Anthropic key, which then fails at request time with a provider
22
+ * auth error that says nothing about the real mistake.
23
+ */
24
+ private getModelOptions;
25
+ }
26
+ export declare const ModelRolesSettings: typeof ModelRolesSettingsImpl & {
27
+ __abstraction: import("@webiny/di").Abstraction<import("./AiPowerUpsSettings/settingsGroup.js").IAiPowerUpsSettingsGroup>;
28
+ };
29
+ export {};
@@ -0,0 +1,67 @@
1
+ import { AiPowerUpsSettingsGroup } from "./AiPowerUpsSettings/settingsGroup.js";
2
+ import { ListModelsRepository, ListModelsUseCase } from "../features/listModels/abstractions.js";
3
+ import { AI_MODEL_ROLE_DISPLAY } from "./modelRoles.js";
4
+ class ModelRolesSettingsImpl {
5
+ constructor(useCase, repository){
6
+ this.useCase = useCase;
7
+ this.repository = repository;
8
+ this.name = "modelRoles";
9
+ this.label = "Model roles";
10
+ this.description = "Which model does the cheap work, the main work, and the image work.";
11
+ }
12
+ async init() {
13
+ await this.useCase.execute();
14
+ }
15
+ buildForm(form) {
16
+ form.fields((fields)=>({
17
+ roles: fields.object().label("Roles").renderer("passthrough").fields((f)=>Object.fromEntries(AI_MODEL_ROLE_DISPLAY.map((role)=>[
18
+ role.id,
19
+ f.object().label(role.label).renderer("objectAccordionSingle", {
20
+ itemTitle: ()=>role.label,
21
+ itemDescription: ()=>role.description,
22
+ open: true
23
+ }).fields((rf)=>({
24
+ connectionId: rf.text().label("Connection").description("Which API key runs this role.").options(({ form })=>this.getConnectionOptions(form)),
25
+ model: rf.text().label("Model").description("Only models from the selected connection's vendor are listed.").disabledWhen(({ form })=>!this.getSelectedConnection(form, role.id)).options(({ form })=>this.getModelOptions(form, role.id))
26
+ }))
27
+ ])))
28
+ }));
29
+ form.layout((layout)=>[
30
+ layout.row("roles")
31
+ ]);
32
+ }
33
+ getConnections(form) {
34
+ const data = form.getData();
35
+ return data.connections?.presets ?? [];
36
+ }
37
+ getConnectionOptions(form) {
38
+ return this.getConnections(form).filter((c)=>c.id && c.name).map((c)=>({
39
+ label: c.name,
40
+ value: c.id
41
+ }));
42
+ }
43
+ getSelectedConnection(form, roleId) {
44
+ const data = form.getData();
45
+ const connectionId = data.modelRoles?.roles?.[roleId]?.connectionId;
46
+ if (!connectionId) return;
47
+ return this.getConnections(form).find((c)=>c.id === connectionId);
48
+ }
49
+ getModelOptions(form, roleId) {
50
+ const connection = this.getSelectedConnection(form, roleId);
51
+ if (!connection) return [];
52
+ return this.repository.getModels().filter((model)=>model.providerId === connection.sdkName).map((model)=>({
53
+ label: model.modelName,
54
+ value: `${model.providerId}/${model.modelId}`
55
+ }));
56
+ }
57
+ }
58
+ const ModelRolesSettings = AiPowerUpsSettingsGroup.createImplementation({
59
+ implementation: ModelRolesSettingsImpl,
60
+ dependencies: [
61
+ ListModelsUseCase,
62
+ ListModelsRepository
63
+ ]
64
+ });
65
+ export { ModelRolesSettings };
66
+
67
+ //# sourceMappingURL=ModelRolesSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/presentation/ModelRolesSettings.js","sources":["../../../src/admin/presentation/ModelRolesSettings.ts"],"sourcesContent":["import type { IAiPowerUpsConnectionPreset } from \"~/admin/features/settings/shared/abstractions.js\";\nimport { AiPowerUpsSettingsGroup } from \"./AiPowerUpsSettings/settingsGroup.js\";\nimport {\n ListModelsUseCase,\n ListModelsRepository\n} from \"~/admin/features/listModels/abstractions.js\";\nimport { AI_MODEL_ROLE_DISPLAY } from \"./modelRoles.js\";\nimport type { FormModel } from \"@webiny/app-admin\";\n\n/**\n * Three selects, and for most projects this is the whole AI configuration: one connection, one\n * model per role. Everything else in these settings is opt-in.\n */\nclass ModelRolesSettingsImpl implements AiPowerUpsSettingsGroup.Interface {\n name = \"modelRoles\";\n label = \"Model roles\";\n description = \"Which model does the cheap work, the main work, and the image work.\";\n\n constructor(\n private useCase: ListModelsUseCase.Interface,\n private repository: ListModelsRepository.Interface\n ) {}\n\n async init(): Promise<void> {\n await this.useCase.execute();\n }\n\n buildForm(form: AiPowerUpsSettingsGroup.FormBuilder): void {\n form.fields(fields => ({\n roles: fields\n .object()\n .label(\"Roles\")\n .renderer(\"passthrough\")\n .fields(f =>\n Object.fromEntries(\n AI_MODEL_ROLE_DISPLAY.map(role => [\n role.id,\n f\n .object()\n .label(role.label)\n /*\n * Both callbacks, not strings. A string here is a *field name* to\n * read the value from, not literal text, so passing prose made the\n * renderer look for a child field called \"Cheap, low-latency\n * work...\" and render no description at all.\n */\n .renderer(\"objectAccordionSingle\", {\n itemTitle: () => role.label,\n itemDescription: () => role.description,\n open: true\n })\n .fields(rf => ({\n connectionId: rf\n .text()\n .label(\"Connection\")\n .description(\"Which API key runs this role.\")\n .options(({ form }) => this.getConnectionOptions(form)),\n model: rf\n .text()\n .label(\"Model\")\n .description(\n \"Only models from the selected connection's vendor are listed.\"\n )\n .disabledWhen(\n ({ form }) => !this.getSelectedConnection(form, role.id)\n )\n .options(({ form }) => this.getModelOptions(form, role.id))\n }))\n ])\n )\n )\n }));\n\n form.layout(layout => [layout.row(\"roles\")]);\n }\n\n private getConnections(form: FormModel.Interface): IAiPowerUpsConnectionPreset[] {\n const data = form.getData() as {\n connections?: { presets?: IAiPowerUpsConnectionPreset[] };\n };\n return data.connections?.presets ?? [];\n }\n\n private getConnectionOptions(form: FormModel.Interface) {\n return this.getConnections(form)\n .filter(c => c.id && c.name)\n .map(c => ({ label: c.name, value: c.id }));\n }\n\n private getSelectedConnection(\n form: FormModel.Interface,\n roleId: string\n ): IAiPowerUpsConnectionPreset | undefined {\n const data = form.getData() as {\n modelRoles?: { roles?: Record<string, { connectionId?: string }> };\n };\n const connectionId = data.modelRoles?.roles?.[roleId]?.connectionId;\n\n if (!connectionId) {\n return undefined;\n }\n\n return this.getConnections(form).find(c => c.id === connectionId);\n }\n\n /**\n * Filtered by the chosen connection's vendor. Listing every model regardless would let someone\n * pair an OpenAI model with an Anthropic key, which then fails at request time with a provider\n * auth error that says nothing about the real mistake.\n */\n private getModelOptions(form: FormModel.Interface, roleId: string) {\n const connection = this.getSelectedConnection(form, roleId);\n\n if (!connection) {\n return [];\n }\n\n return this.repository\n .getModels()\n .filter(model => model.providerId === connection.sdkName)\n .map(model => ({\n label: model.modelName,\n value: `${model.providerId}/${model.modelId}`\n }));\n }\n}\n\nexport const ModelRolesSettings = AiPowerUpsSettingsGroup.createImplementation({\n implementation: ModelRolesSettingsImpl,\n dependencies: [ListModelsUseCase, ListModelsRepository]\n});\n"],"names":["ModelRolesSettingsImpl","useCase","repository","form","fields","f","Object","AI_MODEL_ROLE_DISPLAY","role","rf","layout","data","c","roleId","connectionId","connection","model","ModelRolesSettings","AiPowerUpsSettingsGroup","ListModelsUseCase","ListModelsRepository"],"mappings":";;;AAaA,MAAMA;IAKF,YACYC,OAAoC,EACpCC,UAA0C,CACpD;aAFUD,OAAO,GAAPA;aACAC,UAAU,GAAVA;aANZ,IAAI,GAAG;aACP,KAAK,GAAG;aACR,WAAW,GAAG;IAKX;IAEH,MAAM,OAAsB;QACxB,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO;IAC9B;IAEA,UAAUC,IAAyC,EAAQ;QACvDA,KAAK,MAAM,CAACC,CAAAA,SAAW;gBACnB,OAAOA,OACF,MAAM,GACN,KAAK,CAAC,SACN,QAAQ,CAAC,eACT,MAAM,CAACC,CAAAA,IACJC,OAAO,WAAW,CACdC,sBAAsB,GAAG,CAACC,CAAAA,OAAQ;4BAC9BA,KAAK,EAAE;4BACPH,EACK,MAAM,GACN,KAAK,CAACG,KAAK,KAAK,EAOhB,QAAQ,CAAC,yBAAyB;gCAC/B,WAAW,IAAMA,KAAK,KAAK;gCAC3B,iBAAiB,IAAMA,KAAK,WAAW;gCACvC,MAAM;4BACV,GACC,MAAM,CAACC,CAAAA,KAAO;oCACX,cAAcA,GACT,IAAI,GACJ,KAAK,CAAC,cACN,WAAW,CAAC,iCACZ,OAAO,CAAC,CAAC,EAAEN,IAAI,EAAE,GAAK,IAAI,CAAC,oBAAoB,CAACA;oCACrD,OAAOM,GACF,IAAI,GACJ,KAAK,CAAC,SACN,WAAW,CACR,iEAEH,YAAY,CACT,CAAC,EAAEN,IAAI,EAAE,GAAK,CAAC,IAAI,CAAC,qBAAqB,CAACA,MAAMK,KAAK,EAAE,GAE1D,OAAO,CAAC,CAAC,EAAEL,IAAI,EAAE,GAAK,IAAI,CAAC,eAAe,CAACA,MAAMK,KAAK,EAAE;gCACjE;yBACP;YAGjB;QAEAL,KAAK,MAAM,CAACO,CAAAA,SAAU;gBAACA,OAAO,GAAG,CAAC;aAAS;IAC/C;IAEQ,eAAeP,IAAyB,EAAiC;QAC7E,MAAMQ,OAAOR,KAAK,OAAO;QAGzB,OAAOQ,KAAK,WAAW,EAAE,WAAW,EAAE;IAC1C;IAEQ,qBAAqBR,IAAyB,EAAE;QACpD,OAAO,IAAI,CAAC,cAAc,CAACA,MACtB,MAAM,CAACS,CAAAA,IAAKA,EAAE,EAAE,IAAIA,EAAE,IAAI,EAC1B,GAAG,CAACA,CAAAA,IAAM;gBAAE,OAAOA,EAAE,IAAI;gBAAE,OAAOA,EAAE,EAAE;YAAC;IAChD;IAEQ,sBACJT,IAAyB,EACzBU,MAAc,EACyB;QACvC,MAAMF,OAAOR,KAAK,OAAO;QAGzB,MAAMW,eAAeH,KAAK,UAAU,EAAE,OAAO,CAACE,OAAO,EAAE;QAEvD,IAAI,CAACC,cACD;QAGJ,OAAO,IAAI,CAAC,cAAc,CAACX,MAAM,IAAI,CAACS,CAAAA,IAAKA,EAAE,EAAE,KAAKE;IACxD;IAOQ,gBAAgBX,IAAyB,EAAEU,MAAc,EAAE;QAC/D,MAAME,aAAa,IAAI,CAAC,qBAAqB,CAACZ,MAAMU;QAEpD,IAAI,CAACE,YACD,OAAO,EAAE;QAGb,OAAO,IAAI,CAAC,UAAU,CACjB,SAAS,GACT,MAAM,CAACC,CAAAA,QAASA,MAAM,UAAU,KAAKD,WAAW,OAAO,EACvD,GAAG,CAACC,CAAAA,QAAU;gBACX,OAAOA,MAAM,SAAS;gBACtB,OAAO,GAAGA,MAAM,UAAU,CAAC,CAAC,EAAEA,MAAM,OAAO,EAAE;YACjD;IACR;AACJ;AAEO,MAAMC,qBAAqBC,wBAAwB,oBAAoB,CAAC;IAC3E,gBAAgBlB;IAChB,cAAc;QAACmB;QAAmBC;KAAqB;AAC3D"}
@@ -3,11 +3,11 @@ import { IconButton } from "@webiny/admin-ui";
3
3
  import { ReactComponent } from "@webiny/icons/auto_fix_high.svg";
4
4
  import { useOpenDialog } from "@webiny/app-admin";
5
5
  import { GENERATE_CONTENT_DIALOG } from "./GenerateContentDialog.js";
6
- import { useAiPowerUpsSettings } from "../AiPowerUpsSettings/index.js";
6
+ import { hasUsableModel, useAiPowerUpsSettings } from "../AiPowerUpsSettings/index.js";
7
7
  const GenerateContentButton = ()=>{
8
8
  const { openDialog } = useOpenDialog();
9
9
  const { settings } = useAiPowerUpsSettings();
10
- if (!settings || 0 === settings.providers.presets.length) return null;
10
+ if (!hasUsableModel(settings)) return null;
11
11
  return /*#__PURE__*/ react.createElement(IconButton, {
12
12
  variant: "ghost",
13
13
  icon: /*#__PURE__*/ react.createElement(ReactComponent, null),
@@ -1 +1 @@
1
- {"version":3,"file":"admin/presentation/WbContentGeneration/GenerateContentButton.js","sources":["../../../../src/admin/presentation/WbContentGeneration/GenerateContentButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as ChatIcon } from \"@webiny/icons/auto_fix_high.svg\";\nimport { useOpenDialog } from \"@webiny/app-admin\";\nimport { GENERATE_CONTENT_DIALOG } from \"./GenerateContentDialog.js\";\nimport { useAiPowerUpsSettings } from \"~/admin/presentation/AiPowerUpsSettings/index.js\";\n\nexport const GenerateContentButton = () => {\n const { openDialog } = useOpenDialog();\n const { settings } = useAiPowerUpsSettings();\n\n if (!settings || settings.providers.presets.length === 0) {\n return null;\n }\n\n return (\n <IconButton\n variant=\"ghost\"\n icon={<ChatIcon />}\n onClick={() => openDialog(GENERATE_CONTENT_DIALOG, {})}\n />\n );\n};\n"],"names":["GenerateContentButton","openDialog","useOpenDialog","settings","useAiPowerUpsSettings","IconButton","ChatIcon","GENERATE_CONTENT_DIALOG"],"mappings":";;;;;;AAOO,MAAMA,wBAAwB;IACjC,MAAM,EAAEC,UAAU,EAAE,GAAGC;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC;IAErB,IAAI,CAACD,YAAYA,AAAsC,MAAtCA,SAAS,SAAS,CAAC,OAAO,CAAC,MAAM,EAC9C,OAAO;IAGX,OAAO,WAAP,GACI,oBAACE,YAAUA;QACP,SAAQ;QACR,oBAAM,oBAACC,gBAAQA;QACf,SAAS,IAAML,WAAWM,yBAAyB,CAAC;;AAGhE"}
1
+ {"version":3,"file":"admin/presentation/WbContentGeneration/GenerateContentButton.js","sources":["../../../../src/admin/presentation/WbContentGeneration/GenerateContentButton.tsx"],"sourcesContent":["import React from \"react\";\nimport { IconButton } from \"@webiny/admin-ui\";\nimport { ReactComponent as ChatIcon } from \"@webiny/icons/auto_fix_high.svg\";\nimport { useOpenDialog } from \"@webiny/app-admin\";\nimport { GENERATE_CONTENT_DIALOG } from \"./GenerateContentDialog.js\";\nimport {\n hasUsableModel,\n useAiPowerUpsSettings\n} from \"~/admin/presentation/AiPowerUpsSettings/index.js\";\n\nexport const GenerateContentButton = () => {\n const { openDialog } = useOpenDialog();\n const { settings } = useAiPowerUpsSettings();\n\n if (!hasUsableModel(settings)) {\n return null;\n }\n\n return (\n <IconButton\n variant=\"ghost\"\n icon={<ChatIcon />}\n onClick={() => openDialog(GENERATE_CONTENT_DIALOG, {})}\n />\n );\n};\n"],"names":["GenerateContentButton","openDialog","useOpenDialog","settings","useAiPowerUpsSettings","hasUsableModel","IconButton","ChatIcon","GENERATE_CONTENT_DIALOG"],"mappings":";;;;;;AAUO,MAAMA,wBAAwB;IACjC,MAAM,EAAEC,UAAU,EAAE,GAAGC;IACvB,MAAM,EAAEC,QAAQ,EAAE,GAAGC;IAErB,IAAI,CAACC,eAAeF,WAChB,OAAO;IAGX,OAAO,WAAP,GACI,oBAACG,YAAUA;QACP,SAAQ;QACR,oBAAM,oBAACC,gBAAQA;QACf,SAAS,IAAMN,WAAWO,yBAAyB,CAAC;;AAGhE"}
@@ -0,0 +1,10 @@
1
+ import { AI_MODEL_ROLE_IDS } from "../../admin/domain/modelRoles.js";
2
+ import type { AiModelRoleId } from "../../admin/domain/modelRoles.js";
3
+ export { AI_MODEL_ROLE_IDS };
4
+ export type { AiModelRoleId };
5
+ export declare const AI_MODEL_ROLE_DISPLAY: {
6
+ label: string;
7
+ description: string;
8
+ id: "fast" | "standard" | "vision";
9
+ }[];
10
+ export declare const roleLabel: (id: string) => string;
@@ -0,0 +1,23 @@
1
+ import { AI_MODEL_ROLE_IDS } from "../domain/modelRoles.js";
2
+ const ROLE_TEXT = {
3
+ fast: {
4
+ label: "Fast",
5
+ description: "Cheap, low-latency work where a smaller model is good enough: summarising, classifying, comparing."
6
+ },
7
+ standard: {
8
+ label: "Standard",
9
+ description: "The workhorse. Anything that writes content a person will read, or has to follow a schema and call tools. Every other role falls back to this one when left empty."
10
+ },
11
+ vision: {
12
+ label: "Vision",
13
+ description: "Work that reads images. Pick a model that accepts image input. Left empty, image features fall back to Standard and only work there if Standard is multimodal too."
14
+ }
15
+ };
16
+ const AI_MODEL_ROLE_DISPLAY = AI_MODEL_ROLE_IDS.map((id)=>({
17
+ id,
18
+ ...ROLE_TEXT[id]
19
+ }));
20
+ const roleLabel = (id)=>id in ROLE_TEXT ? ROLE_TEXT[id].label : id;
21
+ export { AI_MODEL_ROLE_DISPLAY, AI_MODEL_ROLE_IDS, roleLabel };
22
+
23
+ //# sourceMappingURL=modelRoles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin/presentation/modelRoles.js","sources":["../../../src/admin/presentation/modelRoles.ts"],"sourcesContent":["import { AI_MODEL_ROLE_IDS } from \"~/admin/domain/modelRoles.js\";\nimport type { AiModelRoleId } from \"~/admin/domain/modelRoles.js\";\n\n/**\n * The model roles, as the settings screen presents them. The ids live in\n * `~/admin/domain/modelRoles.js`; this owns the words.\n *\n * Typed as a full record, so adding a role id without a label is a compile error.\n */\nconst ROLE_TEXT: Record<AiModelRoleId, { label: string; description: string }> = {\n fast: {\n label: \"Fast\",\n description:\n \"Cheap, low-latency work where a smaller model is good enough: summarising, classifying, comparing.\"\n },\n standard: {\n label: \"Standard\",\n description:\n \"The workhorse. Anything that writes content a person will read, or has to follow a schema and call tools. Every other role falls back to this one when left empty.\"\n },\n vision: {\n label: \"Vision\",\n description:\n \"Work that reads images. Pick a model that accepts image input. Left empty, image features fall back to Standard and only work there if Standard is multimodal too.\"\n }\n};\n\nexport { AI_MODEL_ROLE_IDS };\nexport type { AiModelRoleId };\n\nexport const AI_MODEL_ROLE_DISPLAY = AI_MODEL_ROLE_IDS.map(id => ({ id, ...ROLE_TEXT[id] }));\n\nexport const roleLabel = (id: string): string =>\n id in ROLE_TEXT ? ROLE_TEXT[id as AiModelRoleId].label : id;\n"],"names":["ROLE_TEXT","AI_MODEL_ROLE_DISPLAY","AI_MODEL_ROLE_IDS","id","roleLabel"],"mappings":";AASA,MAAMA,YAA2E;IAC7E,MAAM;QACF,OAAO;QACP,aACI;IACR;IACA,UAAU;QACN,OAAO;QACP,aACI;IACR;IACA,QAAQ;QACJ,OAAO;QACP,aACI;IACR;AACJ;AAKO,MAAMC,wBAAwBC,kBAAkB,GAAG,CAACC,CAAAA,KAAO;QAAEA;QAAI,GAAGH,SAAS,CAACG,GAAG;IAAC;AAElF,MAAMC,YAAY,CAACD,KACtBA,MAAMH,YAAYA,SAAS,CAACG,GAAoB,CAAC,KAAK,GAAGA"}