@ai-stack/payloadcms 3.2.26 → 3.68.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{LICENSE.md → LICENSE} +1 -1
- package/README.md +218 -229
- package/dist/access/checkAccess.d.ts +4 -0
- package/dist/access/checkAccess.js +20 -0
- package/dist/access/checkAccess.js.map +1 -0
- package/dist/ai/core/generateObject.d.ts +7 -0
- package/dist/ai/core/generateObject.js +35 -0
- package/dist/ai/core/generateObject.js.map +1 -0
- package/dist/ai/core/generateText.d.ts +7 -0
- package/dist/ai/core/generateText.js +31 -0
- package/dist/ai/core/generateText.js.map +1 -0
- package/dist/ai/core/index.d.ts +11 -0
- package/dist/ai/core/index.js +10 -0
- package/dist/ai/core/index.js.map +1 -0
- package/dist/ai/core/media/generateMedia.d.ts +7 -0
- package/dist/ai/core/media/generateMedia.js +50 -0
- package/dist/ai/core/media/generateMedia.js.map +1 -0
- package/dist/ai/core/media/image/generateImage.d.ts +6 -0
- package/dist/ai/core/media/image/generateImage.js +41 -0
- package/dist/ai/core/media/image/generateImage.js.map +1 -0
- package/dist/ai/core/media/image/handlers/multimodal.d.ts +7 -0
- package/dist/ai/core/media/image/handlers/multimodal.js +100 -0
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -0
- package/dist/ai/core/media/image/handlers/standard.d.ts +7 -0
- package/dist/ai/core/media/image/handlers/standard.js +28 -0
- package/dist/ai/core/media/image/handlers/standard.js.map +1 -0
- package/dist/ai/core/media/image/index.d.ts +2 -0
- package/dist/ai/core/media/image/index.js +3 -0
- package/dist/ai/core/media/image/index.js.map +1 -0
- package/dist/ai/core/media/index.d.ts +2 -0
- package/dist/ai/core/media/index.js +3 -0
- package/dist/ai/core/media/index.js.map +1 -0
- package/dist/ai/core/media/speech/generateSpeech.d.ts +5 -0
- package/dist/ai/core/media/speech/generateSpeech.js +55 -0
- package/dist/ai/core/media/speech/generateSpeech.js.map +1 -0
- package/dist/ai/core/media/speech/index.d.ts +2 -0
- package/dist/ai/core/media/speech/index.js +3 -0
- package/dist/ai/core/media/speech/index.js.map +1 -0
- package/dist/ai/core/media/types.d.ts +74 -0
- package/dist/ai/core/media/types.js +5 -0
- package/dist/ai/core/media/types.js.map +1 -0
- package/dist/ai/core/media/utils.d.ts +11 -0
- package/dist/ai/core/media/utils.js +34 -0
- package/dist/ai/core/media/utils.js.map +1 -0
- package/dist/ai/core/media/video/generateVideo.d.ts +6 -0
- package/dist/ai/core/media/video/generateVideo.js +32 -0
- package/dist/ai/core/media/video/generateVideo.js.map +1 -0
- package/dist/ai/core/media/video/index.d.ts +2 -0
- package/dist/ai/core/media/video/index.js +3 -0
- package/dist/ai/core/media/video/index.js.map +1 -0
- package/dist/ai/core/streamObject.d.ts +7 -0
- package/dist/ai/core/streamObject.js +54 -0
- package/dist/ai/core/streamObject.js.map +1 -0
- package/dist/ai/core/streamText.d.ts +7 -0
- package/dist/ai/core/streamText.js +30 -0
- package/dist/ai/core/streamText.js.map +1 -0
- package/dist/ai/core/types.d.ts +85 -0
- package/dist/ai/core/types.js +5 -0
- package/dist/ai/core/types.js.map +1 -0
- package/dist/ai/index.d.ts +11 -0
- package/dist/ai/index.js +25 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/providers/blocks/anthropic.d.ts +2 -0
- package/dist/ai/providers/blocks/anthropic.js +223 -0
- package/dist/ai/providers/blocks/anthropic.js.map +1 -0
- package/dist/ai/providers/blocks/elevenlabs.d.ts +2 -0
- package/dist/ai/providers/blocks/elevenlabs.js +449 -0
- package/dist/ai/providers/blocks/elevenlabs.js.map +1 -0
- package/dist/ai/providers/blocks/fal.d.ts +2 -0
- package/dist/ai/providers/blocks/fal.js +312 -0
- package/dist/ai/providers/blocks/fal.js.map +1 -0
- package/dist/ai/providers/blocks/google.d.ts +2 -0
- package/dist/ai/providers/blocks/google.js +623 -0
- package/dist/ai/providers/blocks/google.js.map +1 -0
- package/dist/ai/providers/blocks/index.d.ts +2 -0
- package/dist/ai/providers/blocks/index.js +18 -0
- package/dist/ai/providers/blocks/index.js.map +1 -0
- package/dist/ai/providers/blocks/openai-compatible.d.ts +2 -0
- package/dist/ai/providers/blocks/openai-compatible.js +308 -0
- package/dist/ai/providers/blocks/openai-compatible.js.map +1 -0
- package/dist/ai/providers/blocks/openai.d.ts +2 -0
- package/dist/ai/providers/blocks/openai.js +600 -0
- package/dist/ai/providers/blocks/openai.js.map +1 -0
- package/dist/ai/providers/blocks/xai.d.ts +2 -0
- package/dist/ai/providers/blocks/xai.js +247 -0
- package/dist/ai/providers/blocks/xai.js.map +1 -0
- package/dist/ai/providers/icons.d.ts +7 -0
- package/dist/ai/providers/icons.js +9 -0
- package/dist/ai/providers/icons.js.map +1 -0
- package/dist/ai/providers/index.d.ts +2 -0
- package/dist/ai/providers/index.js +6 -0
- package/dist/ai/providers/index.js.map +1 -0
- package/dist/ai/providers/registry.d.ts +40 -0
- package/dist/ai/providers/registry.js +267 -0
- package/dist/ai/providers/registry.js.map +1 -0
- package/dist/ai/providers/types.d.ts +115 -0
- package/dist/ai/providers/types.js +4 -0
- package/dist/ai/providers/types.js.map +1 -0
- package/dist/ai/utils/systemGenerate.d.ts +1 -1
- package/dist/ai/utils/systemGenerate.js +19 -19
- package/dist/ai/utils/systemGenerate.js.map +1 -1
- package/dist/collections/AIJobs.d.ts +2 -0
- package/dist/collections/AIJobs.js +81 -0
- package/dist/collections/AIJobs.js.map +1 -0
- package/dist/collections/AISettings.d.ts +2 -0
- package/dist/collections/AISettings.js +279 -0
- package/dist/collections/AISettings.js.map +1 -0
- package/dist/collections/Instructions.js +224 -50
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/defaults.d.ts +3 -0
- package/dist/defaults.js +3 -0
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/buildPromptUtils.d.ts +19 -0
- package/dist/endpoints/buildPromptUtils.js +114 -0
- package/dist/endpoints/buildPromptUtils.js.map +1 -0
- package/dist/endpoints/chat.d.ts +4 -0
- package/dist/endpoints/fetchFields.js +0 -7
- package/dist/endpoints/fetchFields.js.map +1 -1
- package/dist/endpoints/fetchVoices.d.ts +2 -0
- package/dist/endpoints/fetchVoices.js +79 -0
- package/dist/endpoints/fetchVoices.js.map +1 -0
- package/dist/endpoints/index.js +339 -232
- package/dist/endpoints/index.js.map +1 -1
- package/dist/exports/client.d.ts +9 -0
- package/dist/exports/client.js +9 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/fields.d.ts +1 -0
- package/dist/exports/fields.js +1 -0
- package/dist/exports/fields.js.map +1 -1
- package/dist/fields/ArrayComposeField/ArrayComposeField.d.ts +15 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js +87 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.js.map +1 -0
- package/dist/fields/ArrayComposeField/ArrayComposeField.jsx +73 -0
- package/dist/fields/ComposeField/ComposeField.js +2 -2
- package/dist/fields/ComposeField/ComposeField.js.map +1 -1
- package/dist/fields/ComposeField/ComposeField.jsx +2 -2
- package/dist/fields/PromptEditorField/PromptEditorField.js +162 -16
- package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
- package/dist/fields/PromptEditorField/PromptEditorField.jsx +123 -5
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init.js +63 -65
- package/dist/init.js.map +1 -1
- package/dist/payload-ai.d.ts +149 -0
- package/dist/plugin.js +94 -46
- package/dist/plugin.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.js +38 -7
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +30 -4
- package/dist/providers/InstructionsProvider/context.d.ts +1 -0
- package/dist/providers/InstructionsProvider/context.js +1 -0
- package/dist/providers/InstructionsProvider/context.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js +30 -10
- package/dist/providers/InstructionsProvider/useInstructions.js.map +1 -1
- package/dist/styles.d.ts +11 -0
- package/dist/types/handlebars-async-helpers.d.ts +1 -0
- package/dist/types/handlebars-dist-handlebars.d.ts +1 -0
- package/dist/types/react-mentions.d.ts +1 -0
- package/dist/types.d.ts +6 -16
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.d.ts +2 -0
- package/dist/ui/AIConfigDashboard/index.js +46 -0
- package/dist/ui/AIConfigDashboard/index.js.map +1 -0
- package/dist/ui/AIConfigDashboard/index.jsx +24 -0
- package/dist/ui/ApiKeyStatusIndicator/index.d.ts +6 -0
- package/dist/ui/ApiKeyStatusIndicator/index.js +39 -0
- package/dist/ui/ApiKeyStatusIndicator/index.js.map +1 -0
- package/dist/ui/ApiKeyStatusIndicator/index.jsx +29 -0
- package/dist/ui/Compose/Compose.d.ts +2 -2
- package/dist/ui/Compose/Compose.js +118 -92
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +113 -103
- package/dist/ui/Compose/ComposePlaceholder.d.ts +7 -0
- package/dist/ui/Compose/ComposePlaceholder.js +78 -0
- package/dist/ui/Compose/ComposePlaceholder.js.map +1 -0
- package/dist/ui/Compose/ComposePlaceholder.jsx +66 -0
- package/dist/ui/Compose/UndoRedoActions.d.ts +2 -2
- package/dist/ui/Compose/UndoRedoActions.js +11 -6
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.jsx +8 -6
- package/dist/ui/Compose/compose.module.css +57 -17
- package/dist/ui/Compose/hooks/menu/itemsMap.js +13 -7
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.d.ts +2 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js +28 -17
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.jsx +27 -14
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js +69 -10
- package/dist/ui/Compose/hooks/useActiveFieldTracking.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerate.d.ts +3 -0
- package/dist/ui/Compose/hooks/useGenerate.js +71 -11
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useHistory.d.ts +0 -1
- package/dist/ui/Compose/hooks/useHistory.js +113 -26
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/ui/DynamicModelSelect/index.d.ts +7 -0
- package/dist/ui/DynamicModelSelect/index.js +231 -0
- package/dist/ui/DynamicModelSelect/index.js.map +1 -0
- package/dist/ui/DynamicModelSelect/index.jsx +207 -0
- package/dist/ui/DynamicProviderSelect/index.d.ts +7 -0
- package/dist/ui/DynamicProviderSelect/index.js +101 -0
- package/dist/ui/DynamicProviderSelect/index.js.map +1 -0
- package/dist/ui/DynamicProviderSelect/index.jsx +90 -0
- package/dist/ui/DynamicVoiceSelect/index.d.ts +7 -0
- package/dist/ui/DynamicVoiceSelect/index.js +156 -0
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -0
- package/dist/ui/DynamicVoiceSelect/index.jsx +102 -0
- package/dist/ui/EncryptedTextField/index.d.ts +8 -0
- package/dist/ui/EncryptedTextField/index.js +74 -0
- package/dist/ui/EncryptedTextField/index.js.map +1 -0
- package/dist/ui/EncryptedTextField/index.jsx +35 -0
- package/dist/ui/Icons/LottieAnimation.js +3 -1
- package/dist/ui/Icons/LottieAnimation.js.map +1 -1
- package/dist/ui/Icons/LottieAnimation.jsx +2 -1
- package/dist/ui/ModelRowLabel/index.d.ts +6 -0
- package/dist/ui/ModelRowLabel/index.js +41 -0
- package/dist/ui/ModelRowLabel/index.js.map +1 -0
- package/dist/ui/ModelRowLabel/index.jsx +26 -0
- package/dist/ui/ProviderOptionsEditor/index.d.ts +7 -0
- package/dist/ui/ProviderOptionsEditor/index.js +291 -0
- package/dist/ui/ProviderOptionsEditor/index.js.map +1 -0
- package/dist/ui/ProviderOptionsEditor/index.jsx +210 -0
- package/dist/ui/VoicesFetcher/index.d.ts +7 -0
- package/dist/ui/VoicesFetcher/index.js +118 -0
- package/dist/ui/VoicesFetcher/index.js.map +1 -0
- package/dist/ui/VoicesFetcher/index.jsx +79 -0
- package/dist/utilities/buildSmartPrompt.d.ts +22 -0
- package/dist/utilities/buildSmartPrompt.js +143 -0
- package/dist/utilities/buildSmartPrompt.js.map +1 -0
- package/dist/utilities/encryption.d.ts +2 -0
- package/dist/utilities/encryption.js +47 -0
- package/dist/utilities/encryption.js.map +1 -0
- package/dist/utilities/extractImageData.d.ts +9 -0
- package/dist/utilities/extractImageData.js +12 -2
- package/dist/utilities/extractImageData.js.map +1 -1
- package/dist/utilities/fetchImages.d.ts +14 -0
- package/dist/utilities/fetchImages.js +38 -0
- package/dist/utilities/fetchImages.js.map +1 -0
- package/dist/utilities/fieldToJsonSchema.d.ts +2 -1
- package/dist/utilities/fieldToJsonSchema.js +66 -3
- package/dist/utilities/fieldToJsonSchema.js.map +1 -1
- package/dist/utilities/getFieldBySchemaPath.d.ts +2 -2
- package/dist/utilities/getFieldBySchemaPath.js +15 -0
- package/dist/utilities/getFieldBySchemaPath.js.map +1 -1
- package/dist/utilities/getProviderOptionsFields.d.ts +16 -0
- package/dist/utilities/getProviderOptionsFields.js +80 -0
- package/dist/utilities/getProviderOptionsFields.js.map +1 -0
- package/dist/utilities/isPluginActivated.js +1 -2
- package/dist/utilities/isPluginActivated.js.map +1 -1
- package/dist/utilities/lexicalToHTML.js.map +1 -1
- package/dist/utilities/resolveImageReferences.d.ts +30 -0
- package/dist/utilities/resolveImageReferences.js +167 -0
- package/dist/utilities/resolveImageReferences.js.map +1 -0
- package/dist/utilities/schemaConverter.d.ts +3 -0
- package/dist/utilities/schemaConverter.js +93 -0
- package/dist/utilities/schemaConverter.js.map +1 -0
- package/dist/utilities/setSafeLexicalState.d.ts +1 -3
- package/dist/utilities/setSafeLexicalState.js +1 -1
- package/dist/utilities/setSafeLexicalState.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.js +27 -43
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +23 -24
- package/dist/ai/models/anthropic/index.d.ts +0 -2
- package/dist/ai/models/anthropic/index.js +0 -129
- package/dist/ai/models/anthropic/index.js.map +0 -1
- package/dist/ai/models/elevenLabs/generateVoice.d.ts +0 -8
- package/dist/ai/models/elevenLabs/generateVoice.js +0 -20
- package/dist/ai/models/elevenLabs/generateVoice.js.map +0 -1
- package/dist/ai/models/elevenLabs/index.d.ts +0 -2
- package/dist/ai/models/elevenLabs/index.js +0 -133
- package/dist/ai/models/elevenLabs/index.js.map +0 -1
- package/dist/ai/models/elevenLabs/voices.d.ts +0 -8
- package/dist/ai/models/elevenLabs/voices.js +0 -24
- package/dist/ai/models/elevenLabs/voices.js.map +0 -1
- package/dist/ai/models/generateObject.d.ts +0 -11
- package/dist/ai/models/generateObject.js +0 -22
- package/dist/ai/models/generateObject.js.map +0 -1
- package/dist/ai/models/google/generateImage.d.ts +0 -9
- package/dist/ai/models/google/generateImage.js +0 -27
- package/dist/ai/models/google/generateImage.js.map +0 -1
- package/dist/ai/models/google/index.d.ts +0 -2
- package/dist/ai/models/google/index.js +0 -201
- package/dist/ai/models/google/index.js.map +0 -1
- package/dist/ai/models/index.d.ts +0 -2
- package/dist/ai/models/index.js +0 -13
- package/dist/ai/models/index.js.map +0 -1
- package/dist/ai/models/openai/generateImage.d.ts +0 -5
- package/dist/ai/models/openai/generateImage.js +0 -31
- package/dist/ai/models/openai/generateImage.js.map +0 -1
- package/dist/ai/models/openai/generateVoice.d.ts +0 -6
- package/dist/ai/models/openai/generateVoice.js +0 -19
- package/dist/ai/models/openai/generateVoice.js.map +0 -1
- package/dist/ai/models/openai/index.d.ts +0 -2
- package/dist/ai/models/openai/index.js +0 -428
- package/dist/ai/models/openai/index.js.map +0 -1
- package/dist/ai/models/openai/openai.d.ts +0 -1
- package/dist/ai/models/openai/openai.js +0 -8
- package/dist/ai/models/openai/openai.js.map +0 -1
- package/dist/ai/utils/editImagesWithOpenAI.d.ts +0 -10
- package/dist/ai/utils/editImagesWithOpenAI.js +0 -37
- package/dist/ai/utils/editImagesWithOpenAI.js.map +0 -1
- package/dist/styles.d.js +0 -2
- package/dist/styles.d.js.map +0 -1
- package/dist/types/handlebars-async-helpers.d.js +0 -2
- package/dist/types/handlebars-async-helpers.d.js.map +0 -1
- package/dist/types/handlebars-dist-handlebars.d.js +0 -2
- package/dist/types/handlebars-dist-handlebars.d.js.map +0 -1
- package/dist/types/react-mentions.d.js +0 -2
- package/dist/types/react-mentions.d.js.map +0 -1
- package/dist/utilities/getGenerationModels.d.ts +0 -2
- package/dist/utilities/getGenerationModels.js +0 -10
- package/dist/utilities/getGenerationModels.js.map +0 -1
package/dist/endpoints/index.js
CHANGED
|
@@ -1,144 +1,19 @@
|
|
|
1
1
|
import * as process from 'node:process';
|
|
2
|
-
import {
|
|
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';
|
|
7
|
+
import { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js';
|
|
9
8
|
import { extractImageData } from '../utilities/extractImageData.js';
|
|
9
|
+
import { fetchImages } from '../utilities/fetchImages.js';
|
|
10
10
|
import { fieldToJsonSchema } from '../utilities/fieldToJsonSchema.js';
|
|
11
11
|
import { getFieldBySchemaPath } from '../utilities/getFieldBySchemaPath.js';
|
|
12
|
-
import {
|
|
13
|
-
|
|
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
|
|
137
|
-
};
|
|
138
|
-
};
|
|
12
|
+
import { resolveImageReferences } from '../utilities/resolveImageReferences.js';
|
|
13
|
+
import { assignPrompt, extendContextWithPromptFields } from './buildPromptUtils.js';
|
|
139
14
|
export const endpoints = (pluginConfig)=>({
|
|
140
15
|
textarea: {
|
|
141
|
-
//
|
|
16
|
+
// Text/rich-text generation endpoint using payload.ai.streamObject
|
|
142
17
|
handler: async (req)=>{
|
|
143
18
|
try {
|
|
144
19
|
// Check authentication and authorization first
|
|
@@ -150,15 +25,10 @@ export const endpoints = (pluginConfig)=>({
|
|
|
150
25
|
if (!instructionId) {
|
|
151
26
|
throw new Error(`Instruction ID is required for "${PLUGIN_NAME}" to work, please check your configuration, or try again`);
|
|
152
27
|
}
|
|
153
|
-
const { defaultLocale, locales = [] } = req.payload.config.localization || {};
|
|
154
|
-
const localeData = locales.find((l)=>{
|
|
155
|
-
return l.code === locale;
|
|
156
|
-
});
|
|
157
28
|
// Verify user has access to the specific instruction
|
|
158
29
|
const instructions = await req.payload.findByID({
|
|
159
30
|
id: instructionId,
|
|
160
31
|
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
161
|
-
locale: locales.length > 0 && locale ? locale : undefined,
|
|
162
32
|
req
|
|
163
33
|
});
|
|
164
34
|
const { collections } = req.payload.config;
|
|
@@ -168,30 +38,38 @@ export const endpoints = (pluginConfig)=>({
|
|
|
168
38
|
}
|
|
169
39
|
const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection.admin;
|
|
170
40
|
const { schema: editorSchema = {} } = editorConfig;
|
|
171
|
-
|
|
41
|
+
let { prompt: promptTemplate = '' } = instructions;
|
|
42
|
+
// Smart fallback: if prompt is generic, build a contextual prompt from field metadata
|
|
43
|
+
if (isGenericPrompt(promptTemplate)) {
|
|
44
|
+
const schemaPath = String(instructions['schema-path']);
|
|
45
|
+
promptTemplate = buildSmartPrompt({
|
|
46
|
+
documentData: contextData,
|
|
47
|
+
payload: req.payload,
|
|
48
|
+
schemaPath
|
|
49
|
+
});
|
|
50
|
+
if (pluginConfig.debugging) {
|
|
51
|
+
req.payload.logger.info({
|
|
52
|
+
smartPrompt: promptTemplate
|
|
53
|
+
}, `— AI Plugin: Using smart fallback prompt for ${schemaPath}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
172
56
|
let allowedEditorSchema = editorSchema;
|
|
173
57
|
if (allowedEditorNodes.length) {
|
|
174
58
|
allowedEditorSchema = filterEditorSchemaByNodes(editorSchema, allowedEditorNodes);
|
|
175
59
|
}
|
|
176
|
-
const schemaPath = instructions['schema-path'];
|
|
177
|
-
const parts = schemaPath
|
|
60
|
+
const schemaPath = String(instructions['schema-path']);
|
|
61
|
+
const parts = (schemaPath || '').split('.') || [];
|
|
178
62
|
const collectionName = parts[0];
|
|
179
63
|
const fieldName = parts.length > 1 ? parts[parts.length - 1] : '';
|
|
180
64
|
registerEditorHelper(req.payload, schemaPath);
|
|
65
|
+
const { defaultLocale, locales = [] } = req.payload.config.localization || {};
|
|
66
|
+
const localeData = locales.find((l)=>{
|
|
67
|
+
return l.code === locale;
|
|
68
|
+
});
|
|
181
69
|
let localeInfo = locale;
|
|
182
70
|
if (localeData && defaultLocale && localeData.label && typeof localeData.label === 'object' && defaultLocale in localeData.label) {
|
|
183
71
|
localeInfo = localeData.label[defaultLocale];
|
|
184
72
|
}
|
|
185
|
-
const models = getGenerationModels(pluginConfig);
|
|
186
|
-
const model = models && Array.isArray(models) ? models.find((model)=>model.id === instructions['model-id']) : undefined;
|
|
187
|
-
if (!model) {
|
|
188
|
-
throw new Error('Model not found');
|
|
189
|
-
}
|
|
190
|
-
const settingsName = model.settings && 'name' in model.settings ? model.settings.name : undefined;
|
|
191
|
-
if (!settingsName) {
|
|
192
|
-
req.payload.logger.error('— AI Plugin: Error fetching settings name!');
|
|
193
|
-
}
|
|
194
|
-
const modelOptions = settingsName ? instructions[settingsName] || {} : {};
|
|
195
73
|
const prompts = await assignPrompt(action, {
|
|
196
74
|
type: String(instructions['field-type']),
|
|
197
75
|
actionParams,
|
|
@@ -207,17 +85,16 @@ export const endpoints = (pluginConfig)=>({
|
|
|
207
85
|
if (pluginConfig.debugging) {
|
|
208
86
|
req.payload.logger.info({
|
|
209
87
|
prompts
|
|
210
|
-
}, `— AI Plugin: Executing text prompt on ${schemaPath}
|
|
88
|
+
}, `— AI Plugin: Executing text prompt on ${schemaPath}`);
|
|
211
89
|
}
|
|
212
90
|
// Build per-field JSON schema for structured generation when applicable
|
|
213
91
|
let jsonSchema = allowedEditorSchema;
|
|
214
92
|
try {
|
|
215
93
|
const targetCollection = req.payload.config.collections.find((c)=>c.slug === collectionName);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (targetConfig && fieldName) {
|
|
219
|
-
const targetField = getFieldBySchemaPath(targetConfig, schemaPath);
|
|
94
|
+
if (targetCollection && fieldName) {
|
|
95
|
+
const targetField = getFieldBySchemaPath(targetCollection, schemaPath);
|
|
220
96
|
const supported = [
|
|
97
|
+
'array',
|
|
221
98
|
'text',
|
|
222
99
|
'textarea',
|
|
223
100
|
'select',
|
|
@@ -229,21 +106,64 @@ export const endpoints = (pluginConfig)=>({
|
|
|
229
106
|
];
|
|
230
107
|
const t = String(targetField?.type || '');
|
|
231
108
|
if (targetField && supported.includes(t)) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
109
|
+
// For array fields, use count from array-settings if available
|
|
110
|
+
if (t === 'array') {
|
|
111
|
+
const arraySettings = instructions['array-settings'] || {};
|
|
112
|
+
const count = arraySettings.count || 3;
|
|
113
|
+
// Override the field's maxRows with the requested count
|
|
114
|
+
const modifiedField = {
|
|
115
|
+
...targetField,
|
|
116
|
+
maxRows: count,
|
|
117
|
+
minRows: count
|
|
118
|
+
};
|
|
119
|
+
jsonSchema = fieldToJsonSchema(modifiedField, {
|
|
120
|
+
nameOverride: fieldName
|
|
121
|
+
});
|
|
122
|
+
} else {
|
|
123
|
+
jsonSchema = fieldToJsonSchema(targetField, {
|
|
124
|
+
nameOverride: fieldName
|
|
125
|
+
});
|
|
126
|
+
}
|
|
235
127
|
}
|
|
236
128
|
}
|
|
237
129
|
} catch (e) {
|
|
238
130
|
req.payload.logger.error(e, '— AI Plugin: Error building field JSON schema');
|
|
239
131
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
132
|
+
// Get model settings from instruction
|
|
133
|
+
const settingsName = instructions['model-id'] === 'richtext' ? 'richtext-settings' : instructions['model-id'] === 'text' ? 'text-settings' : instructions['model-id'] === 'array' ? 'array-settings' : undefined;
|
|
134
|
+
if (!settingsName) {
|
|
135
|
+
throw new Error(`Unsupported model-id: ${instructions['model-id']}`);
|
|
136
|
+
}
|
|
137
|
+
const modelSettings = instructions[settingsName] || {};
|
|
138
|
+
// Resolve @field:filename references from the prompt
|
|
139
|
+
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(prompts.prompt, contextData, req, collectionName);
|
|
140
|
+
// Extract hardcoded URLs from the processed prompt
|
|
141
|
+
const hardcodedImages = extractImageData(processedPrompt);
|
|
142
|
+
// Combine images
|
|
143
|
+
const allImages = [
|
|
144
|
+
...hardcodedImages,
|
|
145
|
+
...resolvedImages
|
|
146
|
+
];
|
|
147
|
+
let images;
|
|
148
|
+
if (allImages.length > 0) {
|
|
149
|
+
const imageParts = await fetchImages(req, allImages);
|
|
150
|
+
if (imageParts.length > 0) {
|
|
151
|
+
images = imageParts;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Use payload.ai.streamObject directly! 🎉
|
|
155
|
+
const streamResult = await req.payload.ai.streamObject({
|
|
156
|
+
// extractAttachments: modelSettings.extractAttachments as boolean | undefined,
|
|
157
|
+
images,
|
|
158
|
+
maxTokens: modelSettings.maxTokens,
|
|
159
|
+
model: modelSettings.model,
|
|
160
|
+
prompt: processedPrompt,
|
|
161
|
+
provider: modelSettings.provider,
|
|
244
162
|
schema: jsonSchema,
|
|
245
|
-
system: prompts.system
|
|
163
|
+
system: prompts.system,
|
|
164
|
+
temperature: modelSettings.temperature
|
|
246
165
|
});
|
|
166
|
+
return streamResult;
|
|
247
167
|
} catch (error) {
|
|
248
168
|
req.payload.logger.error(error, 'Error generating content: ');
|
|
249
169
|
const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
|
|
@@ -261,6 +181,7 @@ export const endpoints = (pluginConfig)=>({
|
|
|
261
181
|
path: PLUGIN_API_ENDPOINT_GENERATE
|
|
262
182
|
},
|
|
263
183
|
upload: {
|
|
184
|
+
// Image/video generation endpoint using payload.ai.generateMedia
|
|
264
185
|
handler: async (req)=>{
|
|
265
186
|
try {
|
|
266
187
|
// Check authentication and authorization first
|
|
@@ -291,107 +212,293 @@ export const endpoints = (pluginConfig)=>({
|
|
|
291
212
|
prompt: ''
|
|
292
213
|
};
|
|
293
214
|
if (instructionId) {
|
|
294
|
-
// Get locale from request if available
|
|
295
|
-
const { locale: requestLocale } = data;
|
|
296
|
-
const { locales = [] } = req.payload.config.localization || {};
|
|
297
215
|
// Verify user has access to the specific instruction
|
|
298
|
-
// Pass locale if localization is enabled for the Instructions collection
|
|
299
216
|
instructions = await req.payload.findByID({
|
|
300
217
|
id: instructionId,
|
|
301
218
|
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
302
|
-
locale: locales.length > 0 && requestLocale ? requestLocale : undefined,
|
|
303
219
|
req
|
|
304
220
|
});
|
|
305
221
|
}
|
|
306
|
-
|
|
307
|
-
const
|
|
222
|
+
let { prompt: promptTemplate = '' } = instructions;
|
|
223
|
+
const { images: sampleImages = [] } = instructions;
|
|
224
|
+
const schemaPath = String(instructions['schema-path']);
|
|
308
225
|
registerEditorHelper(req.payload, schemaPath);
|
|
226
|
+
// Smart fallback: if prompt is generic, build a contextual prompt from field metadata
|
|
227
|
+
if (isGenericPrompt(promptTemplate)) {
|
|
228
|
+
promptTemplate = buildSmartPrompt({
|
|
229
|
+
documentData: contextData,
|
|
230
|
+
payload: req.payload,
|
|
231
|
+
schemaPath
|
|
232
|
+
});
|
|
233
|
+
if (pluginConfig.debugging) {
|
|
234
|
+
req.payload.logger.info({
|
|
235
|
+
smartPrompt: promptTemplate
|
|
236
|
+
}, `— AI Plugin: Using smart fallback prompt for ${schemaPath}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
309
239
|
const extendedContext = extendContextWithPromptFields(contextData, {
|
|
310
|
-
type: instructions['field-type'],
|
|
240
|
+
type: String(instructions['field-type']),
|
|
311
241
|
collection: collectionSlug
|
|
312
242
|
}, pluginConfig);
|
|
313
243
|
const text = await replacePlaceholders(promptTemplate, extendedContext);
|
|
314
244
|
const modelId = instructions['model-id'];
|
|
315
245
|
const uploadCollectionSlug = instructions['relation-to'];
|
|
246
|
+
// Resolve @field:filename references from the prompt
|
|
247
|
+
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(text, contextData, req, collectionSlug);
|
|
248
|
+
// Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images
|
|
316
249
|
const images = [
|
|
317
|
-
...extractImageData(
|
|
250
|
+
...extractImageData(processedPrompt),
|
|
251
|
+
...resolvedImages,
|
|
318
252
|
...sampleImages
|
|
319
253
|
];
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
254
|
+
// Process images - convert to ImagePart format using helper
|
|
255
|
+
const editImages = await fetchImages(req, images);
|
|
256
|
+
if (pluginConfig.debugging) {
|
|
257
|
+
req.payload.logger.info({
|
|
258
|
+
text
|
|
259
|
+
}, `— AI Plugin: Executing media generation`);
|
|
260
|
+
}
|
|
261
|
+
// Prepare callback URL for async jobs
|
|
262
|
+
const serverURL = req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL;
|
|
263
|
+
const callbackUrl = serverURL ? `${serverURL.replace(/\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}` : undefined;
|
|
264
|
+
// Get model settings
|
|
265
|
+
const settingsName = modelId === 'image' ? 'image-settings' : modelId === 'video' ? 'video-settings' : modelId === 'tts' ? 'tts-settings' : undefined;
|
|
266
|
+
if (!settingsName) {
|
|
267
|
+
throw new Error(`Unsupported model-id: ${modelId}`);
|
|
268
|
+
}
|
|
269
|
+
// Get model settings from instruction
|
|
270
|
+
const instructionSettings = instructions[settingsName] || {};
|
|
271
|
+
// Fallback to AISettings global defaults if instruction-level settings are missing
|
|
272
|
+
let globalDefaults = {};
|
|
273
|
+
if (!instructionSettings.provider || !instructionSettings.model) {
|
|
327
274
|
try {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
method: 'GET'
|
|
334
|
-
});
|
|
335
|
-
const blob = await response.blob();
|
|
336
|
-
editImages.push({
|
|
337
|
-
name: img.image.name,
|
|
338
|
-
type: img.image.type,
|
|
339
|
-
data: blob,
|
|
340
|
-
size: blob.size,
|
|
341
|
-
url
|
|
275
|
+
const aiSettings = await req.payload.findGlobal({
|
|
276
|
+
slug: 'ai-settings',
|
|
277
|
+
context: {
|
|
278
|
+
unsafe: true
|
|
279
|
+
}
|
|
342
280
|
});
|
|
281
|
+
// Map modelId to the corresponding default settings key
|
|
282
|
+
const defaultsKey = modelId === 'image' ? 'image' : modelId === 'video' ? 'video' : modelId === 'tts' ? 'tts' : undefined;
|
|
283
|
+
if (defaultsKey && aiSettings?.defaults?.[defaultsKey]) {
|
|
284
|
+
globalDefaults = aiSettings.defaults[defaultsKey];
|
|
285
|
+
if (pluginConfig.debugging) {
|
|
286
|
+
req.payload.logger.info({
|
|
287
|
+
globalDefaults
|
|
288
|
+
}, `— AI Plugin: Using AISettings defaults for ${modelId}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
343
291
|
} catch (e) {
|
|
344
|
-
req.payload.logger.error(e,
|
|
345
|
-
throw Error("We couldn't fetch the images. Please ensure the images are accessible and hosted publicly.");
|
|
292
|
+
req.payload.logger.error(e, '— AI Plugin: Error fetching AISettings defaults');
|
|
346
293
|
}
|
|
347
294
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
295
|
+
// Merge: instruction settings take priority over global defaults
|
|
296
|
+
// Filter out null/undefined values so they don't overwrite valid defaults
|
|
297
|
+
const filteredInstructionSettings = Object.fromEntries(Object.entries(instructionSettings).filter(([_, v])=>v != null));
|
|
298
|
+
const modelSettings = {
|
|
299
|
+
...globalDefaults,
|
|
300
|
+
...filteredInstructionSettings
|
|
301
|
+
};
|
|
302
|
+
// Use payload.ai.generateMedia directly! 🎉
|
|
303
|
+
const result = await req.payload.ai.generateMedia({
|
|
304
|
+
callbackUrl,
|
|
305
|
+
images: editImages,
|
|
306
|
+
instructionId,
|
|
307
|
+
model: modelSettings.model,
|
|
308
|
+
prompt: text,
|
|
309
|
+
provider: modelSettings.provider,
|
|
310
|
+
...modelSettings
|
|
311
|
+
});
|
|
312
|
+
// If model returned a file immediately, proceed with upload
|
|
313
|
+
if (result && 'file' in result) {
|
|
314
|
+
let assetData;
|
|
315
|
+
if (typeof pluginConfig.mediaUpload === 'function') {
|
|
316
|
+
assetData = await pluginConfig.mediaUpload(result, {
|
|
317
|
+
collection: uploadCollectionSlug,
|
|
318
|
+
request: req
|
|
319
|
+
});
|
|
320
|
+
} else {
|
|
321
|
+
assetData = await req.payload.create({
|
|
322
|
+
collection: uploadCollectionSlug,
|
|
323
|
+
data: {
|
|
324
|
+
alt: text
|
|
325
|
+
},
|
|
326
|
+
file: result.file,
|
|
327
|
+
req
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (!assetData.id) {
|
|
331
|
+
req.payload.logger.error('Error uploading generated media, is your media upload function correct?');
|
|
332
|
+
throw new Error('Error uploading generated media!');
|
|
333
|
+
}
|
|
334
|
+
return new Response(JSON.stringify({
|
|
335
|
+
result: {
|
|
336
|
+
id: assetData.id,
|
|
337
|
+
alt: assetData.alt
|
|
338
|
+
}
|
|
339
|
+
}));
|
|
352
340
|
}
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
341
|
+
// Otherwise, assume async job launch
|
|
342
|
+
if (result && ('jobId' in result || 'taskId' in result)) {
|
|
343
|
+
const externalTaskId = result.jobId || result.taskId;
|
|
344
|
+
const status = result.status || 'queued';
|
|
345
|
+
const progress = result.progress ?? 0;
|
|
346
|
+
// Create AI Job doc and return only its id
|
|
347
|
+
const createdJob = await req.payload.create({
|
|
348
|
+
collection: PLUGIN_AI_JOBS_TABLE,
|
|
349
|
+
data: {
|
|
350
|
+
instructionId,
|
|
351
|
+
progress,
|
|
352
|
+
status,
|
|
353
|
+
task_id: externalTaskId
|
|
354
|
+
},
|
|
355
|
+
overrideAccess: true,
|
|
356
|
+
req
|
|
357
|
+
});
|
|
358
|
+
return new Response(JSON.stringify({
|
|
359
|
+
job: {
|
|
360
|
+
id: createdJob.id
|
|
361
|
+
}
|
|
362
|
+
}), {
|
|
363
|
+
headers: {
|
|
364
|
+
'Content-Type': 'application/json'
|
|
365
|
+
}
|
|
366
|
+
});
|
|
357
367
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
368
|
+
throw new Error('Unexpected model response.');
|
|
369
|
+
} catch (error) {
|
|
370
|
+
req.payload.logger.error(// @ts-expect-error
|
|
371
|
+
error?.type || error.message, 'Error generating upload: ');
|
|
372
|
+
const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
|
|
373
|
+
return new Response(JSON.stringify({
|
|
374
|
+
error: message
|
|
375
|
+
}), {
|
|
376
|
+
headers: {
|
|
377
|
+
'Content-Type': 'application/json'
|
|
378
|
+
},
|
|
379
|
+
status: message.includes('Authentication required') || message.includes('Insufficient permissions') ? 401 : 500
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
method: 'post',
|
|
384
|
+
path: PLUGIN_API_ENDPOINT_GENERATE_UPLOAD
|
|
385
|
+
},
|
|
386
|
+
videogenWebhook: {
|
|
387
|
+
handler: async (req)=>{
|
|
388
|
+
console.log('videogenWebhook --> ', req);
|
|
389
|
+
try {
|
|
390
|
+
const urlAll = new URL(req.url || '');
|
|
391
|
+
const qpSecret = urlAll.searchParams.get('secret') || '';
|
|
392
|
+
const headerSecret = req.headers.get('x-webhook-secret') || '';
|
|
393
|
+
const falSecret = process.env.FAL_WEBHOOK_SECRET;
|
|
394
|
+
const legacySecret = process.env.VIDEOGEN_WEBHOOK_SECRET;
|
|
395
|
+
const provided = qpSecret || headerSecret;
|
|
396
|
+
// TODO: fal is failing because of auth but webhook seem to work
|
|
397
|
+
if (!provided || (falSecret ? provided !== falSecret : provided !== legacySecret)) {
|
|
398
|
+
return new Response('Unauthorized', {
|
|
399
|
+
status: 401
|
|
400
|
+
});
|
|
367
401
|
}
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
402
|
+
const instructionId = urlAll.searchParams.get('instructionId');
|
|
403
|
+
if (!instructionId) {
|
|
404
|
+
throw new Error('instructionId missing');
|
|
405
|
+
}
|
|
406
|
+
const body = await req.json?.();
|
|
407
|
+
// Normalize fal webhook payload
|
|
408
|
+
const status = body && (body.status || body.data?.status || body.response?.status) || undefined;
|
|
409
|
+
const progress = (body && (body.progress ?? body.data?.progress ?? body.response?.progress)) ?? undefined;
|
|
410
|
+
const requestId = body && (body.taskId || body.request_id || body.gateway_request_id || body.request?.request_id) || undefined;
|
|
411
|
+
const error = body?.error || body?.data?.error || body?.response?.error;
|
|
412
|
+
// Update AI Job row by task_id (and instructionId)
|
|
413
|
+
const jobSearch = await req.payload.find({
|
|
414
|
+
collection: PLUGIN_AI_JOBS_TABLE,
|
|
415
|
+
depth: 0,
|
|
416
|
+
limit: 1,
|
|
417
|
+
where: {
|
|
418
|
+
and: [
|
|
419
|
+
{
|
|
420
|
+
task_id: {
|
|
421
|
+
equals: requestId
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
instructionId: {
|
|
426
|
+
equals: instructionId
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
const jobDoc = jobSearch.docs?.[0];
|
|
433
|
+
if (jobDoc) {
|
|
434
|
+
await req.payload.update({
|
|
435
|
+
id: jobDoc.id,
|
|
436
|
+
collection: PLUGIN_AI_JOBS_TABLE,
|
|
437
|
+
data: {
|
|
438
|
+
progress,
|
|
439
|
+
status,
|
|
440
|
+
task_id: requestId
|
|
441
|
+
},
|
|
442
|
+
overrideAccess: true,
|
|
443
|
+
req
|
|
374
444
|
});
|
|
375
|
-
}
|
|
376
|
-
|
|
445
|
+
}
|
|
446
|
+
console.log('fal webhook body: ', body);
|
|
447
|
+
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;
|
|
448
|
+
if (status === 'completed' && videoUrl) {
|
|
449
|
+
// Fetch the related instruction to get upload collection
|
|
450
|
+
const instructions = await req.payload.findByID({
|
|
451
|
+
id: instructionId,
|
|
452
|
+
collection: PLUGIN_INSTRUCTIONS_TABLE,
|
|
453
|
+
req
|
|
454
|
+
});
|
|
455
|
+
const uploadCollectionSlug = instructions['relation-to'];
|
|
456
|
+
const videoResp = await fetch(videoUrl);
|
|
457
|
+
if (!videoResp.ok) {
|
|
458
|
+
throw new Error(`Failed to fetch output: ${videoResp.status}`);
|
|
459
|
+
}
|
|
460
|
+
const buffer = Buffer.from(await videoResp.arrayBuffer());
|
|
461
|
+
const created = await req.payload.create({
|
|
377
462
|
collection: uploadCollectionSlug,
|
|
378
|
-
data:
|
|
379
|
-
|
|
463
|
+
data: {
|
|
464
|
+
alt: 'video generation'
|
|
465
|
+
},
|
|
466
|
+
file: {
|
|
467
|
+
name: 'video_generation.mp4',
|
|
468
|
+
data: buffer,
|
|
469
|
+
mimetype: 'video/mp4',
|
|
470
|
+
size: buffer.byteLength
|
|
471
|
+
},
|
|
472
|
+
overrideAccess: true,
|
|
380
473
|
req
|
|
381
474
|
});
|
|
475
|
+
// Persist the result on the AI Job record
|
|
476
|
+
if (jobDoc) {
|
|
477
|
+
await req.payload.update({
|
|
478
|
+
id: jobDoc.id,
|
|
479
|
+
collection: PLUGIN_AI_JOBS_TABLE,
|
|
480
|
+
data: {
|
|
481
|
+
progress: 100,
|
|
482
|
+
result_id: created?.id,
|
|
483
|
+
status: 'completed'
|
|
484
|
+
},
|
|
485
|
+
overrideAccess: true,
|
|
486
|
+
req
|
|
487
|
+
});
|
|
488
|
+
}
|
|
382
489
|
}
|
|
383
|
-
if (
|
|
384
|
-
req.payload.logger.error(
|
|
385
|
-
throw new Error('Error uploading generated media!');
|
|
490
|
+
if (status === 'failed' && error) {
|
|
491
|
+
req.payload.logger.error(error, 'Video generation failed: ');
|
|
386
492
|
}
|
|
387
493
|
return new Response(JSON.stringify({
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
494
|
+
ok: true
|
|
495
|
+
}), {
|
|
496
|
+
headers: {
|
|
497
|
+
'Content-Type': 'application/json'
|
|
391
498
|
}
|
|
392
|
-
})
|
|
499
|
+
});
|
|
393
500
|
} catch (error) {
|
|
394
|
-
req.payload.logger.error(error, 'Error
|
|
501
|
+
req.payload.logger.error(error, 'Error in videogen webhook: ');
|
|
395
502
|
const message = error && typeof error === 'object' && 'message' in error ? error.message : String(error);
|
|
396
503
|
return new Response(JSON.stringify({
|
|
397
504
|
error: message
|
|
@@ -399,12 +506,12 @@ export const endpoints = (pluginConfig)=>({
|
|
|
399
506
|
headers: {
|
|
400
507
|
'Content-Type': 'application/json'
|
|
401
508
|
},
|
|
402
|
-
status:
|
|
509
|
+
status: 500
|
|
403
510
|
});
|
|
404
511
|
}
|
|
405
512
|
},
|
|
406
513
|
method: 'post',
|
|
407
|
-
path:
|
|
514
|
+
path: PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK
|
|
408
515
|
}
|
|
409
516
|
});
|
|
410
517
|
|