@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
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
AutofillReview,
|
|
4
5
|
DataEnhancementController,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
GenerateParams,
|
|
7
|
+
InputProperty,
|
|
8
|
+
ProposedField
|
|
8
9
|
} from "../types/data_enhancement_controller";
|
|
9
|
-
import {
|
|
10
|
-
useAuthController,
|
|
11
|
-
useCustomizationController,
|
|
12
|
-
useSnackbarController
|
|
13
|
-
} from "@rebasepro/app";
|
|
14
|
-
import { useUrlController } from "@rebasepro/admin";
|
|
15
|
-
import { DataDriver, Entity, CollectionConfig } from "@rebasepro/types";
|
|
10
|
+
import { CollectionConfig } from "@rebasepro/types";
|
|
16
11
|
import { PluginFormActionProps } from "@rebasepro/admin-types";
|
|
17
|
-
import {
|
|
18
|
-
import { getAppendableSuggestion } from "../utils/suggestions";
|
|
12
|
+
import { autofillStream, fetchAiStatus, fetchPromptSuggestions } from "../api";
|
|
19
13
|
import { getSimplifiedProperties } from "../utils/properties";
|
|
14
|
+
import { flatMapEntityValues } from "../utils/values";
|
|
20
15
|
import { useEditorAIController } from "../editor/useEditorAIController";
|
|
21
16
|
import { getValueInPath } from "@rebasepro/utils";
|
|
22
17
|
|
|
@@ -24,311 +19,250 @@ const DataEnhancementControllerContext = React.createContext<DataEnhancementCont
|
|
|
24
19
|
|
|
25
20
|
type DataEnhancementControllerProviderProps = {
|
|
26
21
|
|
|
27
|
-
apiKey: string;
|
|
28
|
-
|
|
29
22
|
getConfigForPath?: (props: {
|
|
30
23
|
path: string,
|
|
31
24
|
collection: CollectionConfig
|
|
32
25
|
}) => boolean;
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
endpoint?: string;
|
|
35
28
|
}
|
|
36
29
|
|
|
37
30
|
export const useDataEnhancementController = (): DataEnhancementController => useContext(DataEnhancementControllerContext);
|
|
38
31
|
|
|
39
|
-
function getPropertyFromKey(properties: Record<string, InputProperty>, propertyKey: string) {
|
|
32
|
+
function getPropertyFromKey(properties: Record<string, InputProperty>, propertyKey: string): InputProperty | undefined {
|
|
40
33
|
if (propertyKey in properties) {
|
|
41
34
|
return properties[propertyKey];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
const parentKey = split.slice(0, split.length - 1).join(".");
|
|
49
|
-
return getPropertyFromKey(properties, parentKey);
|
|
35
|
+
}
|
|
36
|
+
const split = propertyKey.split(".");
|
|
37
|
+
if (split.length === 1) return undefined;
|
|
38
|
+
return getPropertyFromKey(properties, split.slice(0, -1).join("."));
|
|
39
|
+
}
|
|
50
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Convert a value off the wire into what the form field expects.
|
|
43
|
+
*
|
|
44
|
+
* Only dates need converting: the service answers ISO-8601 strings because JSON
|
|
45
|
+
* has no date type, and handing a date field a string stores the wrong type
|
|
46
|
+
* without complaining. Everything else — strings, numbers, booleans, arrays of
|
|
47
|
+
* scalars — is already the shape the field wants, which is the point of having
|
|
48
|
+
* the service constrain its answer to a schema derived from these properties.
|
|
49
|
+
*/
|
|
50
|
+
function coerceToProperty(value: unknown, property: InputProperty | undefined): unknown {
|
|
51
|
+
if (property?.type === "date" && typeof value === "string") {
|
|
52
|
+
const date = new Date(value);
|
|
53
|
+
return Number.isNaN(date.getTime()) ? undefined : date;
|
|
51
54
|
}
|
|
55
|
+
return value;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
export function DataEnhancementControllerProvider({
|
|
55
|
-
apiKey,
|
|
56
59
|
getConfigForPath,
|
|
57
60
|
children,
|
|
58
|
-
|
|
61
|
+
endpoint,
|
|
59
62
|
path,
|
|
60
63
|
collection,
|
|
61
64
|
formContext
|
|
62
65
|
}: PropsWithChildren<DataEnhancementControllerProviderProps & PluginFormActionProps>) {
|
|
63
66
|
|
|
64
|
-
const [
|
|
65
|
-
const [
|
|
66
|
-
const [
|
|
67
|
-
|
|
68
|
-
const enhancingInProgress = useRef(false);
|
|
69
|
-
|
|
70
|
-
const authController = useAuthController();
|
|
71
|
-
const snackbarController = useSnackbarController();
|
|
72
|
-
|
|
67
|
+
const [allowedHere, setAllowedHere] = useState(false);
|
|
68
|
+
const [serviceAvailable, setServiceAvailable] = useState(false);
|
|
69
|
+
const [review, setReview] = useState<AutofillReview | null>(null);
|
|
73
70
|
|
|
74
|
-
const properties = useMemo(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (!enhancingInProgress.current)
|
|
79
|
-
valuesRef.current = formContext?.values ?? {};
|
|
80
|
-
}, [formContext?.values]);
|
|
81
|
-
|
|
82
|
-
const allowReferenceDataSelection = false;
|
|
83
|
-
|
|
84
|
-
const updateConfig = useCallback(async () => {
|
|
85
|
-
if (!getConfigForPath) return;
|
|
86
|
-
const config = getConfigForPath({
|
|
87
|
-
path,
|
|
88
|
-
collection
|
|
89
|
-
});
|
|
90
|
-
if (config) {
|
|
91
|
-
setEnabled(true);
|
|
92
|
-
}
|
|
93
|
-
}, [collection, getConfigForPath, path]);
|
|
71
|
+
const properties = useMemo(
|
|
72
|
+
() => getSimplifiedProperties(collection.properties, formContext?.values ?? {}),
|
|
73
|
+
[collection.properties, formContext?.values]
|
|
74
|
+
);
|
|
94
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Read inside the streaming callbacks, which outlive the render that
|
|
78
|
+
* started the run.
|
|
79
|
+
*
|
|
80
|
+
* The operator is free to keep typing while the model works — nothing here
|
|
81
|
+
* writes to the form — so the callbacks must not close over a stale
|
|
82
|
+
* property map from whichever render happened to kick the run off.
|
|
83
|
+
*/
|
|
84
|
+
const propertiesRef = useRef(properties);
|
|
85
|
+
propertiesRef.current = properties;
|
|
86
|
+
|
|
87
|
+
/** The host app's own opt-out. */
|
|
95
88
|
useEffect(() => {
|
|
96
89
|
if (!getConfigForPath) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
updateConfig();
|
|
90
|
+
setAllowedHere(true);
|
|
91
|
+
return;
|
|
100
92
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
93
|
+
setAllowedHere(Boolean(getConfigForPath({ path,
|
|
94
|
+
collection })));
|
|
95
|
+
}, [getConfigForPath, path, collection]);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The service's own availability.
|
|
99
|
+
*
|
|
100
|
+
* Nothing renders until this comes back true. An unreachable host, an
|
|
101
|
+
* unconfigured provider key or an exhausted daily quota all land here, and
|
|
102
|
+
* all of them mean the same thing to the operator: no Autofill button,
|
|
103
|
+
* rather than a button that fails when clicked.
|
|
104
|
+
*/
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!allowedHere) return;
|
|
107
|
+
const abort = new AbortController();
|
|
108
|
+
fetchAiStatus({ endpoint,
|
|
109
|
+
signal: abort.signal })
|
|
110
|
+
.then((status) => setServiceAvailable(status.available))
|
|
111
|
+
.catch(() => setServiceAvailable(false));
|
|
112
|
+
return () => abort.abort();
|
|
113
|
+
}, [allowedHere, endpoint]);
|
|
114
|
+
|
|
115
|
+
const enabled = allowedHere && serviceAvailable;
|
|
116
|
+
|
|
117
|
+
/** Add or update one row in the review, preserving arrival order. */
|
|
118
|
+
const upsertField = useCallback((key: string, update: (existing: ProposedField | undefined) => ProposedField) => {
|
|
119
|
+
setReview((current) => {
|
|
120
|
+
if (!current) return current;
|
|
121
|
+
const index = current.fields.findIndex((f) => f.key === key);
|
|
122
|
+
const next = update(index === -1 ? undefined : current.fields[index]);
|
|
123
|
+
const fields = index === -1
|
|
124
|
+
? [...current.fields, next]
|
|
125
|
+
: current.fields.map((f, i) => (i === index ? next : f));
|
|
126
|
+
return { ...current,
|
|
127
|
+
fields };
|
|
115
128
|
});
|
|
116
129
|
}, []);
|
|
117
130
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
const property = getPropertyFromKey(properties, propertyKey);
|
|
121
|
-
if (delta === null || property?.disabled) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// clearSuggestion(propertyKey);
|
|
126
|
-
const value = getValueInPath(valuesRef.current, propertyKey);
|
|
131
|
+
const generate = useCallback(async (params: GenerateParams<Record<string, unknown>>): Promise<void> => {
|
|
127
132
|
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
// if (currentValue.length === 0) updatedValue = updatedValue.trimStart();
|
|
131
|
-
valuesRef.current = {
|
|
132
|
-
...valuesRef.current,
|
|
133
|
-
[propertyKey]: updatedValue
|
|
134
|
-
};
|
|
135
|
-
formContext?.setFieldValue(propertyKey, updatedValue, false);
|
|
136
|
-
setSuggestions(prev => ({
|
|
137
|
-
...prev,
|
|
138
|
-
[propertyKey]: (prev[propertyKey] ?? "") + delta
|
|
139
|
-
}));
|
|
140
|
-
}, [properties, formContext]);
|
|
133
|
+
const currentProperties = propertiesRef.current;
|
|
134
|
+
const flatValues = flatMapEntityValues(params.values ?? {}) as Record<string, unknown>;
|
|
141
135
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
setReview({
|
|
137
|
+
status: "generating",
|
|
138
|
+
fields: [],
|
|
139
|
+
instructions: params.instructions
|
|
146
140
|
});
|
|
147
141
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const value = getValueInPath(currentValues, propertyKey);
|
|
151
|
-
const property = getPropertyFromKey(properties, propertyKey);
|
|
152
|
-
|
|
153
|
-
if (!property || suggestion === null || property?.disabled) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (typeof suggestion === "number") {
|
|
158
|
-
formContext?.setFieldValue(propertyKey, suggestion);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (replaceValues) {
|
|
163
|
-
formContext?.setFieldValue(propertyKey, suggestion);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const appendableValue = getAppendableSuggestion(suggestion, value);
|
|
142
|
+
const labelFor = (key: string) => currentProperties[key]?.name ?? key;
|
|
168
143
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
144
|
+
try {
|
|
145
|
+
await autofillStream({
|
|
146
|
+
endpoint,
|
|
147
|
+
request: {
|
|
148
|
+
entityName: collection.singularName ?? collection.name,
|
|
149
|
+
entityDescription: collection.description,
|
|
150
|
+
// Flattened to dotted paths so the keys line up with the
|
|
151
|
+
// property map: the service is told about `seo.title`, so it
|
|
152
|
+
// has to be told the value of `seo.title` too, not of `seo`.
|
|
153
|
+
values: flatValues,
|
|
154
|
+
properties: currentProperties,
|
|
155
|
+
propertyKey: params.propertyKey,
|
|
156
|
+
propertyInstructions: params.propertyInstructions,
|
|
157
|
+
instructions: params.instructions
|
|
158
|
+
},
|
|
159
|
+
onDelta: (key, text) => {
|
|
160
|
+
upsertField(key, (existing) => existing
|
|
161
|
+
? { ...existing,
|
|
162
|
+
proposed: String(existing.proposed ?? "") + text }
|
|
163
|
+
: {
|
|
164
|
+
key,
|
|
165
|
+
label: labelFor(key),
|
|
166
|
+
currentValue: getValueInPath(params.values, key),
|
|
167
|
+
proposed: text,
|
|
168
|
+
pending: true,
|
|
169
|
+
selected: true
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
onValue: (key, value) => {
|
|
173
|
+
const coerced = coerceToProperty(value, getPropertyFromKey(currentProperties, key));
|
|
174
|
+
upsertField(key, (existing) => ({
|
|
175
|
+
key,
|
|
176
|
+
label: existing?.label ?? labelFor(key),
|
|
177
|
+
currentValue: existing?.currentValue ?? getValueInPath(params.values, key),
|
|
178
|
+
proposed: coerced,
|
|
179
|
+
pending: false,
|
|
180
|
+
// A row the operator already deselected mid-stream stays
|
|
181
|
+
// deselected when its final value lands.
|
|
182
|
+
selected: existing?.selected ?? true
|
|
183
|
+
}));
|
|
179
184
|
}
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
setSuggestions(prev => ({
|
|
184
|
-
...prev,
|
|
185
|
-
...Object.keys(updatedValues)
|
|
186
|
-
.reduce((acc, key) => {
|
|
187
|
-
const value = getValueInPath(formContext?.values, key);
|
|
188
|
-
const suggestion = updatedValues[key];
|
|
189
|
-
return {
|
|
190
|
-
...acc,
|
|
191
|
-
[key]: getAppendableSuggestion(suggestion, value) ?? suggestion
|
|
192
|
-
};
|
|
193
|
-
}, {})
|
|
194
|
-
}));
|
|
195
|
-
}, [properties, formContext]);
|
|
196
|
-
|
|
197
|
-
const displayNeededSubscriptionSnackbar = useCallback((projectId: unknown) => {
|
|
198
|
-
snackbarController.open({
|
|
199
|
-
type: "warning",
|
|
200
|
-
message: "A valid subscription is needed in order to use this function.",
|
|
201
|
-
autoHideDuration: 4000
|
|
202
|
-
});
|
|
203
|
-
}, [snackbarController]);
|
|
204
|
-
|
|
205
|
-
const editorAIController = useEditorAIController({ getAuthToken: authController.getAuthToken });
|
|
206
|
-
|
|
207
|
-
const clearAllSuggestions = useCallback(() => {
|
|
208
|
-
setSuggestions({});
|
|
209
|
-
}, []);
|
|
210
|
-
|
|
211
|
-
const enhance = useCallback(async (props: EnhanceParams<Record<string, unknown>>): Promise<EnhancedDataResult | null> => {
|
|
212
|
-
|
|
213
|
-
if (!authController.user) {
|
|
214
|
-
snackbarController.open({
|
|
215
|
-
type: "warning",
|
|
216
|
-
message: "You need to be logged in to enhance data"
|
|
217
185
|
});
|
|
218
|
-
return Promise.reject(new Error("Not logged in"));
|
|
219
|
-
}
|
|
220
186
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
187
|
+
setReview((current) => current && {
|
|
188
|
+
...current,
|
|
189
|
+
status: "ready",
|
|
190
|
+
fields: current.fields.map((f) => ({ ...f,
|
|
191
|
+
pending: false }))
|
|
192
|
+
});
|
|
193
|
+
} catch (e: unknown) {
|
|
194
|
+
const message = e instanceof Error ? e.message : "Autofill could not be completed";
|
|
195
|
+
// Kept in the review rather than fired into a snackbar: a run that
|
|
196
|
+
// produced three good fields and then failed should still let the
|
|
197
|
+
// operator apply the three.
|
|
198
|
+
setReview((current) => current && {
|
|
199
|
+
...current,
|
|
200
|
+
status: "failed",
|
|
201
|
+
error: message,
|
|
202
|
+
fields: current.fields.map((f) => ({ ...f,
|
|
203
|
+
pending: false }))
|
|
204
|
+
});
|
|
228
205
|
}
|
|
206
|
+
}, [collection, endpoint, upsertField]);
|
|
229
207
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
208
|
+
const toggleField = useCallback((key: string) => {
|
|
209
|
+
setReview((current) => current && {
|
|
210
|
+
...current,
|
|
211
|
+
fields: current.fields.map((f) => (f.key === key ? { ...f,
|
|
212
|
+
selected: !f.selected } : f))
|
|
213
|
+
});
|
|
214
|
+
}, []);
|
|
234
215
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const projectId = data?.projectId;
|
|
243
|
-
displayNeededSubscriptionSnackbar(projectId);
|
|
244
|
-
} else {
|
|
245
|
-
console.error("Enhance error", e);
|
|
246
|
-
}
|
|
247
|
-
reject(e);
|
|
248
|
-
enhancingInProgress.current = false;
|
|
249
|
-
}
|
|
216
|
+
const toggleAll = useCallback((selected: boolean) => {
|
|
217
|
+
setReview((current) => current && {
|
|
218
|
+
...current,
|
|
219
|
+
fields: current.fields.map((f) => ({ ...f,
|
|
220
|
+
selected }))
|
|
221
|
+
});
|
|
222
|
+
}, []);
|
|
250
223
|
|
|
251
|
-
|
|
252
|
-
enhanceDataAPIStream({
|
|
253
|
-
...props,
|
|
254
|
-
host,
|
|
255
|
-
apiKey,
|
|
256
|
-
properties,
|
|
257
|
-
path: resolvedPath,
|
|
258
|
-
entityName: collection.singularName ?? collection.name,
|
|
259
|
-
entityDescription: collection.description,
|
|
224
|
+
const dismissReview = useCallback(() => setReview(null), []);
|
|
260
225
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
appendValueDelta(propertyKey, partialValue);
|
|
269
|
-
},
|
|
270
|
-
onError,
|
|
271
|
-
onEnd: (result) => {
|
|
272
|
-
console.debug("de onEnd", result);
|
|
273
|
-
if (result.errors) {
|
|
274
|
-
result.errors.forEach((error) => {
|
|
275
|
-
snackbarController.open({
|
|
276
|
-
type: "warning",
|
|
277
|
-
message: error
|
|
278
|
-
})
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
if (Object.keys(result.suggestions).length === 0) {
|
|
282
|
-
snackbarController.open({
|
|
283
|
-
type: "info",
|
|
284
|
-
autoHideDuration: 1800,
|
|
285
|
-
message: "No fields were updated"
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
setLoadingSuggestions([]);
|
|
289
|
-
resolve(result);
|
|
290
|
-
enhancingInProgress.current = false;
|
|
291
|
-
}
|
|
292
|
-
}).catch(onError);
|
|
293
|
-
} catch (e: unknown) {
|
|
294
|
-
onError(e);
|
|
226
|
+
const applyReview = useCallback(() => {
|
|
227
|
+
setReview((current) => {
|
|
228
|
+
if (!current) return null;
|
|
229
|
+
for (const field of current.fields) {
|
|
230
|
+
if (!field.selected || field.pending) continue;
|
|
231
|
+
if (field.proposed === undefined || field.proposed === null) continue;
|
|
232
|
+
formContext?.setFieldValue(field.key, field.proposed);
|
|
295
233
|
}
|
|
234
|
+
return null;
|
|
296
235
|
});
|
|
297
|
-
}, [
|
|
298
|
-
authController, urlController, path, clearSuggestion, clearAllSuggestions,
|
|
299
|
-
properties, host, apiKey, collection, updateSuggestedValues, appendValueDelta, displayNeededSubscriptionSnackbar, snackbarController
|
|
300
|
-
]);
|
|
236
|
+
}, [formContext]);
|
|
301
237
|
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
});
|
|
311
|
-
}, [apiKey, authController.getAuthToken, host]);
|
|
238
|
+
const editorAIController = useEditorAIController({ endpoint });
|
|
239
|
+
|
|
240
|
+
const getSamplePrompts = useCallback(
|
|
241
|
+
(entityName: string, input?: string) => fetchPromptSuggestions({ endpoint,
|
|
242
|
+
entityName,
|
|
243
|
+
input }),
|
|
244
|
+
[endpoint]
|
|
245
|
+
);
|
|
312
246
|
|
|
313
247
|
const dataEnhancementController: DataEnhancementController = useMemo(() => ({
|
|
314
248
|
enabled,
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
249
|
+
review,
|
|
250
|
+
generate,
|
|
251
|
+
toggleField,
|
|
252
|
+
toggleAll,
|
|
253
|
+
applyReview,
|
|
254
|
+
dismissReview,
|
|
320
255
|
getSamplePrompts,
|
|
321
|
-
loadingSuggestions,
|
|
322
256
|
editorAIController
|
|
323
257
|
}), [
|
|
324
258
|
enabled,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
259
|
+
review,
|
|
260
|
+
generate,
|
|
261
|
+
toggleField,
|
|
262
|
+
toggleAll,
|
|
263
|
+
applyReview,
|
|
264
|
+
dismissReview,
|
|
330
265
|
getSamplePrompts,
|
|
331
|
-
loadingSuggestions,
|
|
332
266
|
editorAIController
|
|
333
267
|
]);
|
|
334
268
|
|
|
@@ -339,5 +273,3 @@ export function DataEnhancementControllerProvider({
|
|
|
339
273
|
</DataEnhancementControllerContext.Provider>
|
|
340
274
|
);
|
|
341
275
|
}
|
|
342
|
-
|
|
343
|
-
|