@sanity/assist 4.0.2 → 4.2.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.
- package/README.md +25 -0
- package/dist/index.d.mts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.esm.js +79 -38
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +79 -38
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ImageContext.tsx +4 -4
- package/src/helpers/styleguide.ts +24 -0
- package/src/helpers/typeUtils.ts +13 -3
- package/src/plugin.tsx +4 -5
- package/src/schemas/typeDefExtensions.ts +12 -1
- package/src/translate/FieldTranslationProvider.tsx +14 -3
- package/src/translate/translateActions.tsx +12 -4
- package/src/translate/types.ts +18 -2
- package/src/useApiClient.ts +8 -5
package/README.md
CHANGED
|
@@ -407,6 +407,16 @@ The **Generate image description** action will automatically run whenever the im
|
|
|
407
407
|
`imageDescriptionField` can be a nested field, if the image has an object field, i.e. `imageDescriptionField: 'wrapper.altText'`.
|
|
408
408
|
Fields within array items are not supported.
|
|
409
409
|
|
|
410
|
+
By default, the caption field will regenerate whenever the image asset changes. To disable this behavior use the following configuration:
|
|
411
|
+
```ts
|
|
412
|
+
{
|
|
413
|
+
imageDescriptionField: {
|
|
414
|
+
path: 'wrapper.altText',
|
|
415
|
+
updateOnImageChange: false
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
410
420
|
## Image generation
|
|
411
421
|
|
|
412
422
|
<img width="600" alt="image" src="https://github.com/sanity-io/assist/assets/835514/c4de6791-f530-4cd1-b0c2-96ef988bc256">
|
|
@@ -915,6 +925,21 @@ the plugin will throw upon studio startup.
|
|
|
915
925
|
Note that this is currently only available on a global level - it can not be defined
|
|
916
926
|
per-field for now.
|
|
917
927
|
|
|
928
|
+
### Dynamic styleguide
|
|
929
|
+
|
|
930
|
+
As of 4.1.0 it is also possible to provide a styleguide async function.
|
|
931
|
+
The function is passed a context object with Sanity client and the current documentId and schemaType.
|
|
932
|
+
|
|
933
|
+
Consider caching the results: the function is invoked every time translate runs.
|
|
934
|
+
|
|
935
|
+
```ts
|
|
936
|
+
assist({
|
|
937
|
+
translate: {
|
|
938
|
+
styleguide: ({client, documentId, schemaType}) => client.fetch('* [_id=="styleguide.singleton"][0].styleguide')
|
|
939
|
+
},
|
|
940
|
+
})
|
|
941
|
+
```
|
|
942
|
+
|
|
918
943
|
## Caveats
|
|
919
944
|
|
|
920
945
|
Large Language Models (LLMs) are a new technology. Constraints and limitations are still being explored,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {CurrentUser} from 'sanity'
|
|
2
2
|
import {JSX as JSX_2} from 'react/jsx-runtime'
|
|
3
|
+
import {ObjectSchemaType} from 'sanity'
|
|
3
4
|
import {Path} from 'sanity'
|
|
4
5
|
import {Plugin as Plugin_2} from 'sanity'
|
|
5
6
|
import {PortableTextBlock} from '@portabletext/types'
|
|
@@ -339,6 +340,20 @@ declare type PromptTextBlock = Omit<
|
|
|
339
340
|
|
|
340
341
|
export declare function SchemaTypeTool(): JSX_2.Element
|
|
341
342
|
|
|
343
|
+
export declare type TranslateStyleguide =
|
|
344
|
+
| string
|
|
345
|
+
| ((context: TranslateStyleguideContext) => Promise<string>)
|
|
346
|
+
|
|
347
|
+
export declare interface TranslateStyleguideContext {
|
|
348
|
+
documentId: string
|
|
349
|
+
schemaType: ObjectSchemaType
|
|
350
|
+
client: SanityClient
|
|
351
|
+
/**
|
|
352
|
+
* Only provided for field translations
|
|
353
|
+
*/
|
|
354
|
+
translatePath?: Path
|
|
355
|
+
}
|
|
356
|
+
|
|
342
357
|
export declare interface TranslationConfig {
|
|
343
358
|
/**
|
|
344
359
|
* Config for document types with fields in multiple languages in the same document.
|
|
@@ -352,8 +367,10 @@ export declare interface TranslationConfig {
|
|
|
352
367
|
* A "style guide" that can be used to provide guidance on how to translate content.
|
|
353
368
|
* Will be passed to the LLM - ergo this is only a guide and the model _may_ not
|
|
354
369
|
* always follow it to the letter.
|
|
370
|
+
*
|
|
371
|
+
* When providing a function, consider caching the results of any async operation; it will invoked every time translate runs
|
|
355
372
|
*/
|
|
356
|
-
styleguide?:
|
|
373
|
+
styleguide?: TranslateStyleguide
|
|
357
374
|
}
|
|
358
375
|
|
|
359
376
|
export declare interface TranslationOutput {
|
|
@@ -457,7 +474,18 @@ declare module 'sanity' {
|
|
|
457
474
|
* })
|
|
458
475
|
* ```
|
|
459
476
|
*/
|
|
460
|
-
imageDescriptionField?:
|
|
477
|
+
imageDescriptionField?:
|
|
478
|
+
| string
|
|
479
|
+
| {
|
|
480
|
+
path: string
|
|
481
|
+
/**
|
|
482
|
+
* When updateOnImageChange is true (or undefined), whenever the
|
|
483
|
+
* image asset changes, imageDescriptionField will be regenerated.
|
|
484
|
+
*
|
|
485
|
+
* default: true
|
|
486
|
+
* */
|
|
487
|
+
updateOnImageChange?: boolean
|
|
488
|
+
}
|
|
461
489
|
}
|
|
462
490
|
}
|
|
463
491
|
interface NumberOptions extends AssistOptions {}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {CurrentUser} from 'sanity'
|
|
2
2
|
import {JSX as JSX_2} from 'react/jsx-runtime'
|
|
3
|
+
import {ObjectSchemaType} from 'sanity'
|
|
3
4
|
import {Path} from 'sanity'
|
|
4
5
|
import {Plugin as Plugin_2} from 'sanity'
|
|
5
6
|
import {PortableTextBlock} from '@portabletext/types'
|
|
@@ -339,6 +340,20 @@ declare type PromptTextBlock = Omit<
|
|
|
339
340
|
|
|
340
341
|
export declare function SchemaTypeTool(): JSX_2.Element
|
|
341
342
|
|
|
343
|
+
export declare type TranslateStyleguide =
|
|
344
|
+
| string
|
|
345
|
+
| ((context: TranslateStyleguideContext) => Promise<string>)
|
|
346
|
+
|
|
347
|
+
export declare interface TranslateStyleguideContext {
|
|
348
|
+
documentId: string
|
|
349
|
+
schemaType: ObjectSchemaType
|
|
350
|
+
client: SanityClient
|
|
351
|
+
/**
|
|
352
|
+
* Only provided for field translations
|
|
353
|
+
*/
|
|
354
|
+
translatePath?: Path
|
|
355
|
+
}
|
|
356
|
+
|
|
342
357
|
export declare interface TranslationConfig {
|
|
343
358
|
/**
|
|
344
359
|
* Config for document types with fields in multiple languages in the same document.
|
|
@@ -352,8 +367,10 @@ export declare interface TranslationConfig {
|
|
|
352
367
|
* A "style guide" that can be used to provide guidance on how to translate content.
|
|
353
368
|
* Will be passed to the LLM - ergo this is only a guide and the model _may_ not
|
|
354
369
|
* always follow it to the letter.
|
|
370
|
+
*
|
|
371
|
+
* When providing a function, consider caching the results of any async operation; it will invoked every time translate runs
|
|
355
372
|
*/
|
|
356
|
-
styleguide?:
|
|
373
|
+
styleguide?: TranslateStyleguide
|
|
357
374
|
}
|
|
358
375
|
|
|
359
376
|
export declare interface TranslationOutput {
|
|
@@ -457,7 +474,18 @@ declare module 'sanity' {
|
|
|
457
474
|
* })
|
|
458
475
|
* ```
|
|
459
476
|
*/
|
|
460
|
-
imageDescriptionField?:
|
|
477
|
+
imageDescriptionField?:
|
|
478
|
+
| string
|
|
479
|
+
| {
|
|
480
|
+
path: string
|
|
481
|
+
/**
|
|
482
|
+
* When updateOnImageChange is true (or undefined), whenever the
|
|
483
|
+
* image asset changes, imageDescriptionField will be regenerated.
|
|
484
|
+
*
|
|
485
|
+
* default: true
|
|
486
|
+
* */
|
|
487
|
+
updateOnImageChange?: boolean
|
|
488
|
+
}
|
|
461
489
|
}
|
|
462
490
|
}
|
|
463
491
|
interface NumberOptions extends AssistOptions {}
|
package/dist/index.esm.js
CHANGED
|
@@ -221,7 +221,16 @@ function isImage(schemaType) {
|
|
|
221
221
|
return isType(schemaType, "image");
|
|
222
222
|
}
|
|
223
223
|
function getDescriptionFieldOption(schemaType) {
|
|
224
|
-
|
|
224
|
+
if (!schemaType)
|
|
225
|
+
return;
|
|
226
|
+
const descriptionField = schemaType.options?.aiAssist?.imageDescriptionField;
|
|
227
|
+
return typeof descriptionField == "string" ? {
|
|
228
|
+
path: descriptionField,
|
|
229
|
+
updateOnImageChange: !0
|
|
230
|
+
} : descriptionField ? {
|
|
231
|
+
path: descriptionField.path,
|
|
232
|
+
updateOnImageChange: descriptionField.updateOnImageChange ?? !0
|
|
233
|
+
} : getDescriptionFieldOption(schemaType.type);
|
|
225
234
|
}
|
|
226
235
|
function getImageInstructionFieldOption(schemaType) {
|
|
227
236
|
return schemaType ? schemaType.options?.aiAssist?.imageInstructionField || getImageInstructionFieldOption(schemaType.type) : void 0;
|
|
@@ -642,30 +651,36 @@ function useTranslate(apiClient) {
|
|
|
642
651
|
translatePath,
|
|
643
652
|
fieldLanguageMap,
|
|
644
653
|
conditionalMembers
|
|
645
|
-
}) =>
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
654
|
+
}) => {
|
|
655
|
+
setLoading(!0);
|
|
656
|
+
async function run() {
|
|
657
|
+
return apiClient.request({
|
|
658
|
+
method: "POST",
|
|
659
|
+
url: `/assist/tasks/translate/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
|
|
660
|
+
body: {
|
|
661
|
+
documentId,
|
|
662
|
+
types,
|
|
663
|
+
languagePath,
|
|
664
|
+
userStyleguide: await styleguide(),
|
|
665
|
+
fieldLanguageMap,
|
|
666
|
+
conditionalMembers,
|
|
667
|
+
translatePath: translatePath.length === 0 ? documentRootKey : pathToString(translatePath),
|
|
668
|
+
userId: user?.id
|
|
669
|
+
}
|
|
670
|
+
});
|
|
657
671
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
672
|
+
return run().catch((e) => {
|
|
673
|
+
throw toast.push({
|
|
674
|
+
status: "error",
|
|
675
|
+
title: "Translate failed",
|
|
676
|
+
description: e.message
|
|
677
|
+
}), setLoading(!1), e;
|
|
678
|
+
}).finally(() => {
|
|
679
|
+
setTimeout(() => {
|
|
680
|
+
setLoading(!1);
|
|
681
|
+
}, 2e3);
|
|
682
|
+
});
|
|
683
|
+
},
|
|
669
684
|
[setLoading, apiClient, toast, user, types]
|
|
670
685
|
);
|
|
671
686
|
return useMemo(
|
|
@@ -2220,6 +2235,21 @@ function AssistConnectorsOverlay(props) {
|
|
|
2220
2235
|
DEBUG
|
|
2221
2236
|
] });
|
|
2222
2237
|
}
|
|
2238
|
+
function validateStyleguide(styleguide) {
|
|
2239
|
+
if (styleguide && styleguide.length > 2e3)
|
|
2240
|
+
throw new Error(
|
|
2241
|
+
`[${packageName}]: \`translate.styleguide\` value is too long. It must be 2000 characters or less, but was ${styleguide.length} characters`
|
|
2242
|
+
);
|
|
2243
|
+
return styleguide;
|
|
2244
|
+
}
|
|
2245
|
+
function createStyleGuideResolver(styleguide, context) {
|
|
2246
|
+
return async () => {
|
|
2247
|
+
if (typeof styleguide != "function")
|
|
2248
|
+
return styleguide;
|
|
2249
|
+
const styleguideResult = await styleguide(context);
|
|
2250
|
+
return validateStyleguide(styleguideResult);
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2223
2253
|
const getLanguageParams = (select, document2) => {
|
|
2224
2254
|
if (!select || !document2)
|
|
2225
2255
|
return {};
|
|
@@ -2341,7 +2371,9 @@ function hasValuesToTranslate(fieldLanguageMaps, fromLanguage, basePath2) {
|
|
|
2341
2371
|
function FieldTranslationProvider(props) {
|
|
2342
2372
|
const { config: assistConfig } = useAiAssistanceConfig(), apiClient = useApiClient(assistConfig.__customApiClient), styleguide = assistConfig.translate?.styleguide, config = assistConfig.translate?.field, { translate: runTranslate } = useTranslate(apiClient), [dialogOpen, setDialogOpen] = useState(!1), [fieldTranslationParams, setFieldTranslationParams] = useState(), [languages, setLanguages] = useState(), [fromLanguage, setFromLanguage] = useState(void 0), [toLanguages, setToLanguages] = useState(void 0), [fieldLanguageMaps, setFieldLanguageMaps] = useState(), close = useCallback(() => {
|
|
2343
2373
|
setDialogOpen(!1), setLanguages(void 0), setFieldTranslationParams(void 0);
|
|
2344
|
-
}, []), languageClient = useClient({
|
|
2374
|
+
}, []), languageClient = useClient({
|
|
2375
|
+
apiVersion: config?.apiVersion ?? API_VERSION_WITH_EXTENDED_TYPES
|
|
2376
|
+
}), documentId = fieldTranslationParams?.document?._id, id = useId(), selectFromLanguage = useCallback(
|
|
2345
2377
|
(from, languages2, params) => {
|
|
2346
2378
|
const { document: document2, documentSchema } = params ?? {};
|
|
2347
2379
|
if (setFromLanguage(from), !document2 || !documentSchema || !params || !languages2) {
|
|
@@ -2398,7 +2430,12 @@ function FieldTranslationProvider(props) {
|
|
|
2398
2430
|
fieldLanguageMaps && documentId && translatePath && runTranslate({
|
|
2399
2431
|
documentId,
|
|
2400
2432
|
translatePath,
|
|
2401
|
-
styleguide,
|
|
2433
|
+
styleguide: createStyleGuideResolver(styleguide, {
|
|
2434
|
+
client: languageClient,
|
|
2435
|
+
documentId,
|
|
2436
|
+
schemaType: fieldTranslationParams?.documentSchema,
|
|
2437
|
+
translatePath
|
|
2438
|
+
}),
|
|
2402
2439
|
fieldLanguageMap: fieldLanguageMaps.map((map) => ({
|
|
2403
2440
|
...map,
|
|
2404
2441
|
// eslint-disable-next-line max-nested-callbacks
|
|
@@ -2414,7 +2451,9 @@ function FieldTranslationProvider(props) {
|
|
|
2414
2451
|
close,
|
|
2415
2452
|
toLanguages,
|
|
2416
2453
|
fieldTranslationParams?.translatePath,
|
|
2417
|
-
fieldTranslationParams?.conditionalMembers
|
|
2454
|
+
fieldTranslationParams?.conditionalMembers,
|
|
2455
|
+
fieldTranslationParams?.documentSchema,
|
|
2456
|
+
languageClient
|
|
2418
2457
|
]), runButton = /* @__PURE__ */ jsx(
|
|
2419
2458
|
Button,
|
|
2420
2459
|
{
|
|
@@ -2544,8 +2583,8 @@ function ImageContextProvider(props) {
|
|
|
2544
2583
|
), isShowingOlderRevision = !!usePaneRouter().params?.rev;
|
|
2545
2584
|
useEffect(() => {
|
|
2546
2585
|
const descriptionField = getDescriptionFieldOption(schemaType);
|
|
2547
|
-
assetRef && assistableDocumentId && descriptionField && assetRef !== assetRefState && !isSyncing && !isShowingOlderRevision && !readOnly && (setAssetRefState(assetRef), canUseAssist(status) && generateCaption({
|
|
2548
|
-
path: pathToString([...path, descriptionField]),
|
|
2586
|
+
assetRef && assistableDocumentId && descriptionField?.updateOnImageChange && assetRef !== assetRefState && !isSyncing && !isShowingOlderRevision && !readOnly && (setAssetRefState(assetRef), canUseAssist(status) && generateCaption({
|
|
2587
|
+
path: pathToString([...path, descriptionField.path]),
|
|
2549
2588
|
documentId: assistableDocumentId
|
|
2550
2589
|
}));
|
|
2551
2590
|
}, [
|
|
@@ -2563,7 +2602,7 @@ function ImageContextProvider(props) {
|
|
|
2563
2602
|
const context = useMemo(() => {
|
|
2564
2603
|
const descriptionField = getDescriptionFieldOption(schemaType), imageInstructionField = getImageInstructionFieldOption(schemaType);
|
|
2565
2604
|
return {
|
|
2566
|
-
imageDescriptionPath: descriptionField ? pathToString([...path, descriptionField]) : void 0,
|
|
2605
|
+
imageDescriptionPath: descriptionField?.path ? pathToString([...path, descriptionField.path]) : void 0,
|
|
2567
2606
|
imageInstructionPath: imageInstructionField ? pathToString([...path, imageInstructionField]) : void 0,
|
|
2568
2607
|
assetRef
|
|
2569
2608
|
};
|
|
@@ -2602,7 +2641,7 @@ function useAssistSupported(path, schemaType) {
|
|
|
2602
2641
|
const translateActions = {
|
|
2603
2642
|
name: "sanity-assist-translate",
|
|
2604
2643
|
useAction(props) {
|
|
2605
|
-
const { config, status } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), {
|
|
2644
|
+
const { config, status } = useAiAssistanceConfig(), apiClient = useApiClient(config?.__customApiClient), client = useClient({ apiVersion: API_VERSION_WITH_EXTENDED_TYPES }), {
|
|
2606
2645
|
schemaType: fieldSchemaType,
|
|
2607
2646
|
path,
|
|
2608
2647
|
documentId,
|
|
@@ -2630,7 +2669,11 @@ const translateActions = {
|
|
|
2630
2669
|
translationApi.loading || !languagePath || !documentId || translate({
|
|
2631
2670
|
languagePath,
|
|
2632
2671
|
translatePath: path,
|
|
2633
|
-
styleguide,
|
|
2672
|
+
styleguide: createStyleGuideResolver(styleguide, {
|
|
2673
|
+
client,
|
|
2674
|
+
documentId,
|
|
2675
|
+
schemaType: documentSchemaType
|
|
2676
|
+
}),
|
|
2634
2677
|
documentId: documentId ?? "",
|
|
2635
2678
|
conditionalMembers: formStateRef.current ? getConditionalMembers(formStateRef.current) : []
|
|
2636
2679
|
});
|
|
@@ -2646,7 +2689,9 @@ const translateActions = {
|
|
|
2646
2689
|
translationApi.loading,
|
|
2647
2690
|
documentTranslationEnabled,
|
|
2648
2691
|
path,
|
|
2649
|
-
readOnly
|
|
2692
|
+
readOnly,
|
|
2693
|
+
client,
|
|
2694
|
+
documentSchemaType
|
|
2650
2695
|
]), fieldTranslate = useFieldTranslation(), openFieldTranslation = useDraftDelayedTask({
|
|
2651
2696
|
documentOnChange,
|
|
2652
2697
|
isDocAssistable: documentIsAssistable ?? !1,
|
|
@@ -3887,11 +3932,7 @@ const instructionForm = [
|
|
|
3887
3932
|
contextDocumentSchema
|
|
3888
3933
|
], assist = definePlugin((config) => {
|
|
3889
3934
|
const configWithDefaults = config ?? {}, styleguide = configWithDefaults.translate?.styleguide || "", maxPathDepth = configWithDefaults.assist?.maxPathDepth, temperature = configWithDefaults.assist?.temperature;
|
|
3890
|
-
if (styleguide
|
|
3891
|
-
throw new Error(
|
|
3892
|
-
`[${packageName}]: \`translate.styleguide\` value is too long. It must be 2000 characters or less, was ${styleguide.length} characters`
|
|
3893
|
-
);
|
|
3894
|
-
if (maxPathDepth !== void 0 && (maxPathDepth < 1 || maxPathDepth > 12))
|
|
3935
|
+
if (typeof styleguide == "string" && validateStyleguide(styleguide), maxPathDepth !== void 0 && (maxPathDepth < 1 || maxPathDepth > 12))
|
|
3895
3936
|
throw new Error(
|
|
3896
3937
|
`[${packageName}]: \`assist.maxPathDepth\` must be be in the range [1,12] inclusive, but was ${maxPathDepth}`
|
|
3897
3938
|
);
|