@webiny/ai-powerups 6.6.0-alpha.0 → 6.6.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/AiPowerups.js +2 -2
  2. package/AiPowerups.js.map +1 -1
  3. package/admin/Extension.js +10 -2
  4. package/admin/Extension.js.map +1 -1
  5. package/admin/features/compareEntryRevisions/CompareEntryRevisionsGateway.d.ts +12 -0
  6. package/admin/features/compareEntryRevisions/CompareEntryRevisionsGateway.js +46 -0
  7. package/admin/features/compareEntryRevisions/CompareEntryRevisionsGateway.js.map +1 -0
  8. package/admin/features/compareEntryRevisions/CompareEntryRevisionsRepository.d.ts +11 -0
  9. package/admin/features/compareEntryRevisions/CompareEntryRevisionsRepository.js +18 -0
  10. package/admin/features/compareEntryRevisions/CompareEntryRevisionsRepository.js.map +1 -0
  11. package/admin/features/compareEntryRevisions/CompareEntryRevisionsUseCase.d.ts +11 -0
  12. package/admin/features/compareEntryRevisions/CompareEntryRevisionsUseCase.js +18 -0
  13. package/admin/features/compareEntryRevisions/CompareEntryRevisionsUseCase.js.map +1 -0
  14. package/admin/features/compareEntryRevisions/abstractions.d.ts +32 -0
  15. package/admin/features/compareEntryRevisions/abstractions.js +7 -0
  16. package/admin/features/compareEntryRevisions/abstractions.js.map +1 -0
  17. package/admin/features/compareEntryRevisions/feature.d.ts +1 -0
  18. package/admin/features/compareEntryRevisions/feature.js +15 -0
  19. package/admin/features/compareEntryRevisions/feature.js.map +1 -0
  20. package/admin/features/compareEntryRevisions/index.d.ts +2 -0
  21. package/admin/features/compareEntryRevisions/index.js +2 -0
  22. package/admin/features/feature.js +2 -0
  23. package/admin/features/feature.js.map +1 -1
  24. package/admin/features/generateEntryContent/GenerateEntryContentGateway.js +2 -0
  25. package/admin/features/generateEntryContent/GenerateEntryContentGateway.js.map +1 -1
  26. package/admin/features/generateEntryContent/abstractions.d.ts +1 -0
  27. package/admin/features/generateEntryContent/abstractions.js.map +1 -1
  28. package/admin/features/generatePageContent/GeneratePageContentGateway.js +2 -0
  29. package/admin/features/generatePageContent/GeneratePageContentGateway.js.map +1 -1
  30. package/admin/features/generatePageContent/abstractions.d.ts +1 -0
  31. package/admin/features/generatePageContent/abstractions.js.map +1 -1
  32. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPage.d.ts +2 -2
  33. package/admin/presentation/AiPromptFormFactory/AiPromptFormFactory.d.ts +20 -0
  34. package/admin/presentation/AiPromptFormFactory/AiPromptFormFactory.js +108 -0
  35. package/admin/presentation/AiPromptFormFactory/AiPromptFormFactory.js.map +1 -0
  36. package/admin/presentation/AiPromptFormFactory/abstractions.d.ts +14 -0
  37. package/admin/presentation/AiPromptFormFactory/abstractions.js +5 -0
  38. package/admin/presentation/AiPromptFormFactory/abstractions.js.map +1 -0
  39. package/admin/presentation/AiPromptFormFactory/feature.d.ts +1 -0
  40. package/admin/presentation/AiPromptFormFactory/feature.js +11 -0
  41. package/admin/presentation/AiPromptFormFactory/feature.js.map +1 -0
  42. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDialog.d.ts +9 -0
  43. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDialog.js +118 -0
  44. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDialog.js.map +1 -0
  45. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDrawer.d.ts +9 -0
  46. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDrawer.js +72 -0
  47. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsDrawer.js.map +1 -0
  48. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsMenuItem.d.ts +4 -0
  49. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsMenuItem.js +21 -0
  50. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsMenuItem.js.map +1 -0
  51. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsOverlays.d.ts +2 -0
  52. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsOverlays.js +17 -0
  53. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsOverlays.js.map +1 -0
  54. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsPresenter.d.ts +25 -0
  55. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsPresenter.js +90 -0
  56. package/admin/presentation/CmsCompareEntryRevisions/CmsCompareEntryRevisionsPresenter.js.map +1 -0
  57. package/admin/presentation/CmsCompareEntryRevisions/Extension.d.ts +2 -0
  58. package/admin/presentation/CmsCompareEntryRevisions/Extension.js +16 -0
  59. package/admin/presentation/CmsCompareEntryRevisions/Extension.js.map +1 -0
  60. package/admin/presentation/CmsCompareEntryRevisions/abstractions.d.ts +29 -0
  61. package/admin/presentation/CmsCompareEntryRevisions/abstractions.js +5 -0
  62. package/admin/presentation/CmsCompareEntryRevisions/abstractions.js.map +1 -0
  63. package/admin/presentation/CmsCompareEntryRevisions/feature.d.ts +3 -0
  64. package/admin/presentation/CmsCompareEntryRevisions/feature.js +17 -0
  65. package/admin/presentation/CmsCompareEntryRevisions/feature.js.map +1 -0
  66. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.d.ts +2 -2
  67. package/admin/presentation/CmsContentGeneration/CmsGenerateContentDialog.d.ts +2 -2
  68. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.d.ts +4 -12
  69. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.js +12 -94
  70. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.js.map +1 -1
  71. package/admin/presentation/CmsContentGeneration/Extension.d.ts +1 -1
  72. package/admin/presentation/CmsContentGeneration/Extension.js +9 -5
  73. package/admin/presentation/CmsContentGeneration/Extension.js.map +1 -1
  74. package/admin/presentation/CmsEntryWizard/CmsEntryWizard.d.ts +4 -0
  75. package/admin/presentation/CmsEntryWizard/CmsEntryWizard.js +113 -0
  76. package/admin/presentation/CmsEntryWizard/CmsEntryWizard.js.map +1 -0
  77. package/admin/presentation/CmsEntryWizard/CmsEntryWizardGate.d.ts +4 -0
  78. package/admin/presentation/CmsEntryWizard/CmsEntryWizardGate.js +19 -0
  79. package/admin/presentation/CmsEntryWizard/CmsEntryWizardGate.js.map +1 -0
  80. package/admin/presentation/CmsEntryWizard/Extension.d.ts +2 -0
  81. package/admin/presentation/CmsEntryWizard/Extension.js +10 -0
  82. package/admin/presentation/CmsEntryWizard/Extension.js.map +1 -0
  83. package/admin/presentation/ProvidersSettings.js +1 -1
  84. package/admin/presentation/ProvidersSettings.js.map +1 -1
  85. package/admin/presentation/WbContentGeneration/GenerateContentDialog.d.ts +2 -2
  86. package/admin/presentation/WbContentGeneration/GenerateContentDialog.js +2 -2
  87. package/admin/presentation/WbContentGeneration/GenerateContentDialog.js.map +1 -1
  88. package/admin/presentation/WbContentGeneration/GenerateContentPresenter.d.ts +4 -12
  89. package/admin/presentation/WbContentGeneration/GenerateContentPresenter.js +12 -94
  90. package/admin/presentation/WbContentGeneration/GenerateContentPresenter.js.map +1 -1
  91. package/api/Extension.js +8 -0
  92. package/api/Extension.js.map +1 -1
  93. package/api/features/AiChatProvider/PowerUpsAiChatProvider.d.ts +24 -0
  94. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js +31 -0
  95. package/api/features/AiChatProvider/PowerUpsAiChatProvider.js.map +1 -0
  96. package/api/features/AiChatProvider/feature.d.ts +9 -0
  97. package/api/features/AiChatProvider/feature.js +11 -0
  98. package/api/features/AiChatProvider/feature.js.map +1 -0
  99. package/api/features/AiChatProvider/index.d.ts +2 -0
  100. package/api/features/AiChatProvider/index.js +2 -0
  101. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.d.ts +1 -3
  102. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js +2 -6
  103. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js.map +1 -1
  104. package/api/features/AiImageEnrichment/AiImageEnrichmentStreamRoute.d.ts +36 -0
  105. package/api/features/AiImageEnrichment/AiImageEnrichmentStreamRoute.js +85 -0
  106. package/api/features/AiImageEnrichment/AiImageEnrichmentStreamRoute.js.map +1 -0
  107. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.d.ts +10 -16
  108. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.js +27 -102
  109. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.js.map +1 -1
  110. package/api/features/AiImageEnrichment/ApplyImageEnrichmentUseCase.d.ts +18 -0
  111. package/api/features/AiImageEnrichment/ApplyImageEnrichmentUseCase.js +50 -0
  112. package/api/features/AiImageEnrichment/ApplyImageEnrichmentUseCase.js.map +1 -0
  113. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.d.ts +19 -0
  114. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js +49 -0
  115. package/api/features/AiImageEnrichment/PrepareImageEnrichmentUseCase.js.map +1 -0
  116. package/api/features/AiImageEnrichment/abstractions.d.ts +60 -0
  117. package/api/features/AiImageEnrichment/abstractions.js +12 -0
  118. package/api/features/AiImageEnrichment/abstractions.js.map +1 -0
  119. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.d.ts +12 -0
  120. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js +30 -0
  121. package/api/features/AiImageEnrichment/buildEnrichmentAiRequest.js.map +1 -0
  122. package/api/features/AiImageEnrichment/errors.d.ts +26 -0
  123. package/api/features/AiImageEnrichment/errors.js +28 -0
  124. package/api/features/AiImageEnrichment/errors.js.map +1 -0
  125. package/api/features/AiImageEnrichment/feature.js +9 -0
  126. package/api/features/AiImageEnrichment/feature.js.map +1 -1
  127. package/api/features/AiImageEnrichment/imageEnrichmentErrorStatusCode.d.ts +6 -0
  128. package/api/features/AiImageEnrichment/imageEnrichmentErrorStatusCode.js +9 -0
  129. package/api/features/AiImageEnrichment/imageEnrichmentErrorStatusCode.js.map +1 -0
  130. package/api/features/AiImageEnrichment/readEnrichmentPartial.d.ts +8 -0
  131. package/api/features/AiImageEnrichment/readEnrichmentPartial.js +16 -0
  132. package/api/features/AiImageEnrichment/readEnrichmentPartial.js.map +1 -0
  133. package/api/features/AiImageEnrichment/streamEvents.d.ts +36 -0
  134. package/api/features/AiImageEnrichment/streamEvents.js +0 -0
  135. package/api/features/AiPromptContext/AiPromptContextBuilder.d.ts +8 -1
  136. package/api/features/AiPromptContext/AiPromptContextBuilder.js +46 -2
  137. package/api/features/AiPromptContext/AiPromptContextBuilder.js.map +1 -1
  138. package/api/features/AiPromptContext/abstractions.d.ts +3 -0
  139. package/api/features/AiPromptContext/abstractions.js +9 -1
  140. package/api/features/AiPromptContext/abstractions.js.map +1 -1
  141. package/api/features/AiPromptContext/index.d.ts +1 -1
  142. package/api/features/AiPromptContext/index.js +1 -1
  143. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.d.ts +22 -0
  144. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js +136 -0
  145. package/api/features/CmsCompareEntryRevisions/CmsCompareEntryRevisionsUseCase.js.map +1 -0
  146. package/api/features/CmsCompareEntryRevisions/abstractions.d.ts +18 -0
  147. package/api/features/CmsCompareEntryRevisions/abstractions.js +5 -0
  148. package/api/features/CmsCompareEntryRevisions/abstractions.js.map +1 -0
  149. package/api/features/CmsCompareEntryRevisions/feature.d.ts +4 -0
  150. package/api/features/CmsCompareEntryRevisions/feature.js +11 -0
  151. package/api/features/CmsCompareEntryRevisions/feature.js.map +1 -0
  152. package/api/features/CmsCompareEntryRevisions/index.d.ts +2 -0
  153. package/api/features/CmsCompareEntryRevisions/index.js +2 -0
  154. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.d.ts +1 -0
  155. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.js +20 -10
  156. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.js.map +1 -1
  157. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.d.ts +1 -1
  158. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js +5 -5
  159. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js.map +1 -1
  160. package/api/features/CmsGenerateEntryContent/abstractions.d.ts +10 -3
  161. package/api/features/CmsGenerateEntryContent/abstractions.js.map +1 -1
  162. package/api/features/GetSettings/index.d.ts +1 -0
  163. package/api/features/GetSettings/index.js +1 -0
  164. package/api/features/Providers/types.d.ts +2 -2
  165. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.d.ts +1 -0
  166. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.js +19 -9
  167. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.js.map +1 -1
  168. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js +4 -3
  169. package/api/features/WbGeneratePageContent/WbGeneratePageContentUseCase.js.map +1 -1
  170. package/api/features/WbGeneratePageContent/abstractions.d.ts +1 -0
  171. package/api/features/WbGeneratePageContent/abstractions.js.map +1 -1
  172. package/api/features/WbTranslatePage/LexicalParser.d.ts +10 -0
  173. package/api/features/WbTranslatePage/LexicalParser.js +37 -0
  174. package/api/features/WbTranslatePage/LexicalParser.js.map +1 -0
  175. package/api/features/WbTranslatePage/WbTranslatePageDecorator.d.ts +27 -0
  176. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js +134 -0
  177. package/api/features/WbTranslatePage/WbTranslatePageDecorator.js.map +1 -0
  178. package/api/features/WbTranslatePage/abstractions/LexicalParser.d.ts +7 -0
  179. package/api/features/WbTranslatePage/abstractions/LexicalParser.js +5 -0
  180. package/api/features/WbTranslatePage/abstractions/LexicalParser.js.map +1 -0
  181. package/api/features/WbTranslatePage/feature.d.ts +4 -0
  182. package/api/features/WbTranslatePage/feature.js +13 -0
  183. package/api/features/WbTranslatePage/feature.js.map +1 -0
  184. package/api/features/WbTranslatePage/index.d.ts +2 -0
  185. package/api/features/WbTranslatePage/index.js +2 -0
  186. package/api/graphql/BaseGraphQLSchema.js +11 -4
  187. package/api/graphql/BaseGraphQLSchema.js.map +1 -1
  188. package/api/graphql/CmsCompareEntryRevisionsSchema.d.ts +8 -0
  189. package/api/graphql/CmsCompareEntryRevisionsSchema.js +68 -0
  190. package/api/graphql/CmsCompareEntryRevisionsSchema.js.map +1 -0
  191. package/exports/admin/ai-powerups.d.ts +1 -0
  192. package/exports/admin/ai-powerups.js +1 -0
  193. package/exports/api/ai-powerups.d.ts +2 -0
  194. package/exports/api/ai-powerups.js +2 -0
  195. package/package.json +32 -25
@@ -0,0 +1,30 @@
1
+ import { Output } from "ai";
2
+ import { AI_ENRICHMENT_PROMPT, aiEnrichmentSchema } from "./abstractions.js";
3
+ function buildEnrichmentAiRequest(prepared) {
4
+ return {
5
+ model: prepared.model,
6
+ output: Output.object({
7
+ schema: aiEnrichmentSchema
8
+ }),
9
+ connection: prepared.connection,
10
+ messages: [
11
+ {
12
+ role: "user",
13
+ content: [
14
+ {
15
+ type: "file",
16
+ data: prepared.imageBase64,
17
+ mediaType: prepared.imageMediaType
18
+ },
19
+ {
20
+ type: "text",
21
+ text: AI_ENRICHMENT_PROMPT
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ };
27
+ }
28
+ export { buildEnrichmentAiRequest };
29
+
30
+ //# sourceMappingURL=buildEnrichmentAiRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AiImageEnrichment/buildEnrichmentAiRequest.js","sources":["../../../../src/api/features/AiImageEnrichment/buildEnrichmentAiRequest.ts"],"sourcesContent":["import { Output } from \"ai\";\nimport type { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { AI_ENRICHMENT_PROMPT, aiEnrichmentSchema } from \"./abstractions.js\";\nimport type { IPreparedImageEnrichment } from \"./abstractions.js\";\n\n/**\n * Builds the AI request both enrichment entry points send — the background task via `generateText`\n * and the streaming route via `streamText`. Identical model, structured output, connection, and\n * message payload; only the call differs.\n *\n * This is also where `Output.object(...)` lives rather than on `IPreparedImageEnrichment`: the AI\n * SDK's `Output` type can't be named in emitted declarations (TS4023), so it has to be built inside a\n * function body, not held on an exported interface.\n */\nexport function buildEnrichmentAiRequest(\n prepared: IPreparedImageEnrichment\n): Ai.GenerateTextParams {\n return {\n model: prepared.model,\n output: Output.object({ schema: aiEnrichmentSchema }),\n connection: prepared.connection,\n messages: [\n {\n role: \"user\",\n content: [\n {\n // Sent as base64, not a URL: a URL forces the provider to fetch the file,\n // which fails for private files and can't reach a non-public origin.\n type: \"file\",\n data: prepared.imageBase64,\n mediaType: prepared.imageMediaType\n },\n {\n type: \"text\",\n text: AI_ENRICHMENT_PROMPT\n }\n ]\n }\n ]\n };\n}\n"],"names":["buildEnrichmentAiRequest","prepared","Output","aiEnrichmentSchema","AI_ENRICHMENT_PROMPT"],"mappings":";;AAcO,SAASA,yBACZC,QAAkC;IAElC,OAAO;QACH,OAAOA,SAAS,KAAK;QACrB,QAAQC,OAAO,MAAM,CAAC;YAAE,QAAQC;QAAmB;QACnD,YAAYF,SAAS,UAAU;QAC/B,UAAU;YACN;gBACI,MAAM;gBACN,SAAS;oBACL;wBAGI,MAAM;wBACN,MAAMA,SAAS,WAAW;wBAC1B,WAAWA,SAAS,cAAc;oBACtC;oBACA;wBACI,MAAM;wBACN,MAAMG;oBACV;iBACH;YACL;SACH;IACL;AACJ"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Errors shared by every image-enrichment entry point (the background task and the streaming HTTP
3
+ * route). Each carries a `code` so callers can map it to their own transport: the task turns them
4
+ * into task results, the route into HTTP status codes.
5
+ */
6
+ export declare class EnrichmentFileNotFoundError extends Error {
7
+ readonly code: "ENRICHMENT_FILE_NOT_FOUND";
8
+ constructor(fileId: string);
9
+ }
10
+ export declare class EnrichmentNotAnImageError extends Error {
11
+ readonly code: "ENRICHMENT_NOT_AN_IMAGE";
12
+ constructor(type: string);
13
+ }
14
+ export declare class EnrichmentFileContentsError extends Error {
15
+ readonly code: "ENRICHMENT_FILE_CONTENTS_UNREADABLE";
16
+ constructor(reason: string);
17
+ }
18
+ export declare class EnrichmentNoProviderError extends Error {
19
+ readonly code: "ENRICHMENT_NO_AI_PROVIDER";
20
+ constructor();
21
+ }
22
+ export declare class EnrichmentPersistError extends Error {
23
+ readonly code: "ENRICHMENT_PERSIST_FAILED";
24
+ constructor(reason: string);
25
+ }
26
+ export type ImageEnrichmentError = EnrichmentFileNotFoundError | EnrichmentNotAnImageError | EnrichmentFileContentsError | EnrichmentNoProviderError | EnrichmentPersistError;
@@ -0,0 +1,28 @@
1
+ class EnrichmentFileNotFoundError extends Error {
2
+ constructor(fileId){
3
+ super(`File not found: ${fileId}`), this.code = "ENRICHMENT_FILE_NOT_FOUND";
4
+ }
5
+ }
6
+ class EnrichmentNotAnImageError extends Error {
7
+ constructor(type){
8
+ super(`File is not an image (received "${type}"); skipping AI enrichment.`), this.code = "ENRICHMENT_NOT_AN_IMAGE";
9
+ }
10
+ }
11
+ class EnrichmentFileContentsError extends Error {
12
+ constructor(reason){
13
+ super(`Unable to read file contents: ${reason}`), this.code = "ENRICHMENT_FILE_CONTENTS_UNREADABLE";
14
+ }
15
+ }
16
+ class EnrichmentNoProviderError extends Error {
17
+ constructor(){
18
+ super("No AI provider configured. Add a provider in AI Power Ups settings."), this.code = "ENRICHMENT_NO_AI_PROVIDER";
19
+ }
20
+ }
21
+ class EnrichmentPersistError extends Error {
22
+ constructor(reason){
23
+ super(`Failed to update file: ${reason}`), this.code = "ENRICHMENT_PERSIST_FAILED";
24
+ }
25
+ }
26
+ export { EnrichmentFileContentsError, EnrichmentFileNotFoundError, EnrichmentNoProviderError, EnrichmentNotAnImageError, EnrichmentPersistError };
27
+
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AiImageEnrichment/errors.js","sources":["../../../../src/api/features/AiImageEnrichment/errors.ts"],"sourcesContent":["/**\n * Errors shared by every image-enrichment entry point (the background task and the streaming HTTP\n * route). Each carries a `code` so callers can map it to their own transport: the task turns them\n * into task results, the route into HTTP status codes.\n */\nexport class EnrichmentFileNotFoundError extends Error {\n readonly code = \"ENRICHMENT_FILE_NOT_FOUND\" as const;\n\n constructor(fileId: string) {\n super(`File not found: ${fileId}`);\n }\n}\n\nexport class EnrichmentNotAnImageError extends Error {\n readonly code = \"ENRICHMENT_NOT_AN_IMAGE\" as const;\n\n constructor(type: string) {\n super(`File is not an image (received \"${type}\"); skipping AI enrichment.`);\n }\n}\n\nexport class EnrichmentFileContentsError extends Error {\n readonly code = \"ENRICHMENT_FILE_CONTENTS_UNREADABLE\" as const;\n\n constructor(reason: string) {\n super(`Unable to read file contents: ${reason}`);\n }\n}\n\nexport class EnrichmentNoProviderError extends Error {\n readonly code = \"ENRICHMENT_NO_AI_PROVIDER\" as const;\n\n constructor() {\n super(\"No AI provider configured. Add a provider in AI Power Ups settings.\");\n }\n}\n\nexport class EnrichmentPersistError extends Error {\n readonly code = \"ENRICHMENT_PERSIST_FAILED\" as const;\n\n constructor(reason: string) {\n super(`Failed to update file: ${reason}`);\n }\n}\n\nexport type ImageEnrichmentError =\n | EnrichmentFileNotFoundError\n | EnrichmentNotAnImageError\n | EnrichmentFileContentsError\n | EnrichmentNoProviderError\n | EnrichmentPersistError;\n"],"names":["EnrichmentFileNotFoundError","Error","fileId","EnrichmentNotAnImageError","type","EnrichmentFileContentsError","reason","EnrichmentNoProviderError","EnrichmentPersistError"],"mappings":"AAKO,MAAMA,oCAAoCC;IAG7C,YAAYC,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,gBAAgB,EAAEA,QAAQ,QAH5B,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCF;IAG3C,YAAYG,IAAY,CAAE;QACtB,KAAK,CAAC,CAAC,gCAAgC,EAAEA,KAAK,2BAA2B,CAAC,QAHrE,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,oCAAoCJ;IAG7C,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,8BAA8B,EAAEA,QAAQ,QAH1C,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMC,kCAAkCN;IAG3C,aAAc;QACV,KAAK,CAAC,6EAHD,IAAI,GAAG;IAIhB;AACJ;AAEO,MAAMO,+BAA+BP;IAGxC,YAAYK,MAAc,CAAE;QACxB,KAAK,CAAC,CAAC,uBAAuB,EAAEA,QAAQ,QAHnC,IAAI,GAAG;IAIhB;AACJ"}
@@ -1,11 +1,20 @@
1
1
  import { createFeature } from "@webiny/feature/api";
2
+ import { FeatureFlags } from "@webiny/api-core/features/featureFlags/abstractions.js";
2
3
  import { AiImageEnrichmentAfterCreateHandler } from "./AiImageEnrichmentAfterCreateHandler.js";
3
4
  import { AiImageEnrichmentTask } from "./AiImageEnrichmentTask.js";
5
+ import { AiImageEnrichmentStreamRouteDefinition } from "./AiImageEnrichmentStreamRoute.js";
6
+ import { PrepareImageEnrichmentUseCase } from "./PrepareImageEnrichmentUseCase.js";
7
+ import { ApplyImageEnrichmentUseCase } from "./ApplyImageEnrichmentUseCase.js";
4
8
  const AiImageEnrichmentFeature = createFeature({
5
9
  name: "AiPowerUps/AiImageEnrichment",
6
10
  register (container) {
11
+ const enabled = container.resolve(FeatureFlags).get().isEnabled("aiPowerups.fileManager.imageEnrichment");
12
+ if (!enabled) return;
13
+ container.register(PrepareImageEnrichmentUseCase);
14
+ container.register(ApplyImageEnrichmentUseCase);
7
15
  container.register(AiImageEnrichmentAfterCreateHandler);
8
16
  container.register(AiImageEnrichmentTask);
17
+ container.register(AiImageEnrichmentStreamRouteDefinition);
9
18
  }
10
19
  });
11
20
  export { AiImageEnrichmentFeature };
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/feature.js","sources":["../../../../src/api/features/AiImageEnrichment/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { AiImageEnrichmentAfterCreateHandler } from \"./AiImageEnrichmentAfterCreateHandler.js\";\nimport { AiImageEnrichmentTask } from \"./AiImageEnrichmentTask.js\";\n\nexport const AiImageEnrichmentFeature = createFeature({\n name: \"AiPowerUps/AiImageEnrichment\",\n register(container) {\n container.register(AiImageEnrichmentAfterCreateHandler);\n container.register(AiImageEnrichmentTask);\n }\n});\n"],"names":["AiImageEnrichmentFeature","createFeature","container","AiImageEnrichmentAfterCreateHandler","AiImageEnrichmentTask"],"mappings":";;;AAIO,MAAMA,2BAA2BC,cAAc;IAClD,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;IACvB;AACJ"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/feature.js","sources":["../../../../src/api/features/AiImageEnrichment/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { FeatureFlags } from \"@webiny/api-core/features/featureFlags/abstractions.js\";\nimport { AiImageEnrichmentAfterCreateHandler } from \"./AiImageEnrichmentAfterCreateHandler.js\";\nimport { AiImageEnrichmentTask } from \"./AiImageEnrichmentTask.js\";\nimport { AiImageEnrichmentStreamRouteDefinition } from \"./AiImageEnrichmentStreamRoute.js\";\nimport { PrepareImageEnrichmentUseCase } from \"./PrepareImageEnrichmentUseCase.js\";\nimport { ApplyImageEnrichmentUseCase } from \"./ApplyImageEnrichmentUseCase.js\";\n\nexport const AiImageEnrichmentFeature = createFeature({\n name: \"AiPowerUps/AiImageEnrichment\",\n register(container) {\n // Register-time gate on the effective feature flags (userFlag && live WCP license). Valid at\n // register() time because the license is refreshed PRE-register (WcpLicenseLoader.load() in\n // registerApiRequestStack) and FeatureFlags reads that process cache — and it re-evaluates per\n // request since the child re-registers, so a license change takes effect on the next request.\n const enabled = container\n .resolve(FeatureFlags)\n .get()\n .isEnabled(\"aiPowerups.fileManager.imageEnrichment\");\n\n if (!enabled) {\n return;\n }\n\n container.register(PrepareImageEnrichmentUseCase);\n container.register(ApplyImageEnrichmentUseCase);\n\n // Automatic enrichment after a file is created.\n container.register(AiImageEnrichmentAfterCreateHandler);\n container.register(AiImageEnrichmentTask);\n\n // On-demand re-enrichment, streamed.\n container.register(AiImageEnrichmentStreamRouteDefinition);\n }\n});\n"],"names":["AiImageEnrichmentFeature","createFeature","container","enabled","FeatureFlags","PrepareImageEnrichmentUseCase","ApplyImageEnrichmentUseCase","AiImageEnrichmentAfterCreateHandler","AiImageEnrichmentTask","AiImageEnrichmentStreamRouteDefinition"],"mappings":";;;;;;;AAQO,MAAMA,2BAA2BC,cAAc;IAClD,MAAM;IACN,UAASC,SAAS;QAKd,MAAMC,UAAUD,UACX,OAAO,CAACE,cACR,GAAG,GACH,SAAS,CAAC;QAEf,IAAI,CAACD,SACD;QAGJD,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QAGnBJ,UAAU,QAAQ,CAACK;QACnBL,UAAU,QAAQ,CAACM;QAGnBN,UAAU,QAAQ,CAACO;IACvB;AACJ"}
@@ -0,0 +1,6 @@
1
+ import type { ImageEnrichmentError } from "./errors.js";
2
+ /**
3
+ * Maps an enrichment failure to the HTTP status a transport should answer with. Lives next to the
4
+ * errors rather than in a route, so a second entry point can't invent a different mapping.
5
+ */
6
+ export declare function imageEnrichmentErrorStatusCode(error: ImageEnrichmentError): number;
@@ -0,0 +1,9 @@
1
+ import { EnrichmentFileNotFoundError, EnrichmentNotAnImageError } from "./errors.js";
2
+ function imageEnrichmentErrorStatusCode(error) {
3
+ if (error instanceof EnrichmentFileNotFoundError) return 404;
4
+ if (error instanceof EnrichmentNotAnImageError) return 400;
5
+ return 500;
6
+ }
7
+ export { imageEnrichmentErrorStatusCode };
8
+
9
+ //# sourceMappingURL=imageEnrichmentErrorStatusCode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AiImageEnrichment/imageEnrichmentErrorStatusCode.js","sources":["../../../../src/api/features/AiImageEnrichment/imageEnrichmentErrorStatusCode.ts"],"sourcesContent":["import { EnrichmentFileNotFoundError, EnrichmentNotAnImageError } from \"./errors.js\";\nimport type { ImageEnrichmentError } from \"./errors.js\";\n\n/**\n * Maps an enrichment failure to the HTTP status a transport should answer with. Lives next to the\n * errors rather than in a route, so a second entry point can't invent a different mapping.\n */\nexport function imageEnrichmentErrorStatusCode(error: ImageEnrichmentError): number {\n if (error instanceof EnrichmentFileNotFoundError) {\n return 404;\n }\n if (error instanceof EnrichmentNotAnImageError) {\n return 400;\n }\n return 500;\n}\n"],"names":["imageEnrichmentErrorStatusCode","error","EnrichmentFileNotFoundError","EnrichmentNotAnImageError"],"mappings":";AAOO,SAASA,+BAA+BC,KAA2B;IACtE,IAAIA,iBAAiBC,6BACjB,OAAO;IAEX,IAAID,iBAAiBE,2BACjB,OAAO;IAEX,OAAO;AACX"}
@@ -0,0 +1,8 @@
1
+ import type { IEnrichmentOutput } from "./abstractions.js";
2
+ /**
3
+ * Normalises one value from `streamText`'s `partialOutputStream`.
4
+ *
5
+ * Partial output is exactly that: mid-stream the tag array can hold holes/undefined entries and the
6
+ * description may not have arrived, so callers get only the values that are actually present.
7
+ */
8
+ export declare function readEnrichmentPartial(partial: unknown): IEnrichmentOutput;
@@ -0,0 +1,16 @@
1
+ function readEnrichmentPartial(partial) {
2
+ const value = partial ?? {};
3
+ const tags = [];
4
+ if (Array.isArray(value.tags)) {
5
+ for (const tag of value.tags)if ("string" == typeof tag && "" !== tag) tags.push(tag);
6
+ }
7
+ let description = "";
8
+ if ("string" == typeof value.description) description = value.description;
9
+ return {
10
+ tags,
11
+ description
12
+ };
13
+ }
14
+ export { readEnrichmentPartial };
15
+
16
+ //# sourceMappingURL=readEnrichmentPartial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/AiImageEnrichment/readEnrichmentPartial.js","sources":["../../../../src/api/features/AiImageEnrichment/readEnrichmentPartial.ts"],"sourcesContent":["import type { IEnrichmentOutput } from \"./abstractions.js\";\n\n/**\n * Normalises one value from `streamText`'s `partialOutputStream`.\n *\n * Partial output is exactly that: mid-stream the tag array can hold holes/undefined entries and the\n * description may not have arrived, so callers get only the values that are actually present.\n */\nexport function readEnrichmentPartial(partial: unknown): IEnrichmentOutput {\n const value = (partial ?? {}) as { tags?: unknown; description?: unknown };\n\n const tags: string[] = [];\n\n if (Array.isArray(value.tags)) {\n for (const tag of value.tags) {\n // Empty strings are dropped, not just non-strings: mid-stream the array can hold `\"\"`\n // for an entry the model has opened but not written into yet, which renders as a blank\n // chip.\n if (typeof tag === \"string\" && tag !== \"\") {\n tags.push(tag);\n }\n }\n }\n\n let description = \"\";\n\n if (typeof value.description === \"string\") {\n description = value.description;\n }\n\n return { tags, description };\n}\n"],"names":["readEnrichmentPartial","partial","value","tags","Array","tag","description"],"mappings":"AAQO,SAASA,sBAAsBC,OAAgB;IAClD,MAAMC,QAASD,WAAW,CAAC;IAE3B,MAAME,OAAiB,EAAE;IAEzB,IAAIC,MAAM,OAAO,CAACF,MAAM,IAAI,GACxB;QAAA,KAAK,MAAMG,OAAOH,MAAM,IAAI,CAIxB,IAAI,AAAe,YAAf,OAAOG,OAAoBA,AAAQ,OAARA,KAC3BF,KAAK,IAAI,CAACE;IAElB;IAGJ,IAAIC,cAAc;IAElB,IAAI,AAA6B,YAA7B,OAAOJ,MAAM,WAAW,EACxBI,cAAcJ,MAAM,WAAW;IAGnC,OAAO;QAAEC;QAAMG;IAAY;AAC/B"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Server-sent event payloads emitted by `AiImageEnrichmentStreamRoute` — this feature's stream
3
+ * protocol, not a shared one. The SSE wire format itself (`toSseFrame`) is transport-level and lives
4
+ * in `@webiny/event-handler-core`; what the events are called and what they carry is per feature,
5
+ * since the next streaming route will want a different set.
6
+ *
7
+ * A small domain protocol rather than the AI SDK's UI message stream: the client
8
+ * doesn't render a chat transcript, it renders a progressively-completing `{ tags, description }`
9
+ * object, which is exactly what `streamText`'s `partialOutputStream` produces. The admin app mirrors
10
+ * these types (it can't import an api-side package), so keep the two in sync.
11
+ */
12
+ export interface EnrichmentStreamStartEvent {
13
+ type: "start";
14
+ fileId: string;
15
+ model: string;
16
+ }
17
+ export interface EnrichmentStreamPartialEvent {
18
+ type: "partial";
19
+ tags: string[];
20
+ description: string;
21
+ }
22
+ export interface EnrichmentStreamDoneEvent {
23
+ type: "done";
24
+ fileId: string;
25
+ /**
26
+ * The model's final output. NOT persisted — the streaming route only proposes, and the client
27
+ * saves through the file's normal update path if the user accepts.
28
+ */
29
+ tags: string[];
30
+ description: string;
31
+ }
32
+ export interface EnrichmentStreamErrorEvent {
33
+ type: "error";
34
+ message: string;
35
+ }
36
+ export type EnrichmentStreamEvent = EnrichmentStreamStartEvent | EnrichmentStreamPartialEvent | EnrichmentStreamDoneEvent | EnrichmentStreamErrorEvent;
File without changes
@@ -1,10 +1,17 @@
1
1
  import { GetSettingsUseCase } from "../../../api/features/GetSettings/index.js";
2
+ import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/index.js";
3
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
+ import { TextExtractor } from "@webiny/api-core/features/ai/index.js";
2
5
  import { AiPromptContextBuilder as Abstraction, ProjectFileAssembler, type AiPromptContext, type AiPromptContextParams } from "./abstractions.js";
3
6
  declare class AiPromptContextBuilderImpl implements Abstraction.Interface {
4
7
  private getSettings;
5
8
  private fileAssembler;
6
- constructor(getSettings: GetSettingsUseCase.Interface, fileAssembler: ProjectFileAssembler.Interface);
9
+ private getFileContents;
10
+ private getFile;
11
+ private textExtractor;
12
+ constructor(getSettings: GetSettingsUseCase.Interface, fileAssembler: ProjectFileAssembler.Interface, getFileContents: GetFileContentsByIdUseCase.Interface, getFile: GetFileUseCase.Interface, textExtractor: TextExtractor.Interface);
7
13
  execute(params: AiPromptContextParams): Promise<AiPromptContext>;
14
+ private fetchAdditionalFiles;
8
15
  }
9
16
  export declare const AiPromptContextBuilder: typeof AiPromptContextBuilderImpl & {
10
17
  __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IAiPromptContextBuilder>;
@@ -1,4 +1,7 @@
1
1
  import { GetSettingsUseCase } from "../GetSettings/index.js";
2
+ import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/index.js";
3
+ import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
4
+ import { TextExtractor } from "@webiny/api-core/features/ai/index.js";
2
5
  import { AiPromptContextBuilder, ProjectFileAssembler } from "./abstractions.js";
3
6
  import { WriterPersonaSection } from "./WriterPersonaSection.js";
4
7
  import { ReaderPersonaSection } from "./ReaderPersonaSection.js";
@@ -37,6 +40,7 @@ function emptyContext(warnings) {
37
40
  readerPersona: void 0,
38
41
  writerPersona: void 0,
39
42
  allProjectFiles: [],
43
+ additionalFiles: [],
40
44
  excludedFileIds: new Set(),
41
45
  cacheHit: false,
42
46
  warnings,
@@ -46,9 +50,12 @@ function emptyContext(warnings) {
46
50
  };
47
51
  }
48
52
  class AiPromptContextBuilderImpl {
49
- constructor(getSettings, fileAssembler){
53
+ constructor(getSettings, fileAssembler, getFileContents, getFile, textExtractor){
50
54
  this.getSettings = getSettings;
51
55
  this.fileAssembler = fileAssembler;
56
+ this.getFileContents = getFileContents;
57
+ this.getFile = getFile;
58
+ this.textExtractor = textExtractor;
52
59
  }
53
60
  async execute(params) {
54
61
  const warnings = [];
@@ -76,11 +83,17 @@ class AiPromptContextBuilderImpl {
76
83
  totalTokens: visibleFiles.reduce((sum, f)=>sum + f.tokenCount, 0)
77
84
  };
78
85
  }
86
+ const additionalFiles = await this.fetchAdditionalFiles(params.additionalFileIds, warnings);
87
+ if (additionalFiles.length > 0) allProjectFiles = [
88
+ ...allProjectFiles,
89
+ ...additionalFiles
90
+ ];
79
91
  const ctx = {
80
92
  project,
81
93
  readerPersona: reader,
82
94
  writerPersona: writer,
83
95
  allProjectFiles,
96
+ additionalFiles,
84
97
  excludedFileIds,
85
98
  cacheHit,
86
99
  warnings,
@@ -90,12 +103,43 @@ class AiPromptContextBuilderImpl {
90
103
  };
91
104
  return ctx;
92
105
  }
106
+ async fetchAdditionalFiles(fileIds, warnings) {
107
+ if (!fileIds || 0 === fileIds.length) return [];
108
+ const results = await Promise.all(fileIds.map(async (fileId)=>{
109
+ const [contentsResult, damResult] = await Promise.all([
110
+ this.getFileContents.execute(fileId),
111
+ this.getFile.execute(fileId)
112
+ ]);
113
+ if (contentsResult.isFail()) {
114
+ warnings.push(`Failed to load additional file (${fileId}): ${contentsResult.error.message}`);
115
+ return null;
116
+ }
117
+ const name = damResult.isOk() ? damResult.value.name : fileId;
118
+ const mimeType = damResult.isOk() ? damResult.value.type : "application/octet-stream";
119
+ if (!this.textExtractor.canExtract(mimeType)) {
120
+ warnings.push(`Skipped additional file "${name}" (${fileId}): unsupported type "${mimeType}".`);
121
+ return null;
122
+ }
123
+ const { text: content } = await this.textExtractor.extract(contentsResult.value.buffer, mimeType);
124
+ return {
125
+ id: fileId,
126
+ name,
127
+ content,
128
+ description: damResult.isOk() ? damResult.value.description : void 0,
129
+ tokenCount: Math.ceil(content.length / 4)
130
+ };
131
+ }));
132
+ return results.filter((r)=>null !== r);
133
+ }
93
134
  }
94
135
  const AiPromptContextBuilder_AiPromptContextBuilder = AiPromptContextBuilder.createImplementation({
95
136
  implementation: AiPromptContextBuilderImpl,
96
137
  dependencies: [
97
138
  GetSettingsUseCase,
98
- ProjectFileAssembler
139
+ ProjectFileAssembler,
140
+ GetFileContentsByIdUseCase,
141
+ GetFileUseCase,
142
+ TextExtractor
99
143
  ]
100
144
  });
101
145
  export { AiPromptContextBuilder_AiPromptContextBuilder as AiPromptContextBuilder };
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiPromptContext/AiPromptContextBuilder.js","sources":["../../../../src/api/features/AiPromptContext/AiPromptContextBuilder.ts"],"sourcesContent":["import { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\nimport {\n AiPromptContextBuilder as Abstraction,\n ProjectFileAssembler,\n type AiPromptContext,\n type AiPromptContextParams,\n type ProjectFileContent,\n type ResolvedPersona,\n type ResolvedProject\n} from \"./abstractions.js\";\nimport type { IAiPowerUpsSettings } from \"~/api/types.js\";\nimport type { ProjectPreset } from \"~/api/features/Projects/types.js\";\nimport { WriterPersonaSection } from \"./WriterPersonaSection.js\";\nimport { ReaderPersonaSection } from \"./ReaderPersonaSection.js\";\nimport { ProjectSection } from \"./ProjectSection.js\";\n\nfunction resolvePersonas(\n settings: IAiPowerUpsSettings,\n project: ProjectPreset | undefined,\n params: AiPromptContextParams\n): { reader?: ResolvedPersona; writer?: ResolvedPersona } {\n const readerPersonaId = params.readerPersonaId ?? project?.defaultReaderPersonaId ?? undefined;\n const writerPersonaId = params.writerPersonaId ?? project?.defaultWriterPersonaId ?? undefined;\n\n const readerPreset = readerPersonaId\n ? settings.readerPersonas?.presets?.find(p => p.id === readerPersonaId)\n : undefined;\n\n const writerPreset = writerPersonaId\n ? settings.writerPersonas?.presets?.find(p => p.id === writerPersonaId)\n : undefined;\n\n return {\n reader: readerPreset\n ? {\n name: readerPreset.name,\n description: readerPreset.description,\n style: readerPreset.style\n }\n : undefined,\n writer: writerPreset\n ? {\n name: writerPreset.name,\n description: writerPreset.description,\n style: writerPreset.style\n }\n : undefined\n };\n}\n\nfunction formatContext(ctx: AiPromptContext): string {\n const sections: string[] = [];\n\n if (ctx.writerPersona) {\n sections.push(WriterPersonaSection.format(ctx.writerPersona));\n }\n if (ctx.readerPersona) {\n sections.push(ReaderPersonaSection.format(ctx.readerPersona));\n }\n if (ctx.project) {\n const projectText = ProjectSection.format(ctx.project);\n if (projectText) {\n sections.push(projectText);\n }\n }\n\n return sections.length > 0 ? \"\\n\\n\" + sections.join(\"\\n\\n\") : \"\";\n}\n\nfunction emptyContext(warnings: string[]): AiPromptContext {\n return {\n project: undefined,\n readerPersona: undefined,\n writerPersona: undefined,\n allProjectFiles: [],\n excludedFileIds: new Set(),\n cacheHit: false,\n warnings,\n toString() {\n return \"\";\n }\n };\n}\n\nclass AiPromptContextBuilderImpl implements Abstraction.Interface {\n constructor(\n private getSettings: GetSettingsUseCase.Interface,\n private fileAssembler: ProjectFileAssembler.Interface\n ) {}\n\n async execute(params: AiPromptContextParams): Promise<AiPromptContext> {\n const warnings: string[] = [];\n\n const settingsResult = await this.getSettings.execute();\n if (settingsResult.isFail()) {\n warnings.push(\"Failed to load AI PowerUps settings.\");\n return emptyContext(warnings);\n }\n\n const settings = settingsResult.value;\n const projectPreset = params.projectId\n ? settings.projects?.presets?.find(p => p.id === params.projectId)\n : undefined;\n\n const { reader, writer } = resolvePersonas(settings, projectPreset, params);\n const excludedFileIds = new Set(params.excludedFileIds ?? []);\n\n let project: ResolvedProject | undefined;\n let allProjectFiles: ProjectFileContent[] = [];\n let cacheHit = false;\n\n if (projectPreset) {\n const assembled = await this.fileAssembler.execute(\n projectPreset.id,\n projectPreset.version ?? 0,\n projectPreset.files ?? [],\n warnings\n );\n allProjectFiles = assembled.files;\n cacheHit = assembled.cacheHit;\n\n const visibleFiles =\n excludedFileIds.size > 0\n ? allProjectFiles.filter(f => !excludedFileIds.has(f.id))\n : allProjectFiles;\n\n project = {\n name: projectPreset.name,\n instructions: projectPreset.instructions,\n files: visibleFiles,\n totalTokens: visibleFiles.reduce((sum, f) => sum + f.tokenCount, 0)\n };\n }\n\n const ctx: AiPromptContext = {\n project,\n readerPersona: reader,\n writerPersona: writer,\n allProjectFiles,\n excludedFileIds,\n cacheHit,\n warnings,\n toString() {\n return formatContext(this);\n }\n };\n\n return ctx;\n }\n}\n\nexport const AiPromptContextBuilder = Abstraction.createImplementation({\n implementation: AiPromptContextBuilderImpl,\n dependencies: [GetSettingsUseCase, ProjectFileAssembler]\n});\n"],"names":["resolvePersonas","settings","project","params","readerPersonaId","undefined","writerPersonaId","readerPreset","p","writerPreset","formatContext","ctx","sections","WriterPersonaSection","ReaderPersonaSection","projectText","ProjectSection","emptyContext","warnings","Set","AiPromptContextBuilderImpl","getSettings","fileAssembler","settingsResult","projectPreset","reader","writer","excludedFileIds","allProjectFiles","cacheHit","assembled","visibleFiles","f","sum","AiPromptContextBuilder","Abstraction","GetSettingsUseCase","ProjectFileAssembler"],"mappings":"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"}
1
+ {"version":3,"file":"api/features/AiPromptContext/AiPromptContextBuilder.js","sources":["../../../../src/api/features/AiPromptContext/AiPromptContextBuilder.ts"],"sourcesContent":["import { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\nimport { GetFileContentsByIdUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsById/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { TextExtractor } from \"@webiny/api-core/features/ai/index.js\";\nimport {\n AiPromptContextBuilder as Abstraction,\n ProjectFileAssembler,\n type AiPromptContext,\n type AiPromptContextParams,\n type ProjectFileContent,\n type ResolvedPersona,\n type ResolvedProject\n} from \"./abstractions.js\";\nimport type { IAiPowerUpsSettings } from \"~/api/types.js\";\nimport type { ProjectPreset } from \"~/api/features/Projects/types.js\";\nimport { WriterPersonaSection } from \"./WriterPersonaSection.js\";\nimport { ReaderPersonaSection } from \"./ReaderPersonaSection.js\";\nimport { ProjectSection } from \"./ProjectSection.js\";\n\nfunction resolvePersonas(\n settings: IAiPowerUpsSettings,\n project: ProjectPreset | undefined,\n params: AiPromptContextParams\n): { reader?: ResolvedPersona; writer?: ResolvedPersona } {\n const readerPersonaId = params.readerPersonaId ?? project?.defaultReaderPersonaId ?? undefined;\n const writerPersonaId = params.writerPersonaId ?? project?.defaultWriterPersonaId ?? undefined;\n\n const readerPreset = readerPersonaId\n ? settings.readerPersonas?.presets?.find(p => p.id === readerPersonaId)\n : undefined;\n\n const writerPreset = writerPersonaId\n ? settings.writerPersonas?.presets?.find(p => p.id === writerPersonaId)\n : undefined;\n\n return {\n reader: readerPreset\n ? {\n name: readerPreset.name,\n description: readerPreset.description,\n style: readerPreset.style\n }\n : undefined,\n writer: writerPreset\n ? {\n name: writerPreset.name,\n description: writerPreset.description,\n style: writerPreset.style\n }\n : undefined\n };\n}\n\nfunction formatContext(ctx: AiPromptContext): string {\n const sections: string[] = [];\n\n if (ctx.writerPersona) {\n sections.push(WriterPersonaSection.format(ctx.writerPersona));\n }\n if (ctx.readerPersona) {\n sections.push(ReaderPersonaSection.format(ctx.readerPersona));\n }\n if (ctx.project) {\n const projectText = ProjectSection.format(ctx.project);\n if (projectText) {\n sections.push(projectText);\n }\n }\n return sections.length > 0 ? \"\\n\\n\" + sections.join(\"\\n\\n\") : \"\";\n}\n\nfunction emptyContext(warnings: string[]): AiPromptContext {\n return {\n project: undefined,\n readerPersona: undefined,\n writerPersona: undefined,\n allProjectFiles: [],\n additionalFiles: [],\n excludedFileIds: new Set(),\n cacheHit: false,\n warnings,\n toString() {\n return \"\";\n }\n };\n}\n\nclass AiPromptContextBuilderImpl implements Abstraction.Interface {\n constructor(\n private getSettings: GetSettingsUseCase.Interface,\n private fileAssembler: ProjectFileAssembler.Interface,\n private getFileContents: GetFileContentsByIdUseCase.Interface,\n private getFile: GetFileUseCase.Interface,\n private textExtractor: TextExtractor.Interface\n ) {}\n\n async execute(params: AiPromptContextParams): Promise<AiPromptContext> {\n const warnings: string[] = [];\n\n const settingsResult = await this.getSettings.execute();\n if (settingsResult.isFail()) {\n warnings.push(\"Failed to load AI PowerUps settings.\");\n return emptyContext(warnings);\n }\n\n const settings = settingsResult.value;\n const projectPreset = params.projectId\n ? settings.projects?.presets?.find(p => p.id === params.projectId)\n : undefined;\n\n const { reader, writer } = resolvePersonas(settings, projectPreset, params);\n const excludedFileIds = new Set(params.excludedFileIds ?? []);\n\n let project: ResolvedProject | undefined;\n let allProjectFiles: ProjectFileContent[] = [];\n let cacheHit = false;\n\n if (projectPreset) {\n const assembled = await this.fileAssembler.execute(\n projectPreset.id,\n projectPreset.version ?? 0,\n projectPreset.files ?? [],\n warnings\n );\n allProjectFiles = assembled.files;\n cacheHit = assembled.cacheHit;\n\n const visibleFiles =\n excludedFileIds.size > 0\n ? allProjectFiles.filter(f => !excludedFileIds.has(f.id))\n : allProjectFiles;\n\n project = {\n name: projectPreset.name,\n instructions: projectPreset.instructions,\n files: visibleFiles,\n totalTokens: visibleFiles.reduce((sum, f) => sum + f.tokenCount, 0)\n };\n }\n\n const additionalFiles = await this.fetchAdditionalFiles(params.additionalFileIds, warnings);\n if (additionalFiles.length > 0) {\n allProjectFiles = [...allProjectFiles, ...additionalFiles];\n }\n\n const ctx: AiPromptContext = {\n project,\n readerPersona: reader,\n writerPersona: writer,\n allProjectFiles,\n additionalFiles,\n excludedFileIds,\n cacheHit,\n warnings,\n toString() {\n return formatContext(this);\n }\n };\n\n return ctx;\n }\n\n private async fetchAdditionalFiles(\n fileIds: string[] | null | undefined,\n warnings: string[]\n ): Promise<ProjectFileContent[]> {\n if (!fileIds || fileIds.length === 0) {\n return [];\n }\n\n const results = await Promise.all(\n fileIds.map(async (fileId): Promise<ProjectFileContent | null> => {\n const [contentsResult, damResult] = await Promise.all([\n this.getFileContents.execute(fileId),\n this.getFile.execute(fileId)\n ]);\n\n if (contentsResult.isFail()) {\n warnings.push(\n `Failed to load additional file (${fileId}): ${contentsResult.error.message}`\n );\n return null;\n }\n\n const name = damResult.isOk() ? damResult.value.name : fileId;\n const mimeType = damResult.isOk()\n ? damResult.value.type\n : \"application/octet-stream\";\n\n if (!this.textExtractor.canExtract(mimeType)) {\n warnings.push(\n `Skipped additional file \"${name}\" (${fileId}): unsupported type \"${mimeType}\".`\n );\n return null;\n }\n\n const { text: content } = await this.textExtractor.extract(\n contentsResult.value.buffer,\n mimeType\n );\n\n return {\n id: fileId,\n name,\n content,\n description: damResult.isOk() ? damResult.value.description : undefined,\n tokenCount: Math.ceil(content.length / 4)\n };\n })\n );\n\n return results.filter((r): r is ProjectFileContent => r !== null);\n }\n}\n\nexport const AiPromptContextBuilder = Abstraction.createImplementation({\n implementation: AiPromptContextBuilderImpl,\n dependencies: [\n GetSettingsUseCase,\n ProjectFileAssembler,\n GetFileContentsByIdUseCase,\n GetFileUseCase,\n TextExtractor\n ]\n});\n"],"names":["resolvePersonas","settings","project","params","readerPersonaId","undefined","writerPersonaId","readerPreset","p","writerPreset","formatContext","ctx","sections","WriterPersonaSection","ReaderPersonaSection","projectText","ProjectSection","emptyContext","warnings","Set","AiPromptContextBuilderImpl","getSettings","fileAssembler","getFileContents","getFile","textExtractor","settingsResult","projectPreset","reader","writer","excludedFileIds","allProjectFiles","cacheHit","assembled","visibleFiles","f","sum","additionalFiles","fileIds","results","Promise","fileId","contentsResult","damResult","name","mimeType","content","Math","r","AiPromptContextBuilder","Abstraction","GetSettingsUseCase","ProjectFileAssembler","GetFileContentsByIdUseCase","GetFileUseCase","TextExtractor"],"mappings":"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"}
@@ -28,6 +28,7 @@ export interface AiPromptContextParams {
28
28
  readerPersonaId?: string | null;
29
29
  writerPersonaId?: string | null;
30
30
  excludedFileIds?: string[] | null;
31
+ additionalFileIds?: string[] | null;
31
32
  }
32
33
  export interface ProjectFileContent {
33
34
  id: string;
@@ -52,6 +53,7 @@ export interface AiPromptContext {
52
53
  readerPersona?: ResolvedPersona;
53
54
  writerPersona?: ResolvedPersona;
54
55
  allProjectFiles: ProjectFileContent[];
56
+ additionalFiles: ProjectFileContent[];
55
57
  excludedFileIds: Set<string>;
56
58
  cacheHit: boolean;
57
59
  warnings: string[];
@@ -66,3 +68,4 @@ export declare namespace AiPromptContextBuilder {
66
68
  type Params = AiPromptContextParams;
67
69
  type Context = AiPromptContext;
68
70
  }
71
+ export declare function formatAdditionalFilesContext(files: ProjectFileContent[]): string;
@@ -2,6 +2,14 @@ import { createAbstraction } from "@webiny/feature/api";
2
2
  const ProjectFileCache = createAbstraction("ProjectFileCache");
3
3
  const ProjectFileAssembler = createAbstraction("ProjectFileAssembler");
4
4
  const AiPromptContextBuilder = createAbstraction("AiPowerUpsPromptContextBuilder");
5
- export { AiPromptContextBuilder, ProjectFileAssembler, ProjectFileCache };
5
+ function formatAdditionalFilesContext(files) {
6
+ if (0 === files.length) return "";
7
+ const sections = files.map((f)=>{
8
+ const desc = f.description ? ` description="${f.description}"` : "";
9
+ return `<attached-file name="${f.name}"${desc}>\n${f.content}\n</attached-file>`;
10
+ });
11
+ return `\n\nThe user attached the following files as additional context for this generation:\n\n${sections.join("\n\n")}`;
12
+ }
13
+ export { AiPromptContextBuilder, ProjectFileAssembler, ProjectFileCache, formatAdditionalFilesContext };
6
14
 
7
15
  //# sourceMappingURL=abstractions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiPromptContext/abstractions.js","sources":["../../../../src/api/features/AiPromptContext/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\n// ============================================================================\n// ProjectFileCache\n// ============================================================================\n\nexport interface IProjectFileCache {\n get(projectId: string, version: number): Promise<{ files: ProjectFileContent[]; hit: boolean }>;\n set(projectId: string, version: number, files: ProjectFileContent[]): Promise<void>;\n}\n\nexport const ProjectFileCache = createAbstraction<IProjectFileCache>(\"ProjectFileCache\");\n\nexport namespace ProjectFileCache {\n export type Interface = IProjectFileCache;\n}\n\n// ============================================================================\n// ProjectFileAssembler\n// ============================================================================\n\nexport interface IProjectFileAssembler {\n execute(\n projectId: string,\n version: number,\n files: Array<{ id: string; name: string; mimeType: string }>,\n warnings: string[]\n ): Promise<{ files: ProjectFileContent[]; cacheHit: boolean }>;\n}\n\nexport const ProjectFileAssembler =\n createAbstraction<IProjectFileAssembler>(\"ProjectFileAssembler\");\n\nexport namespace ProjectFileAssembler {\n export type Interface = IProjectFileAssembler;\n}\n\n// ============================================================================\n// AiPromptContext\n// ============================================================================\n\nexport interface AiPromptContextParams {\n projectId?: string | null;\n readerPersonaId?: string | null;\n writerPersonaId?: string | null;\n excludedFileIds?: string[] | null;\n}\n\nexport interface ProjectFileContent {\n id: string;\n name: string;\n content: string;\n description?: string;\n tokenCount: number;\n}\n\nexport interface ResolvedProject {\n name: string;\n instructions?: string;\n files: ProjectFileContent[];\n totalTokens: number;\n}\n\nexport interface ResolvedPersona {\n name: string;\n description: string;\n style?: string;\n}\n\nexport interface AiPromptContext {\n project?: ResolvedProject;\n readerPersona?: ResolvedPersona;\n writerPersona?: ResolvedPersona;\n allProjectFiles: ProjectFileContent[];\n excludedFileIds: Set<string>;\n cacheHit: boolean;\n warnings: string[];\n toString(): string;\n}\n\nexport interface IAiPromptContextBuilder {\n execute(params: AiPromptContextParams): Promise<AiPromptContext>;\n}\n\nexport const AiPromptContextBuilder = createAbstraction<IAiPromptContextBuilder>(\n \"AiPowerUpsPromptContextBuilder\"\n);\n\nexport namespace AiPromptContextBuilder {\n export type Interface = IAiPromptContextBuilder;\n export type Params = AiPromptContextParams;\n export type Context = AiPromptContext;\n}\n"],"names":["ProjectFileCache","createAbstraction","ProjectFileAssembler","AiPromptContextBuilder"],"mappings":";AAWO,MAAMA,mBAAmBC,kBAAqC;AAmB9D,MAAMC,uBACTD,kBAAyC;AAqDtC,MAAME,yBAAyBF,kBAClC"}
1
+ {"version":3,"file":"api/features/AiPromptContext/abstractions.js","sources":["../../../../src/api/features/AiPromptContext/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\n\n// ============================================================================\n// ProjectFileCache\n// ============================================================================\n\nexport interface IProjectFileCache {\n get(projectId: string, version: number): Promise<{ files: ProjectFileContent[]; hit: boolean }>;\n set(projectId: string, version: number, files: ProjectFileContent[]): Promise<void>;\n}\n\nexport const ProjectFileCache = createAbstraction<IProjectFileCache>(\"ProjectFileCache\");\n\nexport namespace ProjectFileCache {\n export type Interface = IProjectFileCache;\n}\n\n// ============================================================================\n// ProjectFileAssembler\n// ============================================================================\n\nexport interface IProjectFileAssembler {\n execute(\n projectId: string,\n version: number,\n files: Array<{ id: string; name: string; mimeType: string }>,\n warnings: string[]\n ): Promise<{ files: ProjectFileContent[]; cacheHit: boolean }>;\n}\n\nexport const ProjectFileAssembler =\n createAbstraction<IProjectFileAssembler>(\"ProjectFileAssembler\");\n\nexport namespace ProjectFileAssembler {\n export type Interface = IProjectFileAssembler;\n}\n\n// ============================================================================\n// AiPromptContext\n// ============================================================================\n\nexport interface AiPromptContextParams {\n projectId?: string | null;\n readerPersonaId?: string | null;\n writerPersonaId?: string | null;\n excludedFileIds?: string[] | null;\n additionalFileIds?: string[] | null;\n}\n\nexport interface ProjectFileContent {\n id: string;\n name: string;\n content: string;\n description?: string;\n tokenCount: number;\n}\n\nexport interface ResolvedProject {\n name: string;\n instructions?: string;\n files: ProjectFileContent[];\n totalTokens: number;\n}\n\nexport interface ResolvedPersona {\n name: string;\n description: string;\n style?: string;\n}\n\nexport interface AiPromptContext {\n project?: ResolvedProject;\n readerPersona?: ResolvedPersona;\n writerPersona?: ResolvedPersona;\n allProjectFiles: ProjectFileContent[];\n additionalFiles: ProjectFileContent[];\n excludedFileIds: Set<string>;\n cacheHit: boolean;\n warnings: string[];\n toString(): string;\n}\n\nexport interface IAiPromptContextBuilder {\n execute(params: AiPromptContextParams): Promise<AiPromptContext>;\n}\n\nexport const AiPromptContextBuilder = createAbstraction<IAiPromptContextBuilder>(\n \"AiPowerUpsPromptContextBuilder\"\n);\n\nexport namespace AiPromptContextBuilder {\n export type Interface = IAiPromptContextBuilder;\n export type Params = AiPromptContextParams;\n export type Context = AiPromptContext;\n}\n\nexport function formatAdditionalFilesContext(files: ProjectFileContent[]): string {\n if (files.length === 0) {\n return \"\";\n }\n const sections = files.map(f => {\n const desc = f.description ? ` description=\"${f.description}\"` : \"\";\n return `<attached-file name=\"${f.name}\"${desc}>\\n${f.content}\\n</attached-file>`;\n });\n return `\\n\\nThe user attached the following files as additional context for this generation:\\n\\n${sections.join(\"\\n\\n\")}`;\n}\n"],"names":["ProjectFileCache","createAbstraction","ProjectFileAssembler","AiPromptContextBuilder","formatAdditionalFilesContext","files","sections","f","desc"],"mappings":";AAWO,MAAMA,mBAAmBC,kBAAqC;AAmB9D,MAAMC,uBACTD,kBAAyC;AAuDtC,MAAME,yBAAyBF,kBAClC;AASG,SAASG,6BAA6BC,KAA2B;IACpE,IAAIA,AAAiB,MAAjBA,MAAM,MAAM,EACZ,OAAO;IAEX,MAAMC,WAAWD,MAAM,GAAG,CAACE,CAAAA;QACvB,MAAMC,OAAOD,EAAE,WAAW,GAAG,CAAC,cAAc,EAAEA,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG;QACjE,OAAO,CAAC,qBAAqB,EAAEA,EAAE,IAAI,CAAC,CAAC,EAAEC,KAAK,GAAG,EAAED,EAAE,OAAO,CAAC,kBAAkB,CAAC;IACpF;IACA,OAAO,CAAC,wFAAwF,EAAED,SAAS,IAAI,CAAC,SAAS;AAC7H"}
@@ -1 +1 @@
1
- export { AiPromptContextBuilder, type ResolvedProject, type ResolvedPersona, type ProjectFileContent } from "./abstractions.js";
1
+ export { AiPromptContextBuilder, formatAdditionalFilesContext, type ResolvedProject, type ResolvedPersona, type ProjectFileContent } from "./abstractions.js";
@@ -1 +1 @@
1
- export { AiPromptContextBuilder } from "./abstractions.js";
1
+ export { AiPromptContextBuilder, formatAdditionalFilesContext } from "./abstractions.js";
@@ -0,0 +1,22 @@
1
+ import { Ai } from "@webiny/api-core/features/ai/index.js";
2
+ import { Encryption } from "@webiny/api-core/features/encryption/index.js";
3
+ import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js";
4
+ import { ModelToAstConverter } from "@webiny/api-headless-cms/features/contentModel/ModelToAstConverter/index.js";
5
+ import { GetRevisionByIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetRevisionById/index.js";
6
+ import { GetSettingsUseCase } from "../../../api/features/GetSettings/index.js";
7
+ import { CmsCompareEntryRevisionsUseCase } from "./abstractions.js";
8
+ import type { ICmsCompareEntryRevisionsParams, ICmsCompareEntryRevisionsResult } from "./abstractions.js";
9
+ declare class CmsCompareEntryRevisionsUseCaseImpl implements CmsCompareEntryRevisionsUseCase.Interface {
10
+ private getSettings;
11
+ private ai;
12
+ private encryption;
13
+ private getModel;
14
+ private getRevisionById;
15
+ private modelToAst;
16
+ constructor(getSettings: GetSettingsUseCase.Interface, ai: Ai.Interface, encryption: Encryption.Interface, getModel: GetModelUseCase.Interface, getRevisionById: GetRevisionByIdUseCase.Interface, modelToAst: ModelToAstConverter.Interface);
17
+ execute(params: ICmsCompareEntryRevisionsParams): Promise<ICmsCompareEntryRevisionsResult>;
18
+ }
19
+ export declare const CmsCompareEntryRevisionsUseCaseImplementation: typeof CmsCompareEntryRevisionsUseCaseImpl & {
20
+ __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").ICmsCompareEntryRevisionsUseCase>;
21
+ };
22
+ export {};
@@ -0,0 +1,136 @@
1
+ import { Ai } from "@webiny/api-core/features/ai/index.js";
2
+ import { Encryption } from "@webiny/api-core/features/encryption/index.js";
3
+ import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js";
4
+ import { ModelToAstConverter } from "@webiny/api-headless-cms/features/contentModel/ModelToAstConverter/index.js";
5
+ import { CmsModelToJsonSchemaConverter } from "@webiny/api-headless-cms/utils/contentModelToJsonSchema/index.js";
6
+ import { GetRevisionByIdUseCase } from "@webiny/api-headless-cms/features/contentEntry/GetRevisionById/index.js";
7
+ import { GetSettingsUseCase } from "../GetSettings/index.js";
8
+ import { CmsCompareEntryRevisionsUseCase } from "./abstractions.js";
9
+ const SYSTEM_PROMPT = `You are a content intelligence assistant specialized in version comparison for headless CMS platforms.
10
+
11
+ I will provide:
12
+
13
+ 1. The content model JSON Schema that defines the structure of the entry.
14
+ 2. Two versions of the content entry values in JSON format: Version A and Version B.
15
+
16
+ Your task:
17
+
18
+ - Parse both versions according to the JSON Schema.
19
+ - Identify all differences between Version A and Version B.
20
+ - For each difference, explain:
21
+ - The field name (use the human-readable label from the schema description if available)
22
+ - The value in Version A
23
+ - The value in Version B
24
+ - A brief summary of the change (e.g., "Title changed from 'Old' to 'New'")
25
+ - If nested fields or objects exist, perform a deep comparison.
26
+ - Return the comparison in clean HTML format.
27
+
28
+ Output format:
29
+
30
+ <div class="comparison-report">
31
+ <table class="comparison-table">
32
+ <thead>
33
+ <tr>
34
+ <th>Field</th>
35
+ <th>Version A</th>
36
+ <th>Version B</th>
37
+ <th>Change Summary</th>
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ <tr>
42
+ <td><strong>title</strong></td>
43
+ <td>Launch Plan</td>
44
+ <td>Updated Launch Plan</td>
45
+ <td>Title changed from 'Launch Plan' to 'Updated Launch Plan'</td>
46
+ </tr>
47
+ </tbody>
48
+ </table>
49
+ </div>
50
+
51
+ If no differences are found, return: <div class="no-changes"><h3>No differences detected between Version A and Version B.</h3></div>
52
+
53
+ Use semantic HTML with appropriate CSS classes for styling. Do not include <style> tags or CSS — only return the HTML structure.
54
+ For rich text or complex nested values, show a concise summary rather than raw JSON.`;
55
+ class CmsCompareEntryRevisionsUseCaseImpl {
56
+ constructor(getSettings, ai, encryption, getModel, getRevisionById, modelToAst){
57
+ this.getSettings = getSettings;
58
+ this.ai = ai;
59
+ this.encryption = encryption;
60
+ this.getModel = getModel;
61
+ this.getRevisionById = getRevisionById;
62
+ this.modelToAst = modelToAst;
63
+ }
64
+ async execute(params) {
65
+ const { modelId, revisionId1, revisionId2 } = params;
66
+ const settingsResult = await this.getSettings.execute();
67
+ if (settingsResult.isFail()) throw new Error("Failed to load AI Power Ups settings.");
68
+ const settings = settingsResult.value;
69
+ const firstProvider = settings.providers.presets[0];
70
+ if (!firstProvider) throw new Error("No AI provider configured. Add a provider in AI Power Ups settings.");
71
+ const modelResult = await this.getModel.execute(modelId);
72
+ if (modelResult.isFail()) throw new Error(`Content model "${modelId}" not found.`);
73
+ const model = modelResult.value;
74
+ const [rev1Result, rev2Result] = await Promise.all([
75
+ this.getRevisionById.execute(model, revisionId1),
76
+ this.getRevisionById.execute(model, revisionId2)
77
+ ]);
78
+ if (rev1Result.isFail()) throw new Error(`Revision "${revisionId1}" not found.`);
79
+ if (rev2Result.isFail()) throw new Error(`Revision "${revisionId2}" not found.`);
80
+ const revision1 = rev1Result.value;
81
+ const revision2 = rev2Result.value;
82
+ const modelAst = this.modelToAst.toAst(model);
83
+ const jsonSchemaConverter = new CmsModelToJsonSchemaConverter();
84
+ const entrySchema = jsonSchemaConverter.convert(modelAst, {
85
+ name: model.name,
86
+ description: model.description
87
+ });
88
+ const apiKey = await this.encryption.decrypt(firstProvider.apiKeyEncrypted);
89
+ const userPrompt = `CONTENT MODEL JSON SCHEMA:
90
+ ${JSON.stringify(entrySchema, null, 2)}
91
+
92
+ VERSION A (Revision #${revision1.version}):
93
+ ${JSON.stringify(revision1.values, null, 2)}
94
+
95
+ VERSION B (Revision #${revision2.version}):
96
+ ${JSON.stringify(revision2.values, null, 2)}`;
97
+ const result = await this.ai.generateText({
98
+ model: firstProvider.model,
99
+ connection: {
100
+ sdkName: firstProvider.model.split("/")[0],
101
+ apiKey
102
+ },
103
+ system: SYSTEM_PROMPT,
104
+ prompt: userPrompt,
105
+ temperature: 0.3
106
+ });
107
+ const text = result.text;
108
+ const noChangesMatch = text.match(/No differences detected/);
109
+ let summary = "Content comparison completed";
110
+ if (noChangesMatch) summary = "No differences detected between versions";
111
+ else {
112
+ const headingMatch = text.match(/<h[2-3][^>]*>([^<]+)<\/h[2-3]>/);
113
+ const trCount = (text.match(/<tr>/g) || []).length - 1;
114
+ if (headingMatch) summary = headingMatch[1].trim();
115
+ else if (trCount > 0) summary = `${trCount} field${1 === trCount ? "" : "s"} changed`;
116
+ }
117
+ return {
118
+ html: text,
119
+ summary
120
+ };
121
+ }
122
+ }
123
+ const CmsCompareEntryRevisionsUseCaseImplementation = CmsCompareEntryRevisionsUseCase.createImplementation({
124
+ implementation: CmsCompareEntryRevisionsUseCaseImpl,
125
+ dependencies: [
126
+ GetSettingsUseCase,
127
+ Ai,
128
+ Encryption,
129
+ GetModelUseCase,
130
+ GetRevisionByIdUseCase,
131
+ ModelToAstConverter
132
+ ]
133
+ });
134
+ export { CmsCompareEntryRevisionsUseCaseImplementation };
135
+
136
+ //# sourceMappingURL=CmsCompareEntryRevisionsUseCase.js.map