@simmalugnt-se/payload-editor-assistant 0.3.2 → 0.3.3

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.3
4
+
5
+ - A `model` function may return `null` or `undefined`, for example when no API key is set. The drawer
6
+ then says that no AI model is set up instead of that the AI service did not answer, and the server
7
+ logs a warning instead of an error. Hosts no longer need a cast to keep the plugin installed
8
+ without a model.
9
+
3
10
  ## 0.3.2
4
11
 
5
12
  - One control opens and closes the assistant: the text button with a sparkles icon in Admin's
package/README.md CHANGED
@@ -57,9 +57,17 @@ with its key, e.g. `createAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY })`.
57
57
  Providers also read a default environment variable (`DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`, ...)
58
58
  when no `apiKey` is passed, but passing it explicitly keeps the connection visible in your config.
59
59
 
60
- If the key is missing or wrong, the drawer shows `provider_error` and the cause is logged on the
60
+ If the key is wrong, the drawer says the AI service did not answer and the cause is logged on the
61
61
  server through Payload's logger.
62
62
 
63
+ To keep the plugin installed without a key (its tables stay in your schema and migrations), let
64
+ `model` be a function that returns `null` or `undefined` when there is nothing to call. The drawer then
65
+ tells the editor that no AI model is set up, and a warning is logged:
66
+
67
+ ```ts
68
+ model: () => (process.env.DEEPSEEK_API_KEY ? deepSeek("deepseek-chat") : null),
69
+ ```
70
+
63
71
  ## What the assistant may do
64
72
 
65
73
  Nothing is reachable unless you list it. Each collection or global gets the capabilities it allows:
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { AssistantShortcut } from "../types.ts";
3
3
  import type { DomSelection } from "./selection-dom.ts";
4
- export type AssistantChromeStatus = "idle" | "unavailable" | "unauthorized" | "rate_limited" | "timeout" | "provider_error" | "tool_denied" | "stale_context" | "aborted" | "streaming";
4
+ export type AssistantChromeStatus = "idle" | "unavailable" | "unauthorized" | "rate_limited" | "timeout" | "provider_error" | "no_model" | "tool_denied" | "stale_context" | "aborted" | "streaming";
5
5
  export type DocumentBridgeSnapshot = {
6
6
  collectionSlug?: string;
7
7
  globalSlug?: string;
@@ -43,6 +43,7 @@ export declare const COPY: {
43
43
  readonly rate_limited: "Too many messages at once. Wait a moment and try again.";
44
44
  readonly timeout: "The assistant took too long to answer. Try again.";
45
45
  readonly provider_error: "The AI service did not answer. The server log has the cause.";
46
+ readonly no_model: "No AI model is set up yet, for example because an API key is missing. Ask whoever runs the site to add one.";
46
47
  readonly tool_denied: "That is not something the assistant may do here.";
47
48
  readonly stale_context: "The form changed in the meantime. Try again.";
48
49
  readonly aborted: "Stopped.";
@@ -93,6 +94,7 @@ export declare const COPY: {
93
94
  readonly rate_limited: "För många meddelanden på en gång. Vänta en stund och försök igen.";
94
95
  readonly timeout: "Assistenten tog för lång tid på sig. Försök igen.";
95
96
  readonly provider_error: "AI-tjänsten svarade inte. Orsaken finns i serverloggen.";
97
+ readonly no_model: "Ingen AI-modell är inställd än, till exempel för att en API-nyckel saknas. Be den som sköter webbplatsen att lägga till en.";
96
98
  readonly tool_denied: "Det får assistenten inte göra här.";
97
99
  readonly stale_context: "Formuläret ändrades under tiden. Försök igen.";
98
100
  readonly aborted: "Stoppat.";
@@ -44,6 +44,7 @@ export const COPY = {
44
44
  rate_limited: "Too many messages at once. Wait a moment and try again.",
45
45
  timeout: "The assistant took too long to answer. Try again.",
46
46
  provider_error: "The AI service did not answer. The server log has the cause.",
47
+ no_model: "No AI model is set up yet, for example because an API key is missing. Ask whoever runs the site to add one.",
47
48
  tool_denied: "That is not something the assistant may do here.",
48
49
  stale_context: "The form changed in the meantime. Try again.",
49
50
  aborted: "Stopped.",
@@ -94,6 +95,7 @@ export const COPY = {
94
95
  rate_limited: "För många meddelanden på en gång. Vänta en stund och försök igen.",
95
96
  timeout: "Assistenten tog för lång tid på sig. Försök igen.",
96
97
  provider_error: "AI-tjänsten svarade inte. Orsaken finns i serverloggen.",
98
+ no_model: "Ingen AI-modell är inställd än, till exempel för att en API-nyckel saknas. Be den som sköter webbplatsen att lägga till en.",
97
99
  tool_denied: "Det får assistenten inte göra här.",
98
100
  stale_context: "Formuläret ändrades under tiden. Försök igen.",
99
101
  aborted: "Stoppat.",
@@ -10,8 +10,8 @@ const TIMEOUT_MS = 60_000;
10
10
  export async function runAgentTurn(req, options, input) {
11
11
  const model = await resolveModel(options, req.user?.id);
12
12
  if (!model) {
13
- logProviderError(req, "no model: the `model` option resolved to nothing for this user");
14
- return { ok: false, error: "provider_error" };
13
+ req.payload?.logger?.warn(`${PACKAGE_NAME}: no model: the \`model\` option resolved to nothing for this user`);
14
+ return { ok: false, error: "no_model" };
15
15
  }
16
16
  const ai = await loadAiSdk();
17
17
  if (!ai) {
package/dist/types.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import type { EntityCapability } from "./capabilities.ts";
2
2
  import type { AdminViewKind } from "./view.ts";
3
3
  export type HostLanguageModel = object;
4
+ /**
5
+ * Returns the model for this request. Returning `null` or `undefined` (no API key set, say) keeps the
6
+ * plugin installed and tells the editor in the drawer that no model is set up.
7
+ */
4
8
  export type ModelResolver = (args: {
5
9
  userId?: string | number;
6
- }) => HostLanguageModel | Promise<HostLanguageModel>;
10
+ }) => HostLanguageModel | null | undefined | Promise<HostLanguageModel | null | undefined>;
7
11
  export type FieldAllowlist = true | {
8
12
  include: string[];
9
13
  } | {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simmalugnt-se/payload-editor-assistant",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Context-aware editor assistant plugin for Payload Admin",
5
5
  "keywords": [
6
6
  "payload",
@@ -53,12 +53,12 @@
53
53
  }
54
54
  },
55
55
  "devDependencies": {
56
- "@payloadcms/ui": "3.87.1",
56
+ "@payloadcms/ui": "3.90.2",
57
57
  "@types/node": "^22",
58
58
  "@types/react": "^19.2.18",
59
59
  "@types/react-dom": "^19.2.4",
60
- "next": "16.3.0",
61
- "payload": "3.87.1",
60
+ "next": "16.3.6",
61
+ "payload": "3.90.2",
62
62
  "react": "19.2.8",
63
63
  "react-dom": "19.2.8",
64
64
  "tsx": "^4.21.0",