@mvriu5/payload-ai 1.6.0 → 1.6.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/README.md +131 -104
- package/dist/components/ai-input/AIInput.d.ts +1 -1
- package/dist/components/ai-input/AIInput.js +3 -1
- package/dist/components/generate-field/GenerateField.js +3 -2
- package/dist/handlers/translateDocumentHandler.js +17 -8
- package/dist/index.d.ts +6 -0
- package/dist/index.js +14 -4
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -25,18 +25,18 @@ npm add @mvriu5/payload-ai @openrouter/ai-sdk-provider
|
|
|
25
25
|
## Usage
|
|
26
26
|
|
|
27
27
|
```ts
|
|
28
|
-
import { buildConfig } from "payload"
|
|
29
|
-
import { payloadAiPlugin } from "@mvriu5/payload-ai"
|
|
28
|
+
import { buildConfig } from "payload"
|
|
29
|
+
import { payloadAiPlugin } from "@mvriu5/payload-ai"
|
|
30
30
|
|
|
31
31
|
export default buildConfig({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
})
|
|
32
|
+
plugins: [
|
|
33
|
+
payloadAiPlugin({
|
|
34
|
+
collections: {
|
|
35
|
+
posts: true,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
})
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Without centrally configured providers, the plugin adds two fields to the configured Payload admin user collection:
|
|
@@ -51,64 +51,72 @@ When `providers` is configured, provider selection and API keys are managed cent
|
|
|
51
51
|
## Options
|
|
52
52
|
|
|
53
53
|
```ts
|
|
54
|
-
import type { PayloadAIPluginOptions } from "@mvriu5/payload-ai"
|
|
54
|
+
import type { PayloadAIPluginOptions } from "@mvriu5/payload-ai"
|
|
55
55
|
|
|
56
56
|
const options: PayloadAIPluginOptions = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
update: true,
|
|
57
|
+
aiInput: true,
|
|
58
|
+
allowUserApiKeys: false,
|
|
59
|
+
authCollections: {
|
|
60
|
+
aiInput: false,
|
|
61
|
+
generateFields: false,
|
|
63
62
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
collections: {
|
|
64
|
+
media: {
|
|
65
|
+
read: true,
|
|
66
|
+
update: true,
|
|
67
|
+
},
|
|
68
|
+
posts: {
|
|
69
|
+
read: true,
|
|
70
|
+
create: true,
|
|
71
|
+
update: true,
|
|
72
|
+
delete: false,
|
|
73
|
+
},
|
|
74
|
+
users: true,
|
|
69
75
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
generateFields: true,
|
|
77
|
+
media: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
collectionSlug: "media",
|
|
80
|
+
acceptedMimeTypes: ["image/*"],
|
|
81
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
82
|
+
},
|
|
83
|
+
providers: [
|
|
84
|
+
{
|
|
85
|
+
id: "company-openai",
|
|
86
|
+
label: "Company OpenAI",
|
|
87
|
+
provider: "openai",
|
|
88
|
+
apiKey: process.env.COMPANY_OPENAI_API_KEY,
|
|
89
|
+
models: [
|
|
90
|
+
{ label: "GPT-4.1 Mini", value: "gpt-4.1-mini" },
|
|
91
|
+
{ label: "GPT-4.1", value: "gpt-4.1" },
|
|
92
|
+
],
|
|
93
|
+
defaultModel: "gpt-4.1-mini",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "ollama",
|
|
97
|
+
label: "Local Ollama",
|
|
98
|
+
provider: "openai",
|
|
99
|
+
baseURL: "http://localhost:11434/v1",
|
|
100
|
+
apiKey: "ollama",
|
|
101
|
+
models: [
|
|
102
|
+
{ label: "Llama 3.3", value: "llama3.3" },
|
|
103
|
+
{ label: "Qwen 3", value: "qwen3" },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
maxOutputTokens: 1200,
|
|
108
|
+
promptCaching: true,
|
|
109
|
+
translate: true,
|
|
110
|
+
uploadCollections: {
|
|
111
|
+
aiInput: true,
|
|
112
|
+
generateFields: false,
|
|
90
113
|
},
|
|
91
|
-
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
baseURL: "http://localhost:11434/v1",
|
|
96
|
-
apiKey: "ollama",
|
|
97
|
-
models: [
|
|
98
|
-
{ label: "Llama 3.3", value: "llama3.3" },
|
|
99
|
-
{ label: "Qwen 3", value: "qwen3" },
|
|
100
|
-
],
|
|
114
|
+
maxTokenUsage: {
|
|
115
|
+
type: "user",
|
|
116
|
+
perDay: 50_000,
|
|
117
|
+
perWeek: 250_000,
|
|
101
118
|
},
|
|
102
|
-
|
|
103
|
-
maxOutputTokens: 1200,
|
|
104
|
-
promptCaching: true,
|
|
105
|
-
translate: true,
|
|
106
|
-
maxTokenUsage: {
|
|
107
|
-
type: "user",
|
|
108
|
-
perDay: 50_000,
|
|
109
|
-
perWeek: 250_000,
|
|
110
|
-
},
|
|
111
|
-
};
|
|
119
|
+
}
|
|
112
120
|
```
|
|
113
121
|
|
|
114
122
|
`models` configures model choices for the user-selected provider mode. Use `providers` instead when provider selection, credentials, and endpoints should be managed centrally.
|
|
@@ -119,7 +127,7 @@ Controls whether the collapsible AI Assistant input is added to collection and g
|
|
|
119
127
|
|
|
120
128
|
```ts
|
|
121
129
|
payloadAiPlugin({
|
|
122
|
-
|
|
130
|
+
aiInput: false,
|
|
123
131
|
})
|
|
124
132
|
```
|
|
125
133
|
|
|
@@ -131,19 +139,38 @@ Controls whether supported fields receive a Generate control. It defaults to `tr
|
|
|
131
139
|
|
|
132
140
|
```ts
|
|
133
141
|
payloadAiPlugin({
|
|
134
|
-
|
|
142
|
+
generateFields: false,
|
|
135
143
|
})
|
|
136
144
|
```
|
|
137
145
|
|
|
138
146
|
Generate controls are added to `text`, `textarea`, `richText`, and `json` fields in collection and global edit views. When disabled, the plugin also omits the `/ai-generate-field` endpoint. The embedded AI Assistant and dashboard remain available.
|
|
139
147
|
|
|
148
|
+
### `authCollections` and `uploadCollections`
|
|
149
|
+
|
|
150
|
+
Auth and upload collections do not receive the embedded AI Assistant or Generate controls by default. Enable either feature explicitly for the respective collection type:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
payloadAiPlugin({
|
|
154
|
+
authCollections: {
|
|
155
|
+
aiInput: true,
|
|
156
|
+
generateFields: true,
|
|
157
|
+
},
|
|
158
|
+
uploadCollections: {
|
|
159
|
+
aiInput: true,
|
|
160
|
+
generateFields: true,
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The global `aiInput` and `generateFields` options still take precedence when set to `false`.
|
|
166
|
+
|
|
140
167
|
### `translate`
|
|
141
168
|
|
|
142
169
|
Controls document translation for localized collections and globals. It defaults to `true`.
|
|
143
170
|
|
|
144
171
|
```ts
|
|
145
172
|
payloadAiPlugin({
|
|
146
|
-
|
|
173
|
+
translate: false,
|
|
147
174
|
})
|
|
148
175
|
```
|
|
149
176
|
|
|
@@ -165,9 +192,9 @@ Use `true` to enable all AI actions for a collection:
|
|
|
165
192
|
|
|
166
193
|
```ts
|
|
167
194
|
payloadAiPlugin({
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
195
|
+
collections: {
|
|
196
|
+
posts: true,
|
|
197
|
+
},
|
|
171
198
|
})
|
|
172
199
|
```
|
|
173
200
|
|
|
@@ -175,14 +202,14 @@ Use granular permissions to control each action:
|
|
|
175
202
|
|
|
176
203
|
```ts
|
|
177
204
|
payloadAiPlugin({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
205
|
+
collections: {
|
|
206
|
+
posts: {
|
|
207
|
+
read: true,
|
|
208
|
+
create: true,
|
|
209
|
+
update: true,
|
|
210
|
+
delete: false,
|
|
211
|
+
},
|
|
184
212
|
},
|
|
185
|
-
},
|
|
186
213
|
})
|
|
187
214
|
```
|
|
188
215
|
|
|
@@ -190,14 +217,14 @@ You can mix both forms in the same object:
|
|
|
190
217
|
|
|
191
218
|
```ts
|
|
192
219
|
payloadAiPlugin({
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
220
|
+
collections: {
|
|
221
|
+
posts: true,
|
|
222
|
+
pages: true,
|
|
223
|
+
users: {
|
|
224
|
+
read: true,
|
|
225
|
+
update: true,
|
|
226
|
+
},
|
|
199
227
|
},
|
|
200
|
-
},
|
|
201
228
|
})
|
|
202
229
|
```
|
|
203
230
|
|
|
@@ -209,19 +236,19 @@ Enables media uploads from the AI assistant. Uploaded files are created through
|
|
|
209
236
|
|
|
210
237
|
```ts
|
|
211
238
|
payloadAiPlugin({
|
|
212
|
-
|
|
239
|
+
collections: {
|
|
240
|
+
media: {
|
|
241
|
+
read: true,
|
|
242
|
+
update: true,
|
|
243
|
+
},
|
|
244
|
+
posts: true,
|
|
245
|
+
},
|
|
213
246
|
media: {
|
|
214
|
-
|
|
215
|
-
|
|
247
|
+
enabled: true,
|
|
248
|
+
collectionSlug: "media",
|
|
249
|
+
acceptedMimeTypes: ["image/*"],
|
|
250
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
216
251
|
},
|
|
217
|
-
posts: true,
|
|
218
|
-
},
|
|
219
|
-
media: {
|
|
220
|
-
enabled: true,
|
|
221
|
-
collectionSlug: "media",
|
|
222
|
-
acceptedMimeTypes: ["image/*"],
|
|
223
|
-
maxFileSize: 10 * 1024 * 1024,
|
|
224
|
-
},
|
|
225
252
|
})
|
|
226
253
|
```
|
|
227
254
|
|
|
@@ -276,7 +303,7 @@ Controls the maximum number of output tokens the chat endpoint may generate per
|
|
|
276
303
|
|
|
277
304
|
```ts
|
|
278
305
|
payloadAiPlugin({
|
|
279
|
-
|
|
306
|
+
maxOutputTokens: 1200,
|
|
280
307
|
})
|
|
281
308
|
```
|
|
282
309
|
|
|
@@ -286,11 +313,11 @@ Limits total AI tokens across rolling 24-hour and 7-day windows.
|
|
|
286
313
|
|
|
287
314
|
```ts
|
|
288
315
|
payloadAiPlugin({
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
316
|
+
maxTokenUsage: {
|
|
317
|
+
type: "user",
|
|
318
|
+
perDay: 50_000,
|
|
319
|
+
perWeek: 250_000,
|
|
320
|
+
},
|
|
294
321
|
})
|
|
295
322
|
```
|
|
296
323
|
|
|
@@ -304,7 +331,7 @@ Enables provider prompt-caching hints and defaults to `true`. The chat endpoint
|
|
|
304
331
|
|
|
305
332
|
```ts
|
|
306
333
|
payloadAiPlugin({
|
|
307
|
-
|
|
334
|
+
promptCaching: false,
|
|
308
335
|
})
|
|
309
336
|
```
|
|
310
337
|
|
|
@@ -316,7 +343,7 @@ Controls whether the plugin adds an `aiApiKey` field to the admin user collectio
|
|
|
316
343
|
|
|
317
344
|
```ts
|
|
318
345
|
payloadAiPlugin({
|
|
319
|
-
|
|
346
|
+
allowUserApiKeys: false,
|
|
320
347
|
})
|
|
321
348
|
```
|
|
322
349
|
|
|
@@ -356,12 +383,12 @@ The apply endpoint returns only minimal status/doc references and does not retur
|
|
|
356
383
|
## Exports
|
|
357
384
|
|
|
358
385
|
```ts
|
|
359
|
-
import { payloadAiPlugin } from "@mvriu5/payload-ai"
|
|
360
|
-
import type { PayloadAiPluginOptions } from "@mvriu5/payload-ai"
|
|
386
|
+
import { payloadAiPlugin } from "@mvriu5/payload-ai"
|
|
387
|
+
import type { PayloadAiPluginOptions } from "@mvriu5/payload-ai"
|
|
361
388
|
```
|
|
362
389
|
|
|
363
390
|
Client components are exported through:
|
|
364
391
|
|
|
365
392
|
```ts
|
|
366
|
-
import { AIInput, AIApiKeyField } from "@mvriu5/payload-ai/client"
|
|
393
|
+
import { AIInput, AIApiKeyField } from "@mvriu5/payload-ai/client"
|
|
367
394
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Button, ChevronIcon, useConfig, useDocumentForm, useDocumentInfo, useLocale } from "@payloadcms/ui";
|
|
3
|
+
import { Button, ChevronIcon, useAuth, useConfig, useDocumentForm, useDocumentInfo, useLocale } from "@payloadcms/ui";
|
|
4
4
|
import { formatAdminURL } from "payload/shared";
|
|
5
5
|
import { useEffect, useRef, useState } from "react";
|
|
6
6
|
import { mergeData } from "../../payload/shared.js";
|
|
@@ -540,6 +540,8 @@ const DocumentAIInput = ()=>{
|
|
|
540
540
|
});
|
|
541
541
|
};
|
|
542
542
|
const AIInput = ({ isDashboard = false })=>{
|
|
543
|
+
const { user } = useAuth();
|
|
544
|
+
if (!user) return null;
|
|
543
545
|
if (isDashboard) return /*#__PURE__*/ _jsx(AIInputCore, {
|
|
544
546
|
isDashboard: true
|
|
545
547
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useConfig, useDocumentForm, useDocumentInfo, useField, useLocale } from "@payloadcms/ui";
|
|
3
|
+
import { useAuth, useConfig, useDocumentForm, useDocumentInfo, useField, useLocale } from "@payloadcms/ui";
|
|
4
4
|
import { formatAdminURL } from "payload/shared";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { createGeneratedRichTextValue } from "./richText.js";
|
|
@@ -27,6 +27,7 @@ const requestGeneratedValue = ({ apiRoute, body, cacheKey })=>{
|
|
|
27
27
|
return request;
|
|
28
28
|
};
|
|
29
29
|
const GenerateField = ({ field, generationFieldKey, generationFieldType, path, readOnly })=>{
|
|
30
|
+
const { user } = useAuth();
|
|
30
31
|
const configContext = useConfig();
|
|
31
32
|
const documentForm = useDocumentForm();
|
|
32
33
|
const documentInfo = useDocumentInfo();
|
|
@@ -87,7 +88,7 @@ const GenerateField = ({ field, generationFieldKey, generationFieldType, path, r
|
|
|
87
88
|
setIsGenerating(false);
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
|
-
if (!scope) return null;
|
|
91
|
+
if (!user || !scope) return null;
|
|
91
92
|
return /*#__PURE__*/ _jsxs("div", {
|
|
92
93
|
className: styles.generateField,
|
|
93
94
|
children: [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
1
2
|
import { generateText } from "ai";
|
|
2
3
|
import { isAIProvider } from "../ai/providerOptions.js";
|
|
3
4
|
import { getModel, getProviderConfig } from "../ai/providerRuntime.js";
|
|
@@ -70,17 +71,25 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
70
71
|
locale: defaultLocale,
|
|
71
72
|
req
|
|
72
73
|
});
|
|
74
|
+
const sourceValues = getTranslationValues(pageContext.fields, sourceDocument);
|
|
75
|
+
if (sourceValues.length === 0) return Response.json({
|
|
76
|
+
available: false
|
|
77
|
+
});
|
|
73
78
|
const targetDocument = await loadDocument({
|
|
74
79
|
context: pageContext,
|
|
75
80
|
id: body.id,
|
|
76
81
|
locale,
|
|
77
82
|
req
|
|
78
83
|
});
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
+
const targetValuesByPath = new Map(getTranslationValues(pageContext.fields, targetDocument).map((entry)=>[
|
|
85
|
+
entry.path,
|
|
86
|
+
entry.value
|
|
87
|
+
]));
|
|
88
|
+
const translationSourceValues = sourceValues.filter((entry)=>{
|
|
89
|
+
const targetValue = targetValuesByPath.get(entry.path);
|
|
90
|
+
return targetValue === undefined || isDeepStrictEqual(targetValue, entry.value);
|
|
91
|
+
});
|
|
92
|
+
const available = translationSourceValues.length > 0;
|
|
84
93
|
if (body.action === "status" || !available) return Response.json({
|
|
85
94
|
available
|
|
86
95
|
});
|
|
@@ -139,7 +148,7 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
139
148
|
prompt: JSON.stringify({
|
|
140
149
|
sourceLocale: defaultLocale,
|
|
141
150
|
targetLocale: locale,
|
|
142
|
-
values:
|
|
151
|
+
values: translationSourceValues.map((entry, index)=>({
|
|
143
152
|
id: String(index),
|
|
144
153
|
value: entry.value
|
|
145
154
|
}))
|
|
@@ -151,7 +160,7 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
151
160
|
"Return no Markdown or explanation."
|
|
152
161
|
].join(" ")
|
|
153
162
|
});
|
|
154
|
-
const translatedValues = parseTranslations(result.text,
|
|
163
|
+
const translatedValues = parseTranslations(result.text, translationSourceValues.length);
|
|
155
164
|
if (translatedValues.some((value)=>value === undefined)) throw new Error("Translation response is incomplete.");
|
|
156
165
|
if (result.usage && options.maxTokenUsage) {
|
|
157
166
|
await recordTokenUsage({
|
|
@@ -164,7 +173,7 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
164
173
|
}
|
|
165
174
|
return Response.json({
|
|
166
175
|
available: true,
|
|
167
|
-
values:
|
|
176
|
+
values: translationSourceValues.map((entry, index)=>({
|
|
168
177
|
fieldType: entry.fieldType,
|
|
169
178
|
path: entry.path,
|
|
170
179
|
value: translatedValues[index]
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,14 @@ import { type MaxTokenUsageOptions } from "./ai/tokenUsage.js";
|
|
|
4
4
|
import { type CollectionPermissionMap } from "./payload/collectionPermissions.js";
|
|
5
5
|
export type { AIModelConfig, AIProviderConfig, AIProviderModelOption } from "./ai/providerOptions.js";
|
|
6
6
|
export type { MaxTokenUsageOptions } from "./ai/tokenUsage.js";
|
|
7
|
+
export type CollectionTypeAIOptions = {
|
|
8
|
+
aiInput?: boolean;
|
|
9
|
+
generateFields?: boolean;
|
|
10
|
+
};
|
|
7
11
|
export type PayloadAIPluginOptions = {
|
|
8
12
|
aiInput?: boolean;
|
|
9
13
|
allowUserApiKeys?: boolean;
|
|
14
|
+
authCollections?: CollectionTypeAIOptions;
|
|
10
15
|
collections?: CollectionPermissionMap;
|
|
11
16
|
disabled?: boolean;
|
|
12
17
|
generateFields?: boolean;
|
|
@@ -22,5 +27,6 @@ export type PayloadAIPluginOptions = {
|
|
|
22
27
|
promptCaching?: boolean;
|
|
23
28
|
providers?: AIProviderConfig[];
|
|
24
29
|
translate?: boolean;
|
|
30
|
+
uploadCollections?: CollectionTypeAIOptions;
|
|
25
31
|
};
|
|
26
32
|
export declare const payloadAiPlugin: (pluginOptions: PayloadAIPluginOptions) => (config: Config) => Config;
|
package/dist/index.js
CHANGED
|
@@ -192,6 +192,7 @@ const createAITokenUsageCollection = ()=>({
|
|
|
192
192
|
],
|
|
193
193
|
timestamps: true
|
|
194
194
|
});
|
|
195
|
+
const hasAuthenticatedUser = (_data, _siblingData, { user })=>Boolean(user);
|
|
195
196
|
const addAccountFields = ({ allowUserApiKeys, config })=>{
|
|
196
197
|
const adminUserSlug = config.admin?.user;
|
|
197
198
|
if (!adminUserSlug || !config.collections) return;
|
|
@@ -200,6 +201,9 @@ const addAccountFields = ({ allowUserApiKeys, config })=>{
|
|
|
200
201
|
userCollection.fields.push({
|
|
201
202
|
name: "aiProvider",
|
|
202
203
|
type: "select",
|
|
204
|
+
admin: {
|
|
205
|
+
condition: hasAuthenticatedUser
|
|
206
|
+
},
|
|
203
207
|
defaultValue: "openai",
|
|
204
208
|
label: "AI Provider",
|
|
205
209
|
options: aiProviders
|
|
@@ -209,6 +213,7 @@ const addAccountFields = ({ allowUserApiKeys, config })=>{
|
|
|
209
213
|
name: "aiApiKey",
|
|
210
214
|
type: "text",
|
|
211
215
|
admin: {
|
|
216
|
+
condition: hasAuthenticatedUser,
|
|
212
217
|
components: {
|
|
213
218
|
Field: "@mvriu5/payload-ai/client#AIApiKeyField"
|
|
214
219
|
},
|
|
@@ -222,6 +227,7 @@ const aiField = {
|
|
|
222
227
|
name: "payloadAi",
|
|
223
228
|
type: "ui",
|
|
224
229
|
admin: {
|
|
230
|
+
condition: hasAuthenticatedUser,
|
|
225
231
|
components: {
|
|
226
232
|
Field: "@mvriu5/payload-ai/client#AIInput"
|
|
227
233
|
}
|
|
@@ -236,13 +242,15 @@ const getEntityLabel = (label, fallback)=>{
|
|
|
236
242
|
}
|
|
237
243
|
return fallback;
|
|
238
244
|
};
|
|
239
|
-
const addAIFieldsToDocumentsAndGlobals = ({ addGenerateFields, addAIInput, addTranslation, config })=>{
|
|
245
|
+
const addAIFieldsToDocumentsAndGlobals = ({ addGenerateFields, addAIInput, addTranslation, authCollections, config, uploadCollections })=>{
|
|
240
246
|
const pageContexts = new Map();
|
|
241
247
|
const translationPageContexts = new Map();
|
|
242
248
|
for (const collection of config.collections || []){
|
|
243
249
|
if (isInternalCollection(collection.slug)) continue;
|
|
244
250
|
if (collection.slug === "payload-ai-auditlog") continue;
|
|
245
|
-
|
|
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) {
|
|
246
254
|
const pageContext = addTextGenerationFields({
|
|
247
255
|
fields: collection.fields || [],
|
|
248
256
|
label: getEntityLabel(collection.labels?.singular, collection.slug),
|
|
@@ -251,7 +259,7 @@ const addAIFieldsToDocumentsAndGlobals = ({ addGenerateFields, addAIInput, addTr
|
|
|
251
259
|
});
|
|
252
260
|
pageContexts.set(`collection:${collection.slug}`, pageContext);
|
|
253
261
|
}
|
|
254
|
-
if (
|
|
262
|
+
if (allowAIInput) collection.fields = [
|
|
255
263
|
aiField,
|
|
256
264
|
...collection.fields || []
|
|
257
265
|
];
|
|
@@ -339,7 +347,9 @@ export const payloadAiPlugin = (pluginOptions)=>(config)=>{
|
|
|
339
347
|
addGenerateFields: generateFields,
|
|
340
348
|
addAIInput: pluginOptions.aiInput !== false,
|
|
341
349
|
addTranslation: pluginOptions.translate !== false,
|
|
342
|
-
|
|
350
|
+
authCollections: pluginOptions.authCollections,
|
|
351
|
+
config,
|
|
352
|
+
uploadCollections: pluginOptions.uploadCollections
|
|
343
353
|
});
|
|
344
354
|
const mentionCollectionSlugs = config.collections.flatMap((collection)=>{
|
|
345
355
|
if (isInternalCollection(collection.slug)) return [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mvriu5/payload-ai",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "AI assistant plugin for Payload CMS with provider selection, CMS mentions, and signed action proposals.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -66,10 +66,10 @@
|
|
|
66
66
|
"knip": "knip"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@ai-sdk/anthropic": "^
|
|
69
|
+
"@ai-sdk/anthropic": "^4.0.21",
|
|
70
70
|
"@ai-sdk/google": "^3.0.80",
|
|
71
71
|
"@ai-sdk/mistral": "^4.0.13",
|
|
72
|
-
"@ai-sdk/openai": "^
|
|
72
|
+
"@ai-sdk/openai": "^4.0.20",
|
|
73
73
|
"@floating-ui/react": "0.27.20",
|
|
74
74
|
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
75
75
|
"@payloadcms/db-postgres": "3.86.0",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"cross-env": "^10.1.0",
|
|
86
86
|
"eslint": "^10.5.0",
|
|
87
87
|
"graphql": "^16.8.1",
|
|
88
|
-
"jsdom": "^
|
|
88
|
+
"jsdom": "^30.0.0",
|
|
89
89
|
"knip": "^6.18.0",
|
|
90
90
|
"next": "16.2.11",
|
|
91
91
|
"payload": "3.86.0",
|
|
@@ -98,10 +98,10 @@
|
|
|
98
98
|
"vitest": "4.1.10"
|
|
99
99
|
},
|
|
100
100
|
"peerDependencies": {
|
|
101
|
-
"@ai-sdk/anthropic": "^
|
|
101
|
+
"@ai-sdk/anthropic": "^4.0.21",
|
|
102
102
|
"@ai-sdk/google": "^3.0.80",
|
|
103
103
|
"@ai-sdk/mistral": "^4.0.13",
|
|
104
|
-
"@ai-sdk/openai": "^
|
|
104
|
+
"@ai-sdk/openai": "^4.0.20",
|
|
105
105
|
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
106
106
|
"@payloadcms/ui": "^3.84.1",
|
|
107
107
|
"payload": "^3.84.1"
|