@ai-stack/payloadcms 3.2.24-beta → 3.68.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 (284) hide show
  1. package/{LICENSE.md → LICENSE} +1 -1
  2. package/README.md +218 -229
  3. package/dist/access/checkAccess.d.ts +4 -0
  4. package/dist/access/checkAccess.js +20 -0
  5. package/dist/access/checkAccess.js.map +1 -0
  6. package/dist/ai/core/generateObject.d.ts +7 -0
  7. package/dist/ai/core/generateObject.js +35 -0
  8. package/dist/ai/core/generateObject.js.map +1 -0
  9. package/dist/ai/core/generateText.d.ts +7 -0
  10. package/dist/ai/core/generateText.js +31 -0
  11. package/dist/ai/core/generateText.js.map +1 -0
  12. package/dist/ai/core/index.d.ts +11 -0
  13. package/dist/ai/core/index.js +10 -0
  14. package/dist/ai/core/index.js.map +1 -0
  15. package/dist/ai/core/media/generateMedia.d.ts +7 -0
  16. package/dist/ai/core/media/generateMedia.js +50 -0
  17. package/dist/ai/core/media/generateMedia.js.map +1 -0
  18. package/dist/ai/core/media/image/generateImage.d.ts +6 -0
  19. package/dist/ai/core/media/image/generateImage.js +41 -0
  20. package/dist/ai/core/media/image/generateImage.js.map +1 -0
  21. package/dist/ai/core/media/image/handlers/multimodal.d.ts +7 -0
  22. package/dist/ai/core/media/image/handlers/multimodal.js +95 -0
  23. package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -0
  24. package/dist/ai/core/media/image/handlers/standard.d.ts +7 -0
  25. package/dist/ai/core/media/image/handlers/standard.js +28 -0
  26. package/dist/ai/core/media/image/handlers/standard.js.map +1 -0
  27. package/dist/ai/core/media/image/index.d.ts +2 -0
  28. package/dist/ai/core/media/image/index.js +3 -0
  29. package/dist/ai/core/media/image/index.js.map +1 -0
  30. package/dist/ai/core/media/index.d.ts +2 -0
  31. package/dist/ai/core/media/index.js +3 -0
  32. package/dist/ai/core/media/index.js.map +1 -0
  33. package/dist/ai/core/media/speech/generateSpeech.d.ts +5 -0
  34. package/dist/ai/core/media/speech/generateSpeech.js +55 -0
  35. package/dist/ai/core/media/speech/generateSpeech.js.map +1 -0
  36. package/dist/ai/core/media/speech/index.d.ts +2 -0
  37. package/dist/ai/core/media/speech/index.js +3 -0
  38. package/dist/ai/core/media/speech/index.js.map +1 -0
  39. package/dist/ai/core/media/types.d.ts +74 -0
  40. package/dist/ai/core/media/types.js +5 -0
  41. package/dist/ai/core/media/types.js.map +1 -0
  42. package/dist/ai/core/media/utils.d.ts +11 -0
  43. package/dist/ai/core/media/utils.js +34 -0
  44. package/dist/ai/core/media/utils.js.map +1 -0
  45. package/dist/ai/core/media/video/generateVideo.d.ts +6 -0
  46. package/dist/ai/core/media/video/generateVideo.js +32 -0
  47. package/dist/ai/core/media/video/generateVideo.js.map +1 -0
  48. package/dist/ai/core/media/video/index.d.ts +2 -0
  49. package/dist/ai/core/media/video/index.js +3 -0
  50. package/dist/ai/core/media/video/index.js.map +1 -0
  51. package/dist/ai/core/streamObject.d.ts +7 -0
  52. package/dist/ai/core/streamObject.js +57 -0
  53. package/dist/ai/core/streamObject.js.map +1 -0
  54. package/dist/ai/core/streamText.d.ts +7 -0
  55. package/dist/ai/core/streamText.js +30 -0
  56. package/dist/ai/core/streamText.js.map +1 -0
  57. package/dist/ai/core/types.d.ts +85 -0
  58. package/dist/ai/core/types.js +5 -0
  59. package/dist/ai/core/types.js.map +1 -0
  60. package/dist/ai/index.d.ts +11 -0
  61. package/dist/ai/index.js +25 -0
  62. package/dist/ai/index.js.map +1 -0
  63. package/dist/ai/providers/blocks/anthropic.d.ts +2 -0
  64. package/dist/ai/providers/blocks/anthropic.js +222 -0
  65. package/dist/ai/providers/blocks/anthropic.js.map +1 -0
  66. package/dist/ai/providers/blocks/elevenlabs.d.ts +2 -0
  67. package/dist/ai/providers/blocks/elevenlabs.js +448 -0
  68. package/dist/ai/providers/blocks/elevenlabs.js.map +1 -0
  69. package/dist/ai/providers/blocks/fal.d.ts +2 -0
  70. package/dist/ai/providers/blocks/fal.js +311 -0
  71. package/dist/ai/providers/blocks/fal.js.map +1 -0
  72. package/dist/ai/providers/blocks/google.d.ts +2 -0
  73. package/dist/ai/providers/blocks/google.js +622 -0
  74. package/dist/ai/providers/blocks/google.js.map +1 -0
  75. package/dist/ai/providers/blocks/index.d.ts +2 -0
  76. package/dist/ai/providers/blocks/index.js +18 -0
  77. package/dist/ai/providers/blocks/index.js.map +1 -0
  78. package/dist/ai/providers/blocks/openai-compatible.d.ts +2 -0
  79. package/dist/ai/providers/blocks/openai-compatible.js +307 -0
  80. package/dist/ai/providers/blocks/openai-compatible.js.map +1 -0
  81. package/dist/ai/providers/blocks/openai.d.ts +2 -0
  82. package/dist/ai/providers/blocks/openai.js +599 -0
  83. package/dist/ai/providers/blocks/openai.js.map +1 -0
  84. package/dist/ai/providers/blocks/xai.d.ts +2 -0
  85. package/dist/ai/providers/blocks/xai.js +246 -0
  86. package/dist/ai/providers/blocks/xai.js.map +1 -0
  87. package/dist/ai/providers/index.d.ts +2 -0
  88. package/dist/ai/providers/index.js +6 -0
  89. package/dist/ai/providers/index.js.map +1 -0
  90. package/dist/ai/providers/registry.d.ts +40 -0
  91. package/dist/ai/providers/registry.js +256 -0
  92. package/dist/ai/providers/registry.js.map +1 -0
  93. package/dist/ai/providers/types.d.ts +115 -0
  94. package/dist/ai/providers/types.js +4 -0
  95. package/dist/ai/providers/types.js.map +1 -0
  96. package/dist/ai/utils/systemGenerate.d.ts +1 -1
  97. package/dist/ai/utils/systemGenerate.js +19 -19
  98. package/dist/ai/utils/systemGenerate.js.map +1 -1
  99. package/dist/collections/AIJobs.d.ts +2 -0
  100. package/dist/collections/AIJobs.js +81 -0
  101. package/dist/collections/AIJobs.js.map +1 -0
  102. package/dist/collections/AISettings.d.ts +2 -0
  103. package/dist/collections/AISettings.js +279 -0
  104. package/dist/collections/AISettings.js.map +1 -0
  105. package/dist/collections/Instructions.js +185 -37
  106. package/dist/collections/Instructions.js.map +1 -1
  107. package/dist/defaults.d.ts +3 -0
  108. package/dist/defaults.js +3 -0
  109. package/dist/defaults.js.map +1 -1
  110. package/dist/endpoints/buildPromptUtils.d.ts +19 -0
  111. package/dist/endpoints/buildPromptUtils.js +114 -0
  112. package/dist/endpoints/buildPromptUtils.js.map +1 -0
  113. package/dist/endpoints/chat.d.js +3 -0
  114. package/dist/endpoints/chat.d.js.map +1 -0
  115. package/dist/endpoints/fetchVoices.d.ts +2 -0
  116. package/dist/endpoints/fetchVoices.js +79 -0
  117. package/dist/endpoints/fetchVoices.js.map +1 -0
  118. package/dist/endpoints/index.js +253 -214
  119. package/dist/endpoints/index.js.map +1 -1
  120. package/dist/exports/client.d.ts +9 -0
  121. package/dist/exports/client.js +9 -0
  122. package/dist/exports/client.js.map +1 -1
  123. package/dist/fields/ComposeField/ComposeField.js +2 -2
  124. package/dist/fields/ComposeField/ComposeField.js.map +1 -1
  125. package/dist/fields/ComposeField/ComposeField.jsx +2 -2
  126. package/dist/fields/PromptEditorField/PromptEditorField.js +155 -14
  127. package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
  128. package/dist/fields/PromptEditorField/PromptEditorField.jsx +118 -3
  129. package/dist/index.d.ts +1 -0
  130. package/dist/index.js.map +1 -1
  131. package/dist/init.js +35 -13
  132. package/dist/init.js.map +1 -1
  133. package/dist/payload-ai.d.js +3 -0
  134. package/dist/payload-ai.d.js.map +1 -0
  135. package/dist/plugin.js +80 -9
  136. package/dist/plugin.js.map +1 -1
  137. package/dist/providers/InstructionsProvider/InstructionsProvider.js +35 -7
  138. package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
  139. package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +27 -4
  140. package/dist/providers/InstructionsProvider/context.d.ts +1 -0
  141. package/dist/providers/InstructionsProvider/context.js +1 -0
  142. package/dist/providers/InstructionsProvider/context.js.map +1 -1
  143. package/dist/providers/InstructionsProvider/useInstructions.js +13 -6
  144. package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
  145. package/dist/types.d.ts +7 -7
  146. package/dist/types.js.map +1 -1
  147. package/dist/ui/AIConfigDashboard/index.d.ts +2 -0
  148. package/dist/ui/AIConfigDashboard/index.js +46 -0
  149. package/dist/ui/AIConfigDashboard/index.js.map +1 -0
  150. package/dist/ui/AIConfigDashboard/index.jsx +24 -0
  151. package/dist/ui/ApiKeyStatusIndicator/index.d.ts +6 -0
  152. package/dist/ui/ApiKeyStatusIndicator/index.js +39 -0
  153. package/dist/ui/ApiKeyStatusIndicator/index.js.map +1 -0
  154. package/dist/ui/ApiKeyStatusIndicator/index.jsx +29 -0
  155. package/dist/ui/Compose/Compose.d.ts +1 -2
  156. package/dist/ui/Compose/Compose.js +116 -90
  157. package/dist/ui/Compose/Compose.js.map +1 -1
  158. package/dist/ui/Compose/Compose.jsx +111 -101
  159. package/dist/ui/Compose/ComposePlaceholder.d.ts +7 -0
  160. package/dist/ui/Compose/ComposePlaceholder.js +78 -0
  161. package/dist/ui/Compose/ComposePlaceholder.js.map +1 -0
  162. package/dist/ui/Compose/ComposePlaceholder.jsx +66 -0
  163. package/dist/ui/Compose/UndoRedoActions.js +3 -1
  164. package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
  165. package/dist/ui/Compose/UndoRedoActions.jsx +2 -1
  166. package/dist/ui/Compose/compose.module.css +1 -1
  167. package/dist/ui/Compose/hooks/menu/itemsMap.js +1 -1
  168. package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
  169. package/dist/ui/Compose/hooks/menu/useMenu.d.ts +2 -1
  170. package/dist/ui/Compose/hooks/menu/useMenu.js +2 -2
  171. package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
  172. package/dist/ui/Compose/hooks/menu/useMenu.jsx +2 -2
  173. package/dist/ui/Compose/hooks/useActiveFieldTracking.js +69 -10
  174. package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
  175. package/dist/ui/Compose/hooks/useGenerate.d.ts +3 -0
  176. package/dist/ui/Compose/hooks/useGenerate.js +71 -11
  177. package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
  178. package/dist/ui/Compose/hooks/useHistory.js +52 -5
  179. package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
  180. package/dist/ui/DynamicModelSelect/index.d.ts +7 -0
  181. package/dist/ui/DynamicModelSelect/index.js +231 -0
  182. package/dist/ui/DynamicModelSelect/index.js.map +1 -0
  183. package/dist/ui/DynamicModelSelect/index.jsx +207 -0
  184. package/dist/ui/DynamicProviderSelect/index.d.ts +7 -0
  185. package/dist/ui/DynamicProviderSelect/index.js +101 -0
  186. package/dist/ui/DynamicProviderSelect/index.js.map +1 -0
  187. package/dist/ui/DynamicProviderSelect/index.jsx +90 -0
  188. package/dist/ui/DynamicVoiceSelect/index.d.ts +7 -0
  189. package/dist/ui/DynamicVoiceSelect/index.js +104 -0
  190. package/dist/ui/DynamicVoiceSelect/index.js.map +1 -0
  191. package/dist/ui/DynamicVoiceSelect/index.jsx +69 -0
  192. package/dist/ui/EncryptedTextField/index.d.ts +8 -0
  193. package/dist/ui/EncryptedTextField/index.js +74 -0
  194. package/dist/ui/EncryptedTextField/index.js.map +1 -0
  195. package/dist/ui/EncryptedTextField/index.jsx +35 -0
  196. package/dist/ui/Icons/LottieAnimation.js +3 -1
  197. package/dist/ui/Icons/LottieAnimation.js.map +1 -1
  198. package/dist/ui/Icons/LottieAnimation.jsx +2 -1
  199. package/dist/ui/ModelRowLabel/index.d.ts +6 -0
  200. package/dist/ui/ModelRowLabel/index.js +41 -0
  201. package/dist/ui/ModelRowLabel/index.js.map +1 -0
  202. package/dist/ui/ModelRowLabel/index.jsx +26 -0
  203. package/dist/ui/ProviderOptionsEditor/index.d.ts +7 -0
  204. package/dist/ui/ProviderOptionsEditor/index.js +291 -0
  205. package/dist/ui/ProviderOptionsEditor/index.js.map +1 -0
  206. package/dist/ui/ProviderOptionsEditor/index.jsx +210 -0
  207. package/dist/ui/VoicesFetcher/index.d.ts +7 -0
  208. package/dist/ui/VoicesFetcher/index.js +72 -0
  209. package/dist/ui/VoicesFetcher/index.js.map +1 -0
  210. package/dist/ui/VoicesFetcher/index.jsx +56 -0
  211. package/dist/utilities/encryption.d.ts +2 -0
  212. package/dist/utilities/encryption.js +47 -0
  213. package/dist/utilities/encryption.js.map +1 -0
  214. package/dist/utilities/extractImageData.d.ts +9 -0
  215. package/dist/utilities/extractImageData.js +12 -2
  216. package/dist/utilities/extractImageData.js.map +1 -1
  217. package/dist/utilities/fetchImages.d.ts +14 -0
  218. package/dist/utilities/fetchImages.js +38 -0
  219. package/dist/utilities/fetchImages.js.map +1 -0
  220. package/dist/utilities/fieldToJsonSchema.d.ts +2 -1
  221. package/dist/utilities/fieldToJsonSchema.js +66 -3
  222. package/dist/utilities/fieldToJsonSchema.js.map +1 -1
  223. package/dist/utilities/getFieldBySchemaPath.js +15 -0
  224. package/dist/utilities/getFieldBySchemaPath.js.map +1 -1
  225. package/dist/utilities/getProviderOptionsFields.d.ts +16 -0
  226. package/dist/utilities/getProviderOptionsFields.js +80 -0
  227. package/dist/utilities/getProviderOptionsFields.js.map +1 -0
  228. package/dist/utilities/isPluginActivated.js +1 -2
  229. package/dist/utilities/isPluginActivated.js.map +1 -1
  230. package/dist/utilities/lexicalToHTML.js.map +1 -1
  231. package/dist/utilities/resolveImageReferences.d.ts +28 -0
  232. package/dist/utilities/resolveImageReferences.js +148 -0
  233. package/dist/utilities/resolveImageReferences.js.map +1 -0
  234. package/dist/utilities/schemaConverter.d.ts +3 -0
  235. package/dist/utilities/schemaConverter.js +93 -0
  236. package/dist/utilities/schemaConverter.js.map +1 -0
  237. package/dist/utilities/setSafeLexicalState.d.ts +1 -3
  238. package/dist/utilities/setSafeLexicalState.js +1 -1
  239. package/dist/utilities/setSafeLexicalState.js.map +1 -1
  240. package/package.json +19 -21
  241. package/dist/ai/models/anthropic/index.d.ts +0 -2
  242. package/dist/ai/models/anthropic/index.js +0 -129
  243. package/dist/ai/models/anthropic/index.js.map +0 -1
  244. package/dist/ai/models/elevenLabs/generateVoice.d.ts +0 -8
  245. package/dist/ai/models/elevenLabs/generateVoice.js +0 -20
  246. package/dist/ai/models/elevenLabs/generateVoice.js.map +0 -1
  247. package/dist/ai/models/elevenLabs/index.d.ts +0 -2
  248. package/dist/ai/models/elevenLabs/index.js +0 -133
  249. package/dist/ai/models/elevenLabs/index.js.map +0 -1
  250. package/dist/ai/models/elevenLabs/voices.d.ts +0 -8
  251. package/dist/ai/models/elevenLabs/voices.js +0 -24
  252. package/dist/ai/models/elevenLabs/voices.js.map +0 -1
  253. package/dist/ai/models/generateObject.d.ts +0 -11
  254. package/dist/ai/models/generateObject.js +0 -22
  255. package/dist/ai/models/generateObject.js.map +0 -1
  256. package/dist/ai/models/google/generateImage.d.ts +0 -9
  257. package/dist/ai/models/google/generateImage.js +0 -27
  258. package/dist/ai/models/google/generateImage.js.map +0 -1
  259. package/dist/ai/models/google/index.d.ts +0 -2
  260. package/dist/ai/models/google/index.js +0 -201
  261. package/dist/ai/models/google/index.js.map +0 -1
  262. package/dist/ai/models/index.d.ts +0 -2
  263. package/dist/ai/models/index.js +0 -13
  264. package/dist/ai/models/index.js.map +0 -1
  265. package/dist/ai/models/openai/generateImage.d.ts +0 -5
  266. package/dist/ai/models/openai/generateImage.js +0 -31
  267. package/dist/ai/models/openai/generateImage.js.map +0 -1
  268. package/dist/ai/models/openai/generateVoice.d.ts +0 -6
  269. package/dist/ai/models/openai/generateVoice.js +0 -19
  270. package/dist/ai/models/openai/generateVoice.js.map +0 -1
  271. package/dist/ai/models/openai/index.d.ts +0 -2
  272. package/dist/ai/models/openai/index.js +0 -428
  273. package/dist/ai/models/openai/index.js.map +0 -1
  274. package/dist/ai/models/openai/openai.d.ts +0 -1
  275. package/dist/ai/models/openai/openai.js +0 -8
  276. package/dist/ai/models/openai/openai.js.map +0 -1
  277. package/dist/ai/utils/editImagesWithOpenAI.d.ts +0 -10
  278. package/dist/ai/utils/editImagesWithOpenAI.js +0 -37
  279. package/dist/ai/utils/editImagesWithOpenAI.js.map +0 -1
  280. package/dist/types.d.js +0 -3
  281. package/dist/types.d.js.map +0 -1
  282. package/dist/utilities/getGenerationModels.d.ts +0 -2
  283. package/dist/utilities/getGenerationModels.js +0 -10
  284. package/dist/utilities/getGenerationModels.js.map +0 -1
@@ -1,144 +1,18 @@
1
1
  import * as process from 'node:process';
2
- import { defaultPrompts } from '../ai/prompts.js';
2
+ import { checkAccess } from '../access/checkAccess.js';
3
3
  import { filterEditorSchemaByNodes } from '../ai/utils/filterEditorSchemaByNodes.js';
4
- import { PLUGIN_API_ENDPOINT_GENERATE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD, PLUGIN_INSTRUCTIONS_TABLE, PLUGIN_NAME } from '../defaults.js';
5
- import { asyncHandlebars } from '../libraries/handlebars/asyncHandlebars.js';
4
+ import { PLUGIN_AI_JOBS_TABLE, PLUGIN_API_ENDPOINT_GENERATE, PLUGIN_API_ENDPOINT_GENERATE_UPLOAD, PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK, PLUGIN_INSTRUCTIONS_TABLE, PLUGIN_NAME } from '../defaults.js';
6
5
  import { registerEditorHelper } from '../libraries/handlebars/helpers.js';
7
- import { handlebarsHelpersMap } from '../libraries/handlebars/helpersMap.js';
8
6
  import { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js';
9
7
  import { extractImageData } from '../utilities/extractImageData.js';
8
+ import { fetchImages } from '../utilities/fetchImages.js';
10
9
  import { fieldToJsonSchema } from '../utilities/fieldToJsonSchema.js';
11
10
  import { getFieldBySchemaPath } from '../utilities/getFieldBySchemaPath.js';
12
- import { getGenerationModels } from '../utilities/getGenerationModels.js';
13
- const requireAuthentication = (req)=>{
14
- if (!req.user) {
15
- throw new Error('Authentication required. Please log in to use AI features.');
16
- }
17
- return true;
18
- };
19
- const checkAccess = async (req, pluginConfig)=>{
20
- requireAuthentication(req);
21
- if (pluginConfig.access?.generate) {
22
- const hasAccess = await pluginConfig.access.generate({
23
- req
24
- });
25
- if (!hasAccess) {
26
- throw new Error('Insufficient permissions to use AI generation features.');
27
- }
28
- }
29
- return true;
30
- };
31
- const extendContextWithPromptFields = (data, ctx, pluginConfig)=>{
32
- const { promptFields = [] } = pluginConfig;
33
- const fieldsMap = new Map(promptFields.filter((f)=>!f.collections || f.collections.includes(ctx.collection)).map((f)=>[
34
- f.name,
35
- f
36
- ]));
37
- return new Proxy(data, {
38
- get: (target, prop)=>{
39
- const field = fieldsMap.get(prop);
40
- if (field?.getter) {
41
- const value = field.getter(data, ctx);
42
- return Promise.resolve(value).then((v)=>new asyncHandlebars.SafeString(v));
43
- }
44
- // {{prop}} escapes content by default. Here we make sure it won't be escaped.
45
- const value = typeof target === "object" ? target[prop] : undefined;
46
- return typeof value === 'string' ? new asyncHandlebars.SafeString(value) : value;
47
- },
48
- // It's used by the handlebars library to determine if the property is enumerable
49
- getOwnPropertyDescriptor: (target, prop)=>{
50
- const field = fieldsMap.get(prop);
51
- if (field) {
52
- return {
53
- configurable: true,
54
- enumerable: true
55
- };
56
- }
57
- return Object.getOwnPropertyDescriptor(target, prop);
58
- },
59
- has: (target, prop)=>{
60
- return fieldsMap.has(prop) || target && prop in target;
61
- },
62
- ownKeys: (target)=>{
63
- return [
64
- ...fieldsMap.keys(),
65
- ...Object.keys(target || {})
66
- ];
67
- }
68
- });
69
- };
70
- const buildRichTextSystem = (baseSystem, layout)=>{
71
- return `${baseSystem}
72
-
73
- RULES:
74
- - Generate original and unique content based on the given topic.
75
- - Strictly adhere to the specified layout and formatting instructions.
76
- - Utilize the provided rich text editor tools for appropriate formatting.
77
- - Ensure the output follows the structure of the sample output object.
78
- - Produce valid JSON with no undefined or null values.
79
- ---
80
- LAYOUT INSTRUCTIONS:
81
- ${layout}
82
-
83
- ---
84
- ADDITIONAL GUIDELINES:
85
- - Ensure coherence and logical flow between all sections.
86
- - Maintain a consistent tone and style throughout the content.
87
- - Use clear and concise language appropriate for the target audience.
88
- `;
89
- };
90
- const assignPrompt = async (action, { type, actionParams, collection, context, field, layout, locale, pluginConfig, systemPrompt = '', template })=>{
91
- const extendedContext = extendContextWithPromptFields(context, {
92
- type,
93
- collection
94
- }, pluginConfig);
95
- const prompt = await replacePlaceholders(template, extendedContext);
96
- const toLexicalHTML = type === 'richText' ? handlebarsHelpersMap.toHTML.name : '';
97
- const assignedPrompts = {
98
- layout: type === 'richText' ? layout : undefined,
99
- prompt,
100
- //TODO: Define only once on a collection level
101
- system: type === 'richText' ? buildRichTextSystem(systemPrompt, layout) : undefined
102
- };
103
- if (action === 'Compose') {
104
- if (locale && locale !== 'en') {
105
- /**
106
- * NOTE: Avoid using the "system prompt" for setting the output language,
107
- * as it causes quotation marks to appear in the output (Currently only tested with openai models).
108
- * Appending the language instruction directly to the prompt resolves this issue.
109
- **/ assignedPrompts.prompt += `
110
- ---
111
- OUTPUT LANGUAGE: ${locale}
112
- `;
113
- }
114
- return assignedPrompts;
115
- }
116
- const prompts = [
117
- ...pluginConfig.prompts || [],
118
- ...defaultPrompts
119
- ];
120
- const foundPrompt = prompts.find((p)=>p.name === action);
121
- const getLayout = foundPrompt?.layout;
122
- const getSystemPrompt = foundPrompt?.system;
123
- let updatedLayout = layout;
124
- if (getLayout) {
125
- updatedLayout = getLayout();
126
- }
127
- const system = getSystemPrompt ? getSystemPrompt({
128
- ...actionParams || {},
129
- prompt,
130
- systemPrompt
131
- }) : '';
132
- return {
133
- layout: updatedLayout,
134
- // TODO: revisit this toLexicalHTML
135
- prompt: await replacePlaceholders(`{{${toLexicalHTML} ${field}}}`, extendedContext),
136
- system: type === 'richText' ? buildRichTextSystem(system, updatedLayout) : system
137
- };
138
- };
11
+ import { resolveImageReferences } from '../utilities/resolveImageReferences.js';
12
+ import { assignPrompt, extendContextWithPromptFields } from './buildPromptUtils.js';
139
13
  export const endpoints = (pluginConfig)=>({
140
14
  textarea: {
141
- //TODO: This is the main endpoint for generating content - its just needs to be renamed to 'generate' or something.
15
+ // Text/rich-text generation endpoint using payload.ai.streamObject
142
16
  handler: async (req)=>{
143
17
  try {
144
18
  // Check authentication and authorization first
@@ -168,8 +42,8 @@ export const endpoints = (pluginConfig)=>({
168
42
  if (allowedEditorNodes.length) {
169
43
  allowedEditorSchema = filterEditorSchemaByNodes(editorSchema, allowedEditorNodes);
170
44
  }
171
- const schemaPath = instructions['schema-path'];
172
- const parts = schemaPath?.split('.') || [];
45
+ const schemaPath = String(instructions['schema-path']);
46
+ const parts = (schemaPath || '').split('.') || [];
173
47
  const collectionName = parts[0];
174
48
  const fieldName = parts.length > 1 ? parts[parts.length - 1] : '';
175
49
  registerEditorHelper(req.payload, schemaPath);
@@ -181,16 +55,6 @@ export const endpoints = (pluginConfig)=>({
181
55
  if (localeData && defaultLocale && localeData.label && typeof localeData.label === 'object' && defaultLocale in localeData.label) {
182
56
  localeInfo = localeData.label[defaultLocale];
183
57
  }
184
- const models = getGenerationModels(pluginConfig);
185
- const model = models && Array.isArray(models) ? models.find((model)=>model.id === instructions['model-id']) : undefined;
186
- if (!model) {
187
- throw new Error('Model not found');
188
- }
189
- const settingsName = model.settings && "name" in model.settings ? model.settings.name : undefined;
190
- if (!settingsName) {
191
- req.payload.logger.error('— AI Plugin: Error fetching settings name!');
192
- }
193
- const modelOptions = settingsName ? instructions[settingsName] || {} : {};
194
58
  const prompts = await assignPrompt(action, {
195
59
  type: String(instructions['field-type']),
196
60
  actionParams,
@@ -206,7 +70,7 @@ export const endpoints = (pluginConfig)=>({
206
70
  if (pluginConfig.debugging) {
207
71
  req.payload.logger.info({
208
72
  prompts
209
- }, `— AI Plugin: Executing text prompt on ${schemaPath} using ${model.id}`);
73
+ }, `— AI Plugin: Executing text prompt on ${schemaPath}`);
210
74
  }
211
75
  // Build per-field JSON schema for structured generation when applicable
212
76
  let jsonSchema = allowedEditorSchema;
@@ -234,13 +98,42 @@ export const endpoints = (pluginConfig)=>({
234
98
  } catch (e) {
235
99
  req.payload.logger.error(e, '— AI Plugin: Error building field JSON schema');
236
100
  }
237
- return model.handler?.(prompts.prompt, {
238
- ...modelOptions,
239
- layout: prompts.layout,
240
- locale: localeInfo,
101
+ // Get model settings from instruction
102
+ const settingsName = instructions['model-id'] === 'richtext' ? 'richtext-settings' : instructions['model-id'] === 'text' ? 'text-settings' : undefined;
103
+ if (!settingsName) {
104
+ throw new Error(`Unsupported model-id: ${instructions['model-id']}`);
105
+ }
106
+ const modelSettings = instructions[settingsName] || {};
107
+ // Resolve @field:filename references from the prompt
108
+ const { images: resolvedImages, processedPrompt } = await resolveImageReferences(prompts.prompt, contextData, req);
109
+ console.log('resolvedImagesL ', resolvedImages);
110
+ // Extract hardcoded URLs from the processed prompt
111
+ const hardcodedImages = extractImageData(processedPrompt);
112
+ // Combine images
113
+ const allImages = [
114
+ ...hardcodedImages,
115
+ ...resolvedImages
116
+ ];
117
+ let images;
118
+ if (allImages.length > 0) {
119
+ const imageParts = await fetchImages(req, allImages);
120
+ if (imageParts.length > 0) {
121
+ images = imageParts;
122
+ }
123
+ }
124
+ // Use payload.ai.streamObject directly! 🎉
125
+ const streamResult = await req.payload.ai.streamObject({
126
+ // extractAttachments: modelSettings.extractAttachments as boolean | undefined,
127
+ images,
128
+ maxTokens: modelSettings.maxTokens,
129
+ model: modelSettings.model,
130
+ prompt: processedPrompt,
131
+ provider: modelSettings.provider,
241
132
  schema: jsonSchema,
242
- system: prompts.system
133
+ system: prompts.system,
134
+ temperature: modelSettings.temperature
243
135
  });
136
+ return streamResult;
244
137
  } catch (error) {
245
138
  req.payload.logger.error(error, 'Error generating content: ');
246
139
  const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
@@ -258,6 +151,7 @@ export const endpoints = (pluginConfig)=>({
258
151
  path: PLUGIN_API_ENDPOINT_GENERATE
259
152
  },
260
153
  upload: {
154
+ // Image/video generation endpoint using payload.ai.generateMedia
261
155
  handler: async (req)=>{
262
156
  try {
263
157
  // Check authentication and authorization first
@@ -296,94 +190,239 @@ export const endpoints = (pluginConfig)=>({
296
190
  });
297
191
  }
298
192
  const { images: sampleImages = [], prompt: promptTemplate = '' } = instructions;
299
- const schemaPath = instructions['schema-path'];
193
+ const schemaPath = String(instructions['schema-path']);
300
194
  registerEditorHelper(req.payload, schemaPath);
301
195
  const extendedContext = extendContextWithPromptFields(contextData, {
302
- type: instructions['field-type'],
196
+ type: String(instructions['field-type']),
303
197
  collection: collectionSlug
304
198
  }, pluginConfig);
305
199
  const text = await replacePlaceholders(promptTemplate, extendedContext);
306
200
  const modelId = instructions['model-id'];
307
201
  const uploadCollectionSlug = instructions['relation-to'];
202
+ // Resolve @field:filename references from the prompt
203
+ const { images: resolvedImages, processedPrompt } = await resolveImageReferences(text, contextData, req);
204
+ // Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images
308
205
  const images = [
309
- ...extractImageData(text),
206
+ ...extractImageData(processedPrompt),
207
+ ...resolvedImages,
310
208
  ...sampleImages
311
209
  ];
312
- const editImages = [];
313
- for (const img of images){
314
- const serverURL = req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL;
315
- let url = img.image.thumbnailURL || img.image.url;
316
- if (!url.startsWith('http')) {
317
- url = `${serverURL}${url}`;
318
- }
319
- try {
320
- const response = await fetch(url, {
321
- headers: {
322
- //TODO: Further testing needed or so find a proper way.
323
- Authorization: `Bearer ${req.headers.get('Authorization')?.split('Bearer ')[1] || ''}`
324
- },
325
- method: 'GET'
210
+ // Process images - convert to ImagePart format using helper
211
+ const editImages = await fetchImages(req, images);
212
+ if (pluginConfig.debugging) {
213
+ req.payload.logger.info({
214
+ text
215
+ }, `— AI Plugin: Executing media generation`);
216
+ }
217
+ // Prepare callback URL for async jobs
218
+ const serverURL = req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL;
219
+ const callbackUrl = serverURL ? `${serverURL.replace(/\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}` : undefined;
220
+ // Get model settings
221
+ const settingsName = modelId === 'image' ? 'image-settings' : modelId === 'video' ? 'video-settings' : modelId === 'tts' ? 'tts-settings' : undefined;
222
+ if (!settingsName) {
223
+ throw new Error(`Unsupported model-id: ${modelId}`);
224
+ }
225
+ const modelSettings = instructions[settingsName] || {};
226
+ // Use payload.ai.generateMedia directly! 🎉
227
+ const result = await req.payload.ai.generateMedia({
228
+ callbackUrl,
229
+ images: editImages,
230
+ instructionId,
231
+ model: modelSettings.model,
232
+ prompt: text,
233
+ provider: modelSettings.provider,
234
+ ...modelSettings
235
+ });
236
+ // If model returned a file immediately, proceed with upload
237
+ if (result && 'file' in result) {
238
+ let assetData;
239
+ if (typeof pluginConfig.mediaUpload === 'function') {
240
+ assetData = await pluginConfig.mediaUpload(result, {
241
+ collection: uploadCollectionSlug,
242
+ request: req
326
243
  });
327
- const blob = await response.blob();
328
- editImages.push({
329
- name: img.image.name,
330
- type: img.image.type,
331
- data: blob,
332
- size: blob.size,
333
- url
244
+ } else {
245
+ assetData = await req.payload.create({
246
+ collection: uploadCollectionSlug,
247
+ data: {
248
+ alt: text
249
+ },
250
+ file: result.file,
251
+ req
334
252
  });
335
- } catch (e) {
336
- req.payload.logger.error(e, `Error fetching reference image ${url}`);
337
- throw Error("We couldn't fetch the images. Please ensure the images are accessible and hosted publicly.");
338
253
  }
254
+ if (!assetData.id) {
255
+ req.payload.logger.error('Error uploading generated media, is your media upload function correct?');
256
+ throw new Error('Error uploading generated media!');
257
+ }
258
+ return new Response(JSON.stringify({
259
+ result: {
260
+ id: assetData.id,
261
+ alt: assetData.alt
262
+ }
263
+ }));
339
264
  }
340
- const modelsUpload = getGenerationModels(pluginConfig);
341
- const model = modelsUpload && Array.isArray(modelsUpload) ? modelsUpload.find((model)=>model.id === modelId) : undefined;
342
- if (!model) {
343
- throw new Error('Model not found');
265
+ // Otherwise, assume async job launch
266
+ if (result && ('jobId' in result || 'taskId' in result)) {
267
+ const externalTaskId = result.jobId || result.taskId;
268
+ const status = result.status || 'queued';
269
+ const progress = result.progress ?? 0;
270
+ // Create AI Job doc and return only its id
271
+ const createdJob = await req.payload.create({
272
+ collection: PLUGIN_AI_JOBS_TABLE,
273
+ data: {
274
+ instructionId,
275
+ progress,
276
+ status,
277
+ task_id: externalTaskId
278
+ },
279
+ overrideAccess: true,
280
+ req
281
+ });
282
+ return new Response(JSON.stringify({
283
+ job: {
284
+ id: createdJob.id
285
+ }
286
+ }), {
287
+ headers: {
288
+ 'Content-Type': 'application/json'
289
+ }
290
+ });
344
291
  }
345
- // @ts-ignore
346
- const settingsName = model && model.settings ? model.settings.name : undefined;
347
- if (!settingsName) {
348
- req.payload.logger.error('— AI Plugin: Error fetching settings name!');
292
+ throw new Error('Unexpected model response.');
293
+ } catch (error) {
294
+ req.payload.logger.error(// @ts-expect-error
295
+ error?.type || error.message, 'Error generating upload: ');
296
+ const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
297
+ return new Response(JSON.stringify({
298
+ error: message
299
+ }), {
300
+ headers: {
301
+ 'Content-Type': 'application/json'
302
+ },
303
+ status: message.includes('Authentication required') || message.includes('Insufficient permissions') ? 401 : 500
304
+ });
305
+ }
306
+ },
307
+ method: 'post',
308
+ path: PLUGIN_API_ENDPOINT_GENERATE_UPLOAD
309
+ },
310
+ videogenWebhook: {
311
+ handler: async (req)=>{
312
+ console.log('videogenWebhook --> ', req);
313
+ try {
314
+ const urlAll = new URL(req.url || '');
315
+ const qpSecret = urlAll.searchParams.get('secret') || '';
316
+ const headerSecret = req.headers.get('x-webhook-secret') || '';
317
+ const falSecret = process.env.FAL_WEBHOOK_SECRET;
318
+ const legacySecret = process.env.VIDEOGEN_WEBHOOK_SECRET;
319
+ const provided = qpSecret || headerSecret;
320
+ // TODO: fal is failing because of auth but webhook seem to work
321
+ if (!provided || (falSecret ? provided !== falSecret : provided !== legacySecret)) {
322
+ return new Response('Unauthorized', {
323
+ status: 401
324
+ });
349
325
  }
350
- let modelOptions = settingsName ? instructions[settingsName] || {} : {};
351
- modelOptions = {
352
- ...modelOptions,
353
- images: editImages
354
- };
355
- if (pluginConfig.debugging) {
356
- req.payload.logger.info({
357
- text
358
- }, `— AI Plugin: Executing image prompt using ${model.id}`);
326
+ const instructionId = urlAll.searchParams.get('instructionId');
327
+ if (!instructionId) {
328
+ throw new Error('instructionId missing');
359
329
  }
360
- const result = await model.handler?.(text, modelOptions);
361
- let assetData;
362
- if (typeof pluginConfig.mediaUpload === 'function') {
363
- assetData = await pluginConfig.mediaUpload(result, {
364
- collection: uploadCollectionSlug,
365
- request: req
330
+ const body = await req.json?.();
331
+ // Normalize fal webhook payload
332
+ const status = body && (body.status || body.data?.status || body.response?.status) || undefined;
333
+ const progress = (body && (body.progress ?? body.data?.progress ?? body.response?.progress)) ?? undefined;
334
+ const requestId = body && (body.taskId || body.request_id || body.gateway_request_id || body.request?.request_id) || undefined;
335
+ const error = body?.error || body?.data?.error || body?.response?.error;
336
+ // Update AI Job row by task_id (and instructionId)
337
+ const jobSearch = await req.payload.find({
338
+ collection: PLUGIN_AI_JOBS_TABLE,
339
+ depth: 0,
340
+ limit: 1,
341
+ where: {
342
+ and: [
343
+ {
344
+ task_id: {
345
+ equals: requestId
346
+ }
347
+ },
348
+ {
349
+ instructionId: {
350
+ equals: instructionId
351
+ }
352
+ }
353
+ ]
354
+ }
355
+ });
356
+ const jobDoc = jobSearch.docs?.[0];
357
+ if (jobDoc) {
358
+ await req.payload.update({
359
+ id: jobDoc.id,
360
+ collection: PLUGIN_AI_JOBS_TABLE,
361
+ data: {
362
+ progress,
363
+ status,
364
+ task_id: requestId
365
+ },
366
+ overrideAccess: true,
367
+ req
366
368
  });
367
- } else {
368
- assetData = await req.payload.create({
369
+ }
370
+ console.log('fal webhook body: ', body);
371
+ const videoUrl = body?.outputs?.[0]?.url || body?.data?.outputs?.[0]?.url || body?.video?.url || body?.data?.video?.url || body?.response?.video?.url || body?.videos?.[0]?.url || body?.data?.videos?.[0]?.url;
372
+ if (status === 'completed' && videoUrl) {
373
+ // Fetch the related instruction to get upload collection
374
+ const instructions = await req.payload.findByID({
375
+ id: instructionId,
376
+ collection: PLUGIN_INSTRUCTIONS_TABLE,
377
+ req
378
+ });
379
+ const uploadCollectionSlug = instructions['relation-to'];
380
+ const videoResp = await fetch(videoUrl);
381
+ if (!videoResp.ok) {
382
+ throw new Error(`Failed to fetch output: ${videoResp.status}`);
383
+ }
384
+ const buffer = Buffer.from(await videoResp.arrayBuffer());
385
+ const created = await req.payload.create({
369
386
  collection: uploadCollectionSlug,
370
- data: result.data,
371
- file: result.file,
387
+ data: {
388
+ alt: 'video generation'
389
+ },
390
+ file: {
391
+ name: 'video_generation.mp4',
392
+ data: buffer,
393
+ mimetype: 'video/mp4',
394
+ size: buffer.byteLength
395
+ },
396
+ overrideAccess: true,
372
397
  req
373
398
  });
399
+ // Persist the result on the AI Job record
400
+ if (jobDoc) {
401
+ await req.payload.update({
402
+ id: jobDoc.id,
403
+ collection: PLUGIN_AI_JOBS_TABLE,
404
+ data: {
405
+ progress: 100,
406
+ result_id: created?.id,
407
+ status: 'completed'
408
+ },
409
+ overrideAccess: true,
410
+ req
411
+ });
412
+ }
374
413
  }
375
- if (!assetData.id) {
376
- req.payload.logger.error('Error uploading generated media, is your media upload function correct?');
377
- throw new Error('Error uploading generated media!');
414
+ if (status === 'failed' && error) {
415
+ req.payload.logger.error(error, 'Video generation failed: ');
378
416
  }
379
417
  return new Response(JSON.stringify({
380
- result: {
381
- id: assetData.id,
382
- alt: assetData.alt
418
+ ok: true
419
+ }), {
420
+ headers: {
421
+ 'Content-Type': 'application/json'
383
422
  }
384
- }));
423
+ });
385
424
  } catch (error) {
386
- req.payload.logger.error(error, 'Error generating upload: ');
425
+ req.payload.logger.error(error, 'Error in videogen webhook: ');
387
426
  const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
388
427
  return new Response(JSON.stringify({
389
428
  error: message
@@ -391,12 +430,12 @@ export const endpoints = (pluginConfig)=>({
391
430
  headers: {
392
431
  'Content-Type': 'application/json'
393
432
  },
394
- status: message.includes('Authentication required') || message.includes('Insufficient permissions') ? 401 : 500
433
+ status: 500
395
434
  });
396
435
  }
397
436
  },
398
437
  method: 'post',
399
- path: PLUGIN_API_ENDPOINT_GENERATE_UPLOAD
438
+ path: PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK
400
439
  }
401
440
  });
402
441