@mvriu5/payload-ai 1.6.3 → 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 (125) 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/hooks/useAIChatStream.d.ts +1 -1
  8. package/dist/components/hooks/useAISettings.d.ts +1 -1
  9. package/dist/components/hooks/useAISettings.js +1 -1
  10. package/dist/components/hooks/useDocumentMentionSuggestions.js +1 -1
  11. package/dist/components/hooks/useMentions.js +1 -1
  12. package/dist/components/hooks/usePluginConfig.d.ts +6 -6
  13. package/dist/components/hooks/usePluginConfig.js +1 -1
  14. package/dist/{payload → features}/collectionPermissions.js +1 -1
  15. package/dist/{ai → features}/promptCaching.d.ts +2 -2
  16. package/dist/features/proposals/containerNormalization.d.ts +8 -0
  17. package/dist/features/proposals/containerNormalization.js +112 -0
  18. package/dist/{payload/proposalData.d.ts → features/proposals/data.d.ts} +5 -10
  19. package/dist/features/proposals/data.js +113 -0
  20. package/dist/features/proposals/fieldNormalization.d.ts +2 -0
  21. package/dist/features/proposals/fieldNormalization.js +69 -0
  22. package/dist/features/proposals/normalizationTypes.d.ts +31 -0
  23. package/dist/features/proposals/normalizationTypes.js +1 -0
  24. package/dist/features/proposals/optionInference.d.ts +13 -0
  25. package/dist/features/proposals/optionInference.js +46 -0
  26. package/dist/features/proposals/recordNormalization.d.ts +6 -0
  27. package/dist/features/proposals/recordNormalization.js +78 -0
  28. package/dist/features/proposals/relationshipNormalization.d.ts +8 -0
  29. package/dist/features/proposals/relationshipNormalization.js +71 -0
  30. package/dist/{ai/proposalRepair.d.ts → features/proposals/repair.d.ts} +1 -1
  31. package/dist/{ai/proposalSigning.d.ts → features/proposals/signing.d.ts} +2 -5
  32. package/dist/{ai/proposalSigning.js → features/proposals/signing.js} +1 -1
  33. package/dist/features/proposals/target.d.ts +52 -0
  34. package/dist/features/proposals/target.js +125 -0
  35. package/dist/features/proposals/types.d.ts +49 -0
  36. package/dist/features/proposals/types.js +1 -0
  37. package/dist/features/proposals/validation.d.ts +12 -0
  38. package/dist/features/proposals/validation.js +26 -0
  39. package/dist/features/providers/requestContext.d.ts +53 -0
  40. package/dist/features/providers/requestContext.js +90 -0
  41. package/dist/{ai/providerRuntime.d.ts → features/providers/runtime.d.ts} +1 -1
  42. package/dist/{ai/providerRuntime.js → features/providers/runtime.js} +1 -1
  43. package/dist/features/schema/context.d.ts +7 -0
  44. package/dist/features/schema/context.js +11 -0
  45. package/dist/{payload/schemaContext.d.ts → features/schema/description.d.ts} +6 -33
  46. package/dist/{payload/schemaContext.js → features/schema/description.js} +3 -207
  47. package/dist/{payload/toolFieldSelection.d.ts → features/schema/fieldSelection.d.ts} +1 -1
  48. package/dist/{payload/toolFieldSelection.js → features/schema/fieldSelection.js} +2 -2
  49. package/dist/features/schema/mentionDocuments.d.ts +15 -0
  50. package/dist/features/schema/mentionDocuments.js +50 -0
  51. package/dist/features/schema/mentions.d.ts +18 -0
  52. package/dist/features/schema/mentions.js +100 -0
  53. package/dist/{payload/normalizeData.js → features/schema/normalize.js} +1 -1
  54. package/dist/features/schema/registry.d.ts +30 -0
  55. package/dist/features/schema/registry.js +73 -0
  56. package/dist/{payload → features/schema}/toolSchemas.d.ts +1 -1
  57. package/dist/{payload → features/schema}/toolSchemas.js +1 -1
  58. package/dist/{ai → features}/sensitiveData.js +1 -1
  59. package/dist/{ai → features}/tokenUsage.d.ts +1 -2
  60. package/dist/handlers/apply/audit.d.ts +42 -0
  61. package/dist/handlers/apply/audit.js +150 -0
  62. package/dist/handlers/apply/operations.d.ts +18 -0
  63. package/dist/handlers/apply/operations.js +377 -0
  64. package/dist/handlers/apply/types.d.ts +33 -0
  65. package/dist/handlers/apply/types.js +1 -0
  66. package/dist/handlers/apply/validation.d.ts +45 -0
  67. package/dist/handlers/apply/validation.js +125 -0
  68. package/dist/handlers/applyActionHandler.d.ts +1 -6
  69. package/dist/handlers/applyActionHandler.js +13 -757
  70. package/dist/handlers/auditLogHandler.js +4 -8
  71. package/dist/handlers/chat/intent.d.ts +32 -0
  72. package/dist/handlers/chat/intent.js +155 -0
  73. package/dist/handlers/chat/prompt.d.ts +20 -0
  74. package/dist/handlers/chat/prompt.js +55 -0
  75. package/dist/handlers/chat/proposalTools.d.ts +96 -0
  76. package/dist/handlers/chat/proposalTools.js +451 -0
  77. package/dist/handlers/chat/streaming.d.ts +38 -0
  78. package/dist/handlers/chat/streaming.js +136 -0
  79. package/dist/handlers/chat/toolFactory.d.ts +79 -0
  80. package/dist/handlers/chat/toolFactory.js +156 -0
  81. package/dist/handlers/chat/types.d.ts +57 -0
  82. package/dist/handlers/chat/types.js +1 -0
  83. package/dist/handlers/chatHandler.d.ts +1 -44
  84. package/dist/handlers/chatHandler.js +186 -1162
  85. package/dist/handlers/generateFieldHandler.d.ts +3 -8
  86. package/dist/handlers/generateFieldHandler.js +26 -97
  87. package/dist/handlers/http.d.ts +17 -0
  88. package/dist/handlers/http.js +21 -0
  89. package/dist/handlers/mediaUploadHandler.js +12 -34
  90. package/dist/handlers/mentionSuggestionHandler.d.ts +1 -1
  91. package/dist/handlers/mentionSuggestionHandler.js +6 -10
  92. package/dist/handlers/proposalDiffHandler.d.ts +1 -1
  93. package/dist/handlers/proposalDiffHandler.js +72 -195
  94. package/dist/handlers/translateDocumentHandler.d.ts +3 -8
  95. package/dist/handlers/translateDocumentHandler.js +26 -82
  96. package/dist/index.d.ts +4 -30
  97. package/dist/index.js +31 -421
  98. package/dist/plugin/admin.d.ts +30 -0
  99. package/dist/plugin/admin.js +155 -0
  100. package/dist/plugin/collections.d.ts +3 -0
  101. package/dist/plugin/collections.js +169 -0
  102. package/dist/plugin/endpoints.d.ts +23 -0
  103. package/dist/plugin/endpoints.js +84 -0
  104. package/dist/plugin/options.d.ts +4 -0
  105. package/dist/plugin/options.js +13 -0
  106. package/dist/plugin/types.d.ts +28 -0
  107. package/dist/plugin/types.js +1 -0
  108. package/dist/{payload/shared.d.ts → utils/data.d.ts} +2 -12
  109. package/package.json +1 -1
  110. package/dist/payload/proposalData.js +0 -578
  111. /package/dist/{payload → features}/collectionPermissions.d.ts +0 -0
  112. /package/dist/{payload → features/content}/documentTranslation.d.ts +0 -0
  113. /package/dist/{payload → features/content}/documentTranslation.js +0 -0
  114. /package/dist/{payload/textFieldGeneration.d.ts → features/content/fieldGeneration.d.ts} +0 -0
  115. /package/dist/{payload/textFieldGeneration.js → features/content/fieldGeneration.js} +0 -0
  116. /package/dist/{ai → features}/promptCaching.js +0 -0
  117. /package/dist/{ai/proposalRepair.js → features/proposals/repair.js} +0 -0
  118. /package/dist/{ai/providerOptions.d.ts → features/providers/options.d.ts} +0 -0
  119. /package/dist/{ai/providerOptions.js → features/providers/options.js} +0 -0
  120. /package/dist/{payload/normalizeData.d.ts → features/schema/normalize.d.ts} +0 -0
  121. /package/dist/{ai → features}/sensitiveData.d.ts +0 -0
  122. /package/dist/{ai → features}/tokenUsage.js +0 -0
  123. /package/dist/{payload/shared.js → utils/data.js} +0 -0
  124. /package/dist/{payload → utils}/logging.d.ts +0 -0
  125. /package/dist/{payload → utils}/logging.js +0 -0
@@ -1,14 +1,9 @@
1
1
  import type { PayloadHandler } from "payload";
2
- import { type ResolvedMaxTokenUsageOptions } from "../ai/tokenUsage.js";
3
- import { type AIModelConfig, type ResolvedAIProviderConfig } from "../ai/providerOptions.js";
4
- import type { TextGenerationPageContext } from "../payload/textFieldGeneration.js";
5
- type GenerateFieldOptions = {
6
- allowUserApiKeys?: boolean;
2
+ import { type AIRequestOptions } from "../features/providers/requestContext.js";
3
+ import type { TextGenerationPageContext } from "../features/content/fieldGeneration.js";
4
+ type GenerateFieldOptions = AIRequestOptions & {
7
5
  maxOutputTokens?: number;
8
- maxTokenUsage?: ResolvedMaxTokenUsageOptions;
9
- models?: AIModelConfig;
10
6
  pageContexts: Map<string, TextGenerationPageContext>;
11
- providers?: ResolvedAIProviderConfig[];
12
7
  };
13
8
  export declare const createGenerateFieldHandler: (options: GenerateFieldOptions) => PayloadHandler;
14
9
  export {};
@@ -1,8 +1,7 @@
1
1
  import { generateText } from "ai";
2
- import { getExceededTokenUsageLimit, recordTokenUsage } from "../ai/tokenUsage.js";
3
- import { isAIProvider } from "../ai/providerOptions.js";
4
- import { getModel, getProviderConfig } from "../ai/providerRuntime.js";
5
- import { redactSensitiveData } from "../ai/sensitiveData.js";
2
+ import { resolveAIRequestContext } from "../features/providers/requestContext.js";
3
+ import { redactSensitiveData } from "../features/sensitiveData.js";
4
+ import { jsonError, logHandlerError, readJSONBody, withAuthenticatedHandler } from "./http.js";
6
5
  const maxContextLength = 12000;
7
6
  const getCompactContext = (context)=>{
8
7
  const redacted = redactSensitiveData(context);
@@ -47,94 +46,33 @@ const parseGeneratedValue = (fieldType, text)=>{
47
46
  const withoutFence = value.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
48
47
  return JSON.parse(withoutFence);
49
48
  };
50
- export const createGenerateFieldHandler = (options)=>async (req)=>{
51
- if (!req.user) return Response.json({
52
- error: "Unauthorized"
53
- }, {
54
- status: 401
55
- });
56
- const body = req.json ? await req.json().catch(()=>null) : null;
49
+ export const createGenerateFieldHandler = (options)=>withAuthenticatedHandler(async (req)=>{
50
+ const body = await readJSONBody(req);
57
51
  const scopeType = body?.scope?.type;
58
52
  const scopeSlug = body?.scope?.slug?.trim();
59
53
  const fieldKey = body?.fieldKey?.trim();
60
54
  if (!scopeType || !scopeSlug || !fieldKey) {
61
- return Response.json({
62
- error: "Page scope and field are required."
63
- }, {
64
- status: 400
65
- });
55
+ return jsonError("Page scope and field are required.");
66
56
  }
67
57
  const pageContext = options.pageContexts.get(`${scopeType}:${scopeSlug}`);
68
58
  const fieldContext = pageContext?.fields.find((field)=>field.key === fieldKey);
69
59
  if (!pageContext || !fieldContext) {
70
- return Response.json({
71
- error: "This field is not available for AI generation."
72
- }, {
73
- status: 400
74
- });
60
+ return jsonError("This field is not available for AI generation.");
75
61
  }
76
- const user = req.user;
77
- const exceededLimit = await getExceededTokenUsageLimit({
78
- maxTokenUsage: options.maxTokenUsage,
62
+ const aiRequestResolution = await resolveAIRequestContext({
63
+ options,
79
64
  req,
80
- userID: user.id
65
+ requestedModel: body?.model,
66
+ requestedProvider: body?.provider,
67
+ user: req.user
81
68
  });
82
- if (exceededLimit) {
83
- return Response.json({
84
- error: "AI token usage limit reached."
85
- }, {
86
- status: 429
87
- });
88
- }
89
- const managedProviders = options.providers?.length ? options.providers : null;
90
- const requestedProvider = body?.provider || (managedProviders ? managedProviders[0].id : user.aiProvider || "openai");
91
- const managedProvider = managedProviders?.find((provider)=>provider.id === requestedProvider);
92
- if (managedProviders && !managedProvider) {
93
- return Response.json({
94
- error: `Unsupported AI provider: ${requestedProvider}`
95
- }, {
96
- status: 400
97
- });
98
- }
99
- if (!managedProvider && !isAIProvider(requestedProvider)) {
100
- return Response.json({
101
- error: `Unsupported AI provider: ${requestedProvider}`
102
- }, {
103
- status: 400
104
- });
105
- }
106
- const provider = managedProvider?.provider || requestedProvider;
107
- const requestedModel = body?.model || managedProvider?.defaultModel;
108
- if (managedProvider && requestedModel && !managedProvider.models.some((model)=>model.value === requestedModel)) {
109
- return Response.json({
110
- error: `Unsupported model "${requestedModel}" for AI provider "${managedProvider.id}".`
111
- }, {
112
- status: 400
113
- });
114
- }
115
- const providerConfig = getProviderConfig({
116
- apiKey: managedProvider ? managedProvider.apiKey : options.allowUserApiKeys === false ? null : user.aiApiKey,
117
- defaultModels: options.models?.defaults,
118
- model: requestedModel,
119
- provider
120
- });
121
- if (!providerConfig.apiKey) {
122
- return Response.json({
123
- error: "Configure an AI provider API key first."
124
- }, {
125
- status: 400
126
- });
69
+ if (!aiRequestResolution.ok) {
70
+ return jsonError(aiRequestResolution.error.message, aiRequestResolution.error.code === "token_limit" ? 429 : 400);
127
71
  }
72
+ const aiRequest = aiRequestResolution.context;
128
73
  try {
129
74
  const blockContext = getBlockContext(body?.context, body?.fieldPath);
130
- const model = await getModel({
131
- apiKey: providerConfig.apiKey,
132
- ...managedProvider?.baseURL ? {
133
- baseURL: managedProvider.baseURL
134
- } : {},
135
- model: providerConfig.modelID,
136
- provider
137
- });
75
+ const model = await aiRequest.loadModel();
138
76
  const result = await generateText({
139
77
  maxOutputTokens: Math.min(options.maxOutputTokens || 300, 600),
140
78
  model,
@@ -159,29 +97,20 @@ export const createGenerateFieldHandler = (options)=>async (req)=>{
159
97
  "Return no labels or explanation. For JSON fields, return strict JSON; for all other fields, return plain text without quotes or Markdown."
160
98
  ].join(" ")
161
99
  });
162
- if (result.usage && options.maxTokenUsage) {
163
- await recordTokenUsage({
164
- model: providerConfig.modelID,
165
- provider: managedProvider?.id || provider,
166
- req,
167
- usage: result.usage,
168
- userID: user.id
169
- });
170
- }
100
+ if (result.usage) await aiRequest.recordUsage(result.usage);
171
101
  const value = parseGeneratedValue(fieldContext.fieldType, result.text);
172
102
  return Response.json({
173
103
  value: fieldContext.maxLength && typeof value === "string" ? value.slice(0, fieldContext.maxLength) : value
174
104
  });
175
105
  } catch (error) {
176
- req.payload.logger.error({
177
- err: error,
178
- fieldKey,
179
- msg: "AI field generation failed"
180
- });
181
- return Response.json({
182
- error: "AI field generation failed."
183
- }, {
184
- status: 500
106
+ return logHandlerError({
107
+ details: {
108
+ fieldKey
109
+ },
110
+ error,
111
+ logMessage: "AI field generation failed",
112
+ publicMessage: "AI field generation failed.",
113
+ req
185
114
  });
186
115
  }
187
- };
116
+ });
@@ -0,0 +1,17 @@
1
+ import type { PayloadHandler } from "payload";
2
+ type HandlerRequest = Parameters<PayloadHandler>[0];
3
+ type AuthenticatedRequest = HandlerRequest & {
4
+ user: NonNullable<HandlerRequest["user"]>;
5
+ };
6
+ export declare const jsonError: (error: string, status?: number) => Response;
7
+ export declare const readJSONBody: <Body>(req: HandlerRequest) => Promise<Body | null>;
8
+ export declare const withAuthenticatedHandler: (handler: (req: AuthenticatedRequest) => Promise<Response> | Response) => PayloadHandler;
9
+ export declare const logHandlerError: ({ details, error, logMessage, publicMessage, req, status, }: {
10
+ details?: Record<string, unknown>;
11
+ error: unknown;
12
+ logMessage: string;
13
+ publicMessage: string;
14
+ req: HandlerRequest;
15
+ status?: number;
16
+ }) => Response;
17
+ export {};
@@ -0,0 +1,21 @@
1
+ export const jsonError = (error, status = 400)=>Response.json({
2
+ error
3
+ }, {
4
+ status
5
+ });
6
+ export const readJSONBody = async (req)=>{
7
+ if (!req.json) return null;
8
+ return await req.json().catch(()=>null);
9
+ };
10
+ export const withAuthenticatedHandler = (handler)=>async (req)=>{
11
+ if (!req.user) return jsonError("Unauthorized", 401);
12
+ return handler(req);
13
+ };
14
+ export const logHandlerError = ({ details, error, logMessage, publicMessage, req, status = 500 })=>{
15
+ req.payload.logger.error({
16
+ ...details,
17
+ err: error,
18
+ msg: logMessage
19
+ });
20
+ return jsonError(publicMessage, status);
21
+ };
@@ -1,5 +1,6 @@
1
1
  import { addDataAndFileToRequest } from "payload";
2
- import { getNumber, getString, isRecord } from "../payload/shared.js";
2
+ import { getNumber, getString, isRecord } from "../utils/data.js";
3
+ import { jsonError, logHandlerError } from "./http.js";
3
4
  const mimeTypeMatches = (mimeType, acceptedMimeType)=>{
4
5
  if (acceptedMimeType === mimeType) return true;
5
6
  if (!acceptedMimeType.endsWith("/*")) return false;
@@ -33,42 +34,22 @@ export const createMediaUploadHandler = ({ acceptedMimeTypes, collectionSlug, ma
33
34
  await addDataAndFileToRequest(uploadReq);
34
35
  const collectionConfig = uploadReq.payload.config.collections.find((collection)=>collection.slug === collectionSlug);
35
36
  if (!collectionConfig) {
36
- return Response.json({
37
- error: `Upload collection not found: ${collectionSlug}`
38
- }, {
39
- status: 400
40
- });
37
+ return jsonError(`Upload collection not found: ${collectionSlug}`);
41
38
  }
42
39
  if (!collectionConfig.upload) {
43
- return Response.json({
44
- error: `Collection is not upload-enabled: ${collectionSlug}`
45
- }, {
46
- status: 400
47
- });
40
+ return jsonError(`Collection is not upload-enabled: ${collectionSlug}`);
48
41
  }
49
42
  const file = uploadReq.file;
50
43
  if (!file) {
51
- return Response.json({
52
- error: "File is required"
53
- }, {
54
- status: 400
55
- });
44
+ return jsonError("File is required");
56
45
  }
57
46
  const fileSize = typeof file.size === "number" && Number.isFinite(file.size) ? file.size : 0;
58
47
  if (maxFileSize && fileSize > maxFileSize) {
59
- return Response.json({
60
- error: `File exceeds max size of ${maxFileSize} bytes`
61
- }, {
62
- status: 413
63
- });
48
+ return jsonError(`File exceeds max size of ${maxFileSize} bytes`, 413);
64
49
  }
65
50
  const mimeType = file.mimetype || "";
66
51
  if (mimeType && !isAcceptedMimeType(mimeType, acceptedMimeTypes)) {
67
- return Response.json({
68
- error: `File type is not accepted: ${mimeType}`
69
- }, {
70
- status: 415
71
- });
52
+ return jsonError(`File type is not accepted: ${mimeType}`, 415);
72
53
  }
73
54
  const doc = await uploadReq.payload.create({
74
55
  collection: collectionSlug,
@@ -86,14 +67,11 @@ export const createMediaUploadHandler = ({ acceptedMimeTypes, collectionSlug, ma
86
67
  doc
87
68
  });
88
69
  } catch (err) {
89
- uploadReq.payload.logger.error({
90
- err,
91
- msg: "AI media upload failed"
92
- });
93
- return Response.json({
94
- error: "Media upload failed."
95
- }, {
96
- status: 500
70
+ return logHandlerError({
71
+ error: err,
72
+ logMessage: "AI media upload failed",
73
+ publicMessage: "Media upload failed.",
74
+ req: uploadReq
97
75
  });
98
76
  }
99
77
  };
@@ -1,5 +1,5 @@
1
1
  import type { PayloadHandler } from "payload";
2
- import { type ResolvedCollectionPermissionMap } from "../payload/collectionPermissions.js";
2
+ import { type ResolvedCollectionPermissionMap } from "../features/collectionPermissions.js";
3
3
  type MentionSuggestionsOptions = {
4
4
  collections?: ResolvedCollectionPermissionMap;
5
5
  };
@@ -1,5 +1,6 @@
1
- import { isCollectionActionAllowed } from "../payload/collectionPermissions.js";
2
- import { getDocLabel } from "../payload/shared.js";
1
+ import { isCollectionActionAllowed } from "../features/collectionPermissions.js";
2
+ import { getDocLabel } from "../utils/data.js";
3
+ import { readJSONBody, withAuthenticatedHandler } from "./http.js";
3
4
  const getLocaleCodes = (req)=>{
4
5
  const localization = req.payload.config.localization;
5
6
  if (!localization) return [];
@@ -20,13 +21,8 @@ const hasLocalizedFields = (fields)=>{
20
21
  return Boolean(field.localized) || Boolean(field.fields?.length && hasLocalizedFields(field.fields)) || Boolean(field.tabs?.some((tab)=>hasLocalizedFields(tab.fields || [])));
21
22
  });
22
23
  };
23
- export const createMentionSuggestionHandler = (options = {})=>async (req)=>{
24
- if (!req.user) return Response.json({
25
- error: "Unauthorized"
26
- }, {
27
- status: 401
28
- });
29
- const body = req.json ? await req.json().catch(()=>null) : null;
24
+ export const createMentionSuggestionHandler = (options = {})=>withAuthenticatedHandler(async (req)=>{
25
+ const body = await readJSONBody(req);
30
26
  const query = body?.query?.trim();
31
27
  const collectionSlug = body?.collectionSlug?.trim();
32
28
  if (!query && !collectionSlug) return Response.json({
@@ -96,4 +92,4 @@ export const createMentionSuggestionHandler = (options = {})=>async (req)=>{
96
92
  return Response.json({
97
93
  suggestions: suggestions.slice(0, 5)
98
94
  });
99
- };
95
+ });
@@ -1,5 +1,5 @@
1
1
  import type { PayloadHandler } from "payload";
2
- import { type ResolvedCollectionPermissionMap } from "../payload/collectionPermissions.js";
2
+ import { type ResolvedCollectionPermissionMap } from "../features/collectionPermissions.js";
3
3
  type ProposalDiffOptions = {
4
4
  collections?: ResolvedCollectionPermissionMap;
5
5
  };