@simmalugnt-se/payload-editor-assistant 0.1.0 → 0.1.1
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/CHANGELOG.md +12 -0
- package/README.md +81 -21
- package/dist/runtime/agent.js +12 -0
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
- Provider failures are logged on the server through Payload's logger (missing model, `ai` not
|
|
6
|
+
installed, or the provider's own error), instead of only showing `provider_error` in the drawer.
|
|
7
|
+
- README: how to connect the provider's API key, what the assistant may do, and the options.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
- First release.
|
|
12
|
+
- `ai` is a required peer: the AI SDK is loaded with a literal import so bundlers can resolve it.
|
package/README.md
CHANGED
|
@@ -9,24 +9,84 @@ your choice.
|
|
|
9
9
|
|
|
10
10
|
## Setup
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
1. Install the plugin, the AI SDK and a provider (DeepSeek here; any AI SDK provider works):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @simmalugnt-se/payload-editor-assistant ai @ai-sdk/deepseek
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Put the provider's API key in the server environment, e.g. `.env.local` locally and the
|
|
19
|
+
hosting provider's environment variables in production. Never expose it to the browser
|
|
20
|
+
(no `NEXT_PUBLIC_` prefix):
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
DEEPSEEK_API_KEY=sk-...
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
3. Create the model with that key and pass it to the plugin:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { createDeepSeek } from "@ai-sdk/deepseek";
|
|
30
|
+
import { editorAssistantPlugin } from "@simmalugnt-se/payload-editor-assistant";
|
|
31
|
+
|
|
32
|
+
const deepSeek = createDeepSeek({ apiKey: process.env.DEEPSEEK_API_KEY });
|
|
33
|
+
|
|
34
|
+
plugins: [
|
|
35
|
+
editorAssistantPlugin({
|
|
36
|
+
model: deepSeek("deepseek-chat"),
|
|
37
|
+
collections: {
|
|
38
|
+
pages: {
|
|
39
|
+
capabilities: ["content.find", "content.findById", "form.read", "form.propose", "draft.create"],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
globals: {
|
|
43
|
+
header: { capabilities: ["content.findById", "form.read", "form.propose"] },
|
|
44
|
+
},
|
|
45
|
+
}),
|
|
46
|
+
];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
4. Regenerate the import map: `pnpm payload generate:importmap`.
|
|
50
|
+
|
|
51
|
+
## API keys
|
|
52
|
+
|
|
53
|
+
The plugin has no provider or key of its own: it only receives the `model` you create, and the model
|
|
54
|
+
is called on the server. To switch provider, install another AI SDK provider and create the model
|
|
55
|
+
with its key, e.g. `createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })`.
|
|
56
|
+
|
|
57
|
+
Providers also read a default environment variable (`DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, ...)
|
|
58
|
+
when no `apiKey` is passed, but passing it explicitly keeps the connection visible in your config.
|
|
59
|
+
|
|
60
|
+
If the key is missing or wrong, the drawer shows `provider_error` and the cause is logged on the
|
|
61
|
+
server through Payload's logger.
|
|
62
|
+
|
|
63
|
+
## What the assistant may do
|
|
64
|
+
|
|
65
|
+
Nothing is reachable unless you list it. Each collection or global gets the capabilities it allows:
|
|
66
|
+
|
|
67
|
+
| Capability | What it does | Needs approval |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `content.find` | search and list documents | no |
|
|
70
|
+
| `content.findById` | read one document | no |
|
|
71
|
+
| `form.read` | read the open edit form, including unsaved changes | no |
|
|
72
|
+
| `form.propose` | propose changes to the open form; the editor reviews them before they apply | yes |
|
|
73
|
+
| `draft.create` | create a new draft document (collections only) | yes |
|
|
74
|
+
|
|
75
|
+
The assistant never publishes. Approved form changes land in the open form and the editor saves as
|
|
76
|
+
usual; an approved `draft.create` saves a new, unpublished draft. The `users` collection, Payload's own collections and fields that look like
|
|
77
|
+
secrets (`password`, `token`, `apiKey`, ...) are always off limits.
|
|
78
|
+
|
|
79
|
+
## Options
|
|
80
|
+
|
|
81
|
+
| Option | |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `model` | The AI SDK model, or `({ userId }) => model` to choose per user. Required unless `disabled`. |
|
|
84
|
+
| `collections`, `globals` | `{ [slug]: { capabilities, fields? } }`. `fields` narrows what is visible: `{ include: [...] }` or `{ exclude: [...] }`. |
|
|
85
|
+
| `denyFields` | Field names hidden from the assistant in every collection and global. |
|
|
86
|
+
| `instructions` | Project rules the assistant cannot infer from the schema, e.g. "the start page has slug `home`". |
|
|
87
|
+
| `shortcuts` | One-click prompts in the drawer: `{ id, label, prompt, when?, entities? }`. |
|
|
88
|
+
| `enabled` | `({ userId }) => boolean` to offer the assistant to some users only. |
|
|
89
|
+
| `redact` | `({ slug, data }) => data` to strip values before documents reach the model. |
|
|
90
|
+
| `trustedOrigins` | Origins allowed to call the assistant's endpoints. Default: only the origin serving Admin. When set, only the listed origins. |
|
|
91
|
+
| `audit.collection` | Slug of the collection that logs assistant actions (default `editor-assistant-audit`). |
|
|
92
|
+
| `disabled` | Turn the plugin off without removing its config. |
|
package/dist/runtime/agent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PACKAGE_NAME } from "../package-name.js";
|
|
1
2
|
import { loadTurnContext } from "./context.js";
|
|
2
3
|
import { needsProposeRetry } from "./propose-loop.js";
|
|
3
4
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
@@ -9,10 +10,12 @@ const TIMEOUT_MS = 60_000;
|
|
|
9
10
|
export async function runAgentTurn(req, options, input) {
|
|
10
11
|
const model = await resolveModel(options, req.user?.id);
|
|
11
12
|
if (!model) {
|
|
13
|
+
logProviderError(req, "no model: the `model` option resolved to nothing for this user");
|
|
12
14
|
return { ok: false, error: "provider_error" };
|
|
13
15
|
}
|
|
14
16
|
const ai = await loadAiSdk();
|
|
15
17
|
if (!ai) {
|
|
18
|
+
logProviderError(req, "could not load the AI SDK: is `ai` installed in the project?");
|
|
16
19
|
return { ok: false, error: "provider_error" };
|
|
17
20
|
}
|
|
18
21
|
const ctx = {
|
|
@@ -39,6 +42,7 @@ export async function runAgentTurn(req, options, input) {
|
|
|
39
42
|
signal,
|
|
40
43
|
});
|
|
41
44
|
if (!first) {
|
|
45
|
+
logProviderError(req, "the model returned no result");
|
|
42
46
|
return { ok: false, error: "provider_error" };
|
|
43
47
|
}
|
|
44
48
|
const collected = collectResult(String(first.text ?? ""), first, captured, input.bridge.language ?? input.bridge.locale);
|
|
@@ -79,6 +83,7 @@ export async function runAgentTurn(req, options, input) {
|
|
|
79
83
|
if (signal.aborted) {
|
|
80
84
|
return { ok: false, error: "timeout" };
|
|
81
85
|
}
|
|
86
|
+
logProviderError(req, "the model provider failed", error);
|
|
82
87
|
return {
|
|
83
88
|
ok: false,
|
|
84
89
|
error: "provider_error",
|
|
@@ -246,6 +251,13 @@ async function resolveModel(options, userId) {
|
|
|
246
251
|
}
|
|
247
252
|
return options.model;
|
|
248
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* The drawer only shows "provider_error"; the cause (missing key, wrong model id, provider outage)
|
|
256
|
+
* belongs in the server log where the host can see it.
|
|
257
|
+
*/
|
|
258
|
+
function logProviderError(req, message, error) {
|
|
259
|
+
req.payload?.logger?.error({ err: error, msg: `${PACKAGE_NAME}: ${message}` });
|
|
260
|
+
}
|
|
249
261
|
async function loadAiSdk() {
|
|
250
262
|
try {
|
|
251
263
|
// A literal specifier: bundlers only resolve `import()` they can read, and the host's `ai`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simmalugnt-se/payload-editor-assistant",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Context-aware editor assistant plugin for Payload Admin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"payload",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"node": ">=22"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"CHANGELOG.md"
|
|
22
23
|
],
|
|
23
24
|
"sideEffects": [
|
|
24
25
|
"*.css"
|