@humaan/payload-plugin-ai-assistant 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -2
- package/dist/admin/AiAssistantControl.d.ts +11 -0
- package/dist/admin/AiAssistantControl.js +31 -0
- package/dist/admin/AiAssistantControl.js.map +1 -0
- package/dist/admin/AiAssistantPanel.d.ts +10 -0
- package/dist/admin/AiAssistantPanel.js +208 -0
- package/dist/admin/AiAssistantPanel.js.map +1 -0
- package/dist/admin/AiAssistantPanel.module.scss +689 -0
- package/dist/admin/AiSourceSwitch.d.ts +13 -0
- package/dist/admin/AiSourceSwitch.js +14 -0
- package/dist/admin/AiSourceSwitch.js.map +1 -0
- package/dist/admin/BudgetField.d.ts +8 -0
- package/dist/admin/BudgetField.js +145 -0
- package/dist/admin/BudgetField.js.map +1 -0
- package/dist/admin/BudgetField.module.scss +47 -0
- package/dist/admin/Conversation.d.ts +12 -0
- package/dist/admin/Conversation.js +469 -0
- package/dist/admin/Conversation.js.map +1 -0
- package/dist/admin/Markdown.d.ts +9 -0
- package/dist/admin/Markdown.js +63 -0
- package/dist/admin/Markdown.js.map +1 -0
- package/dist/admin/PageContextBridge.d.ts +5 -0
- package/dist/admin/PageContextBridge.js +72 -0
- package/dist/admin/PageContextBridge.js.map +1 -0
- package/dist/admin/Pill.d.ts +11 -0
- package/dist/admin/Pill.js +27 -0
- package/dist/admin/Pill.js.map +1 -0
- package/dist/admin/PlanCard.d.ts +14 -0
- package/dist/admin/PlanCard.js +465 -0
- package/dist/admin/PlanCard.js.map +1 -0
- package/dist/admin/SettingsAlerts.d.ts +7 -0
- package/dist/admin/SettingsAlerts.js +37 -0
- package/dist/admin/SettingsAlerts.js.map +1 -0
- package/dist/admin/UsageSummary.d.ts +11 -0
- package/dist/admin/UsageSummary.js +129 -0
- package/dist/admin/UsageSummary.js.map +1 -0
- package/dist/admin/attachmentUpload.d.ts +25 -0
- package/dist/admin/attachmentUpload.js +76 -0
- package/dist/admin/attachmentUpload.js.map +1 -0
- package/dist/admin/openDocument.d.ts +13 -0
- package/dist/admin/openDocument.js +34 -0
- package/dist/admin/openDocument.js.map +1 -0
- package/dist/admin/percent.d.ts +10 -0
- package/dist/admin/percent.js +21 -0
- package/dist/admin/percent.js.map +1 -0
- package/dist/admin/threads.d.ts +21 -0
- package/dist/admin/threads.js +92 -0
- package/dist/admin/threads.js.map +1 -0
- package/dist/admin/toolSteps.d.ts +12 -0
- package/dist/admin/toolSteps.js +57 -0
- package/dist/admin/toolSteps.js.map +1 -0
- package/dist/admin/transport.d.ts +24 -0
- package/dist/admin/transport.js +75 -0
- package/dist/admin/transport.js.map +1 -0
- package/dist/admin/useAttachments.d.ts +32 -0
- package/dist/admin/useAttachments.js +91 -0
- package/dist/admin/useAttachments.js.map +1 -0
- package/dist/admin/useEntityLabel.d.ts +3 -0
- package/dist/admin/useEntityLabel.js +23 -0
- package/dist/admin/useEntityLabel.js.map +1 -0
- package/dist/attachments/collection.d.ts +15 -0
- package/dist/attachments/collection.js +90 -0
- package/dist/attachments/collection.js.map +1 -0
- package/dist/attachments/conversation.d.ts +25 -0
- package/dist/attachments/conversation.js +105 -0
- package/dist/attachments/conversation.js.map +1 -0
- package/dist/attachments/rules.d.ts +9 -0
- package/dist/attachments/rules.js +17 -0
- package/dist/attachments/rules.js.map +1 -0
- package/dist/attachments/store.d.ts +27 -0
- package/dist/attachments/store.js +187 -0
- package/dist/attachments/store.js.map +1 -0
- package/dist/billing.d.ts +60 -0
- package/dist/billing.js +116 -0
- package/dist/billing.js.map +1 -0
- package/dist/chat/history.d.ts +37 -0
- package/dist/chat/history.js +165 -0
- package/dist/chat/history.js.map +1 -0
- package/dist/chat/messages.d.ts +5 -0
- package/dist/chat/messages.js +3 -0
- package/dist/chat/messages.js.map +1 -0
- package/dist/chat/threads.d.ts +33 -0
- package/dist/chat/threads.js +77 -0
- package/dist/chat/threads.js.map +1 -0
- package/dist/chat/tools.d.ts +76 -0
- package/dist/chat/tools.js +596 -0
- package/dist/chat/tools.js.map +1 -0
- package/dist/chat/usage.d.ts +66 -0
- package/dist/chat/usage.js +219 -0
- package/dist/chat/usage.js.map +1 -0
- package/dist/collectionAccess.d.ts +6 -0
- package/dist/collectionAccess.js +32 -0
- package/dist/collectionAccess.js.map +1 -0
- package/dist/constants.d.ts +64 -0
- package/dist/constants.js +51 -0
- package/dist/constants.js.map +1 -0
- package/dist/endpoints/access.d.ts +4 -0
- package/dist/endpoints/access.js +28 -0
- package/dist/endpoints/access.js.map +1 -0
- package/dist/endpoints/allowance.d.ts +22 -0
- package/dist/endpoints/allowance.js +37 -0
- package/dist/endpoints/allowance.js.map +1 -0
- package/dist/endpoints/applyEndpoint.d.ts +12 -0
- package/dist/endpoints/applyEndpoint.js +170 -0
- package/dist/endpoints/applyEndpoint.js.map +1 -0
- package/dist/endpoints/budget.d.ts +30 -0
- package/dist/endpoints/budget.js +153 -0
- package/dist/endpoints/budget.js.map +1 -0
- package/dist/endpoints/chatEndpoint.d.ts +12 -0
- package/dist/endpoints/chatEndpoint.js +295 -0
- package/dist/endpoints/chatEndpoint.js.map +1 -0
- package/dist/endpoints/form.d.ts +23 -0
- package/dist/endpoints/form.js +95 -0
- package/dist/endpoints/form.js.map +1 -0
- package/dist/endpoints/usageEndpoint.d.ts +13 -0
- package/dist/endpoints/usageEndpoint.js +70 -0
- package/dist/endpoints/usageEndpoint.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lexical/editorConfig.d.ts +4 -0
- package/dist/lexical/editorConfig.js +19 -0
- package/dist/lexical/editorConfig.js.map +1 -0
- package/dist/lexical/lexicalToMarkdown.d.ts +10 -0
- package/dist/lexical/lexicalToMarkdown.js +10 -0
- package/dist/lexical/lexicalToMarkdown.js.map +1 -0
- package/dist/lexical/markdownToLexical.d.ts +7 -0
- package/dist/lexical/markdownToLexical.js +10 -0
- package/dist/lexical/markdownToLexical.js.map +1 -0
- package/dist/plan/allowed.d.ts +7 -0
- package/dist/plan/allowed.js +19 -0
- package/dist/plan/allowed.js.map +1 -0
- package/dist/plan/applyPlan.d.ts +16 -0
- package/dist/plan/applyPlan.js +353 -0
- package/dist/plan/applyPlan.js.map +1 -0
- package/dist/plan/catalogue.d.ts +37 -0
- package/dist/plan/catalogue.js +157 -0
- package/dist/plan/catalogue.js.map +1 -0
- package/dist/plan/diff.d.ts +13 -0
- package/dist/plan/diff.js +87 -0
- package/dist/plan/diff.js.map +1 -0
- package/dist/plan/parsePlan.d.ts +27 -0
- package/dist/plan/parsePlan.js +322 -0
- package/dist/plan/parsePlan.js.map +1 -0
- package/dist/plan/prompt.d.ts +1 -0
- package/dist/plan/prompt.js +45 -0
- package/dist/plan/prompt.js.map +1 -0
- package/dist/plan/stored.d.ts +53 -0
- package/dist/plan/stored.js +3 -0
- package/dist/plan/stored.js.map +1 -0
- package/dist/plan/tokens.d.ts +8 -0
- package/dist/plan/tokens.js +79 -0
- package/dist/plan/tokens.js.map +1 -0
- package/dist/plan/view.d.ts +4 -0
- package/dist/plan/view.js +43 -0
- package/dist/plan/view.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +464 -0
- package/dist/plugin.js.map +1 -0
- package/dist/read/outline.d.ts +7 -0
- package/dist/read/outline.js +70 -0
- package/dist/read/outline.js.map +1 -0
- package/dist/read/readTarget.d.ts +4 -0
- package/dist/read/readTarget.js +13 -0
- package/dist/read/readTarget.js.map +1 -0
- package/dist/read/readable.d.ts +8 -0
- package/dist/read/readable.js +131 -0
- package/dist/read/readable.js.map +1 -0
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.js +14 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schema/fields.d.ts +113 -0
- package/dist/schema/fields.js +1036 -0
- package/dist/schema/fields.js.map +1 -0
- package/dist/schema/listingFields.d.ts +9 -0
- package/dist/schema/listingFields.js +95 -0
- package/dist/schema/listingFields.js.map +1 -0
- package/dist/schema/slugify.d.ts +2 -0
- package/dist/schema/slugify.js +5 -0
- package/dist/schema/slugify.js.map +1 -0
- package/dist/settings.d.ts +10 -0
- package/dist/settings.js +54 -0
- package/dist/settings.js.map +1 -0
- package/dist/types.d.ts +198 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/uploads/routeAsset.d.ts +27 -0
- package/dist/uploads/routeAsset.js +127 -0
- package/dist/uploads/routeAsset.js.map +1 -0
- package/package.json +122 -3
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { withAttachmentRules } from './attachments/collection.js';
|
|
2
|
+
import { byokProblem, resolveByok } from './billing.js';
|
|
3
|
+
import { ownerAccess } from './collectionAccess.js';
|
|
4
|
+
import { aiAssistantAttachmentSlug, aiAssistantGlobalSlug, aiAssistantPlanSlug, aiAssistantThreadSlug, aiAssistantUsageSlug, assistantControlPath, budgetFieldPath, defaultDailySpendCapPercent, deniedSlugs, pageContextBridgePath, pluginCustomKey, settingsAlertsPath } from './constants.js';
|
|
5
|
+
import { applyEndpoint, cancelEndpoint } from './endpoints/applyEndpoint.js';
|
|
6
|
+
import { chatEndpoint } from './endpoints/chatEndpoint.js';
|
|
7
|
+
import { usageEndpoint } from './endpoints/usageEndpoint.js';
|
|
8
|
+
import { isAdminRole } from './settings.js';
|
|
9
|
+
const adminOnly = ({ req: { user } })=>isAdminRole(user);
|
|
10
|
+
/** The collection admins log in with: `admin.user`, as Payload reads it, or the first auth collection. */ function usersCollectionOf(config) {
|
|
11
|
+
const slug = config.admin?.user ?? config.collections?.find((collection)=>collection.auth)?.slug ?? 'users';
|
|
12
|
+
return slug;
|
|
13
|
+
}
|
|
14
|
+
function assertOptions(config, options) {
|
|
15
|
+
if (options.roles.length === 0) {
|
|
16
|
+
throw new Error('aiAssistantPlugin requires at least one role.');
|
|
17
|
+
}
|
|
18
|
+
if (!options.siteRules.trim()) {
|
|
19
|
+
throw new Error('aiAssistantPlugin requires site rules.');
|
|
20
|
+
}
|
|
21
|
+
for (const slug of Object.keys(options.collectionRules ?? {})){
|
|
22
|
+
const collection = config.collections?.some((item)=>item.slug === slug);
|
|
23
|
+
const global = config.globals?.some((item)=>item.slug === slug);
|
|
24
|
+
if (!collection && !global) {
|
|
25
|
+
throw new Error(`aiAssistantPlugin collectionRules refers to unknown target "${slug}".`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// A mistyped slug would leave that collection open to the assistant, so it fails loudly instead.
|
|
29
|
+
for (const slug of options.deniedCollections ?? []){
|
|
30
|
+
if (!config.collections?.some((item)=>item.slug === slug)) {
|
|
31
|
+
throw new Error(`aiAssistantPlugin deniedCollections refers to unknown collection "${slug}".`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
for (const slug of options.deniedGlobals ?? []){
|
|
35
|
+
if (!config.globals?.some((item)=>item.slug === slug)) {
|
|
36
|
+
throw new Error(`aiAssistantPlugin deniedGlobals refers to unknown global "${slug}".`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const byok = byokProblem(options.byok);
|
|
40
|
+
if (byok) {
|
|
41
|
+
throw new Error(`aiAssistantPlugin byok has ${byok}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function plansCollection(usersSlug, fallback) {
|
|
45
|
+
const owned = ownerAccess(fallback);
|
|
46
|
+
return {
|
|
47
|
+
slug: aiAssistantPlanSlug,
|
|
48
|
+
labels: {
|
|
49
|
+
singular: 'Humaan AI plan',
|
|
50
|
+
plural: 'Humaan AI plans'
|
|
51
|
+
},
|
|
52
|
+
admin: {
|
|
53
|
+
hidden: true
|
|
54
|
+
},
|
|
55
|
+
// The REST API can read and delete an editor's own plans, but not write them. Otherwise a plan could be
|
|
56
|
+
// written by hand and applied without the model or its checks.
|
|
57
|
+
access: {
|
|
58
|
+
create: ()=>false,
|
|
59
|
+
read: owned,
|
|
60
|
+
update: ()=>false,
|
|
61
|
+
delete: owned
|
|
62
|
+
},
|
|
63
|
+
fields: [
|
|
64
|
+
{
|
|
65
|
+
name: 'owner',
|
|
66
|
+
type: 'relationship',
|
|
67
|
+
relationTo: usersSlug,
|
|
68
|
+
required: true,
|
|
69
|
+
index: true
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'thread',
|
|
73
|
+
type: 'text',
|
|
74
|
+
index: true,
|
|
75
|
+
admin: {
|
|
76
|
+
description: 'The conversation the plan was proposed in.'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'status',
|
|
81
|
+
type: 'select',
|
|
82
|
+
required: true,
|
|
83
|
+
defaultValue: 'proposed',
|
|
84
|
+
options: [
|
|
85
|
+
{
|
|
86
|
+
label: 'Proposed',
|
|
87
|
+
value: 'proposed'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: 'Blocked',
|
|
91
|
+
value: 'blocked'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: 'Applied',
|
|
95
|
+
value: 'applied'
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
label: 'Cancelled',
|
|
99
|
+
value: 'cancelled'
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'summary',
|
|
105
|
+
type: 'textarea',
|
|
106
|
+
required: true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'plan',
|
|
110
|
+
type: 'json',
|
|
111
|
+
required: true
|
|
112
|
+
}
|
|
113
|
+
]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Conversations are written only by the assistant endpoints, so the REST API can read and delete them but not edit them.
|
|
118
|
+
* An edited history could put words in the assistant's mouth.
|
|
119
|
+
*/ function threadsCollection(usersSlug, fallback) {
|
|
120
|
+
const owned = ownerAccess(fallback);
|
|
121
|
+
return {
|
|
122
|
+
slug: aiAssistantThreadSlug,
|
|
123
|
+
labels: {
|
|
124
|
+
singular: 'Humaan AI conversation',
|
|
125
|
+
plural: 'Humaan AI conversations'
|
|
126
|
+
},
|
|
127
|
+
admin: {
|
|
128
|
+
hidden: true,
|
|
129
|
+
useAsTitle: 'title'
|
|
130
|
+
},
|
|
131
|
+
access: {
|
|
132
|
+
create: ()=>false,
|
|
133
|
+
read: owned,
|
|
134
|
+
update: ()=>false,
|
|
135
|
+
delete: owned
|
|
136
|
+
},
|
|
137
|
+
fields: [
|
|
138
|
+
{
|
|
139
|
+
name: 'owner',
|
|
140
|
+
type: 'relationship',
|
|
141
|
+
relationTo: usersSlug,
|
|
142
|
+
required: true,
|
|
143
|
+
index: true
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'chatId',
|
|
147
|
+
type: 'text',
|
|
148
|
+
required: true,
|
|
149
|
+
index: true
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'title',
|
|
153
|
+
type: 'text'
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'messages',
|
|
157
|
+
type: 'json'
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'files',
|
|
161
|
+
type: 'json',
|
|
162
|
+
admin: {
|
|
163
|
+
description: 'Names and sizes of attached files. The files stay in the browser until a plan saves them.'
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Each editor's spend per day. Written only by the chat endpoint; editors can read their own, admins all. */ function usageCollection(usersSlug, fallback) {
|
|
170
|
+
const owned = ownerAccess(fallback);
|
|
171
|
+
return {
|
|
172
|
+
slug: aiAssistantUsageSlug,
|
|
173
|
+
labels: {
|
|
174
|
+
singular: 'Humaan AI usage',
|
|
175
|
+
plural: 'Humaan AI usage'
|
|
176
|
+
},
|
|
177
|
+
admin: {
|
|
178
|
+
hidden: true
|
|
179
|
+
},
|
|
180
|
+
access: {
|
|
181
|
+
create: ()=>false,
|
|
182
|
+
read: async (args)=>isAdminRole(args.req.user) ? true : owned(args),
|
|
183
|
+
update: ()=>false,
|
|
184
|
+
delete: ()=>false
|
|
185
|
+
},
|
|
186
|
+
fields: [
|
|
187
|
+
{
|
|
188
|
+
name: 'owner',
|
|
189
|
+
type: 'relationship',
|
|
190
|
+
relationTo: usersSlug,
|
|
191
|
+
required: true,
|
|
192
|
+
index: true
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'day',
|
|
196
|
+
type: 'text',
|
|
197
|
+
required: true,
|
|
198
|
+
index: true,
|
|
199
|
+
admin: {
|
|
200
|
+
description: "YYYY-MM-DD in the site's time zone."
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'costUsd',
|
|
205
|
+
type: 'number',
|
|
206
|
+
required: true,
|
|
207
|
+
defaultValue: 0,
|
|
208
|
+
admin: {
|
|
209
|
+
description: "Billed to Vercel's credit."
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'ownKeyCostUsd',
|
|
214
|
+
type: 'number',
|
|
215
|
+
required: true,
|
|
216
|
+
defaultValue: 0,
|
|
217
|
+
admin: {
|
|
218
|
+
description: "Billed to the site's own provider key."
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
name: 'fallbackSteps',
|
|
223
|
+
type: 'number',
|
|
224
|
+
required: true,
|
|
225
|
+
defaultValue: 0,
|
|
226
|
+
admin: {
|
|
227
|
+
description: "Calls meant for the own key that ran on Vercel's credit."
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: 'tokens',
|
|
232
|
+
type: 'number',
|
|
233
|
+
required: true,
|
|
234
|
+
defaultValue: 0
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: 'replies',
|
|
238
|
+
type: 'number',
|
|
239
|
+
required: true,
|
|
240
|
+
defaultValue: 0
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/** Lets the drawer in the header know which document is open, and read its unsaved form values. */ function withPageContext(entity, kind) {
|
|
246
|
+
const denied = new Set(deniedSlugs);
|
|
247
|
+
if (denied.has(entity.slug) || 'auth' in entity && entity.auth) {
|
|
248
|
+
return entity;
|
|
249
|
+
}
|
|
250
|
+
const admin = entity.admin ?? {};
|
|
251
|
+
const components = admin.components ?? {};
|
|
252
|
+
if (kind === 'collection') {
|
|
253
|
+
const edit = components.edit ?? {};
|
|
254
|
+
return {
|
|
255
|
+
...entity,
|
|
256
|
+
admin: {
|
|
257
|
+
...admin,
|
|
258
|
+
components: {
|
|
259
|
+
...components,
|
|
260
|
+
edit: {
|
|
261
|
+
...edit,
|
|
262
|
+
beforeDocumentControls: [
|
|
263
|
+
...edit.beforeDocumentControls ?? [],
|
|
264
|
+
pageContextBridgePath
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const elements = components.elements ?? {};
|
|
272
|
+
return {
|
|
273
|
+
...entity,
|
|
274
|
+
admin: {
|
|
275
|
+
...admin,
|
|
276
|
+
components: {
|
|
277
|
+
...components,
|
|
278
|
+
elements: {
|
|
279
|
+
...elements,
|
|
280
|
+
beforeDocumentControls: [
|
|
281
|
+
...elements.beforeDocumentControls ?? [],
|
|
282
|
+
pageContextBridgePath
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function settingsGlobal(options, fallback, ownKey) {
|
|
290
|
+
// Without complete credentials there is no choice to make, so only Vercel shows.
|
|
291
|
+
const ownKeyAvailable = ownKey !== undefined;
|
|
292
|
+
const fields = [
|
|
293
|
+
{
|
|
294
|
+
name: 'alerts',
|
|
295
|
+
type: 'ui',
|
|
296
|
+
admin: {
|
|
297
|
+
components: {
|
|
298
|
+
Field: settingsAlertsPath
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: 'billing',
|
|
304
|
+
label: 'AI Source',
|
|
305
|
+
type: 'select',
|
|
306
|
+
required: true,
|
|
307
|
+
defaultValue: fallback.billing,
|
|
308
|
+
options: [
|
|
309
|
+
{
|
|
310
|
+
label: 'Humaan AI',
|
|
311
|
+
value: 'vercel'
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
label: ownKey ? `Own provider key (${ownKey.label})` : 'Own provider key',
|
|
315
|
+
value: 'own-key'
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
admin: {
|
|
319
|
+
hidden: !ownKeyAvailable,
|
|
320
|
+
description: 'Where replies come from. Humaan AI is managed by Humaan. With your own provider key, the provider bills the account that owns the key, and the daily spend cap does not apply. If the key stops working, replies continue through Humaan AI.'
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'budget',
|
|
325
|
+
type: 'ui',
|
|
326
|
+
admin: {
|
|
327
|
+
components: {
|
|
328
|
+
Field: budgetFieldPath
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: 'allowedRoles',
|
|
334
|
+
label: 'Who can run it',
|
|
335
|
+
type: 'select',
|
|
336
|
+
hasMany: true,
|
|
337
|
+
required: true,
|
|
338
|
+
defaultValue: fallback.allowedRoles,
|
|
339
|
+
options: options.roles
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'dailySpendCapPercent',
|
|
343
|
+
label: 'Daily spend cap per editor (% of budget)',
|
|
344
|
+
type: 'number',
|
|
345
|
+
min: 0,
|
|
346
|
+
max: 100,
|
|
347
|
+
defaultValue: fallback.dailySpendCapPercent,
|
|
348
|
+
admin: {
|
|
349
|
+
// Shares of the Vercel budget mean nothing when the provider bills the site's own key.
|
|
350
|
+
condition: ownKeyAvailable ? (data)=>data?.billing !== 'own-key' : undefined,
|
|
351
|
+
description: "The share of the project's Humaan AI budget that each editor can spend per day. Set 0 for no cap."
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
];
|
|
355
|
+
return {
|
|
356
|
+
slug: aiAssistantGlobalSlug,
|
|
357
|
+
label: 'Humaan AI',
|
|
358
|
+
typescript: {
|
|
359
|
+
interface: 'AiAssistantGlobal'
|
|
360
|
+
},
|
|
361
|
+
admin: {
|
|
362
|
+
group: options.adminGroup ?? 'Settings'
|
|
363
|
+
},
|
|
364
|
+
access: {
|
|
365
|
+
read: adminOnly,
|
|
366
|
+
update: adminOnly
|
|
367
|
+
},
|
|
368
|
+
fields
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
export const aiAssistantPlugin = (options)=>(config)=>{
|
|
372
|
+
assertOptions(config, options);
|
|
373
|
+
const fallback = {
|
|
374
|
+
billing: 'vercel',
|
|
375
|
+
allowedRoles: options.roles.some((role)=>role.value === 'admin') ? [
|
|
376
|
+
'admin'
|
|
377
|
+
] : [
|
|
378
|
+
options.roles[0].value
|
|
379
|
+
],
|
|
380
|
+
dailySpendCapPercent: defaultDailySpendCapPercent
|
|
381
|
+
};
|
|
382
|
+
const usersSlug = usersCollectionOf(config);
|
|
383
|
+
const collections = [
|
|
384
|
+
plansCollection(usersSlug, fallback),
|
|
385
|
+
threadsCollection(usersSlug, fallback),
|
|
386
|
+
usageCollection(usersSlug, fallback)
|
|
387
|
+
];
|
|
388
|
+
// Resolved once, when the config is built: environment variables are read in the project's config.
|
|
389
|
+
const byok = resolveByok(options.byok);
|
|
390
|
+
const global = settingsGlobal(options, fallback, byok.key);
|
|
391
|
+
// The project registers the attachments collection so its storage adapter handles the files; the rules are added here.
|
|
392
|
+
const existing = (config.collections ?? []).map((collection)=>collection.slug === aiAssistantAttachmentSlug ? withAttachmentRules(collection, usersSlug, fallback) : collection);
|
|
393
|
+
const runtime = {
|
|
394
|
+
attachmentReadParams: options.attachmentReadParams,
|
|
395
|
+
timeZone: options.timeZone ?? 'UTC',
|
|
396
|
+
// Only which providers, so the settings screen can describe the own key. The credentials stay in the endpoint.
|
|
397
|
+
...byok.key ? {
|
|
398
|
+
ownKey: {
|
|
399
|
+
providers: byok.key.providers,
|
|
400
|
+
label: byok.key.label
|
|
401
|
+
}
|
|
402
|
+
} : {},
|
|
403
|
+
ownKeyIncomplete: byok.incomplete,
|
|
404
|
+
deniedCollections: options.deniedCollections ?? [],
|
|
405
|
+
deniedGlobals: options.deniedGlobals ?? []
|
|
406
|
+
};
|
|
407
|
+
const custom = {
|
|
408
|
+
...config.custom,
|
|
409
|
+
[pluginCustomKey]: runtime
|
|
410
|
+
};
|
|
411
|
+
// Disabled: keep the schema, so turning the assistant back on needs no migration, but add nothing editors see.
|
|
412
|
+
if (options.enabled === false) {
|
|
413
|
+
return {
|
|
414
|
+
...config,
|
|
415
|
+
custom,
|
|
416
|
+
collections: [
|
|
417
|
+
...existing,
|
|
418
|
+
...collections
|
|
419
|
+
],
|
|
420
|
+
globals: [
|
|
421
|
+
...config.globals ?? [],
|
|
422
|
+
global
|
|
423
|
+
]
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
const admin = config.admin ?? {};
|
|
427
|
+
const components = admin.components ?? {};
|
|
428
|
+
return {
|
|
429
|
+
...config,
|
|
430
|
+
custom,
|
|
431
|
+
admin: {
|
|
432
|
+
...admin,
|
|
433
|
+
components: {
|
|
434
|
+
...components,
|
|
435
|
+
actions: [
|
|
436
|
+
...components.actions ?? [],
|
|
437
|
+
{
|
|
438
|
+
path: assistantControlPath,
|
|
439
|
+
serverProps: {
|
|
440
|
+
fallbackSettings: fallback
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
]
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
collections: [
|
|
447
|
+
...existing.map((collection)=>withPageContext(collection, 'collection')),
|
|
448
|
+
...collections
|
|
449
|
+
],
|
|
450
|
+
globals: [
|
|
451
|
+
...(config.globals ?? []).map((entity)=>withPageContext(entity, 'global')),
|
|
452
|
+
global
|
|
453
|
+
],
|
|
454
|
+
endpoints: [
|
|
455
|
+
...config.endpoints ?? [],
|
|
456
|
+
chatEndpoint(options, fallback, byok.key),
|
|
457
|
+
applyEndpoint(fallback),
|
|
458
|
+
cancelEndpoint(fallback),
|
|
459
|
+
usageEndpoint(fallback, options, byok.key)
|
|
460
|
+
]
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin.ts"],"sourcesContent":["import type {\n Access,\n CollectionConfig,\n CollectionSlug,\n Config,\n Field,\n GlobalConfig,\n Plugin,\n} from 'payload'\n\nimport type { OwnKey } from './billing.js'\nimport type { PluginRuntime } from './runtime.js'\nimport type { AiAssistantPluginOptions, AssistantSettings } from './types.js'\n\nimport { withAttachmentRules } from './attachments/collection.js'\nimport { byokProblem, resolveByok } from './billing.js'\nimport { ownerAccess } from './collectionAccess.js'\nimport {\n aiAssistantAttachmentSlug,\n aiAssistantGlobalSlug,\n aiAssistantPlanSlug,\n aiAssistantThreadSlug,\n aiAssistantUsageSlug,\n assistantControlPath,\n budgetFieldPath,\n defaultDailySpendCapPercent,\n deniedSlugs,\n pageContextBridgePath,\n pluginCustomKey,\n settingsAlertsPath,\n} from './constants.js'\nimport { applyEndpoint, cancelEndpoint } from './endpoints/applyEndpoint.js'\nimport { chatEndpoint } from './endpoints/chatEndpoint.js'\nimport { usageEndpoint } from './endpoints/usageEndpoint.js'\nimport { isAdminRole } from './settings.js'\n\nconst adminOnly: Access = ({ req: { user } }) => isAdminRole(user)\n\n/** The collection admins log in with: `admin.user`, as Payload reads it, or the first auth collection. */\nfunction usersCollectionOf(config: Config): CollectionSlug {\n const slug =\n config.admin?.user ?? config.collections?.find((collection) => collection.auth)?.slug ?? 'users'\n return slug as CollectionSlug\n}\n\nfunction assertOptions(config: Config, options: AiAssistantPluginOptions): void {\n if (options.roles.length === 0) {\n throw new Error('aiAssistantPlugin requires at least one role.')\n }\n if (!options.siteRules.trim()) {\n throw new Error('aiAssistantPlugin requires site rules.')\n }\n for (const slug of Object.keys(options.collectionRules ?? {})) {\n const collection = config.collections?.some((item) => item.slug === slug)\n const global = config.globals?.some((item) => item.slug === slug)\n if (!collection && !global) {\n throw new Error(`aiAssistantPlugin collectionRules refers to unknown target \"${slug}\".`)\n }\n }\n // A mistyped slug would leave that collection open to the assistant, so it fails loudly instead.\n for (const slug of options.deniedCollections ?? []) {\n if (!config.collections?.some((item) => item.slug === slug)) {\n throw new Error(`aiAssistantPlugin deniedCollections refers to unknown collection \"${slug}\".`)\n }\n }\n for (const slug of options.deniedGlobals ?? []) {\n if (!config.globals?.some((item) => item.slug === slug)) {\n throw new Error(`aiAssistantPlugin deniedGlobals refers to unknown global \"${slug}\".`)\n }\n }\n const byok = byokProblem(options.byok)\n if (byok) {\n throw new Error(`aiAssistantPlugin byok has ${byok}`)\n }\n}\n\nfunction plansCollection(usersSlug: CollectionSlug, fallback: AssistantSettings): CollectionConfig {\n const owned = ownerAccess(fallback)\n\n return {\n slug: aiAssistantPlanSlug,\n labels: { singular: 'Humaan AI plan', plural: 'Humaan AI plans' },\n admin: { hidden: true },\n // The REST API can read and delete an editor's own plans, but not write them. Otherwise a plan could be\n // written by hand and applied without the model or its checks.\n access: {\n create: () => false,\n read: owned,\n update: () => false,\n delete: owned,\n },\n fields: [\n {\n name: 'owner',\n type: 'relationship',\n relationTo: usersSlug,\n required: true,\n index: true,\n },\n {\n name: 'thread',\n type: 'text',\n index: true,\n admin: { description: 'The conversation the plan was proposed in.' },\n },\n {\n name: 'status',\n type: 'select',\n required: true,\n defaultValue: 'proposed',\n options: [\n { label: 'Proposed', value: 'proposed' },\n { label: 'Blocked', value: 'blocked' },\n { label: 'Applied', value: 'applied' },\n { label: 'Cancelled', value: 'cancelled' },\n ],\n },\n { name: 'summary', type: 'textarea', required: true },\n { name: 'plan', type: 'json', required: true },\n ],\n }\n}\n\n/**\n * Conversations are written only by the assistant endpoints, so the REST API can read and delete them but not edit them.\n * An edited history could put words in the assistant's mouth.\n */\nfunction threadsCollection(\n usersSlug: CollectionSlug,\n fallback: AssistantSettings,\n): CollectionConfig {\n const owned = ownerAccess(fallback)\n\n return {\n slug: aiAssistantThreadSlug,\n labels: { singular: 'Humaan AI conversation', plural: 'Humaan AI conversations' },\n admin: { hidden: true, useAsTitle: 'title' },\n access: {\n create: () => false,\n read: owned,\n update: () => false,\n delete: owned,\n },\n fields: [\n {\n name: 'owner',\n type: 'relationship',\n relationTo: usersSlug,\n required: true,\n index: true,\n },\n {\n name: 'chatId',\n type: 'text',\n required: true,\n index: true,\n },\n { name: 'title', type: 'text' },\n { name: 'messages', type: 'json' },\n {\n name: 'files',\n type: 'json',\n admin: {\n description:\n 'Names and sizes of attached files. The files stay in the browser until a plan saves them.',\n },\n },\n ],\n }\n}\n\n/** Each editor's spend per day. Written only by the chat endpoint; editors can read their own, admins all. */\nfunction usageCollection(usersSlug: CollectionSlug, fallback: AssistantSettings): CollectionConfig {\n const owned = ownerAccess(fallback)\n return {\n slug: aiAssistantUsageSlug,\n labels: { singular: 'Humaan AI usage', plural: 'Humaan AI usage' },\n admin: { hidden: true },\n access: {\n create: () => false,\n read: async (args) => (isAdminRole(args.req.user) ? true : owned(args)),\n update: () => false,\n delete: () => false,\n },\n fields: [\n { name: 'owner', type: 'relationship', relationTo: usersSlug, required: true, index: true },\n {\n name: 'day',\n type: 'text',\n required: true,\n index: true,\n admin: { description: \"YYYY-MM-DD in the site's time zone.\" },\n },\n {\n name: 'costUsd',\n type: 'number',\n required: true,\n defaultValue: 0,\n admin: { description: \"Billed to Vercel's credit.\" },\n },\n {\n name: 'ownKeyCostUsd',\n type: 'number',\n required: true,\n defaultValue: 0,\n admin: { description: \"Billed to the site's own provider key.\" },\n },\n {\n name: 'fallbackSteps',\n type: 'number',\n required: true,\n defaultValue: 0,\n admin: { description: \"Calls meant for the own key that ran on Vercel's credit.\" },\n },\n { name: 'tokens', type: 'number', required: true, defaultValue: 0 },\n { name: 'replies', type: 'number', required: true, defaultValue: 0 },\n ],\n }\n}\n\n/** Lets the drawer in the header know which document is open, and read its unsaved form values. */\nfunction withPageContext<T extends CollectionConfig | GlobalConfig>(\n entity: T,\n kind: 'collection' | 'global',\n): T {\n const denied = new Set<string>(deniedSlugs)\n if (denied.has(entity.slug) || ('auth' in entity && entity.auth)) {\n return entity\n }\n const admin = entity.admin ?? {}\n const components = admin.components ?? {}\n if (kind === 'collection') {\n const edit =\n (components as NonNullable<NonNullable<CollectionConfig['admin']>['components']>).edit ?? {}\n return {\n ...entity,\n admin: {\n ...admin,\n components: {\n ...components,\n edit: {\n ...edit,\n beforeDocumentControls: [...(edit.beforeDocumentControls ?? []), pageContextBridgePath],\n },\n },\n },\n }\n }\n const elements =\n (components as NonNullable<NonNullable<GlobalConfig['admin']>['components']>).elements ?? {}\n return {\n ...entity,\n admin: {\n ...admin,\n components: {\n ...components,\n elements: {\n ...elements,\n beforeDocumentControls: [\n ...(elements.beforeDocumentControls ?? []),\n pageContextBridgePath,\n ],\n },\n },\n },\n }\n}\n\nfunction settingsGlobal(\n options: AiAssistantPluginOptions,\n fallback: AssistantSettings,\n ownKey: OwnKey | undefined,\n): GlobalConfig {\n // Without complete credentials there is no choice to make, so only Vercel shows.\n const ownKeyAvailable = ownKey !== undefined\n const fields: Field[] = [\n {\n name: 'alerts',\n type: 'ui',\n admin: {\n components: {\n Field: settingsAlertsPath,\n },\n },\n },\n {\n name: 'billing',\n label: 'AI Source',\n type: 'select',\n required: true,\n defaultValue: fallback.billing,\n options: [\n { label: 'Humaan AI', value: 'vercel' },\n {\n label: ownKey ? `Own provider key (${ownKey.label})` : 'Own provider key',\n value: 'own-key',\n },\n ],\n admin: {\n hidden: !ownKeyAvailable,\n description:\n 'Where replies come from. Humaan AI is managed by Humaan. With your own provider key, the provider bills the account that owns the key, and the daily spend cap does not apply. If the key stops working, replies continue through Humaan AI.',\n },\n },\n {\n name: 'budget',\n type: 'ui',\n admin: {\n components: {\n Field: budgetFieldPath,\n },\n },\n },\n {\n name: 'allowedRoles',\n label: 'Who can run it',\n type: 'select',\n hasMany: true,\n required: true,\n defaultValue: fallback.allowedRoles,\n options: options.roles,\n },\n {\n name: 'dailySpendCapPercent',\n label: 'Daily spend cap per editor (% of budget)',\n type: 'number',\n min: 0,\n max: 100,\n defaultValue: fallback.dailySpendCapPercent,\n admin: {\n // Shares of the Vercel budget mean nothing when the provider bills the site's own key.\n condition: ownKeyAvailable ? (data) => data?.billing !== 'own-key' : undefined,\n description:\n \"The share of the project's Humaan AI budget that each editor can spend per day. Set 0 for no cap.\",\n },\n },\n ]\n\n return {\n slug: aiAssistantGlobalSlug,\n label: 'Humaan AI',\n typescript: { interface: 'AiAssistantGlobal' },\n admin: { group: options.adminGroup ?? 'Settings' },\n access: {\n read: adminOnly,\n update: adminOnly,\n },\n fields,\n }\n}\n\nexport const aiAssistantPlugin =\n (options: AiAssistantPluginOptions): Plugin =>\n (config: Config): Config => {\n assertOptions(config, options)\n\n const fallback: AssistantSettings = {\n billing: 'vercel',\n allowedRoles: options.roles.some((role) => role.value === 'admin')\n ? ['admin']\n : [options.roles[0].value],\n dailySpendCapPercent: defaultDailySpendCapPercent,\n }\n const usersSlug = usersCollectionOf(config)\n const collections = [\n plansCollection(usersSlug, fallback),\n threadsCollection(usersSlug, fallback),\n usageCollection(usersSlug, fallback),\n ]\n // Resolved once, when the config is built: environment variables are read in the project's config.\n const byok = resolveByok(options.byok)\n const global = settingsGlobal(options, fallback, byok.key)\n // The project registers the attachments collection so its storage adapter handles the files; the rules are added here.\n const existing = (config.collections ?? []).map((collection) =>\n collection.slug === aiAssistantAttachmentSlug\n ? withAttachmentRules(collection, usersSlug, fallback)\n : collection,\n )\n\n const runtime: PluginRuntime = {\n attachmentReadParams: options.attachmentReadParams,\n timeZone: options.timeZone ?? 'UTC',\n // Only which providers, so the settings screen can describe the own key. The credentials stay in the endpoint.\n ...(byok.key ? { ownKey: { providers: byok.key.providers, label: byok.key.label } } : {}),\n ownKeyIncomplete: byok.incomplete,\n deniedCollections: options.deniedCollections ?? [],\n deniedGlobals: options.deniedGlobals ?? [],\n }\n const custom = { ...config.custom, [pluginCustomKey]: runtime }\n\n // Disabled: keep the schema, so turning the assistant back on needs no migration, but add nothing editors see.\n if (options.enabled === false) {\n return {\n ...config,\n custom,\n collections: [...existing, ...collections],\n globals: [...(config.globals ?? []), global],\n }\n }\n\n const admin = config.admin ?? {}\n const components = admin.components ?? {}\n return {\n ...config,\n custom,\n admin: {\n ...admin,\n components: {\n ...components,\n actions: [\n ...(components.actions ?? []),\n {\n path: assistantControlPath,\n serverProps: { fallbackSettings: fallback },\n },\n ],\n },\n },\n collections: [\n ...existing.map((collection) => withPageContext(collection, 'collection')),\n ...collections,\n ],\n globals: [\n ...(config.globals ?? []).map((entity) => withPageContext(entity, 'global')),\n global,\n ],\n endpoints: [\n ...(config.endpoints ?? []),\n chatEndpoint(options, fallback, byok.key),\n applyEndpoint(fallback),\n cancelEndpoint(fallback),\n usageEndpoint(fallback, options, byok.key),\n ],\n }\n }\n"],"names":["withAttachmentRules","byokProblem","resolveByok","ownerAccess","aiAssistantAttachmentSlug","aiAssistantGlobalSlug","aiAssistantPlanSlug","aiAssistantThreadSlug","aiAssistantUsageSlug","assistantControlPath","budgetFieldPath","defaultDailySpendCapPercent","deniedSlugs","pageContextBridgePath","pluginCustomKey","settingsAlertsPath","applyEndpoint","cancelEndpoint","chatEndpoint","usageEndpoint","isAdminRole","adminOnly","req","user","usersCollectionOf","config","slug","admin","collections","find","collection","auth","assertOptions","options","roles","length","Error","siteRules","trim","Object","keys","collectionRules","some","item","global","globals","deniedCollections","deniedGlobals","byok","plansCollection","usersSlug","fallback","owned","labels","singular","plural","hidden","access","create","read","update","delete","fields","name","type","relationTo","required","index","description","defaultValue","label","value","threadsCollection","useAsTitle","usageCollection","args","withPageContext","entity","kind","denied","Set","has","components","edit","beforeDocumentControls","elements","settingsGlobal","ownKey","ownKeyAvailable","undefined","Field","billing","hasMany","allowedRoles","min","max","dailySpendCapPercent","condition","data","typescript","interface","group","adminGroup","aiAssistantPlugin","role","key","existing","map","runtime","attachmentReadParams","timeZone","providers","ownKeyIncomplete","incomplete","custom","enabled","actions","path","serverProps","fallbackSettings","endpoints"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short view of a readable document, sent with each message so the assistant knows what is on the open page.
|
|
3
|
+
* Text is cut, each block becomes one line, and long lists are shortened. The full value is one readDocument call away.
|
|
4
|
+
*/
|
|
5
|
+
export declare function outline(value: unknown, depth?: number): unknown;
|
|
6
|
+
/** Reads `content.content.3.title` from a document, through objects and lists. */
|
|
7
|
+
export declare function valueAtReadablePath(source: unknown, path: string): unknown;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const maxText = 240;
|
|
2
|
+
const maxBlockPreview = 120;
|
|
3
|
+
const maxListItems = 12;
|
|
4
|
+
const maxDepth = 5;
|
|
5
|
+
/**
|
|
6
|
+
* A short view of a readable document, sent with each message so the assistant knows what is on the open page.
|
|
7
|
+
* Text is cut, each block becomes one line, and long lists are shortened. The full value is one readDocument call away.
|
|
8
|
+
*/ export function outline(value, depth = 0) {
|
|
9
|
+
if (typeof value === 'string') {
|
|
10
|
+
return cut(value, maxText);
|
|
11
|
+
}
|
|
12
|
+
if (depth > maxDepth) {
|
|
13
|
+
return '[more]';
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
const items = value.slice(0, maxListItems).map((item, index)=>isRecord(item) && typeof item.blockType === 'string' ? blockLine(item, index) : outline(item, depth + 1));
|
|
17
|
+
const hidden = value.length - items.length;
|
|
18
|
+
return hidden > 0 ? [
|
|
19
|
+
...items,
|
|
20
|
+
`[${hidden} more]`
|
|
21
|
+
] : items;
|
|
22
|
+
}
|
|
23
|
+
if (!isRecord(value)) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
return Object.fromEntries(Object.entries(value).map(([key, item])=>[
|
|
27
|
+
key,
|
|
28
|
+
outline(item, depth + 1)
|
|
29
|
+
]));
|
|
30
|
+
}
|
|
31
|
+
/** `3: accordionGroup "FAQ" – Why should I choose Storco? …` */ function blockLine(block, index) {
|
|
32
|
+
const name = typeof block.blockName === 'string' && block.blockName ? ` "${block.blockName}"` : '';
|
|
33
|
+
const preview = firstText(block);
|
|
34
|
+
return `${index}: ${String(block.blockType)}${name}${preview ? ` – ${cut(preview, maxBlockPreview)}` : ''}`;
|
|
35
|
+
}
|
|
36
|
+
/** The first readable words in a block, skipping ids and other short codes. */ function firstText(value) {
|
|
37
|
+
if (typeof value === 'string') {
|
|
38
|
+
const text = value.replace(/\s+/g, ' ').trim();
|
|
39
|
+
return text.includes(' ') ? text : undefined;
|
|
40
|
+
}
|
|
41
|
+
const items = Array.isArray(value) ? value : isRecord(value) ? Object.entries(value).filter(([key])=>key !== 'id' && key !== 'blockType').map(([, item])=>item) : [];
|
|
42
|
+
for (const item of items){
|
|
43
|
+
const found = firstText(item);
|
|
44
|
+
if (found) {
|
|
45
|
+
return found;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
/** Reads `content.content.3.title` from a document, through objects and lists. */ export function valueAtReadablePath(source, path) {
|
|
51
|
+
let current = source;
|
|
52
|
+
for (const key of path.split('.').filter(Boolean)){
|
|
53
|
+
if (Array.isArray(current) && /^\d+$/.test(key)) {
|
|
54
|
+
current = current[Number(key)];
|
|
55
|
+
} else if (isRecord(current)) {
|
|
56
|
+
current = current[key];
|
|
57
|
+
} else {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
function cut(value, limit) {
|
|
64
|
+
return value.length > limit ? `${value.slice(0, limit)}…` : value;
|
|
65
|
+
}
|
|
66
|
+
function isRecord(value) {
|
|
67
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=outline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/read/outline.ts"],"sourcesContent":["const maxText = 240\nconst maxBlockPreview = 120\nconst maxListItems = 12\nconst maxDepth = 5\n\n/**\n * A short view of a readable document, sent with each message so the assistant knows what is on the open page.\n * Text is cut, each block becomes one line, and long lists are shortened. The full value is one readDocument call away.\n */\nexport function outline(value: unknown, depth = 0): unknown {\n if (typeof value === 'string') {\n return cut(value, maxText)\n }\n if (depth > maxDepth) {\n return '[more]'\n }\n if (Array.isArray(value)) {\n const items = value\n .slice(0, maxListItems)\n .map((item, index) =>\n isRecord(item) && typeof item.blockType === 'string'\n ? blockLine(item, index)\n : outline(item, depth + 1),\n )\n const hidden = value.length - items.length\n return hidden > 0 ? [...items, `[${hidden} more]`] : items\n }\n if (!isRecord(value)) {\n return value\n }\n return Object.fromEntries(\n Object.entries(value).map(([key, item]) => [key, outline(item, depth + 1)]),\n )\n}\n\n/** `3: accordionGroup \"FAQ\" – Why should I choose Storco? …` */\nfunction blockLine(block: Record<string, unknown>, index: number): string {\n const name = typeof block.blockName === 'string' && block.blockName ? ` \"${block.blockName}\"` : ''\n const preview = firstText(block)\n return `${index}: ${String(block.blockType)}${name}${preview ? ` – ${cut(preview, maxBlockPreview)}` : ''}`\n}\n\n/** The first readable words in a block, skipping ids and other short codes. */\nfunction firstText(value: unknown): string | undefined {\n if (typeof value === 'string') {\n const text = value.replace(/\\s+/g, ' ').trim()\n return text.includes(' ') ? text : undefined\n }\n const items = Array.isArray(value)\n ? value\n : isRecord(value)\n ? Object.entries(value)\n .filter(([key]) => key !== 'id' && key !== 'blockType')\n .map(([, item]) => item)\n : []\n for (const item of items) {\n const found = firstText(item)\n if (found) {\n return found\n }\n }\n return undefined\n}\n\n/** Reads `content.content.3.title` from a document, through objects and lists. */\nexport function valueAtReadablePath(source: unknown, path: string): unknown {\n let current = source\n for (const key of path.split('.').filter(Boolean)) {\n if (Array.isArray(current) && /^\\d+$/.test(key)) {\n current = current[Number(key)]\n } else if (isRecord(current)) {\n current = current[key]\n } else {\n return undefined\n }\n }\n return current\n}\n\nfunction cut(value: string, limit: number): string {\n return value.length > limit ? `${value.slice(0, limit)}…` : value\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n"],"names":["maxText","maxBlockPreview","maxListItems","maxDepth","outline","value","depth","cut","Array","isArray","items","slice","map","item","index","isRecord","blockType","blockLine","hidden","length","Object","fromEntries","entries","key","block","name","blockName","preview","firstText","String","text","replace","trim","includes","undefined","filter","found","valueAtReadablePath","source","path","current","split","Boolean","test","Number","limit"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SanitizedConfig } from 'payload';
|
|
2
|
+
import type { PlanTargetKind } from '../types.js';
|
|
3
|
+
/** Reads a collection document or global with its rich text as markdown. */
|
|
4
|
+
export declare function readableTarget(config: SanitizedConfig, kind: PlanTargetKind, slug: string, data: unknown): Promise<unknown>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { lexicalToMarkdown } from '../lexical/lexicalToMarkdown.js';
|
|
2
|
+
import { collectionFields, globalFields } from '../plan/catalogue.js';
|
|
3
|
+
import { readableDocument } from './readable.js';
|
|
4
|
+
/** Reads a collection document or global with its rich text as markdown. */ export function readableTarget(config, kind, slug, data) {
|
|
5
|
+
const fields = (kind === 'collection' ? collectionFields(config, slug) : globalFields(config, slug)) ?? [];
|
|
6
|
+
return readableDocument(fields, data, (field, value)=>lexicalToMarkdown({
|
|
7
|
+
config,
|
|
8
|
+
field,
|
|
9
|
+
data: value
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=readTarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/read/readTarget.ts"],"sourcesContent":["import type { SanitizedConfig } from 'payload'\n\nimport type { PlanTargetKind } from '../types.js'\n\nimport { lexicalToMarkdown } from '../lexical/lexicalToMarkdown.js'\nimport { collectionFields, globalFields } from '../plan/catalogue.js'\nimport { readableDocument } from './readable.js'\n\n/** Reads a collection document or global with its rich text as markdown. */\nexport function readableTarget(\n config: SanitizedConfig,\n kind: PlanTargetKind,\n slug: string,\n data: unknown,\n): Promise<unknown> {\n const fields =\n (kind === 'collection' ? collectionFields(config, slug) : globalFields(config, slug)) ?? []\n return readableDocument(fields, data, (field, value) =>\n lexicalToMarkdown({\n config,\n field,\n data: value as unknown as Parameters<typeof lexicalToMarkdown>[0]['data'],\n }),\n )\n}\n"],"names":["lexicalToMarkdown","collectionFields","globalFields","readableDocument","readableTarget","config","kind","slug","data","fields","field","value"],"mappings":"AAIA,SAASA,iBAAiB,QAAQ,kCAAiC;AACnE,SAASC,gBAAgB,EAAEC,YAAY,QAAQ,uBAAsB;AACrE,SAASC,gBAAgB,QAAQ,gBAAe;AAEhD,0EAA0E,GAC1E,OAAO,SAASC,eACdC,MAAuB,EACvBC,IAAoB,EACpBC,IAAY,EACZC,IAAa;IAEb,MAAMC,SACJ,AAACH,CAAAA,SAAS,eAAeL,iBAAiBI,QAAQE,QAAQL,aAAaG,QAAQE,KAAI,KAAM,EAAE;IAC7F,OAAOJ,iBAAiBM,QAAQD,MAAM,CAACE,OAAOC,QAC5CX,kBAAkB;YAChBK;YACAK;YACAF,MAAMG;QACR;AAEJ"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Field, RichTextField } from 'payload';
|
|
2
|
+
/** Turns one rich text value into markdown. Injected so the walk can be tested without a Payload config. */
|
|
3
|
+
export type RichTextReader = (field: RichTextField, value: Record<string, unknown>) => Promise<string>;
|
|
4
|
+
/**
|
|
5
|
+
* A document as the assistant reads it. Rich text becomes markdown, timestamps and image sizes are dropped,
|
|
6
|
+
* and long lists and strings are cut so one read cannot fill the model's context.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readableDocument(fields: Field[], data: unknown, readRichText: RichTextReader): Promise<unknown>;
|