@mvriu5/payload-ai 1.6.2 → 1.6.4

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 (126) hide show
  1. package/dist/components/action-toast/ActionToast.d.ts +1 -14
  2. package/dist/components/action-toast/ActionToast.js +1 -1
  3. package/dist/components/ai-input/AIInput.js +1 -1
  4. package/dist/components/audit-log-list/AuditLogList.d.ts +2 -3
  5. package/dist/components/diff-dialog/DiffDialog.d.ts +2 -6
  6. package/dist/components/diff-dialog/DiffDialog.js +1 -1
  7. package/dist/components/generate-field/GenerateField.js +2 -0
  8. package/dist/components/hooks/useAIChatStream.d.ts +1 -1
  9. package/dist/components/hooks/useAISettings.d.ts +1 -1
  10. package/dist/components/hooks/useAISettings.js +1 -1
  11. package/dist/components/hooks/useDocumentMentionSuggestions.js +1 -1
  12. package/dist/components/hooks/useMentions.js +1 -1
  13. package/dist/components/hooks/usePluginConfig.d.ts +6 -6
  14. package/dist/components/hooks/usePluginConfig.js +1 -1
  15. package/dist/{payload → features}/collectionPermissions.js +1 -1
  16. package/dist/{payload/textFieldGeneration.d.ts → features/content/fieldGeneration.d.ts} +4 -0
  17. package/dist/{payload/textFieldGeneration.js → features/content/fieldGeneration.js} +19 -3
  18. package/dist/{ai → features}/promptCaching.d.ts +2 -2
  19. package/dist/features/proposals/containerNormalization.d.ts +8 -0
  20. package/dist/features/proposals/containerNormalization.js +112 -0
  21. package/dist/{payload/proposalData.d.ts → features/proposals/data.d.ts} +5 -10
  22. package/dist/features/proposals/data.js +113 -0
  23. package/dist/features/proposals/fieldNormalization.d.ts +2 -0
  24. package/dist/features/proposals/fieldNormalization.js +69 -0
  25. package/dist/features/proposals/normalizationTypes.d.ts +31 -0
  26. package/dist/features/proposals/normalizationTypes.js +1 -0
  27. package/dist/features/proposals/optionInference.d.ts +13 -0
  28. package/dist/features/proposals/optionInference.js +46 -0
  29. package/dist/features/proposals/recordNormalization.d.ts +6 -0
  30. package/dist/features/proposals/recordNormalization.js +78 -0
  31. package/dist/features/proposals/relationshipNormalization.d.ts +8 -0
  32. package/dist/features/proposals/relationshipNormalization.js +71 -0
  33. package/dist/{ai/proposalRepair.d.ts → features/proposals/repair.d.ts} +1 -1
  34. package/dist/{ai/proposalSigning.d.ts → features/proposals/signing.d.ts} +2 -5
  35. package/dist/{ai/proposalSigning.js → features/proposals/signing.js} +1 -1
  36. package/dist/features/proposals/target.d.ts +52 -0
  37. package/dist/features/proposals/target.js +125 -0
  38. package/dist/features/proposals/types.d.ts +49 -0
  39. package/dist/features/proposals/types.js +1 -0
  40. package/dist/features/proposals/validation.d.ts +12 -0
  41. package/dist/features/proposals/validation.js +26 -0
  42. package/dist/features/providers/requestContext.d.ts +53 -0
  43. package/dist/features/providers/requestContext.js +90 -0
  44. package/dist/{ai/providerRuntime.d.ts → features/providers/runtime.d.ts} +1 -1
  45. package/dist/{ai/providerRuntime.js → features/providers/runtime.js} +1 -1
  46. package/dist/features/schema/context.d.ts +7 -0
  47. package/dist/features/schema/context.js +11 -0
  48. package/dist/{payload/schemaContext.d.ts → features/schema/description.d.ts} +6 -33
  49. package/dist/{payload/schemaContext.js → features/schema/description.js} +3 -207
  50. package/dist/{payload/toolFieldSelection.d.ts → features/schema/fieldSelection.d.ts} +1 -1
  51. package/dist/{payload/toolFieldSelection.js → features/schema/fieldSelection.js} +2 -2
  52. package/dist/features/schema/mentionDocuments.d.ts +15 -0
  53. package/dist/features/schema/mentionDocuments.js +50 -0
  54. package/dist/features/schema/mentions.d.ts +18 -0
  55. package/dist/features/schema/mentions.js +100 -0
  56. package/dist/{payload/normalizeData.js → features/schema/normalize.js} +1 -1
  57. package/dist/features/schema/registry.d.ts +30 -0
  58. package/dist/features/schema/registry.js +73 -0
  59. package/dist/{payload → features/schema}/toolSchemas.d.ts +1 -1
  60. package/dist/{payload → features/schema}/toolSchemas.js +1 -1
  61. package/dist/{ai → features}/sensitiveData.js +1 -1
  62. package/dist/{ai → features}/tokenUsage.d.ts +1 -2
  63. package/dist/handlers/apply/audit.d.ts +42 -0
  64. package/dist/handlers/apply/audit.js +150 -0
  65. package/dist/handlers/apply/operations.d.ts +18 -0
  66. package/dist/handlers/apply/operations.js +377 -0
  67. package/dist/handlers/apply/types.d.ts +33 -0
  68. package/dist/handlers/apply/types.js +1 -0
  69. package/dist/handlers/apply/validation.d.ts +45 -0
  70. package/dist/handlers/apply/validation.js +125 -0
  71. package/dist/handlers/applyActionHandler.d.ts +1 -6
  72. package/dist/handlers/applyActionHandler.js +13 -757
  73. package/dist/handlers/auditLogHandler.js +4 -8
  74. package/dist/handlers/chat/intent.d.ts +32 -0
  75. package/dist/handlers/chat/intent.js +155 -0
  76. package/dist/handlers/chat/prompt.d.ts +20 -0
  77. package/dist/handlers/chat/prompt.js +55 -0
  78. package/dist/handlers/chat/proposalTools.d.ts +96 -0
  79. package/dist/handlers/chat/proposalTools.js +451 -0
  80. package/dist/handlers/chat/streaming.d.ts +38 -0
  81. package/dist/handlers/chat/streaming.js +136 -0
  82. package/dist/handlers/chat/toolFactory.d.ts +79 -0
  83. package/dist/handlers/chat/toolFactory.js +156 -0
  84. package/dist/handlers/chat/types.d.ts +57 -0
  85. package/dist/handlers/chat/types.js +1 -0
  86. package/dist/handlers/chatHandler.d.ts +1 -44
  87. package/dist/handlers/chatHandler.js +186 -1162
  88. package/dist/handlers/generateFieldHandler.d.ts +3 -8
  89. package/dist/handlers/generateFieldHandler.js +71 -99
  90. package/dist/handlers/http.d.ts +17 -0
  91. package/dist/handlers/http.js +21 -0
  92. package/dist/handlers/mediaUploadHandler.js +12 -34
  93. package/dist/handlers/mentionSuggestionHandler.d.ts +1 -1
  94. package/dist/handlers/mentionSuggestionHandler.js +6 -10
  95. package/dist/handlers/proposalDiffHandler.d.ts +1 -1
  96. package/dist/handlers/proposalDiffHandler.js +72 -195
  97. package/dist/handlers/translateDocumentHandler.d.ts +3 -8
  98. package/dist/handlers/translateDocumentHandler.js +26 -82
  99. package/dist/index.d.ts +4 -30
  100. package/dist/index.js +31 -421
  101. package/dist/plugin/admin.d.ts +30 -0
  102. package/dist/plugin/admin.js +155 -0
  103. package/dist/plugin/collections.d.ts +3 -0
  104. package/dist/plugin/collections.js +169 -0
  105. package/dist/plugin/endpoints.d.ts +23 -0
  106. package/dist/plugin/endpoints.js +84 -0
  107. package/dist/plugin/options.d.ts +4 -0
  108. package/dist/plugin/options.js +13 -0
  109. package/dist/plugin/types.d.ts +28 -0
  110. package/dist/plugin/types.js +1 -0
  111. package/dist/{payload/shared.d.ts → utils/data.d.ts} +2 -12
  112. package/package.json +1 -1
  113. package/dist/payload/proposalData.js +0 -578
  114. /package/dist/{payload → features}/collectionPermissions.d.ts +0 -0
  115. /package/dist/{payload → features/content}/documentTranslation.d.ts +0 -0
  116. /package/dist/{payload → features/content}/documentTranslation.js +0 -0
  117. /package/dist/{ai → features}/promptCaching.js +0 -0
  118. /package/dist/{ai/proposalRepair.js → features/proposals/repair.js} +0 -0
  119. /package/dist/{ai/providerOptions.d.ts → features/providers/options.d.ts} +0 -0
  120. /package/dist/{ai/providerOptions.js → features/providers/options.js} +0 -0
  121. /package/dist/{payload/normalizeData.d.ts → features/schema/normalize.d.ts} +0 -0
  122. /package/dist/{ai → features}/sensitiveData.d.ts +0 -0
  123. /package/dist/{ai → features}/tokenUsage.js +0 -0
  124. /package/dist/{payload/shared.js → utils/data.js} +0 -0
  125. /package/dist/{payload → utils}/logging.d.ts +0 -0
  126. /package/dist/{payload → utils}/logging.js +0 -0
package/dist/index.js CHANGED
@@ -1,325 +1,11 @@
1
- import { aiProviders, getResolvedAIModelConfig, resolveAIProviderConfigs, toClientAIProviderProfiles } from "./ai/providerOptions.js";
2
- import { resolveMaxTokenUsageOptions, tokenUsageCollectionSlug } from "./ai/tokenUsage.js";
3
- import { createApplyActionHandler } from "./handlers/applyActionHandler.js";
4
- import { createChatHandler } from "./handlers/chatHandler.js";
5
- import { createGenerateFieldHandler } from "./handlers/generateFieldHandler.js";
6
- import { createTranslateDocumentHandler } from "./handlers/translateDocumentHandler.js";
7
- import { createMentionSuggestionHandler } from "./handlers/mentionSuggestionHandler.js";
8
- import { createMediaUploadHandler } from "./handlers/mediaUploadHandler.js";
9
- import { createProposalDiffHandler } from "./handlers/proposalDiffHandler.js";
10
- import { createAuditLogHandler } from "./handlers/auditLogHandler.js";
11
- import { resolveCollectionPermissions } from "./payload/collectionPermissions.js";
12
- import { isInternalCollection } from "./payload/shared.js";
13
- import { addTextGenerationFields } from "./payload/textFieldGeneration.js";
14
- import { hasLocalizedFields } from "./payload/documentTranslation.js";
15
- const resolveMediaUploadOptions = (media)=>{
16
- if (!media || media.enabled === false) return null;
17
- return {
18
- ...media.acceptedMimeTypes ? {
19
- acceptedMimeTypes: media.acceptedMimeTypes
20
- } : {},
21
- collectionSlug: media.collectionSlug || "media",
22
- ...typeof media.maxFileSize === "number" && Number.isFinite(media.maxFileSize) && media.maxFileSize > 0 ? {
23
- maxFileSize: Math.floor(media.maxFileSize)
24
- } : {}
25
- };
26
- };
27
- const createAIChangesCollection = ()=>({
28
- slug: "payload-ai-auditlog",
29
- access: {
30
- create: ()=>false,
31
- delete: ()=>false,
32
- read: ({ req })=>Boolean(req.user),
33
- update: ()=>false
34
- },
35
- admin: {
36
- defaultColumns: [
37
- "title",
38
- "action",
39
- "additions",
40
- "removals",
41
- "createdAt"
42
- ],
43
- group: "AI",
44
- useAsTitle: "title"
45
- },
46
- labels: {
47
- plural: "AI Changes",
48
- singular: "AI Change"
49
- },
50
- fields: [
51
- {
52
- name: "title",
53
- type: "text",
54
- required: true
55
- },
56
- {
57
- name: "action",
58
- type: "select",
59
- options: [
60
- "create",
61
- "update",
62
- "delete",
63
- "updateGlobal"
64
- ],
65
- required: true
66
- },
67
- {
68
- name: "targetType",
69
- type: "select",
70
- options: [
71
- "collection",
72
- "global"
73
- ],
74
- required: true
75
- },
76
- {
77
- name: "collection",
78
- type: "text"
79
- },
80
- {
81
- name: "slug",
82
- type: "text"
83
- },
84
- {
85
- name: "documentID",
86
- type: "text"
87
- },
88
- {
89
- name: "targetURL",
90
- type: "text"
91
- },
92
- {
93
- name: "additions",
94
- type: "number",
95
- defaultValue: 0
96
- },
97
- {
98
- name: "removals",
99
- type: "number",
100
- defaultValue: 0
101
- },
102
- {
103
- name: "before",
104
- type: "json"
105
- },
106
- {
107
- name: "after",
108
- type: "json"
109
- },
110
- {
111
- name: "proposal",
112
- type: "json"
113
- },
114
- {
115
- name: "prompt",
116
- type: "textarea"
117
- },
118
- {
119
- name: "inputTokens",
120
- type: "number"
121
- },
122
- {
123
- name: "outputTokens",
124
- type: "number"
125
- },
126
- {
127
- name: "totalTokens",
128
- type: "number"
129
- },
130
- {
131
- name: "aiResponse",
132
- type: "textarea"
133
- },
134
- {
135
- name: "userID",
136
- type: "text"
137
- },
138
- {
139
- name: "userLabel",
140
- type: "text"
141
- }
142
- ],
143
- timestamps: true
144
- });
145
- const createAITokenUsageCollection = ()=>({
146
- slug: tokenUsageCollectionSlug,
147
- access: {
148
- create: ()=>false,
149
- delete: ()=>false,
150
- read: ()=>false,
151
- update: ()=>false
152
- },
153
- admin: {
154
- hidden: true
155
- },
156
- fields: [
157
- {
158
- name: "userID",
159
- type: "text",
160
- index: true,
161
- required: true
162
- },
163
- {
164
- name: "provider",
165
- type: "text",
166
- required: true
167
- },
168
- {
169
- name: "model",
170
- type: "text",
171
- required: true
172
- },
173
- {
174
- name: "inputTokens",
175
- type: "number"
176
- },
177
- {
178
- name: "outputTokens",
179
- type: "number"
180
- },
181
- {
182
- name: "totalTokens",
183
- type: "number",
184
- required: true
185
- },
186
- {
187
- name: "recordedAt",
188
- type: "date",
189
- index: true,
190
- required: true
191
- }
192
- ],
193
- timestamps: true
194
- });
195
- const hasAuthenticatedUser = (_data, _siblingData, { user })=>Boolean(user);
196
- const addAccountFields = ({ allowUserApiKeys, config })=>{
197
- const adminUserSlug = config.admin?.user;
198
- if (!adminUserSlug || !config.collections) return;
199
- const userCollection = config.collections.find((c)=>c.slug === adminUserSlug);
200
- if (!userCollection) return;
201
- userCollection.fields.push({
202
- name: "aiProvider",
203
- type: "select",
204
- admin: {
205
- condition: hasAuthenticatedUser
206
- },
207
- defaultValue: "openai",
208
- label: "AI Provider",
209
- options: aiProviders
210
- });
211
- if (allowUserApiKeys) {
212
- userCollection.fields.push({
213
- name: "aiApiKey",
214
- type: "text",
215
- admin: {
216
- condition: hasAuthenticatedUser,
217
- components: {
218
- Field: "@mvriu5/payload-ai/client#AIApiKeyField"
219
- },
220
- description: "Optional. If empty, the plugin uses the provider API key from environment variables."
221
- },
222
- label: "AI API Key"
223
- });
224
- }
225
- };
226
- const aiField = {
227
- name: "payloadAi",
228
- type: "ui",
229
- admin: {
230
- condition: hasAuthenticatedUser,
231
- components: {
232
- Field: "@mvriu5/payload-ai/client#AIInput"
233
- }
234
- }
235
- };
236
- const translateDocumentComponent = "@mvriu5/payload-ai/client#TranslateDocumentButton";
237
- const getEntityLabel = (label, fallback)=>{
238
- if (typeof label === "string") return label;
239
- if (label && typeof label === "object") {
240
- const translatedLabel = Object.values(label).find((value)=>typeof value === "string");
241
- if (typeof translatedLabel === "string") return translatedLabel;
242
- }
243
- return fallback;
244
- };
245
- const addAIFieldsToDocumentsAndGlobals = ({ addGenerateFields, addAIInput, addTranslation, authCollections, config, uploadCollections })=>{
246
- const pageContexts = new Map();
247
- const translationPageContexts = new Map();
248
- for (const collection of config.collections || []){
249
- if (isInternalCollection(collection.slug)) continue;
250
- if (collection.slug === "payload-ai-auditlog") continue;
251
- const allowAIInput = addAIInput && (!collection.auth || authCollections?.aiInput === true) && (!collection.upload || uploadCollections?.aiInput === true);
252
- const allowGenerateFields = addGenerateFields && (!collection.auth || authCollections?.generateFields === true) && (!collection.upload || uploadCollections?.generateFields === true);
253
- if (allowGenerateFields) {
254
- const pageContext = addTextGenerationFields({
255
- fields: collection.fields || [],
256
- label: getEntityLabel(collection.labels?.singular, collection.slug),
257
- slug: collection.slug,
258
- type: "collection"
259
- });
260
- pageContexts.set(`collection:${collection.slug}`, pageContext);
261
- }
262
- if (allowAIInput) collection.fields = [
263
- aiField,
264
- ...collection.fields || []
265
- ];
266
- if (addTranslation && hasLocalizedFields(collection.fields || [])) {
267
- translationPageContexts.set(`collection:${collection.slug}`, {
268
- fields: collection.fields || [],
269
- label: getEntityLabel(collection.labels?.singular, collection.slug),
270
- slug: collection.slug,
271
- type: "collection"
272
- });
273
- collection.admin = collection.admin || {};
274
- collection.admin.components = collection.admin.components || {};
275
- collection.admin.components.edit = collection.admin.components.edit || {};
276
- const controls = collection.admin.components.edit.beforeDocumentControls || [];
277
- if (!controls.includes(translateDocumentComponent)) {
278
- collection.admin.components.edit.beforeDocumentControls = [
279
- ...controls,
280
- translateDocumentComponent
281
- ];
282
- }
283
- }
284
- }
285
- for (const global of config.globals || []){
286
- if (addGenerateFields) {
287
- const pageContext = addTextGenerationFields({
288
- fields: global.fields || [],
289
- label: getEntityLabel(global.label, global.slug),
290
- slug: global.slug,
291
- type: "global"
292
- });
293
- pageContexts.set(`global:${global.slug}`, pageContext);
294
- }
295
- if (addAIInput) global.fields = [
296
- aiField,
297
- ...global.fields || []
298
- ];
299
- if (addTranslation && hasLocalizedFields(global.fields || [])) {
300
- translationPageContexts.set(`global:${global.slug}`, {
301
- fields: global.fields || [],
302
- label: getEntityLabel(global.label, global.slug),
303
- slug: global.slug,
304
- type: "global"
305
- });
306
- global.admin = global.admin || {};
307
- global.admin.components = global.admin.components || {};
308
- global.admin.components.elements = global.admin.components.elements || {};
309
- const controls = global.admin.components.elements.beforeDocumentControls || [];
310
- if (!controls.includes(translateDocumentComponent)) {
311
- global.admin.components.elements.beforeDocumentControls = [
312
- ...controls,
313
- translateDocumentComponent
314
- ];
315
- }
316
- }
317
- }
318
- return {
319
- textGenerationPageContexts: pageContexts,
320
- translationPageContexts
321
- };
322
- };
1
+ import { getResolvedAIModelConfig, resolveAIProviderConfigs } from "./features/providers/options.js";
2
+ import { resolveMaxTokenUsageOptions, tokenUsageCollectionSlug } from "./features/tokenUsage.js";
3
+ import { isInternalCollection } from "./utils/data.js";
4
+ import { resolveCollectionPermissions } from "./features/collectionPermissions.js";
5
+ import { addAccountFields, addAIFieldsToDocumentsAndGlobals, configureAIAdmin } from "./plugin/admin.js";
6
+ import { createAIChangesCollection, createAITokenUsageCollection } from "./plugin/collections.js";
7
+ import { registerAIEndpoints } from "./plugin/endpoints.js";
8
+ import { resolveMaxOutputTokens, resolveMediaUploadOptions } from "./plugin/options.js";
323
9
  export const payloadAiPlugin = (pluginOptions)=>(config)=>{
324
10
  const incomingOnInit = config.onInit;
325
11
  const collectionPermissions = resolveCollectionPermissions(pluginOptions.collections);
@@ -329,7 +15,7 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
329
15
  const modelConfig = getResolvedAIModelConfig(pluginOptions.models);
330
16
  const maxTokenUsage = resolveMaxTokenUsageOptions(pluginOptions.maxTokenUsage);
331
17
  const mediaUploadOptions = resolveMediaUploadOptions(pluginOptions.media);
332
- const maxOutputTokens = typeof pluginOptions.maxOutputTokens === "number" && Number.isFinite(pluginOptions.maxOutputTokens) && pluginOptions.maxOutputTokens > 0 ? Math.floor(pluginOptions.maxOutputTokens) : undefined;
18
+ const maxOutputTokens = resolveMaxOutputTokens(pluginOptions.maxOutputTokens);
333
19
  if (!config.collections) config.collections = [];
334
20
  if (!config.collections.some((collection)=>collection.slug === "payload-ai-auditlog")) {
335
21
  config.collections.push(createAIChangesCollection());
@@ -344,8 +30,8 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
344
30
  if (pluginOptions.disabled) return config;
345
31
  const generateFields = pluginOptions.generateFields !== false;
346
32
  const { textGenerationPageContexts, translationPageContexts } = addAIFieldsToDocumentsAndGlobals({
347
- addGenerateFields: generateFields,
348
33
  addAIInput: pluginOptions.aiInput !== false,
34
+ addGenerateFields: generateFields,
349
35
  addTranslation: pluginOptions.translate !== false,
350
36
  authCollections: pluginOptions.authCollections,
351
37
  config,
@@ -358,105 +44,29 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
358
44
  collection.slug
359
45
  ];
360
46
  });
361
- if (!config.endpoints) config.endpoints = [];
362
- if (!config.admin) config.admin = {};
363
- config.admin.custom = {
364
- ...config.admin.custom || {},
365
- payloadAiPlugin: {
366
- ...config.admin.custom?.payloadAiPlugin || {},
367
- collectionSlugs: mentionCollectionSlugs,
368
- allowUserApiKeys,
369
- managedProviders,
370
- ...mediaUploadOptions ? {
371
- media: {
372
- ...mediaUploadOptions,
373
- enabled: true
374
- }
375
- } : {},
376
- models: modelConfig,
377
- providers: toClientAIProviderProfiles(providerConfigs)
378
- }
379
- };
380
- if (!config.admin.components) config.admin.components = {};
381
- if (!config.admin.components.beforeDashboard) config.admin.components.beforeDashboard = [];
382
- config.admin.components.beforeDashboard.push(`@mvriu5/payload-ai/client#Dashboard`);
383
- config.endpoints.push({
384
- handler: createChatHandler({
385
- allowUserApiKeys,
386
- collections: collectionPermissions,
387
- maxOutputTokens,
388
- maxTokenUsage,
389
- models: modelConfig,
390
- promptCaching: pluginOptions.promptCaching !== false,
391
- providers: providerConfigs
392
- }),
393
- method: "post",
394
- path: "/ai-chat"
395
- });
396
- config.endpoints.push({
397
- handler: createApplyActionHandler({
398
- changeLogCollection: "payload-ai-auditlog",
399
- collections: collectionPermissions
400
- }),
401
- method: "post",
402
- path: "/ai-apply-action"
403
- });
404
- config.endpoints.push({
405
- handler: createAuditLogHandler({
406
- changeLogCollection: "payload-ai-auditlog"
407
- }),
408
- method: "get",
409
- path: "/ai-audit-log"
410
- });
411
- config.endpoints.push({
412
- handler: createProposalDiffHandler({
413
- collections: collectionPermissions
414
- }),
415
- method: "post",
416
- path: "/ai-proposal-diff"
47
+ configureAIAdmin({
48
+ allowUserApiKeys,
49
+ collectionSlugs: mentionCollectionSlugs,
50
+ config,
51
+ managedProviders,
52
+ mediaUploadOptions,
53
+ modelConfig,
54
+ providerConfigs
417
55
  });
418
- config.endpoints.push({
419
- handler: createMentionSuggestionHandler({
420
- collections: collectionPermissions
421
- }),
422
- method: "post",
423
- path: "/ai-mention-suggestion"
56
+ registerAIEndpoints({
57
+ allowUserApiKeys,
58
+ collectionPermissions,
59
+ config,
60
+ generateFields,
61
+ maxOutputTokens,
62
+ maxTokenUsage,
63
+ mediaUploadOptions,
64
+ modelConfig,
65
+ promptCaching: pluginOptions.promptCaching !== false,
66
+ providerConfigs,
67
+ textGenerationPageContexts,
68
+ translationPageContexts
424
69
  });
425
- if (generateFields) {
426
- config.endpoints.push({
427
- handler: createGenerateFieldHandler({
428
- allowUserApiKeys,
429
- maxOutputTokens,
430
- maxTokenUsage,
431
- models: modelConfig,
432
- pageContexts: textGenerationPageContexts,
433
- providers: providerConfigs
434
- }),
435
- method: "post",
436
- path: "/ai-generate-field"
437
- });
438
- }
439
- if (translationPageContexts.size > 0) {
440
- config.endpoints.push({
441
- handler: createTranslateDocumentHandler({
442
- allowUserApiKeys,
443
- maxOutputTokens,
444
- maxTokenUsage,
445
- models: modelConfig,
446
- pageContexts: translationPageContexts,
447
- providers: providerConfigs
448
- }),
449
- method: "post",
450
- path: "/ai-translate-document"
451
- });
452
- }
453
- if (mediaUploadOptions) {
454
- config.endpoints.push({
455
- handler: createMediaUploadHandler(mediaUploadOptions),
456
- method: "post",
457
- path: "/ai-upload-media"
458
- });
459
- }
460
70
  if (incomingOnInit) {
461
71
  config.onInit = async (payload)=>{
462
72
  await incomingOnInit(payload);
@@ -0,0 +1,30 @@
1
+ import type { Config } from "payload";
2
+ import { type getResolvedAIModelConfig, type ResolvedAIProviderConfig } from "../features/providers/options.js";
3
+ import type { MediaUploadOptions } from "../handlers/mediaUploadHandler.js";
4
+ import { type TranslationPageContext } from "../features/content/documentTranslation.js";
5
+ import { type TextGenerationPageContext } from "../features/content/fieldGeneration.js";
6
+ import type { CollectionTypeAIOptions } from "./types.js";
7
+ export declare const addAccountFields: ({ allowUserApiKeys, config }: {
8
+ allowUserApiKeys: boolean;
9
+ config: Config;
10
+ }) => void;
11
+ export declare const addAIFieldsToDocumentsAndGlobals: ({ addGenerateFields, addAIInput, addTranslation, authCollections, config, uploadCollections, }: {
12
+ addGenerateFields: boolean;
13
+ addAIInput: boolean;
14
+ addTranslation: boolean;
15
+ authCollections?: CollectionTypeAIOptions;
16
+ config: Config;
17
+ uploadCollections?: CollectionTypeAIOptions;
18
+ }) => {
19
+ textGenerationPageContexts: Map<string, TextGenerationPageContext>;
20
+ translationPageContexts: Map<string, TranslationPageContext>;
21
+ };
22
+ export declare const configureAIAdmin: ({ allowUserApiKeys, collectionSlugs, config, managedProviders, mediaUploadOptions, modelConfig, providerConfigs, }: {
23
+ allowUserApiKeys: boolean;
24
+ collectionSlugs: string[];
25
+ config: Config;
26
+ managedProviders: boolean;
27
+ mediaUploadOptions: MediaUploadOptions | null;
28
+ modelConfig: ReturnType<typeof getResolvedAIModelConfig>;
29
+ providerConfigs: ResolvedAIProviderConfig[];
30
+ }) => void;
@@ -0,0 +1,155 @@
1
+ import { aiProviders, toClientAIProviderProfiles } from "../features/providers/options.js";
2
+ import { hasLocalizedFields } from "../features/content/documentTranslation.js";
3
+ import { addTextGenerationFields } from "../features/content/fieldGeneration.js";
4
+ import { isInternalCollection } from "../utils/data.js";
5
+ const hasAuthenticatedUser = (_data, _siblingData, { user })=>Boolean(user);
6
+ export const addAccountFields = ({ allowUserApiKeys, config })=>{
7
+ const adminUserSlug = config.admin?.user;
8
+ if (!adminUserSlug || !config.collections) return;
9
+ const userCollection = config.collections.find((c)=>c.slug === adminUserSlug);
10
+ if (!userCollection) return;
11
+ userCollection.fields.push({
12
+ name: "aiProvider",
13
+ type: "select",
14
+ admin: {
15
+ condition: hasAuthenticatedUser
16
+ },
17
+ defaultValue: "openai",
18
+ label: "AI Provider",
19
+ options: aiProviders
20
+ });
21
+ if (allowUserApiKeys) {
22
+ userCollection.fields.push({
23
+ name: "aiApiKey",
24
+ type: "text",
25
+ admin: {
26
+ condition: hasAuthenticatedUser,
27
+ components: {
28
+ Field: "@mvriu5/payload-ai/client#AIApiKeyField"
29
+ },
30
+ description: "Optional. If empty, the plugin uses the provider API key from environment variables."
31
+ },
32
+ label: "AI API Key"
33
+ });
34
+ }
35
+ };
36
+ const aiField = {
37
+ name: "payloadAi",
38
+ type: "ui",
39
+ admin: {
40
+ condition: hasAuthenticatedUser,
41
+ components: {
42
+ Field: "@mvriu5/payload-ai/client#AIInput"
43
+ }
44
+ }
45
+ };
46
+ const translateDocumentComponent = "@mvriu5/payload-ai/client#TranslateDocumentButton";
47
+ const getEntityLabel = (label, fallback)=>{
48
+ if (typeof label === "string") return label;
49
+ if (label && typeof label === "object") {
50
+ const translatedLabel = Object.values(label).find((value)=>typeof value === "string");
51
+ if (typeof translatedLabel === "string") return translatedLabel;
52
+ }
53
+ return fallback;
54
+ };
55
+ export const addAIFieldsToDocumentsAndGlobals = ({ addGenerateFields, addAIInput, addTranslation, authCollections, config, uploadCollections })=>{
56
+ const pageContexts = new Map();
57
+ const translationPageContexts = new Map();
58
+ for (const collection of config.collections || []){
59
+ if (isInternalCollection(collection.slug)) continue;
60
+ if (collection.slug === "payload-ai-auditlog") continue;
61
+ const allowAIInput = addAIInput && (!collection.auth || authCollections?.aiInput === true) && (!collection.upload || uploadCollections?.aiInput === true);
62
+ const allowGenerateFields = addGenerateFields && (!collection.auth || authCollections?.generateFields === true) && (!collection.upload || uploadCollections?.generateFields === true);
63
+ if (allowGenerateFields) {
64
+ const pageContext = addTextGenerationFields({
65
+ fields: collection.fields || [],
66
+ label: getEntityLabel(collection.labels?.singular, collection.slug),
67
+ slug: collection.slug,
68
+ type: "collection"
69
+ });
70
+ pageContexts.set(`collection:${collection.slug}`, pageContext);
71
+ }
72
+ if (allowAIInput) collection.fields = [
73
+ aiField,
74
+ ...collection.fields || []
75
+ ];
76
+ if (addTranslation && hasLocalizedFields(collection.fields || [])) {
77
+ translationPageContexts.set(`collection:${collection.slug}`, {
78
+ fields: collection.fields || [],
79
+ label: getEntityLabel(collection.labels?.singular, collection.slug),
80
+ slug: collection.slug,
81
+ type: "collection"
82
+ });
83
+ collection.admin = collection.admin || {};
84
+ collection.admin.components = collection.admin.components || {};
85
+ collection.admin.components.edit = collection.admin.components.edit || {};
86
+ const controls = collection.admin.components.edit.beforeDocumentControls || [];
87
+ if (!controls.includes(translateDocumentComponent)) {
88
+ collection.admin.components.edit.beforeDocumentControls = [
89
+ ...controls,
90
+ translateDocumentComponent
91
+ ];
92
+ }
93
+ }
94
+ }
95
+ for (const global of config.globals || []){
96
+ if (addGenerateFields) {
97
+ const pageContext = addTextGenerationFields({
98
+ fields: global.fields || [],
99
+ label: getEntityLabel(global.label, global.slug),
100
+ slug: global.slug,
101
+ type: "global"
102
+ });
103
+ pageContexts.set(`global:${global.slug}`, pageContext);
104
+ }
105
+ if (addAIInput) global.fields = [
106
+ aiField,
107
+ ...global.fields || []
108
+ ];
109
+ if (addTranslation && hasLocalizedFields(global.fields || [])) {
110
+ translationPageContexts.set(`global:${global.slug}`, {
111
+ fields: global.fields || [],
112
+ label: getEntityLabel(global.label, global.slug),
113
+ slug: global.slug,
114
+ type: "global"
115
+ });
116
+ global.admin = global.admin || {};
117
+ global.admin.components = global.admin.components || {};
118
+ global.admin.components.elements = global.admin.components.elements || {};
119
+ const controls = global.admin.components.elements.beforeDocumentControls || [];
120
+ if (!controls.includes(translateDocumentComponent)) {
121
+ global.admin.components.elements.beforeDocumentControls = [
122
+ ...controls,
123
+ translateDocumentComponent
124
+ ];
125
+ }
126
+ }
127
+ }
128
+ return {
129
+ textGenerationPageContexts: pageContexts,
130
+ translationPageContexts
131
+ };
132
+ };
133
+ export const configureAIAdmin = ({ allowUserApiKeys, collectionSlugs, config, managedProviders, mediaUploadOptions, modelConfig, providerConfigs })=>{
134
+ if (!config.admin) config.admin = {};
135
+ config.admin.custom = {
136
+ ...config.admin.custom || {},
137
+ payloadAiPlugin: {
138
+ ...config.admin.custom?.payloadAiPlugin || {},
139
+ allowUserApiKeys,
140
+ collectionSlugs,
141
+ managedProviders,
142
+ ...mediaUploadOptions ? {
143
+ media: {
144
+ ...mediaUploadOptions,
145
+ enabled: true
146
+ }
147
+ } : {},
148
+ models: modelConfig,
149
+ providers: toClientAIProviderProfiles(providerConfigs)
150
+ }
151
+ };
152
+ if (!config.admin.components) config.admin.components = {};
153
+ if (!config.admin.components.beforeDashboard) config.admin.components.beforeDashboard = [];
154
+ config.admin.components.beforeDashboard.push("@mvriu5/payload-ai/client#Dashboard");
155
+ };
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from "payload";
2
+ export declare const createAIChangesCollection: () => CollectionConfig;
3
+ export declare const createAITokenUsageCollection: () => CollectionConfig;