@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 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?: string
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?: string
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?: string
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?: string
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
- return schemaType ? schemaType.options?.aiAssist?.imageDescriptionField || getDescriptionFieldOption(schemaType.type) : void 0;
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
- }) => (setLoading(!0), apiClient.request({
646
- method: "POST",
647
- url: `/assist/tasks/translate/${apiClient.config().dataset}?projectId=${apiClient.config().projectId}`,
648
- body: {
649
- documentId,
650
- types,
651
- languagePath,
652
- userStyleguide: styleguide,
653
- fieldLanguageMap,
654
- conditionalMembers,
655
- translatePath: translatePath.length === 0 ? documentRootKey : pathToString(translatePath),
656
- userId: user?.id
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
- }).catch((e) => {
659
- throw toast.push({
660
- status: "error",
661
- title: "Translate failed",
662
- description: e.message
663
- }), setLoading(!1), e;
664
- }).finally(() => {
665
- setTimeout(() => {
666
- setLoading(!1);
667
- }, 2e3);
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({ apiVersion: config?.apiVersion ?? "2022-11-27" }), documentId = fieldTranslationParams?.document?._id, id = useId(), selectFromLanguage = useCallback(
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.length > 2e3)
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
  );