@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/assist",
3
- "version": "4.0.2",
3
+ "version": "4.2.0",
4
4
  "description": "You create the instructions; Sanity AI Assist does the rest.",
5
5
  "keywords": [
6
6
  "sanity",
@@ -40,7 +40,7 @@ export function ImageContextProvider(props: InputProps) {
40
40
  if (
41
41
  assetRef &&
42
42
  assistableDocumentId &&
43
- descriptionField &&
43
+ descriptionField?.updateOnImageChange &&
44
44
  assetRef !== assetRefState &&
45
45
  !isSyncing &&
46
46
  !isShowingOlderRevision &&
@@ -49,7 +49,7 @@ export function ImageContextProvider(props: InputProps) {
49
49
  setAssetRefState(assetRef)
50
50
  if (canUseAssist(status)) {
51
51
  generateCaption({
52
- path: pathToString([...path, descriptionField]),
52
+ path: pathToString([...path, descriptionField.path]),
53
53
  documentId: assistableDocumentId,
54
54
  })
55
55
  }
@@ -71,8 +71,8 @@ export function ImageContextProvider(props: InputProps) {
71
71
  const descriptionField = getDescriptionFieldOption(schemaType)
72
72
  const imageInstructionField = getImageInstructionFieldOption(schemaType)
73
73
  return {
74
- imageDescriptionPath: descriptionField
75
- ? pathToString([...path, descriptionField])
74
+ imageDescriptionPath: descriptionField?.path
75
+ ? pathToString([...path, descriptionField.path])
76
76
  : undefined,
77
77
  imageInstructionPath: imageInstructionField
78
78
  ? pathToString([...path, imageInstructionField])
@@ -0,0 +1,24 @@
1
+ import {packageName} from '../constants'
2
+ import {TranslateStyleguide, TranslateStyleguideContext} from '../translate/types'
3
+
4
+ export function validateStyleguide(styleguide: string | undefined) {
5
+ if (styleguide && styleguide.length > 2000) {
6
+ throw new Error(
7
+ `[${packageName}]: \`translate.styleguide\` value is too long. It must be 2000 characters or less, but was ${styleguide.length} characters`,
8
+ )
9
+ }
10
+ return styleguide
11
+ }
12
+
13
+ export function createStyleGuideResolver(
14
+ styleguide: TranslateStyleguide | undefined,
15
+ context: TranslateStyleguideContext,
16
+ ) {
17
+ return async () => {
18
+ if (typeof styleguide !== 'function') {
19
+ return styleguide
20
+ }
21
+ const styleguideResult = await styleguide(context)
22
+ return validateStyleguide(styleguideResult)
23
+ }
24
+ }
@@ -18,13 +18,23 @@ export function isImage(schemaType: SchemaType) {
18
18
  return isType(schemaType, 'image')
19
19
  }
20
20
 
21
- export function getDescriptionFieldOption(schemaType: SchemaType | undefined): string | undefined {
21
+ export function getDescriptionFieldOption(
22
+ schemaType: SchemaType | undefined,
23
+ ): {path: string; updateOnImageChange: boolean} | undefined {
22
24
  if (!schemaType) {
23
25
  return undefined
24
26
  }
25
27
  const descriptionField = (schemaType.options as ImageOptions)?.aiAssist?.imageDescriptionField
26
- if (descriptionField) {
27
- return descriptionField
28
+ if (typeof descriptionField === 'string') {
29
+ return {
30
+ path: descriptionField,
31
+ updateOnImageChange: true,
32
+ }
33
+ } else if (descriptionField) {
34
+ return {
35
+ path: descriptionField.path,
36
+ updateOnImageChange: descriptionField.updateOnImageChange ?? true,
37
+ }
28
38
  }
29
39
  return getDescriptionFieldOption(schemaType.type)
30
40
  }
package/src/plugin.tsx CHANGED
@@ -9,17 +9,18 @@ import {AssistInlineFormBlock} from './assistFormComponents/AssistInlineFormBloc
9
9
  import {AssistItem} from './assistFormComponents/AssistItem'
10
10
  import {assistInspector} from './assistInspector'
11
11
  import {AssistLayout} from './assistLayout/AssistLayout'
12
+ import {AssistConfig} from './assistTypes'
12
13
  import {ImageContextProvider} from './components/ImageContext'
13
14
  import {SafeValueInput} from './components/SafeValueInput'
14
15
  import {packageName} from './constants'
15
16
  import {assistFieldActions} from './fieldActions/assistFieldActions'
16
17
  import {isSchemaAssistEnabled} from './helpers/assistSupported'
18
+ import {validateStyleguide} from './helpers/styleguide'
17
19
  import {isImage} from './helpers/typeUtils'
18
20
  import {createAssistDocumentPresence} from './presence/AssistDocumentPresence'
19
21
  import {schemaTypes} from './schemas'
20
22
  import {TranslationConfig} from './translate/types'
21
23
  import {assistDocumentTypeName, AssistPreset} from './types'
22
- import {AssistConfig} from './assistTypes'
23
24
 
24
25
  export interface AssistPluginConfig {
25
26
  translate?: TranslationConfig
@@ -46,10 +47,8 @@ export const assist = definePlugin<AssistPluginConfig | void>((config) => {
46
47
  const maxPathDepth = configWithDefaults.assist?.maxPathDepth
47
48
  const temperature = configWithDefaults.assist?.temperature
48
49
 
49
- if (styleguide.length > 2000) {
50
- throw new Error(
51
- `[${packageName}]: \`translate.styleguide\` value is too long. It must be 2000 characters or less, was ${styleguide.length} characters`,
52
- )
50
+ if (typeof styleguide === 'string') {
51
+ validateStyleguide(styleguide)
53
52
  }
54
53
 
55
54
  if (maxPathDepth !== undefined && (maxPathDepth < 1 || maxPathDepth > 12)) {
@@ -90,7 +90,18 @@ declare module 'sanity' {
90
90
  * })
91
91
  * ```
92
92
  */
93
- imageDescriptionField?: string
93
+ imageDescriptionField?:
94
+ | string
95
+ | {
96
+ path: string
97
+ /**
98
+ * When updateOnImageChange is true (or undefined), whenever the
99
+ * image asset changes, imageDescriptionField will be regenerated.
100
+ *
101
+ * default: true
102
+ * */
103
+ updateOnImageChange?: boolean
104
+ }
94
105
  }
95
106
  }
96
107
  interface NumberOptions extends AssistOptions {}
@@ -19,7 +19,8 @@ import {
19
19
 
20
20
  import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
21
21
  import type {ConditionalMemberState} from '../helpers/conditionalMembers'
22
- import {useApiClient, useTranslate} from '../useApiClient'
22
+ import {createStyleGuideResolver} from '../helpers/styleguide'
23
+ import {API_VERSION_WITH_EXTENDED_TYPES, useApiClient, useTranslate} from '../useApiClient'
23
24
  import {getLanguageParams} from './getLanguageParams'
24
25
  import {getPreferredToFieldLanguages, setPreferredToFieldLanguages} from './languageStore'
25
26
  import {
@@ -67,6 +68,7 @@ function hasValuesToTranslate(
67
68
  // eslint-disable-next-line @typescript-eslint/ban-types
68
69
  export function FieldTranslationProvider(props: PropsWithChildren<{}>) {
69
70
  const {config: assistConfig} = useAiAssistanceConfig()
71
+
70
72
  const apiClient = useApiClient(assistConfig.__customApiClient)
71
73
  const styleguide = assistConfig.translate?.styleguide
72
74
  const config = assistConfig.translate?.field
@@ -87,7 +89,9 @@ export function FieldTranslationProvider(props: PropsWithChildren<{}>) {
87
89
  setLanguages(undefined)
88
90
  setFieldTranslationParams(undefined)
89
91
  }, [])
90
- const languageClient = useClient({apiVersion: config?.apiVersion ?? '2022-11-27'})
92
+ const languageClient = useClient({
93
+ apiVersion: config?.apiVersion ?? API_VERSION_WITH_EXTENDED_TYPES,
94
+ })
91
95
  const documentId = fieldTranslationParams?.document?._id
92
96
  const id = useId()
93
97
 
@@ -195,7 +199,12 @@ export function FieldTranslationProvider(props: PropsWithChildren<{}>) {
195
199
  runTranslate({
196
200
  documentId,
197
201
  translatePath,
198
- styleguide,
202
+ styleguide: createStyleGuideResolver(styleguide, {
203
+ client: languageClient,
204
+ documentId,
205
+ schemaType: fieldTranslationParams?.documentSchema,
206
+ translatePath,
207
+ }),
199
208
  fieldLanguageMap: fieldLanguageMaps.map((map) => ({
200
209
  ...map,
201
210
  // eslint-disable-next-line max-nested-callbacks
@@ -214,6 +223,8 @@ export function FieldTranslationProvider(props: PropsWithChildren<{}>) {
214
223
  toLanguages,
215
224
  fieldTranslationParams?.translatePath,
216
225
  fieldTranslationParams?.conditionalMembers,
226
+ fieldTranslationParams?.documentSchema,
227
+ languageClient,
217
228
  ])
218
229
 
219
230
  const runButton = (
@@ -2,12 +2,13 @@
2
2
  import {TranslateIcon} from '@sanity/icons'
3
3
  import {Box, Spinner} from '@sanity/ui'
4
4
  import {useMemo, useRef} from 'react'
5
- import type {
5
+ import {
6
6
  DocumentFieldAction,
7
7
  DocumentFieldActionGroup,
8
8
  DocumentFieldActionItem,
9
9
  DocumentFieldActionProps,
10
10
  ObjectSchemaType,
11
+ useClient,
11
12
  } from 'sanity'
12
13
  import {useDocumentPane} from 'sanity/structure'
13
14
 
@@ -15,8 +16,9 @@ import {useDraftDelayedTask} from '../assistDocument/RequestRunInstructionProvid
15
16
  import {useAiAssistanceConfig} from '../assistLayout/AiAssistanceConfigContext'
16
17
  import {isAssistSupported} from '../helpers/assistSupported'
17
18
  import {getConditionalMembers} from '../helpers/conditionalMembers'
19
+ import {createStyleGuideResolver} from '../helpers/styleguide'
18
20
  import type {AssistOptions} from '../schemas/typeDefExtensions'
19
- import {useApiClient, useTranslate} from '../useApiClient'
21
+ import {API_VERSION_WITH_EXTENDED_TYPES, useApiClient, useTranslate} from '../useApiClient'
20
22
  import {useFieldTranslation} from './FieldTranslationProvider'
21
23
 
22
24
  function node(node: DocumentFieldActionItem | DocumentFieldActionGroup) {
@@ -32,7 +34,7 @@ export const translateActions: DocumentFieldAction = {
32
34
  useAction(props: TranslateProps) {
33
35
  const {config, status} = useAiAssistanceConfig()
34
36
  const apiClient = useApiClient(config?.__customApiClient)
35
-
37
+ const client = useClient({apiVersion: API_VERSION_WITH_EXTENDED_TYPES})
36
38
  const {
37
39
  schemaType: fieldSchemaType,
38
40
  path,
@@ -99,7 +101,11 @@ export const translateActions: DocumentFieldAction = {
99
101
  translate({
100
102
  languagePath,
101
103
  translatePath: path,
102
- styleguide,
104
+ styleguide: createStyleGuideResolver(styleguide, {
105
+ client,
106
+ documentId,
107
+ schemaType: documentSchemaType,
108
+ }),
103
109
  documentId: documentId ?? '',
104
110
  conditionalMembers: formStateRef.current
105
111
  ? getConditionalMembers(formStateRef.current)
@@ -118,6 +124,8 @@ export const translateActions: DocumentFieldAction = {
118
124
  documentTranslationEnabled,
119
125
  path,
120
126
  readOnly,
127
+ client,
128
+ documentSchemaType,
121
129
  ])
122
130
  const fieldTranslate = useFieldTranslation()
123
131
  const openFieldTranslation = useDraftDelayedTask({
@@ -1,4 +1,4 @@
1
- import {Path, SanityClient, SchemaType} from 'sanity'
1
+ import {ObjectSchemaType, Path, SanityClient, SchemaType} from 'sanity'
2
2
 
3
3
  export interface Language {
4
4
  id: string
@@ -163,6 +163,20 @@ export interface DocumentTranslationConfig {
163
163
  documentTypes?: string[]
164
164
  }
165
165
 
166
+ export interface TranslateStyleguideContext {
167
+ documentId: string
168
+ schemaType: ObjectSchemaType
169
+ client: SanityClient
170
+ /**
171
+ * Only provided for field translations
172
+ */
173
+ translatePath?: Path
174
+ }
175
+
176
+ export type TranslateStyleguide =
177
+ | string
178
+ | ((context: TranslateStyleguideContext) => Promise<string>)
179
+
166
180
  export interface TranslationConfig {
167
181
  /**
168
182
  * Config for document types with fields in multiple languages in the same document.
@@ -176,6 +190,8 @@ export interface TranslationConfig {
176
190
  * A "style guide" that can be used to provide guidance on how to translate content.
177
191
  * Will be passed to the LLM - ergo this is only a guide and the model _may_ not
178
192
  * always follow it to the letter.
193
+ *
194
+ * When providing a function, consider caching the results of any async operation; it will invoked every time translate runs
179
195
  */
180
- styleguide?: string
196
+ styleguide?: TranslateStyleguide
181
197
  }
@@ -35,13 +35,13 @@ export interface TranslateRequest {
35
35
  documentId: string
36
36
  translatePath: Path
37
37
  languagePath?: string
38
- styleguide?: string
38
+ styleguide: () => Promise<string | undefined>
39
39
  fieldLanguageMap?: FieldLanguageMap[]
40
40
  conditionalMembers?: ConditionalMemberState[]
41
41
  }
42
42
 
43
43
  const basePath = '/assist/tasks/instruction'
44
- const API_VERSION_WITH_EXTENDED_TYPES = '2025-04-01'
44
+ export const API_VERSION_WITH_EXTENDED_TYPES = '2025-04-01'
45
45
 
46
46
  export function canUseAssist(status: InstructStatus | undefined) {
47
47
  return status?.enabled && status.initialized && status.validToken
@@ -73,8 +73,8 @@ export function useTranslate(apiClient: SanityClient) {
73
73
  }: TranslateRequest) => {
74
74
  setLoading(true)
75
75
 
76
- return apiClient
77
- .request({
76
+ async function run() {
77
+ return apiClient.request({
78
78
  method: 'POST',
79
79
  url: `/assist/tasks/translate/${apiClient.config().dataset}?projectId=${
80
80
  apiClient.config().projectId
@@ -83,7 +83,7 @@ export function useTranslate(apiClient: SanityClient) {
83
83
  documentId,
84
84
  types,
85
85
  languagePath,
86
- userStyleguide: styleguide,
86
+ userStyleguide: await styleguide(),
87
87
  fieldLanguageMap,
88
88
  conditionalMembers,
89
89
  translatePath:
@@ -91,6 +91,9 @@ export function useTranslate(apiClient: SanityClient) {
91
91
  userId: user?.id,
92
92
  },
93
93
  })
94
+ }
95
+
96
+ return run()
94
97
  .catch((e) => {
95
98
  toast.push({
96
99
  status: 'error',