@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.
- package/dist/components/action-toast/ActionToast.d.ts +1 -14
- package/dist/components/action-toast/ActionToast.js +1 -1
- package/dist/components/ai-input/AIInput.js +1 -1
- package/dist/components/audit-log-list/AuditLogList.d.ts +2 -3
- package/dist/components/diff-dialog/DiffDialog.d.ts +2 -6
- package/dist/components/diff-dialog/DiffDialog.js +1 -1
- package/dist/components/generate-field/GenerateField.js +2 -0
- package/dist/components/hooks/useAIChatStream.d.ts +1 -1
- package/dist/components/hooks/useAISettings.d.ts +1 -1
- package/dist/components/hooks/useAISettings.js +1 -1
- package/dist/components/hooks/useDocumentMentionSuggestions.js +1 -1
- package/dist/components/hooks/useMentions.js +1 -1
- package/dist/components/hooks/usePluginConfig.d.ts +6 -6
- package/dist/components/hooks/usePluginConfig.js +1 -1
- package/dist/{payload → features}/collectionPermissions.js +1 -1
- package/dist/{payload/textFieldGeneration.d.ts → features/content/fieldGeneration.d.ts} +4 -0
- package/dist/{payload/textFieldGeneration.js → features/content/fieldGeneration.js} +19 -3
- package/dist/{ai → features}/promptCaching.d.ts +2 -2
- package/dist/features/proposals/containerNormalization.d.ts +8 -0
- package/dist/features/proposals/containerNormalization.js +112 -0
- package/dist/{payload/proposalData.d.ts → features/proposals/data.d.ts} +5 -10
- package/dist/features/proposals/data.js +113 -0
- package/dist/features/proposals/fieldNormalization.d.ts +2 -0
- package/dist/features/proposals/fieldNormalization.js +69 -0
- package/dist/features/proposals/normalizationTypes.d.ts +31 -0
- package/dist/features/proposals/normalizationTypes.js +1 -0
- package/dist/features/proposals/optionInference.d.ts +13 -0
- package/dist/features/proposals/optionInference.js +46 -0
- package/dist/features/proposals/recordNormalization.d.ts +6 -0
- package/dist/features/proposals/recordNormalization.js +78 -0
- package/dist/features/proposals/relationshipNormalization.d.ts +8 -0
- package/dist/features/proposals/relationshipNormalization.js +71 -0
- package/dist/{ai/proposalRepair.d.ts → features/proposals/repair.d.ts} +1 -1
- package/dist/{ai/proposalSigning.d.ts → features/proposals/signing.d.ts} +2 -5
- package/dist/{ai/proposalSigning.js → features/proposals/signing.js} +1 -1
- package/dist/features/proposals/target.d.ts +52 -0
- package/dist/features/proposals/target.js +125 -0
- package/dist/features/proposals/types.d.ts +49 -0
- package/dist/features/proposals/types.js +1 -0
- package/dist/features/proposals/validation.d.ts +12 -0
- package/dist/features/proposals/validation.js +26 -0
- package/dist/features/providers/requestContext.d.ts +53 -0
- package/dist/features/providers/requestContext.js +90 -0
- package/dist/{ai/providerRuntime.d.ts → features/providers/runtime.d.ts} +1 -1
- package/dist/{ai/providerRuntime.js → features/providers/runtime.js} +1 -1
- package/dist/features/schema/context.d.ts +7 -0
- package/dist/features/schema/context.js +11 -0
- package/dist/{payload/schemaContext.d.ts → features/schema/description.d.ts} +6 -33
- package/dist/{payload/schemaContext.js → features/schema/description.js} +3 -207
- package/dist/{payload/toolFieldSelection.d.ts → features/schema/fieldSelection.d.ts} +1 -1
- package/dist/{payload/toolFieldSelection.js → features/schema/fieldSelection.js} +2 -2
- package/dist/features/schema/mentionDocuments.d.ts +15 -0
- package/dist/features/schema/mentionDocuments.js +50 -0
- package/dist/features/schema/mentions.d.ts +18 -0
- package/dist/features/schema/mentions.js +100 -0
- package/dist/{payload/normalizeData.js → features/schema/normalize.js} +1 -1
- package/dist/features/schema/registry.d.ts +30 -0
- package/dist/features/schema/registry.js +73 -0
- package/dist/{payload → features/schema}/toolSchemas.d.ts +1 -1
- package/dist/{payload → features/schema}/toolSchemas.js +1 -1
- package/dist/{ai → features}/sensitiveData.js +1 -1
- package/dist/{ai → features}/tokenUsage.d.ts +1 -2
- package/dist/handlers/apply/audit.d.ts +42 -0
- package/dist/handlers/apply/audit.js +150 -0
- package/dist/handlers/apply/operations.d.ts +18 -0
- package/dist/handlers/apply/operations.js +377 -0
- package/dist/handlers/apply/types.d.ts +33 -0
- package/dist/handlers/apply/types.js +1 -0
- package/dist/handlers/apply/validation.d.ts +45 -0
- package/dist/handlers/apply/validation.js +125 -0
- package/dist/handlers/applyActionHandler.d.ts +1 -6
- package/dist/handlers/applyActionHandler.js +13 -757
- package/dist/handlers/auditLogHandler.js +4 -8
- package/dist/handlers/chat/intent.d.ts +32 -0
- package/dist/handlers/chat/intent.js +155 -0
- package/dist/handlers/chat/prompt.d.ts +20 -0
- package/dist/handlers/chat/prompt.js +55 -0
- package/dist/handlers/chat/proposalTools.d.ts +96 -0
- package/dist/handlers/chat/proposalTools.js +451 -0
- package/dist/handlers/chat/streaming.d.ts +38 -0
- package/dist/handlers/chat/streaming.js +136 -0
- package/dist/handlers/chat/toolFactory.d.ts +79 -0
- package/dist/handlers/chat/toolFactory.js +156 -0
- package/dist/handlers/chat/types.d.ts +57 -0
- package/dist/handlers/chat/types.js +1 -0
- package/dist/handlers/chatHandler.d.ts +1 -44
- package/dist/handlers/chatHandler.js +186 -1162
- package/dist/handlers/generateFieldHandler.d.ts +3 -8
- package/dist/handlers/generateFieldHandler.js +71 -99
- package/dist/handlers/http.d.ts +17 -0
- package/dist/handlers/http.js +21 -0
- package/dist/handlers/mediaUploadHandler.js +12 -34
- package/dist/handlers/mentionSuggestionHandler.d.ts +1 -1
- package/dist/handlers/mentionSuggestionHandler.js +6 -10
- package/dist/handlers/proposalDiffHandler.d.ts +1 -1
- package/dist/handlers/proposalDiffHandler.js +72 -195
- package/dist/handlers/translateDocumentHandler.d.ts +3 -8
- package/dist/handlers/translateDocumentHandler.js +26 -82
- package/dist/index.d.ts +4 -30
- package/dist/index.js +31 -421
- package/dist/plugin/admin.d.ts +30 -0
- package/dist/plugin/admin.js +155 -0
- package/dist/plugin/collections.d.ts +3 -0
- package/dist/plugin/collections.js +169 -0
- package/dist/plugin/endpoints.d.ts +23 -0
- package/dist/plugin/endpoints.js +84 -0
- package/dist/plugin/options.d.ts +4 -0
- package/dist/plugin/options.js +13 -0
- package/dist/plugin/types.d.ts +28 -0
- package/dist/plugin/types.js +1 -0
- package/dist/{payload/shared.d.ts → utils/data.d.ts} +2 -12
- package/package.json +1 -1
- package/dist/payload/proposalData.js +0 -578
- /package/dist/{payload → features}/collectionPermissions.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.d.ts +0 -0
- /package/dist/{payload → features/content}/documentTranslation.js +0 -0
- /package/dist/{ai → features}/promptCaching.js +0 -0
- /package/dist/{ai/proposalRepair.js → features/proposals/repair.js} +0 -0
- /package/dist/{ai/providerOptions.d.ts → features/providers/options.d.ts} +0 -0
- /package/dist/{ai/providerOptions.js → features/providers/options.js} +0 -0
- /package/dist/{payload/normalizeData.d.ts → features/schema/normalize.d.ts} +0 -0
- /package/dist/{ai → features}/sensitiveData.d.ts +0 -0
- /package/dist/{ai → features}/tokenUsage.js +0 -0
- /package/dist/{payload/shared.js → utils/data.js} +0 -0
- /package/dist/{payload → utils}/logging.d.ts +0 -0
- /package/dist/{payload → utils}/logging.js +0 -0
|
@@ -1,128 +1,63 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { applyLocalizedRequiredFallbackToPreparedData
|
|
5
|
-
import { isCollectionActionAllowed } from "../
|
|
6
|
-
import { getDefaultLocale, hasLocalizedData,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}, {
|
|
18
|
-
status: 400
|
|
19
|
-
});
|
|
20
|
-
if (!verifyActionProposal(proposal)) return Response.json({
|
|
21
|
-
error: "Proposal signature is invalid or expired."
|
|
22
|
-
}, {
|
|
23
|
-
status: 400
|
|
24
|
-
});
|
|
25
|
-
if (!isActionProposal(proposal)) return Response.json({
|
|
26
|
-
error: "Proposal is invalid."
|
|
27
|
-
}, {
|
|
28
|
-
status: 400
|
|
29
|
-
});
|
|
1
|
+
import { redactSensitiveData } from "../features/sensitiveData.js";
|
|
2
|
+
import { validateSignedProposal } from "../features/proposals/validation.js";
|
|
3
|
+
import { prepareLocalizedTargetUpdates, prepareProposalTargetData, resolveProposalTarget } from "../features/proposals/target.js";
|
|
4
|
+
import { applyLocalizedRequiredFallbackToPreparedData } from "../features/proposals/data.js";
|
|
5
|
+
import { isCollectionActionAllowed } from "../features/collectionPermissions.js";
|
|
6
|
+
import { getDefaultLocale, hasLocalizedData, mergeData } from "../utils/data.js";
|
|
7
|
+
import { jsonError, logHandlerError, readJSONBody, withAuthenticatedHandler } from "./http.js";
|
|
8
|
+
export const createProposalDiffHandler = (options = {})=>withAuthenticatedHandler(async (req)=>{
|
|
9
|
+
const body = await readJSONBody(req);
|
|
10
|
+
const proposalValidation = validateSignedProposal(body?.proposal);
|
|
11
|
+
if (!proposalValidation.ok) {
|
|
12
|
+
if (proposalValidation.error === "missing") return jsonError("Proposal is required");
|
|
13
|
+
if (proposalValidation.error === "invalid_signature") return jsonError("Proposal signature is invalid or expired.");
|
|
14
|
+
return jsonError("Proposal is invalid.");
|
|
15
|
+
}
|
|
16
|
+
const proposal = proposalValidation.proposal;
|
|
30
17
|
try {
|
|
31
18
|
const inferenceText = body?.prompt;
|
|
32
19
|
const defaultLocale = getDefaultLocale(req);
|
|
20
|
+
const target = resolveProposalTarget({
|
|
21
|
+
proposal,
|
|
22
|
+
req
|
|
23
|
+
});
|
|
33
24
|
if (proposal.action === "updateGlobal") {
|
|
34
|
-
|
|
35
|
-
if (!globalConfig) return Response.json({
|
|
36
|
-
error: "Unknown global"
|
|
37
|
-
}, {
|
|
38
|
-
status: 400
|
|
39
|
-
});
|
|
25
|
+
if (!target || target.type !== "global") return jsonError("Unknown global");
|
|
40
26
|
if (hasLocalizedData(proposal)) {
|
|
41
|
-
const
|
|
42
|
-
const afterByLocale = {};
|
|
43
|
-
const globalFields = getSchemaFields({
|
|
44
|
-
fields: globalConfig.fields || [],
|
|
45
|
-
slug: proposal.slug
|
|
46
|
-
});
|
|
47
|
-
const preparedData = prepareProposalWriteData({
|
|
48
|
-
collectionConfig: {
|
|
49
|
-
fields: globalConfig.fields || [],
|
|
50
|
-
slug: proposal.slug
|
|
51
|
-
},
|
|
27
|
+
const preparedData = prepareProposalTargetData({
|
|
52
28
|
inferenceText,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
mode: "update"
|
|
29
|
+
proposal,
|
|
30
|
+
target
|
|
56
31
|
});
|
|
57
32
|
if (preparedData.issues.length > 0 || !preparedData.localizedData) {
|
|
58
|
-
return
|
|
59
|
-
error: "Proposal is invalid."
|
|
60
|
-
}, {
|
|
61
|
-
status: 400
|
|
62
|
-
});
|
|
33
|
+
return jsonError("Proposal is invalid.");
|
|
63
34
|
}
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
overrideAccess: false,
|
|
69
|
-
req,
|
|
70
|
-
slug: proposal.slug
|
|
71
|
-
}) : null;
|
|
72
|
-
const localizedResults = await Promise.all(Object.entries(preparedData.localizedData).map(async ([locale, localeData])=>{
|
|
73
|
-
const doc = await req.payload.findGlobal({
|
|
74
|
-
depth: 2,
|
|
75
|
-
fallbackLocale: false,
|
|
76
|
-
locale,
|
|
77
|
-
overrideAccess: false,
|
|
78
|
-
req,
|
|
79
|
-
slug: proposal.slug
|
|
80
|
-
});
|
|
81
|
-
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
82
|
-
fallbackSource: locale === defaultLocale ? doc : defaultLocaleDoc || doc,
|
|
83
|
-
fields: globalFields,
|
|
84
|
-
preparedData: localeData
|
|
85
|
-
});
|
|
86
|
-
return {
|
|
87
|
-
completedData,
|
|
88
|
-
doc,
|
|
89
|
-
locale
|
|
90
|
-
};
|
|
91
|
-
}));
|
|
92
|
-
localizedResults.forEach(({ completedData, doc, locale })=>{
|
|
93
|
-
beforeByLocale[locale] = redactSensitiveData(doc);
|
|
94
|
-
afterByLocale[locale] = redactSensitiveData(mergeData(doc, completedData));
|
|
35
|
+
const localizedResults = await prepareLocalizedTargetUpdates({
|
|
36
|
+
defaultLocale: defaultLocale || undefined,
|
|
37
|
+
localizedData: preparedData.localizedData,
|
|
38
|
+
target
|
|
95
39
|
});
|
|
96
40
|
return Response.json({
|
|
97
|
-
after:
|
|
98
|
-
|
|
41
|
+
after: Object.fromEntries(localizedResults.map(({ after, locale })=>[
|
|
42
|
+
locale,
|
|
43
|
+
redactSensitiveData(after)
|
|
44
|
+
])),
|
|
45
|
+
before: Object.fromEntries(localizedResults.map(({ before, locale })=>[
|
|
46
|
+
locale,
|
|
47
|
+
redactSensitiveData(before)
|
|
48
|
+
]))
|
|
99
49
|
});
|
|
100
50
|
}
|
|
101
|
-
const preparedData =
|
|
102
|
-
collectionConfig: {
|
|
103
|
-
fields: globalConfig.fields || [],
|
|
104
|
-
slug: proposal.slug
|
|
105
|
-
},
|
|
106
|
-
data: proposal.data,
|
|
51
|
+
const preparedData = prepareProposalTargetData({
|
|
107
52
|
inferenceText,
|
|
108
|
-
|
|
109
|
-
|
|
53
|
+
proposal,
|
|
54
|
+
target
|
|
110
55
|
});
|
|
111
56
|
if (preparedData.issues.length > 0 || !preparedData.data) {
|
|
112
|
-
return
|
|
113
|
-
error: "Proposal is invalid."
|
|
114
|
-
}, {
|
|
115
|
-
status: 400
|
|
116
|
-
});
|
|
57
|
+
return jsonError("Proposal is invalid.");
|
|
117
58
|
}
|
|
118
|
-
const doc = await
|
|
119
|
-
|
|
120
|
-
...proposal.locale ? {
|
|
121
|
-
locale: proposal.locale
|
|
122
|
-
} : {},
|
|
123
|
-
overrideAccess: false,
|
|
124
|
-
req,
|
|
125
|
-
slug: proposal.slug
|
|
59
|
+
const doc = await target.read({
|
|
60
|
+
locale: proposal.locale
|
|
126
61
|
});
|
|
127
62
|
return Response.json({
|
|
128
63
|
after: redactSensitiveData(mergeData(doc, preparedData.data)),
|
|
@@ -134,61 +69,34 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
|
|
|
134
69
|
permissions: options.collections,
|
|
135
70
|
req,
|
|
136
71
|
slug: proposal.collection
|
|
137
|
-
})) return
|
|
138
|
-
|
|
139
|
-
}, {
|
|
140
|
-
status: 400
|
|
141
|
-
});
|
|
72
|
+
})) return jsonError("Unknown collection");
|
|
73
|
+
if (!target || target.type !== "collection") return jsonError("Unknown collection");
|
|
142
74
|
if (proposal.action === "delete") {
|
|
143
|
-
const doc = await
|
|
144
|
-
|
|
145
|
-
depth: 2,
|
|
146
|
-
id: proposal.id,
|
|
147
|
-
...proposal.locale ? {
|
|
148
|
-
locale: proposal.locale
|
|
149
|
-
} : {},
|
|
150
|
-
overrideAccess: false,
|
|
151
|
-
req
|
|
75
|
+
const doc = await target.read({
|
|
76
|
+
locale: proposal.locale
|
|
152
77
|
});
|
|
153
78
|
return Response.json({
|
|
154
79
|
after: {},
|
|
155
80
|
before: redactSensitiveData(doc)
|
|
156
81
|
});
|
|
157
82
|
}
|
|
158
|
-
const collectionConfig = req.payload.config.collections.find((collection)=>collection.slug === proposal.collection);
|
|
159
|
-
const collectionFields = getSchemaFields(collectionConfig);
|
|
160
83
|
if (hasLocalizedData(proposal)) {
|
|
161
|
-
const preparedData =
|
|
162
|
-
collectionConfig,
|
|
84
|
+
const preparedData = prepareProposalTargetData({
|
|
163
85
|
inferenceText,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
mode: proposal.action
|
|
86
|
+
proposal,
|
|
87
|
+
target
|
|
167
88
|
});
|
|
168
89
|
if (preparedData.issues.length > 0 || !preparedData.localizedData) {
|
|
169
|
-
return
|
|
170
|
-
error: "Proposal is invalid."
|
|
171
|
-
}, {
|
|
172
|
-
status: 400
|
|
173
|
-
});
|
|
90
|
+
return jsonError("Proposal is invalid.");
|
|
174
91
|
}
|
|
175
92
|
const afterByLocale = {};
|
|
176
93
|
const beforeByLocale = {};
|
|
177
|
-
const defaultLocaleDoc = proposal.action === "update" && defaultLocale ? await req.payload.findByID({
|
|
178
|
-
collection: proposal.collection,
|
|
179
|
-
depth: 2,
|
|
180
|
-
fallbackLocale: false,
|
|
181
|
-
id: proposal.id,
|
|
182
|
-
locale: defaultLocale,
|
|
183
|
-
overrideAccess: false,
|
|
184
|
-
req
|
|
185
|
-
}) : null;
|
|
186
94
|
let createFallbackSource = null;
|
|
187
95
|
if (proposal.action === "create") {
|
|
188
96
|
for (const [locale, localeData] of Object.entries(preparedData.localizedData)){
|
|
189
97
|
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
190
98
|
fallbackSource: createFallbackSource || {},
|
|
191
|
-
fields:
|
|
99
|
+
fields: target.fields,
|
|
192
100
|
preparedData: localeData
|
|
193
101
|
});
|
|
194
102
|
beforeByLocale[locale] = {};
|
|
@@ -196,30 +104,14 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
|
|
|
196
104
|
createFallbackSource = mergeData(createFallbackSource || {}, completedData);
|
|
197
105
|
}
|
|
198
106
|
} else {
|
|
199
|
-
const localizedResults = await
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
req
|
|
208
|
-
});
|
|
209
|
-
const completedData = applyLocalizedRequiredFallbackToPreparedData({
|
|
210
|
-
fallbackSource: locale === defaultLocale ? doc : defaultLocaleDoc || {},
|
|
211
|
-
fields: collectionFields,
|
|
212
|
-
preparedData: localeData
|
|
213
|
-
});
|
|
214
|
-
return {
|
|
215
|
-
completedData,
|
|
216
|
-
doc,
|
|
217
|
-
locale
|
|
218
|
-
};
|
|
219
|
-
}));
|
|
220
|
-
localizedResults.forEach(({ completedData, doc, locale })=>{
|
|
221
|
-
beforeByLocale[locale] = redactSensitiveData(doc);
|
|
222
|
-
afterByLocale[locale] = redactSensitiveData(mergeData(doc, completedData));
|
|
107
|
+
const localizedResults = await prepareLocalizedTargetUpdates({
|
|
108
|
+
defaultLocale: defaultLocale || undefined,
|
|
109
|
+
localizedData: preparedData.localizedData,
|
|
110
|
+
target
|
|
111
|
+
});
|
|
112
|
+
localizedResults.forEach(({ after, before, locale })=>{
|
|
113
|
+
beforeByLocale[locale] = redactSensitiveData(before);
|
|
114
|
+
afterByLocale[locale] = redactSensitiveData(after);
|
|
223
115
|
});
|
|
224
116
|
}
|
|
225
117
|
return Response.json({
|
|
@@ -227,19 +119,13 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
|
|
|
227
119
|
before: beforeByLocale
|
|
228
120
|
});
|
|
229
121
|
}
|
|
230
|
-
const preparedData =
|
|
231
|
-
collectionConfig,
|
|
232
|
-
data: proposal.data,
|
|
122
|
+
const preparedData = prepareProposalTargetData({
|
|
233
123
|
inferenceText,
|
|
234
|
-
|
|
235
|
-
|
|
124
|
+
proposal,
|
|
125
|
+
target
|
|
236
126
|
});
|
|
237
127
|
if (preparedData.issues.length > 0 || !preparedData.data) {
|
|
238
|
-
return
|
|
239
|
-
error: "Proposal is invalid."
|
|
240
|
-
}, {
|
|
241
|
-
status: 400
|
|
242
|
-
});
|
|
128
|
+
return jsonError("Proposal is invalid.");
|
|
243
129
|
}
|
|
244
130
|
if (proposal.action === "create") {
|
|
245
131
|
return Response.json({
|
|
@@ -247,29 +133,20 @@ export const createProposalDiffHandler = (options = {})=>async (req)=>{
|
|
|
247
133
|
before: {}
|
|
248
134
|
});
|
|
249
135
|
}
|
|
250
|
-
const doc = await
|
|
251
|
-
|
|
252
|
-
depth: 2,
|
|
253
|
-
id: proposal.id,
|
|
254
|
-
...proposal.locale ? {
|
|
255
|
-
locale: proposal.locale
|
|
256
|
-
} : {},
|
|
257
|
-
overrideAccess: false,
|
|
258
|
-
req
|
|
136
|
+
const doc = await target.read({
|
|
137
|
+
locale: proposal.locale
|
|
259
138
|
});
|
|
260
139
|
return Response.json({
|
|
261
140
|
after: redactSensitiveData(mergeData(doc, preparedData.data)),
|
|
262
141
|
before: redactSensitiveData(doc)
|
|
263
142
|
});
|
|
264
143
|
} catch (err) {
|
|
265
|
-
|
|
266
|
-
err,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
error: "Could not load proposal diff."
|
|
271
|
-
}, {
|
|
144
|
+
return logHandlerError({
|
|
145
|
+
error: err,
|
|
146
|
+
logMessage: "AI proposal diff failed",
|
|
147
|
+
publicMessage: "Could not load proposal diff.",
|
|
148
|
+
req,
|
|
272
149
|
status: 400
|
|
273
150
|
});
|
|
274
151
|
}
|
|
275
|
-
};
|
|
152
|
+
});
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import type { PayloadHandler } from "payload";
|
|
2
|
-
import { type
|
|
3
|
-
import { type
|
|
4
|
-
|
|
5
|
-
type TranslateDocumentOptions = {
|
|
6
|
-
allowUserApiKeys?: boolean;
|
|
2
|
+
import { type AIRequestOptions } from "../features/providers/requestContext.js";
|
|
3
|
+
import { type TranslationPageContext } from "../features/content/documentTranslation.js";
|
|
4
|
+
type TranslateDocumentOptions = AIRequestOptions & {
|
|
7
5
|
maxOutputTokens?: number;
|
|
8
|
-
maxTokenUsage?: ResolvedMaxTokenUsageOptions;
|
|
9
|
-
models?: AIModelConfig;
|
|
10
6
|
pageContexts: Map<string, TranslationPageContext>;
|
|
11
|
-
providers?: ResolvedAIProviderConfig[];
|
|
12
7
|
};
|
|
13
8
|
export declare const createTranslateDocumentHandler: (options: TranslateDocumentOptions) => PayloadHandler;
|
|
14
9
|
export {};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
2
|
import { generateText } from "ai";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { getTranslationValues } from "../payload/documentTranslation.js";
|
|
3
|
+
import { resolveAIRequestContext } from "../features/providers/requestContext.js";
|
|
4
|
+
import { getTranslationValues } from "../features/content/documentTranslation.js";
|
|
5
|
+
import { jsonError, logHandlerError, readJSONBody, withAuthenticatedHandler } from "./http.js";
|
|
7
6
|
const getDefaultLocale = (req)=>{
|
|
8
7
|
const localization = req.payload.config.localization;
|
|
9
8
|
if (!localization) return null;
|
|
@@ -39,22 +38,13 @@ const parseTranslations = (text, sourceCount)=>{
|
|
|
39
38
|
length: sourceCount
|
|
40
39
|
}, (_, index)=>parsed.translations?.[String(index)]);
|
|
41
40
|
};
|
|
42
|
-
export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
43
|
-
|
|
44
|
-
error: "Unauthorized"
|
|
45
|
-
}, {
|
|
46
|
-
status: 401
|
|
47
|
-
});
|
|
48
|
-
const body = req.json ? await req.json().catch(()=>null) : null;
|
|
41
|
+
export const createTranslateDocumentHandler = (options)=>withAuthenticatedHandler(async (req)=>{
|
|
42
|
+
const body = await readJSONBody(req);
|
|
49
43
|
const locale = body?.locale?.trim();
|
|
50
44
|
const scopeSlug = body?.scope?.slug?.trim();
|
|
51
45
|
const scopeType = body?.scope?.type;
|
|
52
46
|
if (!body?.action || !locale || !scopeSlug || !scopeType) {
|
|
53
|
-
return
|
|
54
|
-
error: "Action, locale, and page scope are required."
|
|
55
|
-
}, {
|
|
56
|
-
status: 400
|
|
57
|
-
});
|
|
47
|
+
return jsonError("Action, locale, and page scope are required.");
|
|
58
48
|
}
|
|
59
49
|
const pageContext = options.pageContexts.get(`${scopeType}:${scopeSlug}`);
|
|
60
50
|
const defaultLocale = getDefaultLocale(req);
|
|
@@ -93,55 +83,18 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
93
83
|
if (body.action === "status" || !available) return Response.json({
|
|
94
84
|
available
|
|
95
85
|
});
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
maxTokenUsage: options.maxTokenUsage,
|
|
86
|
+
const aiRequestResolution = await resolveAIRequestContext({
|
|
87
|
+
options,
|
|
99
88
|
req,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
error: "AI token usage limit reached."
|
|
104
|
-
}, {
|
|
105
|
-
status: 429
|
|
89
|
+
requestedModel: body.model,
|
|
90
|
+
requestedProvider: body.provider,
|
|
91
|
+
user: req.user
|
|
106
92
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const managedProvider = managedProviders?.find((provider)=>provider.id === requestedProvider);
|
|
110
|
-
if (managedProviders && !managedProvider) {
|
|
111
|
-
return Response.json({
|
|
112
|
-
error: `Unsupported AI provider: ${requestedProvider}`
|
|
113
|
-
}, {
|
|
114
|
-
status: 400
|
|
115
|
-
});
|
|
93
|
+
if (!aiRequestResolution.ok) {
|
|
94
|
+
return jsonError(aiRequestResolution.error.message, aiRequestResolution.error.code === "token_limit" ? 429 : 400);
|
|
116
95
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
error: `Unsupported AI provider: ${requestedProvider}`
|
|
120
|
-
}, {
|
|
121
|
-
status: 400
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
const provider = managedProvider?.provider || requestedProvider;
|
|
125
|
-
const requestedModel = body.model || managedProvider?.defaultModel;
|
|
126
|
-
const providerConfig = getProviderConfig({
|
|
127
|
-
apiKey: managedProvider ? managedProvider.apiKey : options.allowUserApiKeys === false ? null : user.aiApiKey,
|
|
128
|
-
defaultModels: options.models?.defaults,
|
|
129
|
-
model: requestedModel,
|
|
130
|
-
provider
|
|
131
|
-
});
|
|
132
|
-
if (!providerConfig.apiKey) return Response.json({
|
|
133
|
-
error: "Configure an AI provider API key first."
|
|
134
|
-
}, {
|
|
135
|
-
status: 400
|
|
136
|
-
});
|
|
137
|
-
const model = await getModel({
|
|
138
|
-
apiKey: providerConfig.apiKey,
|
|
139
|
-
...managedProvider?.baseURL ? {
|
|
140
|
-
baseURL: managedProvider.baseURL
|
|
141
|
-
} : {},
|
|
142
|
-
model: providerConfig.modelID,
|
|
143
|
-
provider
|
|
144
|
-
});
|
|
96
|
+
const aiRequest = aiRequestResolution.context;
|
|
97
|
+
const model = await aiRequest.loadModel();
|
|
145
98
|
const result = await generateText({
|
|
146
99
|
maxOutputTokens: options.maxOutputTokens || 700,
|
|
147
100
|
model,
|
|
@@ -162,15 +115,7 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
162
115
|
});
|
|
163
116
|
const translatedValues = parseTranslations(result.text, translationSourceValues.length);
|
|
164
117
|
if (translatedValues.some((value)=>value === undefined)) throw new Error("Translation response is incomplete.");
|
|
165
|
-
if (result.usage
|
|
166
|
-
await recordTokenUsage({
|
|
167
|
-
model: providerConfig.modelID,
|
|
168
|
-
provider: managedProvider?.id || provider,
|
|
169
|
-
req,
|
|
170
|
-
usage: result.usage,
|
|
171
|
-
userID: user.id
|
|
172
|
-
});
|
|
173
|
-
}
|
|
118
|
+
if (result.usage) await aiRequest.recordUsage(result.usage);
|
|
174
119
|
return Response.json({
|
|
175
120
|
available: true,
|
|
176
121
|
values: translationSourceValues.map((entry, index)=>({
|
|
@@ -180,16 +125,15 @@ export const createTranslateDocumentHandler = (options)=>async (req)=>{
|
|
|
180
125
|
}))
|
|
181
126
|
});
|
|
182
127
|
} catch (error) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
status: 500
|
|
128
|
+
return logHandlerError({
|
|
129
|
+
details: {
|
|
130
|
+
scopeSlug,
|
|
131
|
+
scopeType
|
|
132
|
+
},
|
|
133
|
+
error,
|
|
134
|
+
logMessage: "AI document translation failed",
|
|
135
|
+
publicMessage: "AI document translation failed.",
|
|
136
|
+
req
|
|
193
137
|
});
|
|
194
138
|
}
|
|
195
|
-
};
|
|
139
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
import type { Config } from "payload";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export type {
|
|
6
|
-
export type { MaxTokenUsageOptions } from "./ai/tokenUsage.js";
|
|
7
|
-
export type CollectionTypeAIOptions = {
|
|
8
|
-
aiInput?: boolean;
|
|
9
|
-
generateFields?: boolean;
|
|
10
|
-
};
|
|
11
|
-
export type PayloadAIPluginOptions = {
|
|
12
|
-
aiInput?: boolean;
|
|
13
|
-
allowUserApiKeys?: boolean;
|
|
14
|
-
authCollections?: CollectionTypeAIOptions;
|
|
15
|
-
collections?: CollectionPermissionMap;
|
|
16
|
-
disabled?: boolean;
|
|
17
|
-
generateFields?: boolean;
|
|
18
|
-
maxOutputTokens?: number;
|
|
19
|
-
media?: {
|
|
20
|
-
acceptedMimeTypes?: string[];
|
|
21
|
-
collectionSlug?: string;
|
|
22
|
-
enabled?: boolean;
|
|
23
|
-
maxFileSize?: number;
|
|
24
|
-
};
|
|
25
|
-
models?: AIModelConfig;
|
|
26
|
-
maxTokenUsage?: MaxTokenUsageOptions;
|
|
27
|
-
promptCaching?: boolean;
|
|
28
|
-
providers?: AIProviderConfig[];
|
|
29
|
-
translate?: boolean;
|
|
30
|
-
uploadCollections?: CollectionTypeAIOptions;
|
|
31
|
-
};
|
|
2
|
+
import type { PayloadAIPluginOptions } from "./plugin/types.js";
|
|
3
|
+
export type { AIModelConfig, AIProviderConfig, AIProviderModelOption } from "./features/providers/options.js";
|
|
4
|
+
export type { MaxTokenUsageOptions } from "./features/tokenUsage.js";
|
|
5
|
+
export type { CollectionTypeAIOptions, PayloadAIPluginOptions } from "./plugin/types.js";
|
|
32
6
|
export declare const payloadAiPlugin: (pluginOptions: PayloadAIPluginOptions) => (config: Config) => Config;
|