@ai-stack/payloadcms 3.68.0 → 3.76.0-beta.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.
- package/dist/ai/core/media/image/generateImage.js +2 -6
- package/dist/ai/core/media/image/generateImage.js.map +1 -1
- package/dist/ai/core/media/image/handlers/multimodal.js +5 -0
- package/dist/ai/core/media/image/handlers/multimodal.js.map +1 -1
- package/dist/ai/core/streamObject.js +3 -3
- package/dist/ai/core/streamObject.js.map +1 -1
- package/dist/ai/core/types.d.ts +3 -0
- package/dist/ai/core/types.js.map +1 -1
- package/dist/ai/prompts.d.ts +1 -2
- package/dist/ai/prompts.js +0 -110
- package/dist/ai/prompts.js.map +1 -1
- package/dist/ai/providers/blocks/anthropic.js +2 -1
- package/dist/ai/providers/blocks/anthropic.js.map +1 -1
- package/dist/ai/providers/blocks/elevenlabs.js +3 -2
- package/dist/ai/providers/blocks/elevenlabs.js.map +1 -1
- package/dist/ai/providers/blocks/fal.js +2 -1
- package/dist/ai/providers/blocks/fal.js.map +1 -1
- package/dist/ai/providers/blocks/google.js +11 -6
- package/dist/ai/providers/blocks/google.js.map +1 -1
- package/dist/ai/providers/blocks/openai-compatible.js +2 -1
- package/dist/ai/providers/blocks/openai-compatible.js.map +1 -1
- package/dist/ai/providers/blocks/openai.js +3 -2
- package/dist/ai/providers/blocks/openai.js.map +1 -1
- package/dist/ai/providers/blocks/xai.js +2 -1
- package/dist/ai/providers/blocks/xai.js.map +1 -1
- 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/registry.js +34 -24
- package/dist/ai/providers/registry.js.map +1 -1
- package/dist/ai/utils/filterEditorSchemaByNodes.d.ts +9 -0
- package/dist/ai/utils/filterEditorSchemaByNodes.js +30 -3
- package/dist/ai/utils/filterEditorSchemaByNodes.js.map +1 -1
- package/dist/ai/utils/nodeToSchemaMap.d.ts +22 -0
- package/dist/ai/utils/nodeToSchemaMap.js +72 -0
- package/dist/ai/utils/nodeToSchemaMap.js.map +1 -0
- package/dist/collections/AIJobs.js +1 -1
- package/dist/collections/AIJobs.js.map +1 -1
- package/dist/collections/AISettings.js +47 -20
- package/dist/collections/AISettings.js.map +1 -1
- package/dist/collections/Instructions.js +37 -0
- package/dist/collections/Instructions.js.map +1 -1
- package/dist/defaults.d.ts +1 -0
- package/dist/defaults.js +8 -0
- package/dist/defaults.js.map +1 -1
- package/dist/endpoints/chat.d.ts +4 -0
- package/dist/endpoints/fetchFields.js +10 -0
- package/dist/endpoints/fetchFields.js.map +1 -1
- package/dist/endpoints/fetchVoices.js +41 -24
- package/dist/endpoints/fetchVoices.js.map +1 -1
- package/dist/endpoints/index.js +194 -16
- package/dist/endpoints/index.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/PromptEditorField/PromptEditorField.js +7 -2
- package/dist/fields/PromptEditorField/PromptEditorField.js.map +1 -1
- package/dist/fields/PromptEditorField/PromptEditorField.jsx +5 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/payload-ai.d.ts +152 -0
- package/dist/plugin.js +16 -32
- package/dist/plugin.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.js +47 -15
- package/dist/providers/InstructionsProvider/InstructionsProvider.js.map +1 -1
- package/dist/providers/InstructionsProvider/InstructionsProvider.jsx +39 -16
- package/dist/providers/InstructionsProvider/context.d.ts +3 -0
- package/dist/providers/InstructionsProvider/context.js +2 -0
- package/dist/providers/InstructionsProvider/context.js.map +1 -1
- package/dist/providers/InstructionsProvider/useInstructions.js +21 -2
- 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 +34 -5
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.js +198 -22
- package/dist/ui/AIConfigDashboard/index.js.map +1 -1
- package/dist/ui/AIConfigDashboard/index.jsx +159 -13
- package/dist/ui/Compose/Compose.d.ts +1 -0
- package/dist/ui/Compose/Compose.js +23 -4
- package/dist/ui/Compose/Compose.js.map +1 -1
- package/dist/ui/Compose/Compose.jsx +23 -4
- package/dist/ui/Compose/UndoRedoActions.d.ts +2 -2
- package/dist/ui/Compose/UndoRedoActions.js +8 -5
- package/dist/ui/Compose/UndoRedoActions.js.map +1 -1
- package/dist/ui/Compose/UndoRedoActions.jsx +6 -5
- package/dist/ui/Compose/compose.module.css +56 -16
- package/dist/ui/Compose/hooks/menu/itemsMap.js +12 -6
- package/dist/ui/Compose/hooks/menu/itemsMap.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.js +26 -15
- package/dist/ui/Compose/hooks/menu/useMenu.js.map +1 -1
- package/dist/ui/Compose/hooks/menu/useMenu.jsx +25 -12
- package/dist/ui/Compose/hooks/useGenerate.js +26 -174
- package/dist/ui/Compose/hooks/useGenerate.js.map +1 -1
- package/dist/ui/Compose/hooks/useGenerateUpload.d.ts +11 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js +150 -0
- package/dist/ui/Compose/hooks/useGenerateUpload.js.map +1 -0
- package/dist/ui/Compose/hooks/useHistory.d.ts +0 -1
- package/dist/ui/Compose/hooks/useHistory.js +65 -25
- package/dist/ui/Compose/hooks/useHistory.js.map +1 -1
- package/dist/ui/Compose/hooks/useStreamingUpdate.d.ts +8 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js +48 -0
- package/dist/ui/Compose/hooks/useStreamingUpdate.js.map +1 -0
- package/dist/ui/DynamicVoiceSelect/index.js +63 -11
- package/dist/ui/DynamicVoiceSelect/index.js.map +1 -1
- package/dist/ui/DynamicVoiceSelect/index.jsx +47 -14
- package/dist/ui/EncryptedTextField/index.js +4 -4
- package/dist/ui/EncryptedTextField/index.js.map +1 -1
- package/dist/ui/EncryptedTextField/index.jsx +4 -4
- package/dist/ui/VoicesFetcher/index.js +34 -16
- package/dist/ui/VoicesFetcher/index.js.map +1 -1
- package/dist/ui/VoicesFetcher/index.jsx +32 -15
- package/dist/utilities/buildSmartPrompt.d.ts +22 -0
- package/dist/utilities/buildSmartPrompt.js +141 -0
- package/dist/utilities/buildSmartPrompt.js.map +1 -0
- package/dist/utilities/encryption.js +2 -1
- package/dist/utilities/encryption.js.map +1 -1
- package/dist/utilities/fieldToJsonSchema.js +32 -3
- package/dist/utilities/fieldToJsonSchema.js.map +1 -1
- package/dist/utilities/resolveImageReferences.d.ts +3 -1
- package/dist/utilities/resolveImageReferences.js +21 -2
- package/dist/utilities/resolveImageReferences.js.map +1 -1
- package/dist/utilities/seedProperties.d.ts +7 -0
- package/dist/utilities/seedProperties.js +100 -0
- package/dist/utilities/seedProperties.js.map +1 -0
- package/dist/utilities/setSafeLexicalState.js +79 -6
- package/dist/utilities/setSafeLexicalState.js.map +1 -1
- package/dist/utilities/updateFieldsConfig.d.ts +1 -1
- package/dist/utilities/updateFieldsConfig.js +8 -1
- package/dist/utilities/updateFieldsConfig.js.map +1 -1
- package/package.json +35 -33
- package/dist/endpoints/chat.d.js +0 -3
- package/dist/endpoints/chat.d.js.map +0 -1
- package/dist/init.d.ts +0 -7
- package/dist/init.js +0 -135
- package/dist/init.js.map +0 -1
- package/dist/payload-ai.d.js +0 -3
- package/dist/payload-ai.d.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
|
@@ -35,34 +35,51 @@ export const fetchVoices = {
|
|
|
35
35
|
status: 400
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
-
// Call ElevenLabs API to fetch voices
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
// Call ElevenLabs API to fetch voices with timeout
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
const timeoutId = setTimeout(()=>controller.abort(), 15000) // 15s timeout
|
|
41
|
+
;
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch('https://api.elevenlabs.io/v1/voices', {
|
|
44
|
+
headers: {
|
|
45
|
+
'xi-api-key': apiKey
|
|
46
|
+
},
|
|
47
|
+
signal: controller.signal
|
|
48
|
+
});
|
|
49
|
+
clearTimeout(timeoutId);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const errorText = await response.text();
|
|
52
|
+
return Response.json({
|
|
53
|
+
message: `ElevenLabs API error: ${errorText}`
|
|
54
|
+
}, {
|
|
55
|
+
status: response.status
|
|
56
|
+
});
|
|
42
57
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
58
|
+
const data = await response.json();
|
|
59
|
+
// Transform voices to match our schema
|
|
60
|
+
const voices = (data.voices || []).map((voice)=>({
|
|
61
|
+
id: voice.voice_id,
|
|
62
|
+
name: voice.name,
|
|
63
|
+
category: voice.category || 'premade',
|
|
64
|
+
enabled: true,
|
|
65
|
+
labels: voice.labels || {},
|
|
66
|
+
preview_url: voice.preview_url || ''
|
|
67
|
+
}));
|
|
46
68
|
return Response.json({
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
status: response.status
|
|
69
|
+
success: true,
|
|
70
|
+
voices
|
|
50
71
|
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
clearTimeout(timeoutId);
|
|
74
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
75
|
+
return Response.json({
|
|
76
|
+
message: 'ElevenLabs API request timed out'
|
|
77
|
+
}, {
|
|
78
|
+
status: 504
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
throw error;
|
|
51
82
|
}
|
|
52
|
-
const data = await response.json();
|
|
53
|
-
// Transform voices to match our schema
|
|
54
|
-
const voices = (data.voices || []).map((voice)=>({
|
|
55
|
-
id: voice.voice_id,
|
|
56
|
-
name: voice.name,
|
|
57
|
-
category: voice.category || 'premade',
|
|
58
|
-
enabled: true,
|
|
59
|
-
labels: voice.labels || {},
|
|
60
|
-
preview_url: voice.preview_url || ''
|
|
61
|
-
}));
|
|
62
|
-
return Response.json({
|
|
63
|
-
success: true,
|
|
64
|
-
voices
|
|
65
|
-
});
|
|
66
83
|
} catch (error) {
|
|
67
84
|
req.payload.logger.error(error, 'Error fetching ElevenLabs voices');
|
|
68
85
|
return Response.json({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/endpoints/fetchVoices.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../defaults.js'\n\ninterface ElevenLabsVoice {\n category?: string\n labels?: Record<string, string>\n name: string\n preview_url?: string\n voice_id: string\n}\n\nexport const fetchVoices: Endpoint = {\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication\n if (!req.user) {\n return Response.json({ message: 'Authentication required' }, { status: 401 })\n }\n\n // Fetch AI Settings global to get the encrypted API key\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true },\n })\n\n // Find the ElevenLabs provider block\n const elevenlabsProvider = aiSettings?.providers?.find(\n (provider: any) => provider.blockType === 'elevenlabs' && provider.enabled,\n )\n\n if (!elevenlabsProvider) {\n return Response.json(\n { message: 'ElevenLabs provider not found or not enabled in AI Settings' },\n { status: 400 },\n )\n }\n\n // Get the API key (already decrypted by afterRead hook due to unsafe context)\n const apiKey = elevenlabsProvider.apiKey\n\n if (!apiKey) {\n return Response.json(\n {\n message: 'API key not found. Please configure your ElevenLabs API key in AI Settings.',\n },\n { status: 400 },\n )\n }\n\n // Call ElevenLabs API to fetch voices\n const response = await fetch('https://api.elevenlabs.io/v1/voices', {\n
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/fetchVoices.ts"],"sourcesContent":["import type { Endpoint, PayloadRequest } from 'payload'\n\nimport { PLUGIN_API_ENDPOINT_FETCH_VOICES } from '../defaults.js'\n\ninterface ElevenLabsVoice {\n category?: string\n labels?: Record<string, string>\n name: string\n preview_url?: string\n voice_id: string\n}\n\nexport const fetchVoices: Endpoint = {\n handler: async (req: PayloadRequest) => {\n try {\n // Check authentication\n if (!req.user) {\n return Response.json({ message: 'Authentication required' }, { status: 401 })\n }\n\n // Fetch AI Settings global to get the encrypted API key\n const aiSettings = await req.payload.findGlobal({\n slug: 'ai-settings',\n context: { unsafe: true },\n })\n\n // Find the ElevenLabs provider block\n const elevenlabsProvider = aiSettings?.providers?.find(\n (provider: any) => provider.blockType === 'elevenlabs' && provider.enabled,\n )\n\n if (!elevenlabsProvider) {\n return Response.json(\n { message: 'ElevenLabs provider not found or not enabled in AI Settings' },\n { status: 400 },\n )\n }\n\n // Get the API key (already decrypted by afterRead hook due to unsafe context)\n const apiKey = elevenlabsProvider.apiKey\n\n if (!apiKey) {\n return Response.json(\n {\n message: 'API key not found. Please configure your ElevenLabs API key in AI Settings.',\n },\n { status: 400 },\n )\n }\n\n // Call ElevenLabs API to fetch voices with timeout\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), 15000) // 15s timeout\n\n try {\n const response = await fetch('https://api.elevenlabs.io/v1/voices', {\n headers: {\n 'xi-api-key': apiKey,\n },\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n const errorText = await response.text()\n return Response.json(\n { message: `ElevenLabs API error: ${errorText}` },\n { status: response.status },\n )\n }\n\n const data = await response.json()\n\n // Transform voices to match our schema\n const voices = (data.voices || []).map((voice: ElevenLabsVoice) => ({\n id: voice.voice_id,\n name: voice.name,\n category: voice.category || 'premade',\n enabled: true,\n labels: voice.labels || {},\n preview_url: voice.preview_url || '',\n }))\n\n return Response.json({\n success: true,\n voices,\n })\n } catch (error: unknown) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n return Response.json({ message: 'ElevenLabs API request timed out' }, { status: 504 })\n }\n throw error\n }\n } catch (error) {\n req.payload.logger.error(error, 'Error fetching ElevenLabs voices')\n return Response.json(\n { message: error instanceof Error ? error.message : 'Internal server error' },\n { status: 500 },\n )\n }\n },\n method: 'post',\n path: PLUGIN_API_ENDPOINT_FETCH_VOICES,\n}\n"],"names":["PLUGIN_API_ENDPOINT_FETCH_VOICES","fetchVoices","handler","req","user","Response","json","message","status","aiSettings","payload","findGlobal","slug","context","unsafe","elevenlabsProvider","providers","find","provider","blockType","enabled","apiKey","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","headers","signal","clearTimeout","ok","errorText","text","data","voices","map","voice","id","voice_id","name","category","labels","preview_url","success","error","Error","logger","method","path"],"mappings":"AAEA,SAASA,gCAAgC,QAAQ,iBAAgB;AAUjE,OAAO,MAAMC,cAAwB;IACnCC,SAAS,OAAOC;QACd,IAAI;YACF,uBAAuB;YACvB,IAAI,CAACA,IAAIC,IAAI,EAAE;gBACb,OAAOC,SAASC,IAAI,CAAC;oBAAEC,SAAS;gBAA0B,GAAG;oBAAEC,QAAQ;gBAAI;YAC7E;YAEA,wDAAwD;YACxD,MAAMC,aAAa,MAAMN,IAAIO,OAAO,CAACC,UAAU,CAAC;gBAC9CC,MAAM;gBACNC,SAAS;oBAAEC,QAAQ;gBAAK;YAC1B;YAEA,qCAAqC;YACrC,MAAMC,qBAAqBN,YAAYO,WAAWC,KAChD,CAACC,WAAkBA,SAASC,SAAS,KAAK,gBAAgBD,SAASE,OAAO;YAG5E,IAAI,CAACL,oBAAoB;gBACvB,OAAOV,SAASC,IAAI,CAClB;oBAAEC,SAAS;gBAA8D,GACzE;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,8EAA8E;YAC9E,MAAMa,SAASN,mBAAmBM,MAAM;YAExC,IAAI,CAACA,QAAQ;gBACX,OAAOhB,SAASC,IAAI,CAClB;oBACEC,SAAS;gBACX,GACA;oBAAEC,QAAQ;gBAAI;YAElB;YAEA,mDAAmD;YACnD,MAAMc,aAAa,IAAIC;YACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAI,OAAO,cAAc;;YAE5E,IAAI;gBACF,MAAMC,WAAW,MAAMC,MAAM,uCAAuC;oBAClEC,SAAS;wBACP,cAAcR;oBAChB;oBACAS,QAAQR,WAAWQ,MAAM;gBAC3B;gBAEAC,aAAaP;gBAEb,IAAI,CAACG,SAASK,EAAE,EAAE;oBAChB,MAAMC,YAAY,MAAMN,SAASO,IAAI;oBACrC,OAAO7B,SAASC,IAAI,CAClB;wBAAEC,SAAS,CAAC,sBAAsB,EAAE0B,UAAU,CAAC;oBAAC,GAChD;wBAAEzB,QAAQmB,SAASnB,MAAM;oBAAC;gBAE9B;gBAEA,MAAM2B,OAAO,MAAMR,SAASrB,IAAI;gBAEhC,uCAAuC;gBACvC,MAAM8B,SAAS,AAACD,CAAAA,KAAKC,MAAM,IAAI,EAAE,AAAD,EAAGC,GAAG,CAAC,CAACC,QAA4B,CAAA;wBAClEC,IAAID,MAAME,QAAQ;wBAClBC,MAAMH,MAAMG,IAAI;wBAChBC,UAAUJ,MAAMI,QAAQ,IAAI;wBAC5BtB,SAAS;wBACTuB,QAAQL,MAAMK,MAAM,IAAI,CAAC;wBACzBC,aAAaN,MAAMM,WAAW,IAAI;oBACpC,CAAA;gBAEA,OAAOvC,SAASC,IAAI,CAAC;oBACnBuC,SAAS;oBACTT;gBACF;YACF,EAAE,OAAOU,OAAgB;gBACvBf,aAAaP;gBACb,IAAIsB,iBAAiBC,SAASD,MAAML,IAAI,KAAK,cAAc;oBACzD,OAAOpC,SAASC,IAAI,CAAC;wBAAEC,SAAS;oBAAmC,GAAG;wBAAEC,QAAQ;oBAAI;gBACtF;gBACA,MAAMsC;YACR;QACF,EAAE,OAAOA,OAAO;YACd3C,IAAIO,OAAO,CAACsC,MAAM,CAACF,KAAK,CAACA,OAAO;YAChC,OAAOzC,SAASC,IAAI,CAClB;gBAAEC,SAASuC,iBAAiBC,QAAQD,MAAMvC,OAAO,GAAG;YAAwB,GAC5E;gBAAEC,QAAQ;YAAI;QAElB;IACF;IACAyC,QAAQ;IACRC,MAAMlD;AACR,EAAC"}
|
package/dist/endpoints/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { filterEditorSchemaByNodes } from '../ai/utils/filterEditorSchemaByNodes
|
|
|
4
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';
|
|
5
5
|
import { registerEditorHelper } from '../libraries/handlebars/helpers.js';
|
|
6
6
|
import { replacePlaceholders } from '../libraries/handlebars/replacePlaceholders.js';
|
|
7
|
+
import { buildSmartPrompt, isGenericPrompt } from '../utilities/buildSmartPrompt.js';
|
|
7
8
|
import { extractImageData } from '../utilities/extractImageData.js';
|
|
8
9
|
import { fetchImages } from '../utilities/fetchImages.js';
|
|
9
10
|
import { fieldToJsonSchema } from '../utilities/fieldToJsonSchema.js';
|
|
@@ -37,10 +38,31 @@ export const endpoints = (pluginConfig)=>({
|
|
|
37
38
|
}
|
|
38
39
|
const { custom: { [PLUGIN_NAME]: { editorConfig = {} } = {} } = {} } = collection.admin;
|
|
39
40
|
const { schema: editorSchema = {} } = editorConfig;
|
|
40
|
-
|
|
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
|
+
}
|
|
41
56
|
let allowedEditorSchema = editorSchema;
|
|
42
57
|
if (allowedEditorNodes.length) {
|
|
43
58
|
allowedEditorSchema = filterEditorSchemaByNodes(editorSchema, allowedEditorNodes);
|
|
59
|
+
// Debug: Log what nodes were received and what definitions remain
|
|
60
|
+
if (pluginConfig.debugging) {
|
|
61
|
+
req.payload.logger.info({
|
|
62
|
+
receivedNodes: allowedEditorNodes,
|
|
63
|
+
remainingDefinitions: Object.keys(allowedEditorSchema.definitions || {})
|
|
64
|
+
}, '— AI Plugin: Schema filtering debug');
|
|
65
|
+
}
|
|
44
66
|
}
|
|
45
67
|
const schemaPath = String(instructions['schema-path']);
|
|
46
68
|
const parts = (schemaPath || '').split('.') || [];
|
|
@@ -74,11 +96,13 @@ export const endpoints = (pluginConfig)=>({
|
|
|
74
96
|
}
|
|
75
97
|
// Build per-field JSON schema for structured generation when applicable
|
|
76
98
|
let jsonSchema = allowedEditorSchema;
|
|
99
|
+
let targetField;
|
|
77
100
|
try {
|
|
78
101
|
const targetCollection = req.payload.config.collections.find((c)=>c.slug === collectionName);
|
|
79
102
|
if (targetCollection && fieldName) {
|
|
80
|
-
|
|
103
|
+
targetField = getFieldBySchemaPath(targetCollection, schemaPath);
|
|
81
104
|
const supported = [
|
|
105
|
+
'array',
|
|
82
106
|
'text',
|
|
83
107
|
'textarea',
|
|
84
108
|
'select',
|
|
@@ -90,23 +114,37 @@ export const endpoints = (pluginConfig)=>({
|
|
|
90
114
|
];
|
|
91
115
|
const t = String(targetField?.type || '');
|
|
92
116
|
if (targetField && supported.includes(t)) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
// For array fields, use count from array-settings if available
|
|
118
|
+
if (t === 'array') {
|
|
119
|
+
const arraySettings = instructions['array-settings'] || {};
|
|
120
|
+
const count = arraySettings.count || 3;
|
|
121
|
+
// Override the field's maxRows with the requested count
|
|
122
|
+
const modifiedField = {
|
|
123
|
+
...targetField,
|
|
124
|
+
maxRows: count,
|
|
125
|
+
minRows: count
|
|
126
|
+
};
|
|
127
|
+
jsonSchema = fieldToJsonSchema(modifiedField, {
|
|
128
|
+
nameOverride: fieldName
|
|
129
|
+
});
|
|
130
|
+
} else {
|
|
131
|
+
jsonSchema = fieldToJsonSchema(targetField, {
|
|
132
|
+
nameOverride: fieldName
|
|
133
|
+
});
|
|
134
|
+
}
|
|
96
135
|
}
|
|
97
136
|
}
|
|
98
137
|
} catch (e) {
|
|
99
138
|
req.payload.logger.error(e, '— AI Plugin: Error building field JSON schema');
|
|
100
139
|
}
|
|
101
140
|
// Get model settings from instruction
|
|
102
|
-
const settingsName = instructions['model-id'] === 'richtext' ? 'richtext-settings' : instructions['model-id'] === 'text' ? 'text-settings' : undefined;
|
|
141
|
+
const settingsName = instructions['model-id'] === 'richtext' ? 'richtext-settings' : instructions['model-id'] === 'text' ? 'text-settings' : instructions['model-id'] === 'array' ? 'array-settings' : undefined;
|
|
103
142
|
if (!settingsName) {
|
|
104
143
|
throw new Error(`Unsupported model-id: ${instructions['model-id']}`);
|
|
105
144
|
}
|
|
106
145
|
const modelSettings = instructions[settingsName] || {};
|
|
107
146
|
// Resolve @field:filename references from the prompt
|
|
108
|
-
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(prompts.prompt, contextData, req);
|
|
109
|
-
console.log('resolvedImagesL ', resolvedImages);
|
|
147
|
+
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(prompts.prompt, contextData, req, collectionName);
|
|
110
148
|
// Extract hardcoded URLs from the processed prompt
|
|
111
149
|
const hardcodedImages = extractImageData(processedPrompt);
|
|
112
150
|
// Combine images
|
|
@@ -121,16 +159,60 @@ export const endpoints = (pluginConfig)=>({
|
|
|
121
159
|
images = imageParts;
|
|
122
160
|
}
|
|
123
161
|
}
|
|
124
|
-
|
|
162
|
+
let promptToUse = processedPrompt;
|
|
163
|
+
let systemToUse = prompts.system;
|
|
164
|
+
// let messagesToUse: any = undefined
|
|
165
|
+
// Execute beforeGenerate hooks
|
|
166
|
+
if (targetField && targetField.custom?.ai?.beforeGenerate) {
|
|
167
|
+
const beforeHooks = targetField.custom.ai.beforeGenerate;
|
|
168
|
+
for (const hook of beforeHooks){
|
|
169
|
+
const result = await hook({
|
|
170
|
+
doc: contextData,
|
|
171
|
+
field: targetField,
|
|
172
|
+
headers: req.headers,
|
|
173
|
+
instructions,
|
|
174
|
+
payload: req.payload,
|
|
175
|
+
prompt: promptToUse,
|
|
176
|
+
req,
|
|
177
|
+
system: systemToUse
|
|
178
|
+
});
|
|
179
|
+
if (result) {
|
|
180
|
+
if (result.prompt) promptToUse = result.prompt;
|
|
181
|
+
if (result.system) systemToUse = result.system;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
125
185
|
const streamResult = await req.payload.ai.streamObject({
|
|
126
186
|
// extractAttachments: modelSettings.extractAttachments as boolean | undefined,
|
|
127
187
|
images,
|
|
128
188
|
maxTokens: modelSettings.maxTokens,
|
|
189
|
+
// messages: messagesToUse,
|
|
129
190
|
model: modelSettings.model,
|
|
191
|
+
onFinish: async ({ object })=>{
|
|
192
|
+
if (targetField && targetField.custom?.ai?.afterGenerate) {
|
|
193
|
+
const afterHooks = targetField.custom.ai.afterGenerate;
|
|
194
|
+
for (const hook of afterHooks){
|
|
195
|
+
await hook({
|
|
196
|
+
doc: contextData,
|
|
197
|
+
field: targetField,
|
|
198
|
+
headers: req.headers,
|
|
199
|
+
instructions,
|
|
200
|
+
payload: req.payload,
|
|
201
|
+
req,
|
|
202
|
+
result: object
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
},
|
|
130
207
|
prompt: processedPrompt,
|
|
131
208
|
provider: modelSettings.provider,
|
|
209
|
+
providerOptions: {
|
|
210
|
+
openai: {
|
|
211
|
+
strictJsonSchema: true
|
|
212
|
+
}
|
|
213
|
+
},
|
|
132
214
|
schema: jsonSchema,
|
|
133
|
-
system:
|
|
215
|
+
system: systemToUse,
|
|
134
216
|
temperature: modelSettings.temperature
|
|
135
217
|
});
|
|
136
218
|
return streamResult;
|
|
@@ -189,18 +271,31 @@ export const endpoints = (pluginConfig)=>({
|
|
|
189
271
|
req
|
|
190
272
|
});
|
|
191
273
|
}
|
|
192
|
-
|
|
274
|
+
let { prompt: promptTemplate = '' } = instructions;
|
|
275
|
+
const { images: sampleImages = [] } = instructions;
|
|
193
276
|
const schemaPath = String(instructions['schema-path']);
|
|
194
277
|
registerEditorHelper(req.payload, schemaPath);
|
|
278
|
+
// Smart fallback: if prompt is generic, build a contextual prompt from field metadata
|
|
279
|
+
if (isGenericPrompt(promptTemplate)) {
|
|
280
|
+
promptTemplate = buildSmartPrompt({
|
|
281
|
+
documentData: contextData,
|
|
282
|
+
payload: req.payload,
|
|
283
|
+
schemaPath
|
|
284
|
+
});
|
|
285
|
+
if (pluginConfig.debugging) {
|
|
286
|
+
req.payload.logger.info({
|
|
287
|
+
smartPrompt: promptTemplate
|
|
288
|
+
}, `— AI Plugin: Using smart fallback prompt for ${schemaPath}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
195
291
|
const extendedContext = extendContextWithPromptFields(contextData, {
|
|
196
292
|
type: String(instructions['field-type']),
|
|
197
293
|
collection: collectionSlug
|
|
198
294
|
}, pluginConfig);
|
|
199
295
|
const text = await replacePlaceholders(promptTemplate, extendedContext);
|
|
200
|
-
const modelId = instructions['model-id'];
|
|
201
296
|
const uploadCollectionSlug = instructions['relation-to'];
|
|
202
297
|
// Resolve @field:filename references from the prompt
|
|
203
|
-
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(text, contextData, req);
|
|
298
|
+
const { images: resolvedImages, processedPrompt } = await resolveImageReferences(text, contextData, req, collectionSlug);
|
|
204
299
|
// Extract hardcoded URLs from the processed prompt and merge with resolved images and sample images
|
|
205
300
|
const images = [
|
|
206
301
|
...extractImageData(processedPrompt),
|
|
@@ -209,30 +304,113 @@ export const endpoints = (pluginConfig)=>({
|
|
|
209
304
|
];
|
|
210
305
|
// Process images - convert to ImagePart format using helper
|
|
211
306
|
const editImages = await fetchImages(req, images);
|
|
307
|
+
let promptToUse = text;
|
|
308
|
+
let targetField;
|
|
309
|
+
try {
|
|
310
|
+
const targetCollection = req.payload.config.collections.find((c)=>c.slug === collectionSlug);
|
|
311
|
+
if (targetCollection && schemaPath) {
|
|
312
|
+
targetField = getFieldBySchemaPath(targetCollection, schemaPath);
|
|
313
|
+
}
|
|
314
|
+
} catch (e) {
|
|
315
|
+
req.payload.logger.error(e, '— AI Plugin: Error finding field for hooks');
|
|
316
|
+
}
|
|
317
|
+
if (targetField && targetField.custom?.ai?.beforeGenerate) {
|
|
318
|
+
const beforeHooks = targetField.custom.ai.beforeGenerate;
|
|
319
|
+
for (const hook of beforeHooks){
|
|
320
|
+
const result = await hook({
|
|
321
|
+
doc: contextData,
|
|
322
|
+
field: targetField,
|
|
323
|
+
headers: req.headers,
|
|
324
|
+
instructions,
|
|
325
|
+
payload: req.payload,
|
|
326
|
+
prompt: promptToUse,
|
|
327
|
+
req
|
|
328
|
+
});
|
|
329
|
+
if (result) {
|
|
330
|
+
if (result.prompt) {
|
|
331
|
+
promptToUse = result.prompt;
|
|
332
|
+
}
|
|
333
|
+
if (result.instructions) {
|
|
334
|
+
instructions = {
|
|
335
|
+
...instructions,
|
|
336
|
+
...result.instructions
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
212
342
|
if (pluginConfig.debugging) {
|
|
213
343
|
req.payload.logger.info({
|
|
214
|
-
text
|
|
344
|
+
text: promptToUse
|
|
215
345
|
}, `— AI Plugin: Executing media generation`);
|
|
216
346
|
}
|
|
217
347
|
// Prepare callback URL for async jobs
|
|
218
348
|
const serverURL = req.payload.config?.serverURL || process.env.SERVER_URL || process.env.NEXT_PUBLIC_SERVER_URL;
|
|
219
349
|
const callbackUrl = serverURL ? `${serverURL.replace(/\/$/, '')}/api${PLUGIN_API_ENDPOINT_VIDEOGEN_WEBHOOK}?instructionId=${instructionId}` : undefined;
|
|
220
350
|
// Get model settings
|
|
351
|
+
// Re-evaluate settings name and settings in case instructions changed
|
|
352
|
+
const modelId = instructions['model-id'];
|
|
221
353
|
const settingsName = modelId === 'image' ? 'image-settings' : modelId === 'video' ? 'video-settings' : modelId === 'tts' ? 'tts-settings' : undefined;
|
|
222
354
|
if (!settingsName) {
|
|
223
355
|
throw new Error(`Unsupported model-id: ${modelId}`);
|
|
224
356
|
}
|
|
225
|
-
|
|
357
|
+
// Get model settings from instruction
|
|
358
|
+
const instructionSettings = instructions[settingsName] || {};
|
|
359
|
+
// Fallback to AISettings global defaults if instruction-level settings are missing
|
|
360
|
+
let globalDefaults = {};
|
|
361
|
+
if (!instructionSettings.provider || !instructionSettings.model) {
|
|
362
|
+
try {
|
|
363
|
+
const aiSettings = await req.payload.findGlobal({
|
|
364
|
+
slug: 'ai-settings',
|
|
365
|
+
context: {
|
|
366
|
+
unsafe: true
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
// Map modelId to the corresponding default settings key
|
|
370
|
+
const defaultsKey = modelId === 'image' ? 'image' : modelId === 'video' ? 'video' : modelId === 'tts' ? 'tts' : undefined;
|
|
371
|
+
if (defaultsKey && aiSettings?.defaults?.[defaultsKey]) {
|
|
372
|
+
globalDefaults = aiSettings.defaults[defaultsKey];
|
|
373
|
+
if (pluginConfig.debugging) {
|
|
374
|
+
req.payload.logger.info({
|
|
375
|
+
globalDefaults
|
|
376
|
+
}, `— AI Plugin: Using AISettings defaults for ${modelId}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} catch (e) {
|
|
380
|
+
req.payload.logger.error(e, '— AI Plugin: Error fetching AISettings defaults');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
// Merge: instruction settings take priority over global defaults
|
|
384
|
+
// Filter out null/undefined values so they don't overwrite valid defaults
|
|
385
|
+
const filteredInstructionSettings = Object.fromEntries(Object.entries(instructionSettings).filter(([_, v])=>v != null));
|
|
386
|
+
const modelSettings = {
|
|
387
|
+
...globalDefaults,
|
|
388
|
+
...filteredInstructionSettings
|
|
389
|
+
};
|
|
226
390
|
// Use payload.ai.generateMedia directly! 🎉
|
|
227
391
|
const result = await req.payload.ai.generateMedia({
|
|
228
392
|
callbackUrl,
|
|
229
393
|
images: editImages,
|
|
230
394
|
instructionId,
|
|
231
395
|
model: modelSettings.model,
|
|
232
|
-
prompt:
|
|
396
|
+
prompt: promptToUse,
|
|
233
397
|
provider: modelSettings.provider,
|
|
234
398
|
...modelSettings
|
|
235
399
|
});
|
|
400
|
+
if (targetField && targetField.custom?.ai?.afterGenerate) {
|
|
401
|
+
const afterHooks = targetField.custom.ai.afterGenerate;
|
|
402
|
+
for (const hook of afterHooks){
|
|
403
|
+
await hook({
|
|
404
|
+
doc: contextData,
|
|
405
|
+
field: targetField,
|
|
406
|
+
headers: req.headers,
|
|
407
|
+
instructions,
|
|
408
|
+
payload: req.payload,
|
|
409
|
+
req,
|
|
410
|
+
result
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
236
414
|
// If model returned a file immediately, proceed with upload
|
|
237
415
|
if (result && 'file' in result) {
|
|
238
416
|
let assetData;
|