@rebasepro/plugin-ai 0.12.0 → 0.12.1-canary.g009ed95
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 +62 -18
- package/dist/api.d.ts +57 -31
- package/dist/components/AutofillReviewDialog.d.ts +16 -0
- package/dist/components/DataEnhancementControllerProvider.d.ts +2 -3
- package/dist/components/FormEnhanceAction.d.ts +1 -1
- package/dist/editor/useEditorAIController.d.ts +11 -2
- package/dist/index.es.js +582 -361
- package/dist/index.es.js.map +1 -1
- package/dist/types/data_enhancement_controller.d.ts +84 -28
- package/dist/useDataEnhancementPlugin.d.ts +10 -5
- package/package.json +23 -19
- package/src/api.ts +238 -174
- package/src/components/AutofillReviewDialog.tsx +203 -0
- package/src/components/DataEnhancementControllerProvider.tsx +196 -264
- package/src/components/FormEnhanceAction.tsx +128 -128
- package/src/editor/useEditorAIController.tsx +20 -33
- package/src/tests/api.test.ts +283 -0
- package/src/tests/review.test.tsx +260 -0
- package/src/tests/useDataEnhancementPlugin.test.tsx +36 -15
- package/src/types/data_enhancement_controller.tsx +98 -31
- package/src/useDataEnhancementPlugin.tsx +13 -12
- package/dist/utils/diffStrings.d.ts +0 -7
- package/dist/utils/strings_counter.d.ts +0 -2
- package/dist/utils/suggestions.d.ts +0 -1
- package/src/tests/diffStrings.test.ts +0 -128
- package/src/tests/strings_counter.test.ts +0 -117
- package/src/tests/suggestions.test.ts +0 -53
- package/src/utils/diffStrings.ts +0 -70
- package/src/utils/strings_counter.ts +0 -22
- package/src/utils/suggestions.ts +0 -6
package/README.md
CHANGED
|
@@ -8,17 +8,52 @@ AI-powered data autofill and text autocomplete plugin for Rebase.
|
|
|
8
8
|
pnpm add @rebasepro/plugin-ai
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
**Peer dependencies:** `react >= 19.
|
|
11
|
+
**Peer dependencies:** `react >= 19.2.7`, `react-dom >= 19.2.7`, `react-router ^8`
|
|
12
12
|
|
|
13
13
|
## What This Package Does
|
|
14
14
|
|
|
15
15
|
This plugin adds AI-powered capabilities to the Rebase admin panel:
|
|
16
16
|
|
|
17
|
-
- **Form autofill** — An "
|
|
18
|
-
- **Editor autocomplete** — A streaming
|
|
17
|
+
- **Form autofill** — An "Autofill" action in the entity form footer that proposes values from the collection schema, whatever is already in the record, and an optional instruction.
|
|
18
|
+
- **Editor autocomplete** — A streaming inline continuation for the rich text editor.
|
|
19
19
|
|
|
20
20
|
It registers as a standard `RebasePlugin`, injecting UI slots and providers automatically.
|
|
21
21
|
|
|
22
|
+
### Autofill proposes; it does not edit
|
|
23
|
+
|
|
24
|
+
Clicking **Autofill** opens a review. Fields stream into that list as the model
|
|
25
|
+
writes them, so a long run shows progress — but the record is not touched. Each row
|
|
26
|
+
shows the proposed value, and the current value struck through when the proposal
|
|
27
|
+
would replace one. Untick anything you don't want, then **Apply** writes the rest in
|
|
28
|
+
a single step. **Discard** leaves the record exactly as it was.
|
|
29
|
+
|
|
30
|
+
This is a deliberate change from the FireCMS-era behaviour, where generated text was
|
|
31
|
+
streamed directly into the live form fields. That approach mutated the record before
|
|
32
|
+
anyone had agreed to it, showed half-written sentences that read as bugs, and relied
|
|
33
|
+
on heuristics to guess whether each token should append to or replace what you had
|
|
34
|
+
already typed — with no way back but retyping.
|
|
35
|
+
|
|
36
|
+
## How it reaches a model
|
|
37
|
+
|
|
38
|
+
The plugin talks to a small hosted service that Rebase runs and pays for (Gemini 3.6 Flash, behind Rebase’s own credits). There is
|
|
39
|
+
**nothing to configure and no API key to obtain** — install the plugin, mount it, done.
|
|
40
|
+
|
|
41
|
+
Two consequences worth knowing:
|
|
42
|
+
|
|
43
|
+
- **Requests are anonymous.** No auth token of any kind leaves your app. The service
|
|
44
|
+
cannot verify a self-hosted backend's JWT (you sign it with your own secret), so
|
|
45
|
+
asking for one would only hand a live credential to a third party that has no use
|
|
46
|
+
for it. Cost is bounded by rate limits and a daily ceiling instead.
|
|
47
|
+
- **Your collection schema and the record's current values are sent** with each
|
|
48
|
+
autofill request, because the service has no other way to know the shape of what
|
|
49
|
+
it is filling. If that is not acceptable for your data, set `endpoint` and run the
|
|
50
|
+
service yourself — the reference implementation is `saas/backend/functions/ai.ts`
|
|
51
|
+
in the Rebase repository, and the wire format is documented in `src/api.ts`.
|
|
52
|
+
|
|
53
|
+
The plugin renders nothing until the service's `GET /status` reports itself
|
|
54
|
+
available, so an unreachable host or an exhausted daily quota means no Autofill
|
|
55
|
+
button — never a button that fails when clicked.
|
|
56
|
+
|
|
22
57
|
## Key Exports
|
|
23
58
|
|
|
24
59
|
| Export | Type | Description |
|
|
@@ -31,38 +66,39 @@ It registers as a standard `RebasePlugin`, injecting UI slots and providers auto
|
|
|
31
66
|
|
|
32
67
|
| Prop | Type | Default | Description |
|
|
33
68
|
|---|---|---|---|
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `host` | `string` | — | Custom API host (development only) |
|
|
69
|
+
| `getConfigForPath` | `(props: { path, collection, user }) => boolean` | — | Return `false` to disable autofill for specific paths |
|
|
70
|
+
| `endpoint` | `string` | Rebase's hosted service | Base URL of the AI service. Point it at your own deployment to keep generation inside your infrastructure |
|
|
37
71
|
|
|
38
72
|
## Quick Start
|
|
39
73
|
|
|
40
74
|
```tsx
|
|
41
75
|
import { useDataEnhancementPlugin } from "@rebasepro/plugin-ai";
|
|
42
76
|
|
|
43
|
-
// In your app setup:
|
|
44
77
|
const dataEnhancementPlugin = useDataEnhancementPlugin({
|
|
45
|
-
getConfigForPath: ({
|
|
46
|
-
// Disable for certain collections
|
|
47
|
-
return collection.name !== "system_logs";
|
|
48
|
-
}
|
|
78
|
+
getConfigForPath: ({ collection }) => collection.name !== "system_logs"
|
|
49
79
|
});
|
|
50
80
|
|
|
51
81
|
// Pass to your Rebase app:
|
|
52
|
-
<
|
|
53
|
-
plugins={[dataEnhancementPlugin]}
|
|
54
|
-
// ...other props
|
|
55
|
-
/>
|
|
82
|
+
// <Rebase plugins={[dataEnhancementPlugin]} ... />
|
|
56
83
|
```
|
|
57
84
|
|
|
85
|
+
### Self-hosting the service
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
const dataEnhancementPlugin = useDataEnhancementPlugin({
|
|
89
|
+
endpoint: "https://ai.internal.example.com"
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Your endpoint needs to answer `GET /status`, `POST /autofill` (SSE), `POST /autocomplete`
|
|
94
|
+
(SSE) and `POST /prompts`.
|
|
95
|
+
|
|
58
96
|
### Editor AI Autocomplete
|
|
59
97
|
|
|
60
98
|
```tsx
|
|
61
99
|
import { useEditorAIController } from "@rebasepro/plugin-ai";
|
|
62
100
|
|
|
63
|
-
const aiController = useEditorAIController(
|
|
64
|
-
getAuthToken: () => firebaseUser.getIdToken()
|
|
65
|
-
});
|
|
101
|
+
const aiController = useEditorAIController();
|
|
66
102
|
|
|
67
103
|
// Use in a rich text editor:
|
|
68
104
|
await aiController.autocomplete(
|
|
@@ -74,6 +110,14 @@ await aiController.autocomplete(
|
|
|
74
110
|
);
|
|
75
111
|
```
|
|
76
112
|
|
|
113
|
+
## Migrating from the FireCMS-era plugin
|
|
114
|
+
|
|
115
|
+
The `apiKey` and `host` props are gone. `apiKey` shipped a hardcoded key in the
|
|
116
|
+
published package and pointed at a service that no longer exists; `host` is now
|
|
117
|
+
`endpoint` and is a supported production option rather than a development-only
|
|
118
|
+
escape hatch. `useEditorAIController` no longer takes `getAuthToken` — it needs no
|
|
119
|
+
token.
|
|
120
|
+
|
|
77
121
|
## Related Packages
|
|
78
122
|
|
|
79
123
|
- `@rebasepro/admin` — The admin panel this plugin extends
|
package/dist/api.d.ts
CHANGED
|
@@ -1,34 +1,60 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
1
|
+
import { AutofillRequest, AutofillResult, AiStatus, SamplePromptsResult } from "./types/data_enhancement_controller";
|
|
2
|
+
/**
|
|
3
|
+
* The hosted service Rebase runs for this plugin.
|
|
4
|
+
*
|
|
5
|
+
* The previous value here was `https://api.rebase.pro`, a FireCMS-era host that
|
|
6
|
+
* resolves but serves nothing — every path 404s — so Autofill had never worked
|
|
7
|
+
* in a Rebase install. This one is served by the control plane
|
|
8
|
+
* (`saas/backend/functions/ai.ts`). Point `endpoint` somewhere else to run your
|
|
9
|
+
* own; the wire format below is the whole contract.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_AI_ENDPOINT = "https://app.rebase.pro/api/functions/ai";
|
|
12
|
+
/**
|
|
13
|
+
* Ask the service whether it can serve a request at all.
|
|
14
|
+
*
|
|
15
|
+
* The plugin gates every affordance on this. A missing provider key, an
|
|
16
|
+
* exhausted daily quota or an unreachable host all resolve to `available:
|
|
17
|
+
* false`, and the Autofill button is simply not rendered — rather than
|
|
18
|
+
* rendered, clicked, and failed.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fetchAiStatus(props: {
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
signal?: AbortSignal;
|
|
23
|
+
}): Promise<AiStatus>;
|
|
24
|
+
/**
|
|
25
|
+
* Fill a record, streaming each field as the service writes it.
|
|
26
|
+
*
|
|
27
|
+
* `onDelta` fires with more text for a field still being written; `onValue`
|
|
28
|
+
* fires once a field is complete and carries its final, correctly typed value.
|
|
29
|
+
* A caller that implements only `onValue` still ends up with the right record —
|
|
30
|
+
* the deltas exist so a long text field fills in visibly instead of appearing
|
|
31
|
+
* all at once.
|
|
32
|
+
*/
|
|
33
|
+
export declare function autofillStream(props: {
|
|
34
|
+
request: AutofillRequest;
|
|
35
|
+
endpoint?: string;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
onDelta: (key: string, text: string) => void;
|
|
38
|
+
onValue: (key: string, value: unknown) => void;
|
|
39
|
+
}): Promise<AutofillResult>;
|
|
40
|
+
/** Inline continuation for the rich-text editor. Streams plain text. */
|
|
28
41
|
export declare function autocompleteStream(props: {
|
|
29
|
-
firebaseToken: string;
|
|
30
42
|
textBefore?: string;
|
|
31
|
-
textAfter
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
textAfter?: string;
|
|
44
|
+
endpoint?: string;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
onDelta: (text: string) => void;
|
|
34
47
|
}): Promise<string>;
|
|
48
|
+
/**
|
|
49
|
+
* Sample prompts for the Autofill menu.
|
|
50
|
+
*
|
|
51
|
+
* Failure is deliberately not thrown: the menu has built-in prompts to fall
|
|
52
|
+
* back on, and an empty suggestion list is a far better outcome than an error
|
|
53
|
+
* toast for something nobody asked for.
|
|
54
|
+
*/
|
|
55
|
+
export declare function fetchPromptSuggestions(props: {
|
|
56
|
+
entityName: string;
|
|
57
|
+
input?: string;
|
|
58
|
+
endpoint?: string;
|
|
59
|
+
signal?: AbortSignal;
|
|
60
|
+
}): Promise<SamplePromptsResult>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The review step.
|
|
4
|
+
*
|
|
5
|
+
* Autofill used to write generated text into the live form as it streamed —
|
|
6
|
+
* fields mutating under the cursor, half-written sentences that looked like
|
|
7
|
+
* bugs, and a pile of heuristics deciding whether each token should append to
|
|
8
|
+
* or replace what the operator had already typed. Getting the old value back
|
|
9
|
+
* meant retyping it.
|
|
10
|
+
*
|
|
11
|
+
* So the generated values land here instead. Streaming still happens, and is
|
|
12
|
+
* still worth having — rows appear and fill in as the model works, so a long
|
|
13
|
+
* run shows progress — but it happens in a surface that owns nothing. The
|
|
14
|
+
* record changes on **Apply**, once, for the rows still ticked.
|
|
15
|
+
*/
|
|
16
|
+
export declare function AutofillReviewDialog(): React.JSX.Element | null;
|
|
@@ -3,13 +3,12 @@ import { DataEnhancementController } from "../types/data_enhancement_controller"
|
|
|
3
3
|
import { CollectionConfig } from "@rebasepro/types";
|
|
4
4
|
import { PluginFormActionProps } from "@rebasepro/admin-types";
|
|
5
5
|
type DataEnhancementControllerProviderProps = {
|
|
6
|
-
apiKey: string;
|
|
7
6
|
getConfigForPath?: (props: {
|
|
8
7
|
path: string;
|
|
9
8
|
collection: CollectionConfig;
|
|
10
9
|
}) => boolean;
|
|
11
|
-
|
|
10
|
+
endpoint?: string;
|
|
12
11
|
};
|
|
13
12
|
export declare const useDataEnhancementController: () => DataEnhancementController;
|
|
14
|
-
export declare function DataEnhancementControllerProvider({
|
|
13
|
+
export declare function DataEnhancementControllerProvider({ getConfigForPath, children, endpoint, path, collection, formContext }: PropsWithChildren<DataEnhancementControllerProviderProps & PluginFormActionProps>): React.JSX.Element;
|
|
15
14
|
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { PluginFormActionProps } from "@rebasepro/admin-types";
|
|
3
|
-
export declare function FormEnhanceAction({
|
|
3
|
+
export declare function FormEnhanceAction({ path, status, collection, formContext }: PluginFormActionProps): React.JSX.Element | null;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { EditorAIController } from "@rebasepro/admin";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Inline continuation for the rich-text editor's slash command.
|
|
4
|
+
*
|
|
5
|
+
* No token is threaded through any more. The previous version demanded a
|
|
6
|
+
* Firebase ID token and threw `"Firebase token is required"` when it could not
|
|
7
|
+
* get one — in a Rebase app there is no such thing, and the token it actually
|
|
8
|
+
* sent was a Rebase JWT the receiving service had no way to verify. The hosted
|
|
9
|
+
* service authenticates nobody; see `src/api.ts`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useEditorAIController({ endpoint }?: {
|
|
12
|
+
endpoint?: string;
|
|
4
13
|
}): EditorAIController;
|