@webiny/ai-powerups 6.4.5-beta.0 → 6.6.0-alpha.0

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 (82) hide show
  1. package/admin/Extension.js +2 -1
  2. package/admin/Extension.js.map +1 -1
  3. package/admin/features/generateEntryContent/GenerateEntryContentGateway.d.ts +12 -0
  4. package/admin/features/generateEntryContent/GenerateEntryContentGateway.js +43 -0
  5. package/admin/features/generateEntryContent/GenerateEntryContentGateway.js.map +1 -0
  6. package/admin/features/generateEntryContent/GenerateEntryContentUseCase.d.ts +11 -0
  7. package/admin/features/generateEntryContent/GenerateEntryContentUseCase.js +18 -0
  8. package/admin/features/generateEntryContent/GenerateEntryContentUseCase.js.map +1 -0
  9. package/admin/features/generateEntryContent/abstractions.d.ts +23 -0
  10. package/admin/features/generateEntryContent/abstractions.js +6 -0
  11. package/admin/features/generateEntryContent/abstractions.js.map +1 -0
  12. package/admin/features/generateEntryContent/feature.d.ts +1 -0
  13. package/admin/features/generateEntryContent/feature.js +13 -0
  14. package/admin/features/generateEntryContent/feature.js.map +1 -0
  15. package/admin/features/generateEntryContent/index.d.ts +3 -0
  16. package/admin/features/generateEntryContent/index.js +2 -0
  17. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPage.js +3 -4
  18. package/admin/presentation/AiPowerUpsSettings/AiPowerUpsSettingsPage.js.map +1 -1
  19. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.d.ts +4 -0
  20. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js +27 -0
  21. package/admin/presentation/CmsContentGeneration/CmsGenerateContentButton.js.map +1 -0
  22. package/admin/presentation/CmsContentGeneration/CmsGenerateContentDialog.d.ts +5 -0
  23. package/admin/presentation/CmsContentGeneration/CmsGenerateContentDialog.js +106 -0
  24. package/admin/presentation/CmsContentGeneration/CmsGenerateContentDialog.js.map +1 -0
  25. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.d.ts +37 -0
  26. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.js +202 -0
  27. package/admin/presentation/CmsContentGeneration/CmsGenerateContentPresenter.js.map +1 -0
  28. package/admin/presentation/CmsContentGeneration/Extension.d.ts +2 -0
  29. package/admin/presentation/CmsContentGeneration/Extension.js +23 -0
  30. package/admin/presentation/CmsContentGeneration/Extension.js.map +1 -0
  31. package/admin/presentation/CmsContentGeneration/abstractions.d.ts +20 -0
  32. package/admin/presentation/CmsContentGeneration/abstractions.js +5 -0
  33. package/admin/presentation/CmsContentGeneration/abstractions.js.map +1 -0
  34. package/admin/presentation/CmsContentGeneration/feature.d.ts +3 -0
  35. package/admin/presentation/CmsContentGeneration/feature.js +17 -0
  36. package/admin/presentation/CmsContentGeneration/feature.js.map +1 -0
  37. package/admin/presentation/ProjectsSettings.js +4 -4
  38. package/admin/presentation/ProjectsSettings.js.map +1 -1
  39. package/admin/presentation/WbContentGeneration/GenerateContentPresenter.js +2 -2
  40. package/admin/presentation/WbContentGeneration/GenerateContentPresenter.js.map +1 -1
  41. package/api/Extension.js +5 -3
  42. package/api/Extension.js.map +1 -1
  43. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.d.ts +3 -1
  44. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js +6 -2
  45. package/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js.map +1 -1
  46. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.d.ts +7 -5
  47. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.js +32 -30
  48. package/api/features/AiImageEnrichment/AiImageEnrichmentTask.js.map +1 -1
  49. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.d.ts +32 -0
  50. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.js +74 -0
  51. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.js.map +1 -0
  52. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.d.ts +29 -0
  53. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js +141 -0
  54. package/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js.map +1 -0
  55. package/api/features/CmsGenerateEntryContent/abstractions.d.ts +29 -0
  56. package/api/features/CmsGenerateEntryContent/abstractions.js +5 -0
  57. package/api/features/CmsGenerateEntryContent/abstractions.js.map +1 -0
  58. package/api/features/CmsGenerateEntryContent/buildPrompt.d.ts +2 -0
  59. package/api/features/CmsGenerateEntryContent/buildPrompt.js +61 -0
  60. package/api/features/CmsGenerateEntryContent/buildPrompt.js.map +1 -0
  61. package/api/features/CmsGenerateEntryContent/feature.d.ts +4 -0
  62. package/api/features/CmsGenerateEntryContent/feature.js +13 -0
  63. package/api/features/CmsGenerateEntryContent/feature.js.map +1 -0
  64. package/api/features/CmsGenerateEntryContent/index.d.ts +1 -0
  65. package/api/features/CmsGenerateEntryContent/index.js +1 -0
  66. package/api/features/CmsGenerateEntryContent/injectDynamicZoneTypenames.d.ts +2 -0
  67. package/api/features/CmsGenerateEntryContent/injectDynamicZoneTypenames.js +30 -0
  68. package/api/features/CmsGenerateEntryContent/injectDynamicZoneTypenames.js.map +1 -0
  69. package/api/features/CmsResolveImageTool/CmsResolveImageTool.d.ts +14 -0
  70. package/api/features/CmsResolveImageTool/CmsResolveImageTool.js +27 -0
  71. package/api/features/CmsResolveImageTool/CmsResolveImageTool.js.map +1 -0
  72. package/api/features/CmsResolveImageTool/feature.d.ts +4 -0
  73. package/api/features/CmsResolveImageTool/feature.js +11 -0
  74. package/api/features/CmsResolveImageTool/feature.js.map +1 -0
  75. package/api/features/GetSettings/GetSettingsUseCase.d.ts +1 -1
  76. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.d.ts +3 -3
  77. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.js +6 -6
  78. package/api/features/WbGeneratePageContent/WbGeneratePageContentTask.js.map +1 -1
  79. package/api/graphql/BaseGraphQLSchema.d.ts +2 -2
  80. package/api/graphql/BaseGraphQLSchema.js +34 -2
  81. package/api/graphql/BaseGraphQLSchema.js.map +1 -1
  82. package/package.json +23 -22
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js","sources":["../../../../src/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.ts"],"sourcesContent":["import { FileAfterCreateEventHandler } from \"@webiny/api-file-manager/features/file/CreateFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport type { IAiImageEnrichmentTaskInput } from \"./AiImageEnrichmentTask.js\";\nimport { AI_IMAGE_ENRICHMENT_TASK_ID } from \"./AiImageEnrichmentTask.js\";\n\nclass AiImageEnrichmentAfterCreateHandlerImpl implements FileAfterCreateEventHandler.Interface {\n constructor(private taskService: TaskService.Interface) {}\n\n async handle(event: FileAfterCreateEventHandler.Event): Promise<void> {\n const { file } = event.payload;\n\n if (!file.type.startsWith(\"image/\")) {\n return;\n }\n\n await this.taskService.trigger<IAiImageEnrichmentTaskInput>({\n definition: AI_IMAGE_ENRICHMENT_TASK_ID,\n input: {\n fileId: file.id\n }\n });\n }\n}\n\nexport const AiImageEnrichmentAfterCreateHandler = FileAfterCreateEventHandler.createImplementation(\n {\n implementation: AiImageEnrichmentAfterCreateHandlerImpl,\n dependencies: [TaskService]\n }\n);\n"],"names":["AiImageEnrichmentAfterCreateHandlerImpl","taskService","event","file","AI_IMAGE_ENRICHMENT_TASK_ID","AiImageEnrichmentAfterCreateHandler","FileAfterCreateEventHandler","TaskService"],"mappings":";;;AAKA,MAAMA;IACF,YAAoBC,WAAkC,CAAE;aAApCA,WAAW,GAAXA;IAAqC;IAEzD,MAAM,OAAOC,KAAwC,EAAiB;QAClE,MAAM,EAAEC,IAAI,EAAE,GAAGD,MAAM,OAAO;QAE9B,IAAI,CAACC,KAAK,IAAI,CAAC,UAAU,CAAC,WACtB;QAGJ,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAA8B;YACxD,YAAYC;YACZ,OAAO;gBACH,QAAQD,KAAK,EAAE;YACnB;QACJ;IACJ;AACJ;AAEO,MAAME,sCAAsCC,4BAA4B,oBAAoB,CAC/F;IACI,gBAAgBN;IAChB,cAAc;QAACO;KAAY;AAC/B"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.js","sources":["../../../../src/api/features/AiImageEnrichment/AiImageEnrichmentAfterCreateHandler.ts"],"sourcesContent":["import { FileAfterCreateEventHandler } from \"@webiny/api-file-manager/features/file/CreateFile/events.js\";\nimport { TaskService } from \"@webiny/api-core/features/task/TaskService/index.js\";\nimport { WcpContext } from \"@webiny/api-core/features/wcp/WcpContext/index.js\";\nimport type { IAiImageEnrichmentTaskInput } from \"./AiImageEnrichmentTask.js\";\nimport { AI_IMAGE_ENRICHMENT_TASK_ID } from \"./AiImageEnrichmentTask.js\";\n\nclass AiImageEnrichmentAfterCreateHandlerImpl implements FileAfterCreateEventHandler.Interface {\n constructor(\n private taskService: TaskService.Interface,\n private wcp: WcpContext.Interface\n ) {}\n\n async handle(event: FileAfterCreateEventHandler.Event): Promise<void> {\n const { file } = event.payload;\n\n if (!file.type.startsWith(\"image/\")) {\n return;\n }\n\n // Gate at trigger-time, NOT at feature-registration time: the WCP license is loaded per\n // request by WcpLicenseInitializer (a RequestInitializer), which runs AFTER the register()\n // phase. A register-time check reads the placeholder NullLicense (canUse* → false), so the\n // feature would never register. handle() runs during a resolver, once the license is loaded.\n if (!this.wcp.canUseAiImageEnrichment()) {\n return;\n }\n\n await this.taskService.trigger<IAiImageEnrichmentTaskInput>({\n definition: AI_IMAGE_ENRICHMENT_TASK_ID,\n input: {\n fileId: file.id\n }\n });\n }\n}\n\nexport const AiImageEnrichmentAfterCreateHandler = FileAfterCreateEventHandler.createImplementation(\n {\n implementation: AiImageEnrichmentAfterCreateHandlerImpl,\n dependencies: [TaskService, WcpContext]\n }\n);\n"],"names":["AiImageEnrichmentAfterCreateHandlerImpl","taskService","wcp","event","file","AI_IMAGE_ENRICHMENT_TASK_ID","AiImageEnrichmentAfterCreateHandler","FileAfterCreateEventHandler","TaskService","WcpContext"],"mappings":";;;;AAMA,MAAMA;IACF,YACYC,WAAkC,EAClCC,GAAyB,CACnC;aAFUD,WAAW,GAAXA;aACAC,GAAG,GAAHA;IACT;IAEH,MAAM,OAAOC,KAAwC,EAAiB;QAClE,MAAM,EAAEC,IAAI,EAAE,GAAGD,MAAM,OAAO;QAE9B,IAAI,CAACC,KAAK,IAAI,CAAC,UAAU,CAAC,WACtB;QAOJ,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,uBAAuB,IACjC;QAGJ,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAA8B;YACxD,YAAYC;YACZ,OAAO;gBACH,QAAQD,KAAK,EAAE;YACnB;QACJ;IACJ;AACJ;AAEO,MAAME,sCAAsCC,4BAA4B,oBAAoB,CAC/F;IACI,gBAAgBP;IAChB,cAAc;QAACQ;QAAaC;KAAW;AAC3C"}
@@ -2,9 +2,10 @@ import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/in
2
2
  import { Ai } from "@webiny/api-core/features/ai/index.js";
3
3
  import { Encryption } from "@webiny/api-core/features/encryption/index.js";
4
4
  import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
5
+ import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js";
5
6
  import { UpdateFileUseCase } from "@webiny/api-file-manager/features/file/UpdateFile/index.js";
6
- import { GetSettingsUseCase as FmGetSettingsUseCase } from "@webiny/api-file-manager/features/settings/GetSettings/abstractions.js";
7
- import { WebsocketService } from "@webiny/api-websockets/features/WebsocketService/index.js";
7
+ import { WebsocketsSendToIdentityUseCase } from "@webiny/api-websockets/features/SendToIdentity/abstractions.js";
8
+ import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
8
9
  import { GetSettingsUseCase } from "../../../api/features/GetSettings/index.js";
9
10
  export declare const AI_IMAGE_ENRICHMENT_TASK_ID = "fmAiImageEnrichment";
10
11
  export interface IAiImageEnrichmentTaskInput {
@@ -12,12 +13,13 @@ export interface IAiImageEnrichmentTaskInput {
12
13
  }
13
14
  declare class AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiImageEnrichmentTaskInput> {
14
15
  private getFile;
15
- private fmSettings;
16
+ private getFileContents;
16
17
  private updateFile;
17
18
  private ai;
18
19
  private getSettings;
19
20
  private encryption;
20
- private websocketService?;
21
+ private identityContext;
22
+ private sendToIdentity;
21
23
  id: string;
22
24
  title: string;
23
25
  description: string;
@@ -25,7 +27,7 @@ declare class AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiI
25
27
  isPrivate: boolean;
26
28
  databaseLogs: boolean;
27
29
  readonly selfCleanup: ("onAbort" | "onSuccess")[];
28
- constructor(getFile: GetFileUseCase.Interface, fmSettings: FmGetSettingsUseCase.Interface, updateFile: UpdateFileUseCase.Interface, ai: Ai.Interface, getSettings: GetSettingsUseCase.Interface, encryption: Encryption.Interface, websocketService?: WebsocketService.Interface | undefined);
30
+ constructor(getFile: GetFileUseCase.Interface, getFileContents: GetFileContentsByIdUseCase.Interface, updateFile: UpdateFileUseCase.Interface, ai: Ai.Interface, getSettings: GetSettingsUseCase.Interface, encryption: Encryption.Interface, identityContext: IdentityContext.Interface, sendToIdentity: WebsocketsSendToIdentityUseCase.Interface);
29
31
  run({ input, controller }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<TaskDefinition.Result<IAiImageEnrichmentTaskInput>>;
30
32
  }
31
33
  export declare const AiImageEnrichmentTask: typeof AiImageEnrichmentTaskImpl & {
@@ -4,10 +4,11 @@ import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/in
4
4
  import { Ai } from "@webiny/api-core/features/ai/index.js";
5
5
  import { Encryption } from "@webiny/api-core/features/encryption/index.js";
6
6
  import { GetFileUseCase } from "@webiny/api-file-manager/features/file/GetFile/index.js";
7
+ import { GetFileContentsByIdUseCase } from "@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js";
7
8
  import { UpdateFileUseCase } from "@webiny/api-file-manager/features/file/UpdateFile/index.js";
8
- import { GetSettingsUseCase } from "@webiny/api-file-manager/features/settings/GetSettings/abstractions.js";
9
- import { WebsocketService } from "@webiny/api-websockets/features/WebsocketService/index.js";
10
- import { GetSettingsUseCase as index_js_GetSettingsUseCase } from "../GetSettings/index.js";
9
+ import { WebsocketsSendToIdentityUseCase } from "@webiny/api-websockets/features/SendToIdentity/abstractions.js";
10
+ import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
11
+ import { GetSettingsUseCase } from "../GetSettings/index.js";
11
12
  const AI_IMAGE_ENRICHMENT_TASK_ID = "fmAiImageEnrichment";
12
13
  const AI_PROMPT = "Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.";
13
14
  const aiOutputSchema = Output.object({
@@ -17,14 +18,15 @@ const aiOutputSchema = Output.object({
17
18
  })
18
19
  });
19
20
  class AiImageEnrichmentTaskImpl {
20
- constructor(getFile, fmSettings, updateFile, ai, getSettings, encryption, websocketService){
21
+ constructor(getFile, getFileContents, updateFile, ai, getSettings, encryption, identityContext, sendToIdentity){
21
22
  this.getFile = getFile;
22
- this.fmSettings = fmSettings;
23
+ this.getFileContents = getFileContents;
23
24
  this.updateFile = updateFile;
24
25
  this.ai = ai;
25
26
  this.getSettings = getSettings;
26
27
  this.encryption = encryption;
27
- this.websocketService = websocketService;
28
+ this.identityContext = identityContext;
29
+ this.sendToIdentity = sendToIdentity;
28
30
  this.id = AI_IMAGE_ENRICHMENT_TASK_ID;
29
31
  this.title = "File Manager - AI Image Enrichment";
30
32
  this.description = "Automatically enriches uploaded images with AI-generated tags and description.";
@@ -44,9 +46,12 @@ class AiImageEnrichmentTaskImpl {
44
46
  });
45
47
  const file = fileResult.value;
46
48
  if (!file.type.startsWith("image/")) return controller.response.done("File is not an image; skipping AI enrichment.");
47
- const settingsResult = await this.fmSettings.execute();
48
- const srcPrefix = settingsResult.isOk() ? settingsResult.value.srcPrefix ?? "" : "";
49
- const imageUrl = `${srcPrefix}${file.key}`;
49
+ const contentsResult = await this.getFileContents.execute(input.fileId);
50
+ if (contentsResult.isFail()) return controller.response.error({
51
+ message: `Unable to read file contents: ${contentsResult.error.message}`
52
+ });
53
+ const imageBase64 = contentsResult.value.buffer.toString("base64");
54
+ const imageMediaType = contentsResult.value.contentType;
50
55
  const aiSettingsResult = await this.getSettings.execute();
51
56
  if (aiSettingsResult.isFail()) return controller.response.error({
52
57
  message: "No AI provider configured. Add a provider in AI Power Ups settings."
@@ -71,8 +76,9 @@ class AiImageEnrichmentTaskImpl {
71
76
  role: "user",
72
77
  content: [
73
78
  {
74
- type: "image",
75
- image: new URL(imageUrl)
79
+ type: "file",
80
+ data: imageBase64,
81
+ mediaType: imageMediaType
76
82
  },
77
83
  {
78
84
  type: "text",
@@ -104,17 +110,17 @@ class AiImageEnrichmentTaskImpl {
104
110
  if (updateResult.isFail()) return controller.response.error({
105
111
  message: `Failed to update file: ${updateResult.error.message}`
106
112
  });
107
- if (this.websocketService) {
108
- const connectionsResult = await this.websocketService.listConnections();
109
- if (connectionsResult.isOk() && connectionsResult.value.length > 0) await this.websocketService.sendToConnections(connectionsResult.value, {
110
- action: "fm.file.enrichment",
111
- data: {
112
- id: file.id,
113
- tags: mergedTags,
114
- description
115
- }
116
- });
117
- }
113
+ const identity = this.identityContext.getIdentity();
114
+ await this.sendToIdentity.execute({
115
+ id: identity.id
116
+ }, {
117
+ action: "fm.file.enrichment",
118
+ data: {
119
+ id: file.id,
120
+ tags: mergedTags,
121
+ description
122
+ }
123
+ });
118
124
  return controller.response.done("AI image enrichment completed successfully.");
119
125
  }
120
126
  }
@@ -122,17 +128,13 @@ const AiImageEnrichmentTask = TaskDefinition.createImplementation({
122
128
  implementation: AiImageEnrichmentTaskImpl,
123
129
  dependencies: [
124
130
  GetFileUseCase,
125
- GetSettingsUseCase,
131
+ GetFileContentsByIdUseCase,
126
132
  UpdateFileUseCase,
127
133
  Ai,
128
- index_js_GetSettingsUseCase,
134
+ GetSettingsUseCase,
129
135
  Encryption,
130
- [
131
- WebsocketService,
132
- {
133
- optional: true
134
- }
135
- ]
136
+ IdentityContext,
137
+ WebsocketsSendToIdentityUseCase
136
138
  ]
137
139
  });
138
140
  export { AI_IMAGE_ENRICHMENT_TASK_ID, AiImageEnrichmentTask };
@@ -1 +1 @@
1
- {"version":3,"file":"api/features/AiImageEnrichment/AiImageEnrichmentTask.js","sources":["../../../../src/api/features/AiImageEnrichment/AiImageEnrichmentTask.ts"],"sourcesContent":["import { Output } from \"ai\";\nimport { z } from \"zod\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { Encryption } from \"@webiny/api-core/features/encryption/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { GetSettingsUseCase as FmGetSettingsUseCase } from \"@webiny/api-file-manager/features/settings/GetSettings/abstractions.js\";\nimport { WebsocketService } from \"@webiny/api-websockets/features/WebsocketService/index.js\";\nimport { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\n\nexport const AI_IMAGE_ENRICHMENT_TASK_ID = \"fmAiImageEnrichment\";\n\nconst AI_PROMPT =\n \"Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.\";\n\nconst aiOutputSchema = Output.object({\n schema: z.object({\n tags: z.array(z.string()),\n description: z.string()\n })\n});\n\nexport interface IAiImageEnrichmentTaskInput {\n fileId: string;\n}\n\nclass AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiImageEnrichmentTaskInput> {\n id = AI_IMAGE_ENRICHMENT_TASK_ID;\n title = \"File Manager - AI Image Enrichment\";\n description = \"Automatically enriches uploaded images with AI-generated tags and description.\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n constructor(\n private getFile: GetFileUseCase.Interface,\n private fmSettings: FmGetSettingsUseCase.Interface,\n private updateFile: UpdateFileUseCase.Interface,\n private ai: Ai.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private encryption: Encryption.Interface,\n private websocketService?: WebsocketService.Interface\n ) {}\n\n async run({\n input,\n controller\n }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<\n TaskDefinition.Result<IAiImageEnrichmentTaskInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const fileResult = await this.getFile.execute(input.fileId);\n if (fileResult.isFail()) {\n return controller.response.error({\n message: `File not found: ${input.fileId}`\n });\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return controller.response.done(\"File is not an image; skipping AI enrichment.\");\n }\n\n const settingsResult = await this.fmSettings.execute();\n const srcPrefix = settingsResult.isOk() ? (settingsResult.value.srcPrefix ?? \"\") : \"\";\n const imageUrl = `${srcPrefix}${file.key}`;\n\n const aiSettingsResult = await this.getSettings.execute();\n\n if (aiSettingsResult.isFail()) {\n return controller.response.error({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n const aiSettings = aiSettingsResult.value;\n\n const firstProvider = aiSettings.providers.presets[0];\n\n if (!firstProvider) {\n return controller.response.done({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n let tags: string[] = [];\n let description = \"\";\n try {\n const aiResult = await this.ai.generateText({\n model: firstProvider.model,\n output: aiOutputSchema,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey: await this.encryption.decrypt(firstProvider.apiKeyEncrypted)\n },\n messages: [\n {\n role: \"user\",\n content: [\n {\n type: \"image\",\n image: new URL(imageUrl)\n },\n {\n type: \"text\",\n text: AI_PROMPT\n }\n ]\n }\n ]\n });\n\n tags = aiResult.output.tags;\n description = aiResult.output.description;\n } catch (error) {\n console.log(\"error\", error.message);\n return controller.response.error({\n message: `AI enrichment failed: ${error instanceof Error ? error.message : String(error)}`\n });\n }\n\n const mergedTags = [...new Set([...file.tags, ...tags])];\n\n const updateResult = await this.updateFile.execute({\n id: file.id,\n tags: mergedTags,\n description\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file: ${updateResult.error.message}`\n });\n }\n\n if (this.websocketService) {\n const connectionsResult = await this.websocketService.listConnections();\n if (connectionsResult.isOk() && connectionsResult.value.length > 0) {\n await this.websocketService.sendToConnections(connectionsResult.value, {\n action: \"fm.file.enrichment\",\n data: {\n id: file.id,\n tags: mergedTags,\n description\n }\n });\n }\n }\n\n return controller.response.done(\"AI image enrichment completed successfully.\");\n }\n}\n\nexport const AiImageEnrichmentTask = TaskDefinition.createImplementation({\n implementation: AiImageEnrichmentTaskImpl,\n dependencies: [\n GetFileUseCase,\n FmGetSettingsUseCase,\n UpdateFileUseCase,\n Ai,\n GetSettingsUseCase,\n Encryption,\n [WebsocketService, { optional: true }]\n ]\n});\n"],"names":["AI_IMAGE_ENRICHMENT_TASK_ID","AI_PROMPT","aiOutputSchema","Output","z","AiImageEnrichmentTaskImpl","getFile","fmSettings","updateFile","ai","getSettings","encryption","websocketService","input","controller","fileResult","file","settingsResult","srcPrefix","imageUrl","aiSettingsResult","aiSettings","firstProvider","tags","description","aiResult","URL","error","console","Error","String","mergedTags","Set","updateResult","connectionsResult","AiImageEnrichmentTask","TaskDefinition","GetFileUseCase","FmGetSettingsUseCase","UpdateFileUseCase","Ai","GetSettingsUseCase","Encryption","WebsocketService"],"mappings":"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"}
1
+ {"version":3,"file":"api/features/AiImageEnrichment/AiImageEnrichmentTask.js","sources":["../../../../src/api/features/AiImageEnrichment/AiImageEnrichmentTask.ts"],"sourcesContent":["import { Output } from \"ai\";\nimport { z } from \"zod\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { Encryption } from \"@webiny/api-core/features/encryption/index.js\";\nimport { GetFileUseCase } from \"@webiny/api-file-manager/features/file/GetFile/index.js\";\nimport { GetFileContentsByIdUseCase } from \"@webiny/api-file-manager/features/file/GetFileContentsById/abstractions.js\";\nimport { UpdateFileUseCase } from \"@webiny/api-file-manager/features/file/UpdateFile/index.js\";\nimport { WebsocketsSendToIdentityUseCase } from \"@webiny/api-websockets/features/SendToIdentity/abstractions.js\";\nimport { IdentityContext } from \"@webiny/api-core/exports/api/security.js\";\nimport { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\n\nexport const AI_IMAGE_ENRICHMENT_TASK_ID = \"fmAiImageEnrichment\";\n\nconst AI_PROMPT =\n \"Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.\";\n\nconst aiOutputSchema = Output.object({\n schema: z.object({\n tags: z.array(z.string()),\n description: z.string()\n })\n});\n\nexport interface IAiImageEnrichmentTaskInput {\n fileId: string;\n}\n\nclass AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiImageEnrichmentTaskInput> {\n id = AI_IMAGE_ENRICHMENT_TASK_ID;\n title = \"File Manager - AI Image Enrichment\";\n description = \"Automatically enriches uploaded images with AI-generated tags and description.\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n constructor(\n private getFile: GetFileUseCase.Interface,\n private getFileContents: GetFileContentsByIdUseCase.Interface,\n private updateFile: UpdateFileUseCase.Interface,\n private ai: Ai.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private encryption: Encryption.Interface,\n private identityContext: IdentityContext.Interface,\n private sendToIdentity: WebsocketsSendToIdentityUseCase.Interface\n ) {}\n\n async run({\n input,\n controller\n }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<\n TaskDefinition.Result<IAiImageEnrichmentTaskInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const fileResult = await this.getFile.execute(input.fileId);\n if (fileResult.isFail()) {\n return controller.response.error({\n message: `File not found: ${input.fileId}`\n });\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return controller.response.done(\"File is not an image; skipping AI enrichment.\");\n }\n\n // Read the image bytes and send them to the AI as base64, NOT a URL. A URL forces the\n // provider to fetch the file — which fails for private/access-controlled files, leaks the\n // domain, and can't reach a non-public origin (e.g. local dev). base64 is a portable standard\n // that works on every setup. (Also resolves the AI-SDK \"image part\" deprecation.)\n const contentsResult = await this.getFileContents.execute(input.fileId);\n if (contentsResult.isFail()) {\n return controller.response.error({\n message: `Unable to read file contents: ${contentsResult.error.message}`\n });\n }\n const imageBase64 = contentsResult.value.buffer.toString(\"base64\");\n const imageMediaType = contentsResult.value.contentType;\n\n const aiSettingsResult = await this.getSettings.execute();\n\n if (aiSettingsResult.isFail()) {\n return controller.response.error({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n const aiSettings = aiSettingsResult.value;\n\n const firstProvider = aiSettings.providers.presets[0];\n\n if (!firstProvider) {\n return controller.response.done({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n let tags: string[] = [];\n let description = \"\";\n try {\n const aiResult = await this.ai.generateText({\n model: firstProvider.model,\n output: aiOutputSchema,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey: await this.encryption.decrypt(firstProvider.apiKeyEncrypted)\n },\n messages: [\n {\n role: \"user\",\n content: [\n {\n type: \"file\",\n data: imageBase64,\n mediaType: imageMediaType\n },\n {\n type: \"text\",\n text: AI_PROMPT\n }\n ]\n }\n ]\n });\n\n tags = aiResult.output.tags;\n description = aiResult.output.description;\n } catch (error) {\n console.log(\"error\", error.message);\n return controller.response.error({\n message: `AI enrichment failed: ${error instanceof Error ? error.message : String(error)}`\n });\n }\n\n const mergedTags = [...new Set([...file.tags, ...tags])];\n\n const updateResult = await this.updateFile.execute({\n id: file.id,\n tags: mergedTags,\n description\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file: ${updateResult.error.message}`\n });\n }\n\n const identity = this.identityContext.getIdentity();\n await this.sendToIdentity.execute(\n { id: identity.id },\n {\n action: \"fm.file.enrichment\",\n data: {\n id: file.id,\n tags: mergedTags,\n description\n }\n }\n );\n\n return controller.response.done(\"AI image enrichment completed successfully.\");\n }\n}\n\nexport const AiImageEnrichmentTask = TaskDefinition.createImplementation({\n implementation: AiImageEnrichmentTaskImpl,\n dependencies: [\n GetFileUseCase,\n GetFileContentsByIdUseCase,\n UpdateFileUseCase,\n Ai,\n GetSettingsUseCase,\n Encryption,\n IdentityContext,\n WebsocketsSendToIdentityUseCase\n ]\n});\n"],"names":["AI_IMAGE_ENRICHMENT_TASK_ID","AI_PROMPT","aiOutputSchema","Output","z","AiImageEnrichmentTaskImpl","getFile","getFileContents","updateFile","ai","getSettings","encryption","identityContext","sendToIdentity","input","controller","fileResult","file","contentsResult","imageBase64","imageMediaType","aiSettingsResult","aiSettings","firstProvider","tags","description","aiResult","error","console","Error","String","mergedTags","Set","updateResult","identity","AiImageEnrichmentTask","TaskDefinition","GetFileUseCase","GetFileContentsByIdUseCase","UpdateFileUseCase","Ai","GetSettingsUseCase","Encryption","IdentityContext","WebsocketsSendToIdentityUseCase"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
+ import { WebsocketsSendToIdentityUseCase } from "@webiny/api-websockets/features/SendToIdentity/abstractions.js";
3
+ import { CmsGenerateEntryContentUseCase } from "../../../api/features/CmsGenerateEntryContent/index.js";
4
+ import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
5
+ export declare const CMS_GENERATE_ENTRY_CONTENT_TASK_ID = "aiPowerUpsGenerateEntryContent";
6
+ export interface ICmsGenerateEntryContentTaskInput {
7
+ prompt: string;
8
+ modelId: string;
9
+ projectId?: string | null;
10
+ excludedFileIds?: string[] | null;
11
+ readerPersonaId?: string | null;
12
+ writerPersonaId?: string | null;
13
+ }
14
+ declare class CmsGenerateEntryContentTaskImpl implements TaskDefinition.Interface<ICmsGenerateEntryContentTaskInput> {
15
+ private identityContext;
16
+ private generateEntryContent;
17
+ private sendToIdentity;
18
+ id: string;
19
+ title: string;
20
+ description: string;
21
+ maxIterations: number;
22
+ isPrivate: boolean;
23
+ databaseLogs: boolean;
24
+ constructor(identityContext: IdentityContext.Interface, generateEntryContent: CmsGenerateEntryContentUseCase.Interface, sendToIdentity: WebsocketsSendToIdentityUseCase.Interface);
25
+ run({ input, controller }: TaskDefinition.RunParams<ICmsGenerateEntryContentTaskInput>): Promise<TaskDefinition.Result<ICmsGenerateEntryContentTaskInput>>;
26
+ private sendContentToUser;
27
+ private sendErrorToUser;
28
+ }
29
+ export declare const CmsGenerateEntryContentTask: typeof CmsGenerateEntryContentTaskImpl & {
30
+ __abstraction: import("@webiny/di").Abstraction<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskDefinition<import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskInput, import("@webiny/api-core/features/task/TaskDefinition/abstractions").ITaskOutput>>;
31
+ };
32
+ export {};
@@ -0,0 +1,74 @@
1
+ import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
2
+ import { WebsocketsSendToIdentityUseCase } from "@webiny/api-websockets/features/SendToIdentity/abstractions.js";
3
+ import { compress } from "@webiny/utils/features/compression/legacy/gzip.js";
4
+ import { CmsGenerateEntryContentUseCase } from "./index.js";
5
+ import { IdentityContext } from "@webiny/api-core/exports/api/security.js";
6
+ const CMS_GENERATE_ENTRY_CONTENT_TASK_ID = "aiPowerUpsGenerateEntryContent";
7
+ class CmsGenerateEntryContentTaskImpl {
8
+ constructor(identityContext, generateEntryContent, sendToIdentity){
9
+ this.identityContext = identityContext;
10
+ this.generateEntryContent = generateEntryContent;
11
+ this.sendToIdentity = sendToIdentity;
12
+ this.id = CMS_GENERATE_ENTRY_CONTENT_TASK_ID;
13
+ this.title = "AI Power-Ups - Generate Entry Content";
14
+ this.description = "Generates CMS entry content using AI based on a user prompt.";
15
+ this.maxIterations = 1;
16
+ this.isPrivate = true;
17
+ this.databaseLogs = false;
18
+ }
19
+ async run({ input, controller }) {
20
+ if (controller.runtime.isAborted()) return controller.response.aborted();
21
+ const result = await this.generateEntryContent.execute({
22
+ prompt: input.prompt,
23
+ modelId: input.modelId,
24
+ projectId: input.projectId,
25
+ excludedFileIds: input.excludedFileIds,
26
+ readerPersonaId: input.readerPersonaId,
27
+ writerPersonaId: input.writerPersonaId
28
+ });
29
+ const identity = this.identityContext.getIdentity();
30
+ if (result.isFail()) {
31
+ await this.sendErrorToUser(identity.id, result.error.message);
32
+ return controller.response.error({
33
+ message: result.error.message
34
+ });
35
+ }
36
+ const compressed = await compress(result.value.output);
37
+ const payload = compressed.toString("base64");
38
+ await this.sendContentToUser(identity.id, payload, result.value.telemetry);
39
+ return controller.response.done("Entry content generated successfully.");
40
+ }
41
+ async sendContentToUser(identityId, payload, telemetry) {
42
+ await this.sendToIdentity.execute({
43
+ id: identityId
44
+ }, {
45
+ action: "aiPowerUps.generateEntryContent.content",
46
+ data: {
47
+ compression: "gzip",
48
+ value: payload,
49
+ telemetry
50
+ }
51
+ });
52
+ }
53
+ async sendErrorToUser(identityId, message) {
54
+ await this.sendToIdentity.execute({
55
+ id: identityId
56
+ }, {
57
+ action: "aiPowerUps.generateEntryContent.error",
58
+ data: {
59
+ message
60
+ }
61
+ });
62
+ }
63
+ }
64
+ const CmsGenerateEntryContentTask = TaskDefinition.createImplementation({
65
+ implementation: CmsGenerateEntryContentTaskImpl,
66
+ dependencies: [
67
+ IdentityContext,
68
+ CmsGenerateEntryContentUseCase,
69
+ WebsocketsSendToIdentityUseCase
70
+ ]
71
+ });
72
+ export { CMS_GENERATE_ENTRY_CONTENT_TASK_ID, CmsGenerateEntryContentTask };
73
+
74
+ //# sourceMappingURL=CmsGenerateEntryContentTask.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.js","sources":["../../../../src/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentTask.ts"],"sourcesContent":["import { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { WebsocketsSendToIdentityUseCase } from \"@webiny/api-websockets/features/SendToIdentity/abstractions.js\";\nimport { compress } from \"@webiny/utils/features/compression/legacy/gzip.js\";\nimport { CmsGenerateEntryContentUseCase } from \"~/api/features/CmsGenerateEntryContent/index.js\";\nimport type { GenerateEntryContentTelemetry } from \"~/api/features/CmsGenerateEntryContent/abstractions.js\";\nimport { IdentityContext } from \"@webiny/api-core/exports/api/security.js\";\n\nexport const CMS_GENERATE_ENTRY_CONTENT_TASK_ID = \"aiPowerUpsGenerateEntryContent\";\n\nexport interface ICmsGenerateEntryContentTaskInput {\n prompt: string;\n modelId: string;\n projectId?: string | null;\n excludedFileIds?: string[] | null;\n readerPersonaId?: string | null;\n writerPersonaId?: string | null;\n}\n\nclass CmsGenerateEntryContentTaskImpl implements TaskDefinition.Interface<ICmsGenerateEntryContentTaskInput> {\n id = CMS_GENERATE_ENTRY_CONTENT_TASK_ID;\n title = \"AI Power-Ups - Generate Entry Content\";\n description = \"Generates CMS entry content using AI based on a user prompt.\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n constructor(\n private identityContext: IdentityContext.Interface,\n private generateEntryContent: CmsGenerateEntryContentUseCase.Interface,\n private sendToIdentity: WebsocketsSendToIdentityUseCase.Interface\n ) {}\n\n async run({\n input,\n controller\n }: TaskDefinition.RunParams<ICmsGenerateEntryContentTaskInput>): Promise<\n TaskDefinition.Result<ICmsGenerateEntryContentTaskInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const result = await this.generateEntryContent.execute({\n prompt: input.prompt,\n modelId: input.modelId,\n projectId: input.projectId,\n excludedFileIds: input.excludedFileIds,\n readerPersonaId: input.readerPersonaId,\n writerPersonaId: input.writerPersonaId\n });\n\n const identity = this.identityContext.getIdentity();\n\n if (result.isFail()) {\n await this.sendErrorToUser(identity.id, result.error.message);\n\n return controller.response.error({\n message: result.error.message\n });\n }\n\n const compressed = await compress(result.value.output);\n const payload = compressed.toString(\"base64\");\n\n await this.sendContentToUser(identity.id, payload, result.value.telemetry);\n\n return controller.response.done(\"Entry content generated successfully.\");\n }\n\n private async sendContentToUser(\n identityId: string,\n payload: string,\n telemetry: GenerateEntryContentTelemetry\n ) {\n await this.sendToIdentity.execute(\n { id: identityId },\n {\n action: \"aiPowerUps.generateEntryContent.content\",\n data: {\n compression: \"gzip\",\n value: payload,\n telemetry\n }\n }\n );\n }\n\n private async sendErrorToUser(identityId: string, message: string) {\n await this.sendToIdentity.execute(\n { id: identityId },\n {\n action: \"aiPowerUps.generateEntryContent.error\",\n data: {\n message\n }\n }\n );\n }\n}\n\nexport const CmsGenerateEntryContentTask = TaskDefinition.createImplementation({\n implementation: CmsGenerateEntryContentTaskImpl,\n dependencies: [IdentityContext, CmsGenerateEntryContentUseCase, WebsocketsSendToIdentityUseCase]\n});\n"],"names":["CMS_GENERATE_ENTRY_CONTENT_TASK_ID","CmsGenerateEntryContentTaskImpl","identityContext","generateEntryContent","sendToIdentity","input","controller","result","identity","compressed","compress","payload","identityId","telemetry","message","CmsGenerateEntryContentTask","TaskDefinition","IdentityContext","CmsGenerateEntryContentUseCase","WebsocketsSendToIdentityUseCase"],"mappings":";;;;;AAOO,MAAMA,qCAAqC;AAWlD,MAAMC;IAQF,YACYC,eAA0C,EAC1CC,oBAA8D,EAC9DC,cAAyD,CACnE;aAHUF,eAAe,GAAfA;aACAC,oBAAoB,GAApBA;aACAC,cAAc,GAAdA;aAVZ,EAAE,GAAGJ;aACL,KAAK,GAAG;aACR,WAAW,GAAG;aACd,aAAa,GAAG;aAChB,SAAS,GAAG;aACZ,YAAY,GAAG;IAMZ;IAEH,MAAM,IAAI,EACNK,KAAK,EACLC,UAAU,EACgD,EAE5D;QACE,IAAIA,WAAW,OAAO,CAAC,SAAS,IAC5B,OAAOA,WAAW,QAAQ,CAAC,OAAO;QAGtC,MAAMC,SAAS,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC;YACnD,QAAQF,MAAM,MAAM;YACpB,SAASA,MAAM,OAAO;YACtB,WAAWA,MAAM,SAAS;YAC1B,iBAAiBA,MAAM,eAAe;YACtC,iBAAiBA,MAAM,eAAe;YACtC,iBAAiBA,MAAM,eAAe;QAC1C;QAEA,MAAMG,WAAW,IAAI,CAAC,eAAe,CAAC,WAAW;QAEjD,IAAID,OAAO,MAAM,IAAI;YACjB,MAAM,IAAI,CAAC,eAAe,CAACC,SAAS,EAAE,EAAED,OAAO,KAAK,CAAC,OAAO;YAE5D,OAAOD,WAAW,QAAQ,CAAC,KAAK,CAAC;gBAC7B,SAASC,OAAO,KAAK,CAAC,OAAO;YACjC;QACJ;QAEA,MAAME,aAAa,MAAMC,SAASH,OAAO,KAAK,CAAC,MAAM;QACrD,MAAMI,UAAUF,WAAW,QAAQ,CAAC;QAEpC,MAAM,IAAI,CAAC,iBAAiB,CAACD,SAAS,EAAE,EAAEG,SAASJ,OAAO,KAAK,CAAC,SAAS;QAEzE,OAAOD,WAAW,QAAQ,CAAC,IAAI,CAAC;IACpC;IAEA,MAAc,kBACVM,UAAkB,EAClBD,OAAe,EACfE,SAAwC,EAC1C;QACE,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B;YAAE,IAAID;QAAW,GACjB;YACI,QAAQ;YACR,MAAM;gBACF,aAAa;gBACb,OAAOD;gBACPE;YACJ;QACJ;IAER;IAEA,MAAc,gBAAgBD,UAAkB,EAAEE,OAAe,EAAE;QAC/D,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B;YAAE,IAAIF;QAAW,GACjB;YACI,QAAQ;YACR,MAAM;gBACFE;YACJ;QACJ;IAER;AACJ;AAEO,MAAMC,8BAA8BC,eAAe,oBAAoB,CAAC;IAC3E,gBAAgBf;IAChB,cAAc;QAACgB;QAAiBC;QAAgCC;KAAgC;AACpG"}
@@ -0,0 +1,29 @@
1
+ import { Result } from "@webiny/feature/api";
2
+ import { Ai } from "@webiny/api-core/features/ai/index.js";
3
+ import { AiSdkTools } from "@webiny/api-core/features/ai/index.js";
4
+ import { AiToolPipelineRunner } from "@webiny/api-core/features/ai/index.js";
5
+ import { Encryption } from "@webiny/api-core/features/encryption/index.js";
6
+ import { ListTagsUseCase } from "@webiny/api-file-manager/features/file/ListTags/index.js";
7
+ import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js";
8
+ import { ModelToAstConverter } from "@webiny/api-headless-cms/features/contentModel/ModelToAstConverter/index.js";
9
+ import { GetSettingsUseCase } from "../../../api/features/GetSettings/index.js";
10
+ import { AiPromptContextBuilder } from "../../../api/features/AiPromptContext/index.js";
11
+ import { CmsGenerateEntryContentUseCase } from "./abstractions.js";
12
+ import type { CmsGenerateEntryContentParams, GenerateEntryContentResult } from "./abstractions.js";
13
+ declare class CmsGenerateEntryContentUseCaseImpl implements CmsGenerateEntryContentUseCase.Interface {
14
+ private promptContextBuilder;
15
+ private getSettings;
16
+ private ai;
17
+ private aiSdkTools;
18
+ private encryption;
19
+ private listTags;
20
+ private getModel;
21
+ private modelToAst;
22
+ private toolPipelineRunner;
23
+ constructor(promptContextBuilder: AiPromptContextBuilder.Interface, getSettings: GetSettingsUseCase.Interface, ai: Ai.Interface, aiSdkTools: AiSdkTools.Interface, encryption: Encryption.Interface, listTags: ListTagsUseCase.Interface, getModel: GetModelUseCase.Interface, modelToAst: ModelToAstConverter.Interface, toolPipelineRunner: AiToolPipelineRunner.Interface);
24
+ execute(params: CmsGenerateEntryContentParams): Promise<Result<GenerateEntryContentResult, Error>>;
25
+ }
26
+ export declare const CmsGenerateEntryContentUseCaseImplementation: typeof CmsGenerateEntryContentUseCaseImpl & {
27
+ __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").ICmsGenerateEntryContentUseCase>;
28
+ };
29
+ export {};
@@ -0,0 +1,141 @@
1
+ import { stepCountIs } from "ai";
2
+ import { Result } from "@webiny/feature/api";
3
+ import { Ai, AiSdkTools, AiToolPipelineRunner } from "@webiny/api-core/features/ai/index.js";
4
+ import { Encryption } from "@webiny/api-core/features/encryption/index.js";
5
+ import { ListTagsUseCase } from "@webiny/api-file-manager/features/file/ListTags/index.js";
6
+ import { GetModelUseCase } from "@webiny/api-headless-cms/features/contentModel/GetModel/index.js";
7
+ import { ModelToAstConverter } from "@webiny/api-headless-cms/features/contentModel/ModelToAstConverter/index.js";
8
+ import { CmsModelToJsonSchemaConverter } from "@webiny/api-headless-cms/utils/contentModelToJsonSchema/index.js";
9
+ import { GetSettingsUseCase } from "../GetSettings/index.js";
10
+ import { AiPromptContextBuilder } from "../AiPromptContext/index.js";
11
+ import { createReadProjectFileTool } from "../AiPromptContext/ReadProjectFileTool.js";
12
+ import { CmsGenerateEntryContentUseCase } from "./abstractions.js";
13
+ import { buildEntryPrompt } from "./buildPrompt.js";
14
+ import { LlmJsonResponse } from "../WbGeneratePageContent/LlmJsonResponse.js";
15
+ import { injectDynamicZoneTypenames } from "./injectDynamicZoneTypenames.js";
16
+ class CmsGenerateEntryContentUseCaseImpl {
17
+ constructor(promptContextBuilder, getSettings, ai, aiSdkTools, encryption, listTags, getModel, modelToAst, toolPipelineRunner){
18
+ this.promptContextBuilder = promptContextBuilder;
19
+ this.getSettings = getSettings;
20
+ this.ai = ai;
21
+ this.aiSdkTools = aiSdkTools;
22
+ this.encryption = encryption;
23
+ this.listTags = listTags;
24
+ this.getModel = getModel;
25
+ this.modelToAst = modelToAst;
26
+ this.toolPipelineRunner = toolPipelineRunner;
27
+ }
28
+ async execute(params) {
29
+ const settingsResult = await this.getSettings.execute();
30
+ if (settingsResult.isFail()) return Result.fail(new Error("Failed to load AI PowerUps settings."));
31
+ const settings = settingsResult.value;
32
+ const firstProvider = settings.providers.presets[0];
33
+ if (!firstProvider) return Result.fail(new Error("No AI provider configured. Add a provider in AI Power Ups settings."));
34
+ const modelResult = await this.getModel.execute(params.modelId);
35
+ if (modelResult.isFail()) return Result.fail(new Error(`Content model "${params.modelId}" not found.`));
36
+ const model = modelResult.value;
37
+ const modelAst = this.modelToAst.toAst(model);
38
+ const jsonSchemaConverter = new CmsModelToJsonSchemaConverter();
39
+ const entrySchema = jsonSchemaConverter.convert(modelAst, {
40
+ name: model.name,
41
+ description: model.description
42
+ });
43
+ const apiKey = await this.encryption.decrypt(firstProvider.apiKeyEncrypted);
44
+ const sdkTools = this.aiSdkTools.getToolSet();
45
+ const context = await this.promptContextBuilder.execute({
46
+ projectId: params.projectId,
47
+ readerPersonaId: params.readerPersonaId,
48
+ writerPersonaId: params.writerPersonaId,
49
+ excludedFileIds: params.excludedFileIds
50
+ });
51
+ if (context.allProjectFiles.length > 0) {
52
+ const projectFileTool = createReadProjectFileTool(context.allProjectFiles, context.excludedFileIds);
53
+ Object.assign(sdkTools, projectFileTool);
54
+ }
55
+ const tagsResult = await this.listTags.execute({
56
+ where: {
57
+ type_startsWith: "image/"
58
+ },
59
+ limit: 100
60
+ });
61
+ const imageTags = tagsResult.isOk() ? tagsResult.value.map((t)=>t.tag) : [];
62
+ const systemText = buildEntryPrompt(model.name, entrySchema, imageTags) + context.toString();
63
+ const system = {
64
+ role: "system",
65
+ content: systemText,
66
+ providerOptions: {
67
+ anthropic: {
68
+ cacheControl: {
69
+ type: "ephemeral"
70
+ }
71
+ }
72
+ }
73
+ };
74
+ try {
75
+ const aiResult = await this.ai.generateText({
76
+ model: firstProvider.model,
77
+ connection: {
78
+ sdkName: firstProvider.model.split("/")[0],
79
+ apiKey
80
+ },
81
+ system,
82
+ toolChoice: "auto",
83
+ prompt: params.prompt,
84
+ ...Object.keys(sdkTools).length > 0 ? {
85
+ tools: sdkTools,
86
+ stopWhen: stepCountIs(20)
87
+ } : {}
88
+ });
89
+ const text = aiResult.text || (aiResult.steps.filter((step)=>step.text.length > 0).pop()?.text ?? "");
90
+ const entry = LlmJsonResponse.fromRawText(text).toArray().pop();
91
+ let resolved;
92
+ if (entry) {
93
+ resolved = await this.toolPipelineRunner.resolve(entry);
94
+ await injectDynamicZoneTypenames(resolved, modelAst, model.singularApiName);
95
+ }
96
+ const output = JSON.stringify(resolved);
97
+ const filesRead = new Set();
98
+ let toolCallsMade = 0;
99
+ for (const step of aiResult.steps)for (const call of step.toolCalls){
100
+ toolCallsMade++;
101
+ if ("read_project_file" === call.toolName) {
102
+ const input = call.input;
103
+ if (input.fileId) filesRead.add(input.fileId);
104
+ }
105
+ }
106
+ const telemetry = {
107
+ filesRead: [
108
+ ...filesRead
109
+ ],
110
+ cacheHit: context.cacheHit,
111
+ toolCallsMade,
112
+ totalSteps: aiResult.steps.length,
113
+ toolsAvailable: Object.keys(sdkTools),
114
+ imageTagsInPrompt: imageTags
115
+ };
116
+ return Result.ok({
117
+ output,
118
+ telemetry
119
+ });
120
+ } catch (error) {
121
+ return Result.fail(new Error(`AI generation failed: ${error instanceof Error ? error.message : String(error)}`));
122
+ }
123
+ }
124
+ }
125
+ const CmsGenerateEntryContentUseCaseImplementation = CmsGenerateEntryContentUseCase.createImplementation({
126
+ implementation: CmsGenerateEntryContentUseCaseImpl,
127
+ dependencies: [
128
+ AiPromptContextBuilder,
129
+ GetSettingsUseCase,
130
+ Ai,
131
+ AiSdkTools,
132
+ Encryption,
133
+ ListTagsUseCase,
134
+ GetModelUseCase,
135
+ ModelToAstConverter,
136
+ AiToolPipelineRunner
137
+ ]
138
+ });
139
+ export { CmsGenerateEntryContentUseCaseImplementation };
140
+
141
+ //# sourceMappingURL=CmsGenerateEntryContentUseCase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.js","sources":["../../../../src/api/features/CmsGenerateEntryContent/CmsGenerateEntryContentUseCase.ts"],"sourcesContent":["import { stepCountIs } from \"ai\";\nimport { Result } from \"@webiny/feature/api\";\nimport { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { AiSdkTools } from \"@webiny/api-core/features/ai/index.js\";\nimport { AiToolPipelineRunner } from \"@webiny/api-core/features/ai/index.js\";\nimport { Encryption } from \"@webiny/api-core/features/encryption/index.js\";\nimport { ListTagsUseCase } from \"@webiny/api-file-manager/features/file/ListTags/index.js\";\nimport { GetModelUseCase } from \"@webiny/api-headless-cms/features/contentModel/GetModel/index.js\";\nimport { ModelToAstConverter } from \"@webiny/api-headless-cms/features/contentModel/ModelToAstConverter/index.js\";\nimport { CmsModelToJsonSchemaConverter } from \"@webiny/api-headless-cms/utils/contentModelToJsonSchema/index.js\";\nimport { GetSettingsUseCase } from \"~/api/features/GetSettings/index.js\";\nimport { AiPromptContextBuilder } from \"~/api/features/AiPromptContext/index.js\";\nimport { createReadProjectFileTool } from \"~/api/features/AiPromptContext/ReadProjectFileTool.js\";\nimport { CmsGenerateEntryContentUseCase } from \"./abstractions.js\";\nimport type {\n CmsGenerateEntryContentParams,\n GenerateEntryContentResult,\n GenerateEntryContentTelemetry\n} from \"./abstractions.js\";\nimport { buildEntryPrompt } from \"./buildPrompt.js\";\nimport { LlmJsonResponse } from \"../WbGeneratePageContent/LlmJsonResponse.js\";\nimport { injectDynamicZoneTypenames } from \"./injectDynamicZoneTypenames.js\";\n\nclass CmsGenerateEntryContentUseCaseImpl implements CmsGenerateEntryContentUseCase.Interface {\n constructor(\n private promptContextBuilder: AiPromptContextBuilder.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private ai: Ai.Interface,\n private aiSdkTools: AiSdkTools.Interface,\n private encryption: Encryption.Interface,\n private listTags: ListTagsUseCase.Interface,\n private getModel: GetModelUseCase.Interface,\n private modelToAst: ModelToAstConverter.Interface,\n private toolPipelineRunner: AiToolPipelineRunner.Interface\n ) {}\n\n async execute(\n params: CmsGenerateEntryContentParams\n ): Promise<Result<GenerateEntryContentResult, Error>> {\n const settingsResult = await this.getSettings.execute();\n if (settingsResult.isFail()) {\n return Result.fail(new Error(\"Failed to load AI PowerUps settings.\"));\n }\n\n const settings = settingsResult.value;\n const firstProvider = settings.providers.presets[0];\n\n if (!firstProvider) {\n return Result.fail(\n new Error(\"No AI provider configured. Add a provider in AI Power Ups settings.\")\n );\n }\n\n const modelResult = await this.getModel.execute(params.modelId);\n if (modelResult.isFail()) {\n return Result.fail(new Error(`Content model \"${params.modelId}\" not found.`));\n }\n\n const model = modelResult.value;\n const modelAst = this.modelToAst.toAst(model);\n const jsonSchemaConverter = new CmsModelToJsonSchemaConverter();\n const entrySchema = jsonSchemaConverter.convert(modelAst, {\n name: model.name,\n description: model.description\n });\n\n const apiKey = await this.encryption.decrypt(firstProvider.apiKeyEncrypted);\n const sdkTools = this.aiSdkTools.getToolSet();\n\n const context = await this.promptContextBuilder.execute({\n projectId: params.projectId,\n readerPersonaId: params.readerPersonaId,\n writerPersonaId: params.writerPersonaId,\n excludedFileIds: params.excludedFileIds\n });\n\n if (context.allProjectFiles.length > 0) {\n const projectFileTool = createReadProjectFileTool(\n context.allProjectFiles,\n context.excludedFileIds\n );\n Object.assign(sdkTools, projectFileTool);\n }\n\n const tagsResult = await this.listTags.execute({\n where: { type_startsWith: \"image/\" },\n limit: 100\n });\n const imageTags = tagsResult.isOk() ? tagsResult.value.map(t => t.tag) : [];\n\n const systemText =\n buildEntryPrompt(model.name, entrySchema, imageTags) + context.toString();\n\n const system = {\n role: \"system\" as const,\n content: systemText,\n providerOptions: {\n anthropic: {\n cacheControl: { type: \"ephemeral\" }\n }\n }\n };\n\n try {\n const aiResult = await this.ai.generateText({\n model: firstProvider.model,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey\n },\n system,\n toolChoice: \"auto\",\n prompt: params.prompt,\n ...(Object.keys(sdkTools).length > 0\n ? { tools: sdkTools, stopWhen: stepCountIs(20) }\n : {})\n });\n\n const text =\n aiResult.text ||\n (aiResult.steps.filter(step => step.text.length > 0).pop()?.text ?? \"\");\n\n const entry = LlmJsonResponse.fromRawText(text).toArray().pop();\n\n let resolved: Record<string, any> | undefined;\n\n if (entry) {\n resolved = (await this.toolPipelineRunner.resolve(entry)) as Record<string, any>;\n await injectDynamicZoneTypenames(resolved, modelAst, model.singularApiName);\n }\n\n const output = JSON.stringify(resolved);\n\n const filesRead = new Set<string>();\n let toolCallsMade = 0;\n for (const step of aiResult.steps) {\n for (const call of step.toolCalls) {\n toolCallsMade++;\n if (call.toolName === \"read_project_file\") {\n const input = call.input as { fileId?: string };\n if (input.fileId) {\n filesRead.add(input.fileId);\n }\n }\n }\n }\n\n const telemetry: GenerateEntryContentTelemetry = {\n filesRead: [...filesRead],\n cacheHit: context.cacheHit,\n toolCallsMade,\n totalSteps: aiResult.steps.length,\n toolsAvailable: Object.keys(sdkTools),\n imageTagsInPrompt: imageTags\n };\n\n return Result.ok({ output, telemetry });\n } catch (error) {\n return Result.fail(\n new Error(\n `AI generation failed: ${error instanceof Error ? error.message : String(error)}`\n )\n );\n }\n }\n}\n\nexport const CmsGenerateEntryContentUseCaseImplementation =\n CmsGenerateEntryContentUseCase.createImplementation({\n implementation: CmsGenerateEntryContentUseCaseImpl,\n dependencies: [\n AiPromptContextBuilder,\n GetSettingsUseCase,\n Ai,\n AiSdkTools,\n Encryption,\n ListTagsUseCase,\n GetModelUseCase,\n ModelToAstConverter,\n AiToolPipelineRunner\n ]\n });\n"],"names":["CmsGenerateEntryContentUseCaseImpl","promptContextBuilder","getSettings","ai","aiSdkTools","encryption","listTags","getModel","modelToAst","toolPipelineRunner","params","settingsResult","Result","Error","settings","firstProvider","modelResult","model","modelAst","jsonSchemaConverter","CmsModelToJsonSchemaConverter","entrySchema","apiKey","sdkTools","context","projectFileTool","createReadProjectFileTool","Object","tagsResult","imageTags","t","systemText","buildEntryPrompt","system","aiResult","stepCountIs","text","step","entry","LlmJsonResponse","resolved","injectDynamicZoneTypenames","output","JSON","filesRead","Set","toolCallsMade","call","input","telemetry","error","String","CmsGenerateEntryContentUseCaseImplementation","CmsGenerateEntryContentUseCase","AiPromptContextBuilder","GetSettingsUseCase","Ai","AiSdkTools","Encryption","ListTagsUseCase","GetModelUseCase","ModelToAstConverter","AiToolPipelineRunner"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { Result } from "@webiny/feature/api";
2
+ export interface CmsGenerateEntryContentParams {
3
+ prompt: string;
4
+ modelId: string;
5
+ projectId?: string | null;
6
+ excludedFileIds?: string[] | null;
7
+ readerPersonaId?: string | null;
8
+ writerPersonaId?: string | null;
9
+ }
10
+ export interface GenerateEntryContentTelemetry {
11
+ filesRead: string[];
12
+ cacheHit: boolean;
13
+ toolCallsMade: number;
14
+ totalSteps: number;
15
+ toolsAvailable: string[];
16
+ imageTagsInPrompt: string[];
17
+ }
18
+ export interface GenerateEntryContentResult {
19
+ output: string;
20
+ telemetry: GenerateEntryContentTelemetry;
21
+ }
22
+ export interface ICmsGenerateEntryContentUseCase {
23
+ execute(params: CmsGenerateEntryContentParams): Promise<Result<GenerateEntryContentResult, Error>>;
24
+ }
25
+ export declare const CmsGenerateEntryContentUseCase: import("@webiny/di").Abstraction<ICmsGenerateEntryContentUseCase>;
26
+ export declare namespace CmsGenerateEntryContentUseCase {
27
+ type Interface = ICmsGenerateEntryContentUseCase;
28
+ type Params = CmsGenerateEntryContentParams;
29
+ }
@@ -0,0 +1,5 @@
1
+ import { createAbstraction } from "@webiny/feature/api";
2
+ const CmsGenerateEntryContentUseCase = createAbstraction("AiPowerUpsCmsGenerateEntryContentUseCase");
3
+ export { CmsGenerateEntryContentUseCase };
4
+
5
+ //# sourceMappingURL=abstractions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api/features/CmsGenerateEntryContent/abstractions.js","sources":["../../../../src/api/features/CmsGenerateEntryContent/abstractions.ts"],"sourcesContent":["import { createAbstraction, Result } from \"@webiny/feature/api\";\n\nexport interface CmsGenerateEntryContentParams {\n prompt: string;\n modelId: string;\n projectId?: string | null;\n excludedFileIds?: string[] | null;\n readerPersonaId?: string | null;\n writerPersonaId?: string | null;\n}\n\nexport interface GenerateEntryContentTelemetry {\n filesRead: string[];\n cacheHit: boolean;\n toolCallsMade: number;\n totalSteps: number;\n toolsAvailable: string[];\n imageTagsInPrompt: string[];\n}\n\nexport interface GenerateEntryContentResult {\n output: string;\n telemetry: GenerateEntryContentTelemetry;\n}\n\nexport interface ICmsGenerateEntryContentUseCase {\n execute(\n params: CmsGenerateEntryContentParams\n ): Promise<Result<GenerateEntryContentResult, Error>>;\n}\n\nexport const CmsGenerateEntryContentUseCase = createAbstraction<ICmsGenerateEntryContentUseCase>(\n \"AiPowerUpsCmsGenerateEntryContentUseCase\"\n);\n\nexport namespace CmsGenerateEntryContentUseCase {\n export type Interface = ICmsGenerateEntryContentUseCase;\n export type Params = CmsGenerateEntryContentParams;\n}\n"],"names":["CmsGenerateEntryContentUseCase","createAbstraction"],"mappings":";AA+BO,MAAMA,iCAAiCC,kBAC1C"}
@@ -0,0 +1,2 @@
1
+ import type { JsonSchema } from "@webiny/api-headless-cms/utils/contentModelToJsonSchema/types.js";
2
+ export declare function buildEntryPrompt(modelName: string, entrySchema: JsonSchema, availableImageTags: string[]): string;