@mvriu5/payload-ai 1.3.2 → 1.4.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.
Files changed (32) hide show
  1. package/README.md +76 -13
  2. package/dist/ai/providerOptions.d.ts +24 -1
  3. package/dist/ai/providerOptions.js +81 -0
  4. package/dist/ai/providerRuntime.d.ts +2 -1
  5. package/dist/ai/providerRuntime.js +5 -2
  6. package/dist/ai/tokenUsage.d.ts +38 -0
  7. package/dist/ai/tokenUsage.js +106 -0
  8. package/dist/components/Icons.d.ts +1 -0
  9. package/dist/components/Icons.js +15 -0
  10. package/dist/components/action-toast/ActionToast.d.ts +5 -1
  11. package/dist/components/action-toast/ActionToast.js +57 -21
  12. package/dist/components/ai-input/AIInput.d.ts +4 -1
  13. package/dist/components/ai-input/AIInput.js +165 -50
  14. package/dist/components/ai-input/AIInput.module.css +40 -2
  15. package/dist/components/audit-log-list/AuditLogList.js +9 -2
  16. package/dist/components/dashboard/Dashboard.js +3 -1
  17. package/dist/components/hooks/useAIChatStream.d.ts +12 -1
  18. package/dist/components/hooks/useAIChatStream.js +13 -4
  19. package/dist/components/hooks/useAISettings.d.ts +6 -4
  20. package/dist/components/hooks/useAISettings.js +62 -22
  21. package/dist/components/hooks/usePluginConfig.d.ts +8 -20
  22. package/dist/components/hooks/usePluginConfig.js +9 -2
  23. package/dist/components/text-shimmer/TextShimmer.d.ts +9 -0
  24. package/dist/components/text-shimmer/TextShimmer.js +34 -0
  25. package/dist/components/text-shimmer/TextShimmer.module.css +19 -0
  26. package/dist/exports/client.d.ts +2 -0
  27. package/dist/exports/client.js +2 -0
  28. package/dist/handlers/chatHandler.d.ts +4 -1
  29. package/dist/handlers/chatHandler.js +198 -17
  30. package/dist/index.d.ts +6 -1
  31. package/dist/index.js +92 -5
  32. package/package.json +18 -16
package/README.md CHANGED
@@ -39,19 +39,21 @@ export default buildConfig({
39
39
  });
40
40
  ```
41
41
 
42
- The plugin adds two fields to the configured Payload admin user collection:
42
+ Without centrally configured providers, the plugin adds two fields to the configured Payload admin user collection:
43
43
 
44
44
  - `aiProvider`
45
45
  - `aiApiKey`
46
46
 
47
47
  Users can select their provider and optionally store their own API key in account settings. If no account-level key is set, the chat endpoint uses provider environment variables.
48
48
 
49
+ When `providers` is configured, provider selection and API keys are managed centrally. The plugin does not add either field to the user collection. All configured provider models are grouped by provider in the AI input.
50
+
49
51
  ## Options
50
52
 
51
53
  ```ts
52
- import type { PayloadAiPluginOptions } from "@mvriu5/payload-ai";
54
+ import type { PayloadAIPluginOptions } from "@mvriu5/payload-ai";
53
55
 
54
- const options: PayloadAiPluginOptions = {
56
+ const options: PayloadAIPluginOptions = {
55
57
  allowUserApiKeys: false,
56
58
  collections: {
57
59
  media: {
@@ -72,22 +74,41 @@ const options: PayloadAiPluginOptions = {
72
74
  acceptedMimeTypes: ["image/*"],
73
75
  maxFileSize: 10 * 1024 * 1024,
74
76
  },
75
- models: {
76
- defaults: {
77
- openai: "gpt-4.1-mini",
78
- openrouter: "openai/gpt-oss-120b",
79
- },
80
- providers: {
81
- openai: [
77
+ providers: [
78
+ {
79
+ id: "company-openai",
80
+ label: "Company OpenAI",
81
+ provider: "openai",
82
+ apiKey: process.env.COMPANY_OPENAI_API_KEY,
83
+ models: [
82
84
  { label: "GPT-4.1 Mini", value: "gpt-4.1-mini" },
83
85
  { label: "GPT-4.1", value: "gpt-4.1" },
84
86
  ],
87
+ defaultModel: "gpt-4.1-mini",
85
88
  },
86
- },
89
+ {
90
+ id: "ollama",
91
+ label: "Local Ollama",
92
+ provider: "openai",
93
+ baseURL: "http://localhost:11434/v1",
94
+ apiKey: "ollama",
95
+ models: [
96
+ { label: "Llama 3.3", value: "llama3.3" },
97
+ { label: "Qwen 3", value: "qwen3" },
98
+ ],
99
+ },
100
+ ],
87
101
  maxOutputTokens: 1200,
102
+ maxTokenUsage: {
103
+ type: "user",
104
+ perDay: 50_000,
105
+ perWeek: 250_000,
106
+ },
88
107
  };
89
108
  ```
90
109
 
110
+ `models` configures model choices for the user-selected provider mode. Use `providers` instead when provider selection, credentials, and endpoints should be managed centrally.
111
+
91
112
  ### `collections`
92
113
 
93
114
  Restricts AI read and write proposals to enabled collection slugs. If omitted, all non-internal Payload collections are available.
@@ -180,6 +201,27 @@ OpenRouter includes these built-in model options:
180
201
  - `anthropic/claude-3.5-sonnet`
181
202
  - `google/gemini-2.0-flash-001`
182
203
 
204
+ ### `providers`
205
+
206
+ Enables centrally managed provider profiles. Each profile supports:
207
+
208
+ - `id`: unique provider profile identifier
209
+ - `label`: provider group label shown in the model select
210
+ - `provider`: SDK adapter (`openai`, `openrouter`, `claude`, `mistral`, or `google`)
211
+ - `models`: allowed model list
212
+ - `defaultModel`: optional default; otherwise the first configured model is used
213
+ - `baseURL`: optional custom provider URL
214
+ - `apiKey`: optional server-side API key
215
+
216
+ When at least one profile is configured:
217
+
218
+ - `aiProvider` and `aiApiKey` are not added to the admin user collection.
219
+ - Models from every configured profile are displayed in provider groups.
220
+ - The chat endpoint validates both provider IDs and model IDs against this configuration.
221
+ - User-level provider and API key values are ignored.
222
+
223
+ `apiKey` and `baseURL` remain server-side and are not included in Payload's public admin configuration. Custom endpoints must implement the protocol expected by their selected SDK adapter. For Ollama, vLLM, and similar APIs, use the `openai` adapter with an OpenAI-compatible `/v1` endpoint.
224
+
183
225
  ### `maxOutputTokens`
184
226
 
185
227
  Controls the maximum number of output tokens the chat endpoint may generate per request. If omitted, the plugin uses `700`.
@@ -190,6 +232,24 @@ payloadAiPlugin({
190
232
  })
191
233
  ```
192
234
 
235
+ ### `maxTokenUsage`
236
+
237
+ Limits total AI tokens across rolling 24-hour and 7-day windows.
238
+
239
+ ```ts
240
+ payloadAiPlugin({
241
+ maxTokenUsage: {
242
+ type: "user",
243
+ perDay: 50_000,
244
+ perWeek: 250_000,
245
+ },
246
+ })
247
+ ```
248
+
249
+ Use `type: "user"` to enforce separate budgets per authenticated user, or `type: "site"` to share one budget across the entire Payload installation. `perDay` and `perWeek` are optional individually, but at least one must be configured.
250
+
251
+ Completed model usage is stored in the hidden `payload-ai-usage` collection. Requests made after a limit is reached return HTTP `429`. Because providers report token usage after completion, the request that crosses a limit is allowed to finish and subsequent requests are blocked.
252
+
193
253
  ### `allowUserApiKeys`
194
254
 
195
255
  Controls whether the plugin adds an `aiApiKey` field to the admin user collection.
@@ -202,6 +262,8 @@ payloadAiPlugin({
202
262
 
203
263
  When disabled, users can still select an AI provider, but API keys must come from environment variables.
204
264
 
265
+ This option only applies when `providers` is not configured. Managed provider mode never adds user-level AI settings.
266
+
205
267
  ### `disabled`
206
268
 
207
269
  Disables endpoint and UI registration while keeping the plugin call in your config.
@@ -213,8 +275,9 @@ The package lazy-loads provider SDKs at runtime. If a user selects `claude`, `go
213
275
 
214
276
  API key priority is:
215
277
 
216
- 1. account-level API key, unless `allowUserApiKeys: false`
217
- 2. provider environment variables
278
+ 1. managed provider `apiKey`, when `providers` is configured
279
+ 2. account-level API key, unless `allowUserApiKeys: false`
280
+ 3. provider environment variables
218
281
 
219
282
  - `OPENAI_API_KEY`, `OPENAI_MODEL`
220
283
  - `OPENROUTER_API_KEY`, `OPENROUTER_MODEL`
@@ -1,4 +1,4 @@
1
- type AIProviderModelOption = {
1
+ export type AIProviderModelOption = {
2
2
  label: string;
3
3
  value: string;
4
4
  };
@@ -71,6 +71,26 @@ declare const aiProviderModels: {
71
71
  };
72
72
  export type AIProvider = keyof typeof aiProviderModels;
73
73
  type AIProviderModels = Record<AIProvider, AIProviderModelOption[]>;
74
+ export type AIProviderConfig = {
75
+ apiKey?: string;
76
+ baseURL?: string;
77
+ defaultModel?: string;
78
+ id: string;
79
+ label: string;
80
+ models: AIProviderModelOption[];
81
+ provider: AIProvider;
82
+ };
83
+ export type AIProviderProfile = {
84
+ defaultModel: string;
85
+ id: string;
86
+ label: string;
87
+ models: AIProviderModelOption[];
88
+ provider: AIProvider;
89
+ };
90
+ export type ResolvedAIProviderConfig = AIProviderProfile & {
91
+ apiKey?: string;
92
+ baseURL?: string;
93
+ };
74
94
  export declare const aiProviders: {
75
95
  label: string;
76
96
  value: AIProvider;
@@ -85,4 +105,7 @@ export declare const getResolvedAIModelConfig: (modelConfig?: AIModelConfig) =>
85
105
  providers: AIProviderModels;
86
106
  };
87
107
  export declare const isAIProvider: (provider: string) => provider is AIProvider;
108
+ export declare const getLegacyAIProviderProfiles: (modelConfig?: AIModelConfig) => AIProviderProfile[];
109
+ export declare const resolveAIProviderConfigs: (providers?: AIProviderConfig[]) => ResolvedAIProviderConfig[];
110
+ export declare const toClientAIProviderProfiles: (providers: ResolvedAIProviderConfig[]) => AIProviderProfile[];
88
111
  export {};
@@ -144,3 +144,84 @@ export const getResolvedAIModelConfig = (modelConfig)=>{
144
144
  };
145
145
  };
146
146
  export const isAIProvider = (provider)=>provider in aiProviderModels;
147
+ export const getLegacyAIProviderProfiles = (modelConfig)=>{
148
+ const resolvedModels = getResolvedAIModelConfig(modelConfig);
149
+ return aiProviders.map(({ label, value })=>({
150
+ defaultModel: resolvedModels.defaults[value],
151
+ id: value,
152
+ label,
153
+ models: resolvedModels.providers[value],
154
+ provider: value
155
+ }));
156
+ };
157
+ export const resolveAIProviderConfigs = (providers)=>{
158
+ if (!providers?.length) return [];
159
+ const providerIDs = new Set();
160
+ return providers.map((providerConfig, index)=>{
161
+ const path = `providers[${index}]`;
162
+ const id = providerConfig.id.trim();
163
+ const label = providerConfig.label.trim();
164
+ if (!id || !/^[a-z0-9][a-z0-9_-]*$/i.test(id)) {
165
+ throw new Error(`${path}.id must contain only letters, numbers, hyphens, or underscores.`);
166
+ }
167
+ if (providerIDs.has(id)) throw new Error(`Duplicate AI provider id: ${id}`);
168
+ providerIDs.add(id);
169
+ if (!label) throw new Error(`${path}.label is required.`);
170
+ if (!isAIProvider(providerConfig.provider)) {
171
+ throw new Error(`${path}.provider is unsupported: ${String(providerConfig.provider)}`);
172
+ }
173
+ if (!providerConfig.models.length) throw new Error(`${path}.models must contain at least one model.`);
174
+ const modelValues = new Set();
175
+ const models = providerConfig.models.map((model, modelIndex)=>{
176
+ const modelPath = `${path}.models[${modelIndex}]`;
177
+ const modelLabel = model.label.trim();
178
+ const value = model.value.trim();
179
+ if (!modelLabel) throw new Error(`${modelPath}.label is required.`);
180
+ if (!value) throw new Error(`${modelPath}.value is required.`);
181
+ if (modelValues.has(value)) throw new Error(`Duplicate model value "${value}" in AI provider "${id}".`);
182
+ modelValues.add(value);
183
+ return {
184
+ label: modelLabel,
185
+ value
186
+ };
187
+ });
188
+ const defaultModel = providerConfig.defaultModel?.trim() || models[0].value;
189
+ if (!modelValues.has(defaultModel)) {
190
+ throw new Error(`${path}.defaultModel must match a configured model value.`);
191
+ }
192
+ if (providerConfig.baseURL) {
193
+ let parsedURL;
194
+ try {
195
+ parsedURL = new URL(providerConfig.baseURL);
196
+ } catch {
197
+ throw new Error(`${path}.baseURL must be a valid URL.`);
198
+ }
199
+ if (![
200
+ "http:",
201
+ "https:"
202
+ ].includes(parsedURL.protocol)) {
203
+ throw new Error(`${path}.baseURL must use http or https.`);
204
+ }
205
+ }
206
+ return {
207
+ ...providerConfig.apiKey ? {
208
+ apiKey: providerConfig.apiKey
209
+ } : {},
210
+ ...providerConfig.baseURL ? {
211
+ baseURL: providerConfig.baseURL
212
+ } : {},
213
+ defaultModel,
214
+ id,
215
+ label,
216
+ models,
217
+ provider: providerConfig.provider
218
+ };
219
+ });
220
+ };
221
+ export const toClientAIProviderProfiles = (providers)=>providers.map(({ defaultModel, id, label, models, provider })=>({
222
+ defaultModel,
223
+ id,
224
+ label,
225
+ models,
226
+ provider
227
+ }));
@@ -8,6 +8,7 @@ type ProviderConfig = {
8
8
  };
9
9
  type ModelConfig = {
10
10
  apiKey: string;
11
+ baseURL?: string;
11
12
  model: string;
12
13
  provider: AIProvider;
13
14
  };
@@ -15,5 +16,5 @@ export declare const getProviderConfig: ({ apiKey, defaultModels, model, provide
15
16
  apiKey: string | undefined;
16
17
  modelID: string;
17
18
  };
18
- export declare const getModel: ({ apiKey, model, provider }: ModelConfig) => Promise<LanguageModel>;
19
+ export declare const getModel: ({ apiKey, baseURL, model, provider }: ModelConfig) => Promise<LanguageModel>;
19
20
  export {};
@@ -33,9 +33,12 @@ export const getProviderConfig = ({ apiKey, defaultModels, model, provider })=>{
33
33
  const getMissingProviderDependencyError = (packageName, provider)=>{
34
34
  return new Error(`Missing optional dependency ${packageName}. Install it to use the ${provider} provider.`);
35
35
  };
36
- export const getModel = async ({ apiKey, model, provider })=>{
36
+ export const getModel = async ({ apiKey, baseURL, model, provider })=>{
37
37
  const providerOptions = {
38
- apiKey
38
+ apiKey,
39
+ ...baseURL ? {
40
+ baseURL
41
+ } : {}
39
42
  };
40
43
  if (provider === "claude") {
41
44
  try {
@@ -0,0 +1,38 @@
1
+ import type { PayloadHandler } from "payload";
2
+ export type MaxTokenUsageOptions = {
3
+ perDay?: number;
4
+ perWeek?: number;
5
+ type: "site" | "user";
6
+ };
7
+ export type ResolvedMaxTokenUsageOptions = {
8
+ perDay?: number;
9
+ perWeek?: number;
10
+ type: "site" | "user";
11
+ };
12
+ export type TokenUsageData = {
13
+ inputTokens?: number;
14
+ outputTokens?: number;
15
+ totalTokens?: number;
16
+ };
17
+ type TokenUsageLimit = {
18
+ limit: number;
19
+ period: "day" | "week";
20
+ used: number;
21
+ };
22
+ export declare const tokenUsageCollectionSlug = "payload-ai-usage";
23
+ export declare const resolveMaxTokenUsageOptions: (options?: MaxTokenUsageOptions) => ResolvedMaxTokenUsageOptions | undefined;
24
+ export declare const getExceededTokenUsageLimit: ({ maxTokenUsage, now, req, userID, }: {
25
+ maxTokenUsage?: ResolvedMaxTokenUsageOptions;
26
+ now?: Date;
27
+ req: Parameters<PayloadHandler>[0];
28
+ userID: number | string;
29
+ }) => Promise<TokenUsageLimit | null>;
30
+ export declare const recordTokenUsage: ({ model, now, provider, req, usage, userID, }: {
31
+ model: string;
32
+ now?: Date;
33
+ provider: string;
34
+ req: Parameters<PayloadHandler>[0];
35
+ usage: TokenUsageData;
36
+ userID: number | string;
37
+ }) => Promise<void>;
38
+ export {};
@@ -0,0 +1,106 @@
1
+ export const tokenUsageCollectionSlug = "payload-ai-usage";
2
+ const dayInMilliseconds = 24 * 60 * 60 * 1000;
3
+ const weekInMilliseconds = 7 * dayInMilliseconds;
4
+ const resolvePositiveInteger = (value, path)=>{
5
+ if (value === undefined) return undefined;
6
+ if (!Number.isFinite(value) || value <= 0) throw new Error(`${path} must be a positive number.`);
7
+ return Math.floor(value);
8
+ };
9
+ export const resolveMaxTokenUsageOptions = (options)=>{
10
+ if (!options) return undefined;
11
+ if (![
12
+ "site",
13
+ "user"
14
+ ].includes(options.type)) throw new Error('maxTokenUsage.type must be either "user" or "site".');
15
+ const perDay = resolvePositiveInteger(options.perDay, "maxTokenUsage.perDay");
16
+ const perWeek = resolvePositiveInteger(options.perWeek, "maxTokenUsage.perWeek");
17
+ if (!perDay && !perWeek) throw new Error("maxTokenUsage must configure perDay, perWeek, or both.");
18
+ return {
19
+ ...perDay ? {
20
+ perDay
21
+ } : {},
22
+ ...perWeek ? {
23
+ perWeek
24
+ } : {},
25
+ type: options.type
26
+ };
27
+ };
28
+ const normalizeTokenCount = (value)=>typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
29
+ const getTokenCount = (usage)=>typeof usage.totalTokens === "number" ? normalizeTokenCount(usage.totalTokens) : normalizeTokenCount(usage.inputTokens) + normalizeTokenCount(usage.outputTokens);
30
+ export const getExceededTokenUsageLimit = async ({ maxTokenUsage, now = new Date(), req, userID })=>{
31
+ if (!maxTokenUsage) return null;
32
+ const dayStart = new Date(now.getTime() - dayInMilliseconds);
33
+ const weekStart = new Date(now.getTime() - weekInMilliseconds);
34
+ const queryStart = maxTokenUsage.perWeek ? weekStart : dayStart;
35
+ let page = 1;
36
+ let usagePerDay = 0;
37
+ let usagePerWeek = 0;
38
+ while(true){
39
+ const result = await req.payload.find({
40
+ collection: tokenUsageCollectionSlug,
41
+ depth: 0,
42
+ limit: 500,
43
+ overrideAccess: true,
44
+ page,
45
+ req,
46
+ where: {
47
+ and: [
48
+ {
49
+ recordedAt: {
50
+ greater_than_equal: queryStart.toISOString()
51
+ }
52
+ },
53
+ ...maxTokenUsage.type === "user" ? [
54
+ {
55
+ userID: {
56
+ equals: String(userID)
57
+ }
58
+ }
59
+ ] : []
60
+ ]
61
+ }
62
+ });
63
+ for (const document of result.docs || []){
64
+ const tokenCount = getTokenCount(document);
65
+ usagePerWeek += tokenCount;
66
+ if (document.recordedAt && new Date(document.recordedAt).getTime() >= dayStart.getTime()) {
67
+ usagePerDay += tokenCount;
68
+ }
69
+ }
70
+ if (!result.hasNextPage || !result.nextPage) break;
71
+ page = result.nextPage;
72
+ }
73
+ if (maxTokenUsage.perDay && usagePerDay >= maxTokenUsage.perDay) {
74
+ return {
75
+ limit: maxTokenUsage.perDay,
76
+ period: "day",
77
+ used: usagePerDay
78
+ };
79
+ }
80
+ if (maxTokenUsage.perWeek && usagePerWeek >= maxTokenUsage.perWeek) {
81
+ return {
82
+ limit: maxTokenUsage.perWeek,
83
+ period: "week",
84
+ used: usagePerWeek
85
+ };
86
+ }
87
+ return null;
88
+ };
89
+ export const recordTokenUsage = async ({ model, now = new Date(), provider, req, usage, userID })=>{
90
+ const totalTokens = getTokenCount(usage);
91
+ if (!totalTokens) return;
92
+ await req.payload.create({
93
+ collection: tokenUsageCollectionSlug,
94
+ data: {
95
+ inputTokens: typeof usage.inputTokens === "number" ? normalizeTokenCount(usage.inputTokens) : undefined,
96
+ model,
97
+ outputTokens: typeof usage.outputTokens === "number" ? normalizeTokenCount(usage.outputTokens) : undefined,
98
+ provider,
99
+ recordedAt: now.toISOString(),
100
+ totalTokens,
101
+ userID: String(userID)
102
+ },
103
+ overrideAccess: true,
104
+ req
105
+ });
106
+ };
@@ -4,3 +4,4 @@ export declare function OpenaiIcon(props: SVGProps<SVGSVGElement>): import("reac
4
4
  export declare function ClaudeIcon(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
5
5
  export declare function GoogleGeminiIcon(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
6
6
  export declare function OpenrouterIcon(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
7
+ export declare function PaperclipIcon(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
@@ -274,3 +274,18 @@ export function OpenrouterIcon(props) {
274
274
  })
275
275
  });
276
276
  }
277
+ export function PaperclipIcon(props) {
278
+ return /*#__PURE__*/ _jsx("svg", {
279
+ xmlns: "http://www.w3.org/2000/svg",
280
+ viewBox: "0 0 24 24",
281
+ fill: "none",
282
+ stroke: "currentColor",
283
+ strokeWidth: "2",
284
+ strokeLinecap: "round",
285
+ strokeLinejoin: "round",
286
+ ...props,
287
+ children: /*#__PURE__*/ _jsx("path", {
288
+ d: "m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551"
289
+ })
290
+ });
291
+ }
@@ -5,8 +5,11 @@ export type ActionProposal = {
5
5
  };
6
6
  action: "create" | "delete" | "update" | "updateGlobal";
7
7
  collection?: string;
8
+ data?: Record<string, unknown>;
8
9
  id?: string;
9
10
  label: string;
11
+ locale?: string;
12
+ localizedData?: Record<string, Record<string, unknown>>;
10
13
  slug?: string;
11
14
  };
12
15
  type ActionToastProps = {
@@ -15,6 +18,7 @@ type ActionToastProps = {
15
18
  error?: string;
16
19
  getViewURL?: (proposal: ActionProposal) => string | null;
17
20
  isApplying: boolean;
21
+ isLoading: boolean;
18
22
  onDismiss?: () => void;
19
23
  onDismissError?: () => void;
20
24
  onApply: (proposal: ActionProposal, index: number) => void;
@@ -26,5 +30,5 @@ type ActionToastProps = {
26
30
  totalTokens?: number;
27
31
  } | null;
28
32
  };
29
- export declare const ActionToast: ({ apiRoute, description, error, getViewURL, isApplying, onDismiss, onDismissError, onApply, prompt, proposals, tokenUsage, }: ActionToastProps) => import("react").JSX.Element | null;
33
+ export declare const ActionToast: ({ apiRoute, description, error, getViewURL, isApplying, isLoading, onDismiss, onDismissError, onApply, prompt, proposals, tokenUsage, }: ActionToastProps) => import("react").JSX.Element | null;
30
34
  export {};