@sanity/assist 2.0.3 → 2.0.4
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 +233 -214
- package/dist/index.esm.js +23 -17
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +23 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/schemas/assistDocumentSchema.tsx +28 -24
- package/src/schemas/index.ts +4 -0
package/README.md
CHANGED
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
- [Develop \& test](#develop--test)
|
|
35
35
|
- [Release new version](#release-new-version)
|
|
36
36
|
|
|
37
|
-
|
|
38
37
|
## About Sanity AI Assist
|
|
39
38
|
|
|
40
39
|
Free your team to do more of what they’re great at (and less busywork) with the AI assistant that works with structured content. Attach reusable AI instructions to fields and documents to supercharge your editorial workflow.
|
|
@@ -68,14 +67,14 @@ This plugin requires `sanity` version `3.26` or greater.
|
|
|
68
67
|
In `sanity.config.ts`, add `assist` to the `plugins` array:
|
|
69
68
|
|
|
70
69
|
```tsx
|
|
71
|
-
import {
|
|
70
|
+
import {assist} from '@sanity/assist'
|
|
72
71
|
|
|
73
72
|
export default defineConfig({
|
|
74
73
|
/* other config */
|
|
75
74
|
plugins: [
|
|
76
75
|
/* other plugins */
|
|
77
|
-
assist()
|
|
78
|
-
]
|
|
76
|
+
assist(),
|
|
77
|
+
],
|
|
79
78
|
})
|
|
80
79
|
```
|
|
81
80
|
|
|
@@ -83,14 +82,14 @@ export default defineConfig({
|
|
|
83
82
|
|
|
84
83
|
After installing and adding the plugin and having the AI Assist feature enabled for your project and its datasets, you need to create a token for the plugin to access the AI Assist API. This needs to be done by a member of the project with token creation permissions (typically someone with an admin or developer role).
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
- Start the studio and open any document
|
|
86
|
+
- Click \*the sparkle icon\*\* (✨) in the document header near the close document X-button
|
|
87
|
+
- Then select **Manage instructions**
|
|
89
88
|
|
|
90
89
|
<img width="210" alt="The AI Assist document menu showing 'Manage instructions' highlighted" src="https://github.com/sanity-io/sanity/assets/835514/58c177ca-4530-4f44-abe0-4adcd9e11c8b">
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
- Selecting **Manage instructions** will open an inspector panel
|
|
92
|
+
- Click the **Enable AI assistance** button to create a token and enable AI Assist for everyone with access to the project
|
|
94
93
|
|
|
95
94
|
<img width="339" alt="The 'Enable Sanity AI Assist' button" src="https://github.com/sanity-io/sanity/assets/835514/38b81861-6a7c-49a2-a7c5-f46816d0c0a8">
|
|
96
95
|
|
|
@@ -118,21 +117,20 @@ themselves can edit can be changed by the instruction instance.
|
|
|
118
117
|
|
|
119
118
|
By default, most string, object, and array field types (including Portable Text!) have AI writing assistance enabled. Your assistant can write to all compatible fields that it detects.
|
|
120
119
|
|
|
121
|
-
|
|
122
120
|
### Disable AI Assist for a schema type
|
|
123
121
|
|
|
124
122
|
```tsx
|
|
125
123
|
// this will disable AI assistance wherever it is used,
|
|
126
124
|
// ie: as field, document, array types
|
|
127
125
|
defineType({
|
|
128
|
-
|
|
126
|
+
name: 'policy',
|
|
129
127
|
type: 'document',
|
|
130
128
|
options: {
|
|
131
|
-
aiAssist: {exclude: true}
|
|
132
|
-
|
|
129
|
+
aiAssist: {exclude: true},
|
|
130
|
+
},
|
|
133
131
|
fields: [
|
|
134
132
|
// ...
|
|
135
|
-
]
|
|
133
|
+
],
|
|
136
134
|
})
|
|
137
135
|
```
|
|
138
136
|
|
|
@@ -141,17 +139,17 @@ defineType({
|
|
|
141
139
|
```tsx
|
|
142
140
|
// this disables AI assistance only for the specific field
|
|
143
141
|
defineType({
|
|
144
|
-
|
|
142
|
+
name: 'product',
|
|
145
143
|
type: 'object',
|
|
146
144
|
fields: [
|
|
147
145
|
defineField({
|
|
148
146
|
name: 'sku',
|
|
149
147
|
type: 'string',
|
|
150
148
|
options: {
|
|
151
|
-
aiAssist: {exclude: true}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
]
|
|
149
|
+
aiAssist: {exclude: true},
|
|
150
|
+
},
|
|
151
|
+
}),
|
|
152
|
+
],
|
|
155
153
|
})
|
|
156
154
|
```
|
|
157
155
|
|
|
@@ -161,30 +159,31 @@ defineType({
|
|
|
161
159
|
// this disables AI assistance for the specific array member
|
|
162
160
|
// if all types in the `of` array are excluded, the array type is also considered excluded
|
|
163
161
|
defineType({
|
|
164
|
-
|
|
162
|
+
name: 'product',
|
|
165
163
|
type: 'array',
|
|
166
164
|
of: [
|
|
167
165
|
defineArrayMember({
|
|
168
166
|
type: 'customProduct',
|
|
169
167
|
options: {
|
|
170
|
-
aiAssist: {exclude: true}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
]
|
|
168
|
+
aiAssist: {exclude: true},
|
|
169
|
+
},
|
|
170
|
+
}),
|
|
171
|
+
],
|
|
174
172
|
})
|
|
175
173
|
```
|
|
176
174
|
|
|
177
175
|
### Unsupported types
|
|
178
176
|
|
|
179
177
|
The following types are not supported, and behave as excluded types:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
|
|
179
|
+
- [Number](https://www.sanity.io/docs/number-type)
|
|
180
|
+
- [Slug](https://www.sanity.io/docs/slug-type)
|
|
181
|
+
- [Url](https://www.sanity.io/docs/url-type)
|
|
182
|
+
- [Date](https://www.sanity.io/docs/date-type)
|
|
183
|
+
- [Datetime](https://www.sanity.io/docs/datetime-type)
|
|
184
|
+
- [Image](https://www.sanity.io/docs/image-type) (supported when image has custom fields)
|
|
185
|
+
- [File](https://www.sanity.io/docs/file-type) (never supported, even when file has custom fields)
|
|
186
|
+
- [Reference](https://www.sanity.io/docs/reference-type) (supported when configured with embeddingsIndex)
|
|
188
187
|
|
|
189
188
|
Fields with these types will not be changed by the assistant, do not have AI Assist actions, and cannot be referenced in instructions.
|
|
190
189
|
|
|
@@ -198,23 +197,25 @@ These fields can be written to by an instruction, as long as the field is non-hi
|
|
|
198
197
|
|
|
199
198
|
Fields with `hidden` or `readOnly` set to literal `true` will be skipped by AI Assist.
|
|
200
199
|
|
|
201
|
-
|
|
200
|
+
_Note_: An instruction will not re-evaluate these states during a run.
|
|
202
201
|
I.e., if an instruction makes a change during its execution that triggers another field to change its `hidden` or `readOnly` status,
|
|
203
202
|
the running instruction will still consider these as if in their original state.
|
|
204
203
|
|
|
205
|
-
If it is essential that AI Assist
|
|
204
|
+
If it is essential that AI Assist _never_ writes to a conditional field,
|
|
206
205
|
it should be marked with `options.aiAssist.exclude: true`.
|
|
207
206
|
|
|
208
207
|
### Reference support
|
|
209
208
|
|
|
210
209
|
#### Create an Embeddings-index
|
|
210
|
+
|
|
211
211
|
To enable AI assist for references, first, your project must have an existing [embeddings-index](https://www.sanity.io/docs/embeddings-index-api-overview)
|
|
212
212
|
that includes the document types it should be able to reference.
|
|
213
213
|
|
|
214
214
|
You can manage your indexes directly in the studio using the [Embeddings Index Dashboard plugin](https://github.com/sanity-io/embeddings-index-ui#embeddings-index-api-dashboard-for-sanity-studio).
|
|
215
215
|
|
|
216
216
|
#### Set schema options
|
|
217
|
-
|
|
217
|
+
|
|
218
|
+
Set `options.aiAssist.embeddingsIndex` for reference fields/types you want to enable reference instructions for.
|
|
218
219
|
Reference fields with this option set can have instructions attached and will be visited when running instructions for object fields and arrays.
|
|
219
220
|
|
|
220
221
|
AI Assist will use the embeddings-index, further filtered by the types allowed by the reference, to look up contextually relevant references.
|
|
@@ -229,10 +230,10 @@ defineField({
|
|
|
229
230
|
type: 'reference',
|
|
230
231
|
name: 'articleReference',
|
|
231
232
|
title: 'Article reference',
|
|
232
|
-
to: [
|
|
233
|
+
to: [{type: 'article'}],
|
|
233
234
|
options: {
|
|
234
235
|
aiAssist: {
|
|
235
|
-
embeddingsIndex: 'article-index'
|
|
236
|
+
embeddingsIndex: 'article-index',
|
|
236
237
|
},
|
|
237
238
|
},
|
|
238
239
|
})
|
|
@@ -251,59 +252,63 @@ If you have a very large schema (that is, many document and field types), it can
|
|
|
251
252
|
|
|
252
253
|
We recommend excluding any and all types that aren't likely to benefit from automated workflows. A quick win is typically to exclude array types. It can be a good idea to exclude most non-block types from Portable Text arrays. This will ensure that AI Assist outputs mostly formatted text.
|
|
253
254
|
|
|
254
|
-
It is also possible to exclude fields/types when creating an instruction. See [Field and type filters](#field-and-type-filters) for more.
|
|
255
|
+
It is also possible to exclude fields/types when creating an instruction. See [Field and type filters](#field-and-type-filters) for more.
|
|
255
256
|
|
|
256
257
|
## Included document types
|
|
257
258
|
|
|
258
|
-
This plugin adds an `AI Context` document type.
|
|
259
|
+
This plugin adds an `AI Context` document type.
|
|
259
260
|
|
|
260
261
|
If your Studio uses [Structure Builder](https://www.sanity.io/docs/structure-builder-introduction) to configure the studio structure,
|
|
261
262
|
you might have to add this document type to your structure.
|
|
262
263
|
|
|
263
264
|
The document type name can be imported from the plugin:
|
|
265
|
+
|
|
264
266
|
```ts
|
|
265
267
|
import {contextDocumentTypeName} from '@sanity/assist'
|
|
266
268
|
|
|
267
|
-
// put into structure in structure
|
|
269
|
+
// put into structure in structure
|
|
268
270
|
S.documentTypeListItem(contextDocumentTypeName)
|
|
269
271
|
```
|
|
270
272
|
|
|
271
273
|
## Field and type filters
|
|
272
274
|
|
|
273
275
|
When creating instructions for documents, object fields, array fields, or portable text fields, you can explicitly control what will be visited by AI Assist.
|
|
274
|
-
By default, the assistant will include all compatible fields and types.
|
|
276
|
+
By default, the assistant will include all compatible fields and types.
|
|
275
277
|
|
|
276
278
|
Opting out fields/types per instruction is done using the respective field/type filter checkboxes under the instruction.
|
|
277
|
-
When using these filters, it is not necessary to tell AI Assist what to include in the instruction text itself.
|
|
279
|
+
When using these filters, it is not necessary to tell AI Assist what to include in the instruction text itself.
|
|
278
280
|
|
|
279
281
|
Note that once the schema targeted by the instruction changes, the following behavior applies:
|
|
280
|
-
|
|
281
|
-
|
|
282
|
+
|
|
283
|
+
- instructions that include all fields or types will automatically also include the new fields or types
|
|
284
|
+
- instructions that have excluded one or more fields or types will NOT include the new fields or types
|
|
282
285
|
|
|
283
286
|
## Image description generation
|
|
287
|
+
|
|
284
288
|
AI Assist can optionally generate descriptions for images. This has to be enabled on an image-type/field,
|
|
285
289
|
by setting the `options.aiAssist.imageDescriptionField` on the image type, where `imageDescriptionField` is the field name of a
|
|
286
290
|
custom string-field on the image object:
|
|
287
291
|
|
|
288
292
|
```tsx
|
|
289
293
|
defineField({
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
294
|
+
type: 'image',
|
|
295
|
+
name: 'inlineImage',
|
|
296
|
+
title: 'Image',
|
|
297
|
+
fields: [
|
|
298
|
+
defineField({
|
|
299
|
+
type: 'string',
|
|
300
|
+
name: 'altText',
|
|
301
|
+
title: 'Alternative text',
|
|
302
|
+
}),
|
|
303
|
+
],
|
|
304
|
+
options: {
|
|
305
|
+
aiAssist: {
|
|
306
|
+
imageDescriptionField: 'altText',
|
|
304
307
|
},
|
|
308
|
+
},
|
|
305
309
|
})
|
|
306
310
|
```
|
|
311
|
+
|
|
307
312
|
This will add a **Generate image description** action to the configured field.
|
|
308
313
|
The **Generate image description** action will automatically run whenever the image changes.
|
|
309
314
|
|
|
@@ -311,15 +316,18 @@ The **Generate image description** action will automatically run whenever the im
|
|
|
311
316
|
Fields within array items are not supported.
|
|
312
317
|
|
|
313
318
|
## Image generation
|
|
319
|
+
|
|
314
320
|
<img width="600" alt="image" src="https://github.com/sanity-io/assist/assets/835514/c4de6791-f530-4cd1-b0c2-96ef988bc256">
|
|
315
321
|
|
|
316
322
|
AI Assist can generate assets for images configured with a prompt field.
|
|
317
323
|
|
|
318
|
-
An image is generated directly by using the **Generate image from prompt** instruction on the prompt field,
|
|
324
|
+
An image is generated directly by using the **Generate image from prompt** instruction on the prompt field,
|
|
319
325
|
or indirectly whenever the image prompt field is written to by an AI Assist instruction.
|
|
320
326
|
|
|
321
327
|
### Configure
|
|
328
|
+
|
|
322
329
|
To enable image generation for an image field, the image must:
|
|
330
|
+
|
|
323
331
|
- set `options.aiAssist.imageInstructionField` to a child-path relative to the image
|
|
324
332
|
- have a `string` or `text` field that corresponds to the `imageInstructionField` path
|
|
325
333
|
|
|
@@ -335,36 +343,38 @@ Use AI context documents to apply a reusable style guide to the prompt rewriting
|
|
|
335
343
|
#### Example
|
|
336
344
|
|
|
337
345
|
Given the following document schema
|
|
346
|
+
|
|
338
347
|
```ts
|
|
339
348
|
defineType({
|
|
340
349
|
type: 'document',
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
350
|
+
name: 'article',
|
|
351
|
+
fields: [
|
|
352
|
+
defineField({
|
|
353
|
+
type: 'image',
|
|
354
|
+
name: 'articleImage',
|
|
355
|
+
fields: [
|
|
356
|
+
defineField({
|
|
357
|
+
type: 'text',
|
|
358
|
+
name: 'imagePrompt',
|
|
359
|
+
title: 'Image prompt',
|
|
360
|
+
rows: 2,
|
|
361
|
+
}),
|
|
362
|
+
],
|
|
363
|
+
options: {
|
|
364
|
+
imagePromptField: 'imagePromptField',
|
|
365
|
+
},
|
|
366
|
+
}),
|
|
367
|
+
],
|
|
359
368
|
})
|
|
360
369
|
```
|
|
361
370
|
|
|
362
|
-
To directly generate an image based on the value in the prompt field,
|
|
371
|
+
To directly generate an image based on the value in the prompt field,
|
|
363
372
|
run the "Generate image from prompt" instruction that is automatically added.
|
|
364
373
|
|
|
365
374
|
For better image results or to ensure a consistent style, rewrite the prompt before generating the image:
|
|
366
375
|
|
|
367
376
|
### Example prompt expansion instruction
|
|
377
|
+
|
|
368
378
|
<img width="267" alt="image" src="https://github.com/sanity-io/assist/assets/835514/dabc6910-80d3-4a69-940f-49ac5cae9ade">
|
|
369
379
|
|
|
370
380
|
For better image results, use an instruction that expands the prompt to be more detailed.
|
|
@@ -388,7 +398,9 @@ The prompt to rewrite is:
|
|
|
388
398
|
```
|
|
389
399
|
|
|
390
400
|
The rules can be extracted into an AI Context document and reused in other instructions as needed. This approach can also be used to inform a reusable styleguide for image generation.
|
|
401
|
+
|
|
391
402
|
## Full document translation
|
|
403
|
+
|
|
392
404
|
<img width="250" alt="Translate document action" src="https://github.com/sanity-io/assist/assets/835514/932968ee-1a8c-4389-8822-338188f88b40">
|
|
393
405
|
|
|
394
406
|
AI assist offers full document translations, which is ideal for pairing with [@sanity/document-internationalization](https://github.com/sanity-io/document-internationalization).
|
|
@@ -420,27 +432,28 @@ If the studio is using [@sanity/document-internationalization](https://github.co
|
|
|
420
432
|
```ts
|
|
421
433
|
// This will add a "Translate document" instruction to all documents with a language field
|
|
422
434
|
assist({
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
435
|
+
translate: {
|
|
436
|
+
document: {
|
|
437
|
+
languageField: 'language',
|
|
438
|
+
},
|
|
439
|
+
},
|
|
428
440
|
})
|
|
429
441
|
```
|
|
430
442
|
|
|
431
443
|
```ts
|
|
432
444
|
// This will add a "Translate document" instruction only to the 'article' document type
|
|
433
445
|
assist({
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
446
|
+
translate: {
|
|
447
|
+
document: {
|
|
448
|
+
languageField: 'language',
|
|
449
|
+
documentTypes: ['article'],
|
|
450
|
+
},
|
|
451
|
+
},
|
|
440
452
|
})
|
|
441
453
|
```
|
|
442
454
|
|
|
443
455
|
**All configuration params**
|
|
456
|
+
|
|
444
457
|
```ts
|
|
445
458
|
assist({
|
|
446
459
|
translate: {
|
|
@@ -472,6 +485,7 @@ assist({
|
|
|
472
485
|
```
|
|
473
486
|
|
|
474
487
|
## Field level translations
|
|
488
|
+
|
|
475
489
|
<img width="250" alt="Translate fields action" src="https://github.com/sanity-io/assist/assets/835514/99819cd4-578e-43b2-8c70-8e39afff5f09">
|
|
476
490
|
|
|
477
491
|
<img width="250" alt="Translate fields dialog" src="https://github.com/sanity-io/assist/assets/835514/fe3d289c-49b6-46dd-ae2f-cd509a01534a">
|
|
@@ -489,20 +503,21 @@ AI Assist supports complex values, so language fields that hold nested objects,
|
|
|
489
503
|
When initiating translations, editors select a language to translate from and which languages to translate to. This means that AI Assist supports partial translations in cases where editors are responsible for only some languages in the document.
|
|
490
504
|
|
|
491
505
|
### Configure field translations
|
|
506
|
+
|
|
492
507
|
To enable field-level translations, set `translate.field.documentTypes` to an array with which document types should get field translations, and `translate.field.languages`
|
|
493
508
|
|
|
494
509
|
```ts
|
|
495
510
|
assist({
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
511
|
+
translate: {
|
|
512
|
+
field: {
|
|
513
|
+
documentTypes: ['article'],
|
|
514
|
+
languages: [
|
|
515
|
+
{id: 'en', title: 'English'},
|
|
516
|
+
{id: 'de', title: 'German'},
|
|
517
|
+
],
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
})
|
|
506
521
|
```
|
|
507
522
|
|
|
508
523
|
These documents will get a **Translate fields** instruction added to the document AI Assist dropdown.
|
|
@@ -511,76 +526,76 @@ Out of the box, this is sufficient config for document types using the `internat
|
|
|
511
526
|
|
|
512
527
|
It will also work without further config for object types named `locale*`, (e.g. `localeTitle`, `localeDescription`) with one field per language:
|
|
513
528
|
|
|
514
|
-
|
|
529
|
+
_Example locale object supported by default_
|
|
515
530
|
|
|
516
531
|
```ts
|
|
517
532
|
// Object type with name starting with 'locale', and one field per language language
|
|
518
533
|
defineType({
|
|
519
|
-
|
|
534
|
+
type: 'object',
|
|
520
535
|
name: 'localeString',
|
|
521
536
|
fields: [
|
|
522
|
-
|
|
537
|
+
defineField({
|
|
523
538
|
// these do not have to be string, could be any type
|
|
524
|
-
|
|
539
|
+
type: 'string',
|
|
525
540
|
name: 'en',
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
541
|
+
title: 'English',
|
|
542
|
+
}),
|
|
543
|
+
defineField({
|
|
544
|
+
type: 'string',
|
|
530
545
|
name: 'de',
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
546
|
+
title: 'German',
|
|
547
|
+
}),
|
|
548
|
+
],
|
|
534
549
|
})
|
|
535
550
|
```
|
|
536
551
|
|
|
537
552
|
**If your schema is not using either of these structures**, refer to the section on [Custom language fields](#custom-language-fields).
|
|
538
553
|
|
|
539
554
|
### Loading field languages
|
|
555
|
+
|
|
540
556
|
Languages must be an array of objects with an id and title.
|
|
541
557
|
|
|
542
558
|
```ts
|
|
543
559
|
assist({
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
560
|
+
translate: {
|
|
561
|
+
field: {
|
|
562
|
+
languages: [
|
|
563
|
+
{id: 'en', title: 'English'},
|
|
564
|
+
{id: 'de', title: 'German'},
|
|
565
|
+
],
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
})
|
|
553
569
|
```
|
|
554
570
|
|
|
555
571
|
Or an asynchronous function that returns an array of objects with an id and title.
|
|
556
572
|
|
|
557
573
|
```ts
|
|
558
574
|
assist({
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
},
|
|
575
|
+
translate: {
|
|
576
|
+
field: {
|
|
577
|
+
languages: async () => {
|
|
578
|
+
const response = await fetch('https://example.com/languages')
|
|
579
|
+
return response.json()
|
|
566
580
|
},
|
|
567
|
-
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
})
|
|
568
584
|
```
|
|
569
585
|
|
|
570
586
|
The async function contains a configured Sanity client in the first parameter, allowing you to store language options as documents. Your query should return an array of objects with an id and title.
|
|
571
587
|
|
|
572
|
-
|
|
573
588
|
```ts
|
|
574
589
|
assist({
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
},
|
|
590
|
+
translate: {
|
|
591
|
+
field: {
|
|
592
|
+
languages: async () => {
|
|
593
|
+
const response = await client.fetch(`*[_type == "language"]{ id, title }`)
|
|
594
|
+
return response
|
|
582
595
|
},
|
|
583
|
-
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
})
|
|
584
599
|
```
|
|
585
600
|
|
|
586
601
|
Additionally, you can "pick" fields from a document, to pass into the query. For example, if you have a concept of "Markets" where only certain language fields are required in certain markets.
|
|
@@ -589,24 +604,25 @@ In this example, each language document has an array of strings called markets t
|
|
|
589
604
|
|
|
590
605
|
```ts
|
|
591
606
|
assist({
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
},
|
|
597
|
-
languages: async (client, {market = ``}) => {
|
|
598
|
-
const response = await client.fetch(
|
|
599
|
-
`*[_type == "language" && $market in markets]{ id, title }`,
|
|
600
|
-
{market}
|
|
601
|
-
)
|
|
602
|
-
return response
|
|
603
|
-
},
|
|
604
|
-
},
|
|
607
|
+
translate: {
|
|
608
|
+
field: {
|
|
609
|
+
selectLanguageParams: {
|
|
610
|
+
market: 'market',
|
|
605
611
|
},
|
|
606
|
-
})
|
|
612
|
+
languages: async (client, {market = ``}) => {
|
|
613
|
+
const response = await client.fetch(
|
|
614
|
+
`*[_type == "language" && $market in markets]{ id, title }`,
|
|
615
|
+
{market}
|
|
616
|
+
)
|
|
617
|
+
return response
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
})
|
|
607
622
|
```
|
|
608
623
|
|
|
609
624
|
### Custom language fields
|
|
625
|
+
|
|
610
626
|
By providing a function to `translate.field.translationOutputs`, you have complete control over which fields belong to which language.
|
|
611
627
|
|
|
612
628
|
`translationOutputs` is used when an editor uses the **Translate fields** instruction.
|
|
@@ -615,10 +631,10 @@ It determines the relationships between document paths: Given a document path an
|
|
|
615
631
|
|
|
616
632
|
`translationOutputs` is invoked once per path in the document (limited to a depth of 6), with the following arguments:
|
|
617
633
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
634
|
+
- `documentMember` - the field or array item for a given path; contains the path and its `schemaType`
|
|
635
|
+
- `enclosingType` - the schema type of the parent holding the member
|
|
636
|
+
- `translateFromLanguageId` - the `languageId` for the language the users want to translate from
|
|
637
|
+
- `translateToLanguageIds` - all `languageId`s the user can translate to
|
|
622
638
|
|
|
623
639
|
The function should return a `TranslationOutput[]` array that contains all the paths where translations from `documentMember` (in the language received in `translateFromLanguageId`) should be output.
|
|
624
640
|
|
|
@@ -635,7 +651,7 @@ Given the following document:
|
|
|
635
651
|
```ts
|
|
636
652
|
{
|
|
637
653
|
titles: {
|
|
638
|
-
_type: 'languageObject',
|
|
654
|
+
_type: 'languageObject',
|
|
639
655
|
en: {
|
|
640
656
|
_type: 'titleObject',
|
|
641
657
|
title: 'Some title',
|
|
@@ -653,8 +669,8 @@ invoked multiple times.
|
|
|
653
669
|
|
|
654
670
|
The following parameters will be the same every invocation:
|
|
655
671
|
|
|
656
|
-
|
|
657
|
-
|
|
672
|
+
- `translateFromLanguageId` will be `'en'`
|
|
673
|
+
- `translateToLanguageIds` will be `['de']`
|
|
658
674
|
|
|
659
675
|
`documentMember` and `enclosingType` will change between each invocation, and take the following values:
|
|
660
676
|
|
|
@@ -669,22 +685,27 @@ To indicate that you want everything under `title.en` to be translated into `tit
|
|
|
669
685
|
The following function enables this:
|
|
670
686
|
|
|
671
687
|
```ts
|
|
672
|
-
function translationOutputs(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
688
|
+
function translationOutputs(
|
|
689
|
+
member,
|
|
690
|
+
enclosingType,
|
|
691
|
+
translateFromLanguageId,
|
|
692
|
+
translateToLanguageIds
|
|
693
|
+
) {
|
|
694
|
+
const parentIsLanguageWrapper =
|
|
695
|
+
enclosingType.jsonType === 'object' && enclosingType.name.startsWith('language')
|
|
696
|
+
|
|
697
|
+
if (parentIsLanguageWrapper && translateFromLanguageId === member.name) {
|
|
677
698
|
// [id: 'de', ]
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
// ignore other members
|
|
687
|
-
|
|
699
|
+
return translateToLanguageIds.map((translateToId) => ({
|
|
700
|
+
id: translateToId,
|
|
701
|
+
// in this example, member.path is 'titles.en'
|
|
702
|
+
// so this changes titles.en -> titles.de
|
|
703
|
+
outputPath: [...member.path.slice(0, -1), translateToId],
|
|
704
|
+
}))
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// ignore other members
|
|
708
|
+
return undefined
|
|
688
709
|
}
|
|
689
710
|
```
|
|
690
711
|
|
|
@@ -692,35 +713,36 @@ function translationOutputs(member, enclosingType, translateFromLanguageId, tran
|
|
|
692
713
|
|
|
693
714
|
```ts
|
|
694
715
|
assist({
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
},
|
|
716
|
+
translate: {
|
|
717
|
+
field: {
|
|
718
|
+
documentTypes: ['article'],
|
|
719
|
+
selectLanguageParams: {market: 'market'},
|
|
720
|
+
apiVersion: '2023-01-01',
|
|
721
|
+
languages: (client, {market}) => {
|
|
722
|
+
return client.fetch(`*[_type == "language" && $market in markets]{ id, title }`, {market})
|
|
723
|
+
},
|
|
724
|
+
translationOutputs: (member, enclosingType, fromLanguageId, toLanguageIds) => {
|
|
725
|
+
// When the document member is named the same as fromLangagueId
|
|
726
|
+
// and it is a field in a object with a name starting with "language"
|
|
727
|
+
// then we return the paths to all other sibling language fields (and their langauge id)
|
|
728
|
+
// It is ok that the member is an object, then all child fields will be translated
|
|
729
|
+
if (
|
|
730
|
+
translateFromLanguageId === member.name &&
|
|
731
|
+
enclosingType.jsonType === 'object' &&
|
|
732
|
+
enclosingType.name.startsWith('locale')
|
|
733
|
+
) {
|
|
734
|
+
return translateToLanguageIds.map((translateToId) => ({
|
|
735
|
+
id: translateToId,
|
|
736
|
+
//changes path.to.en -> path.to.de (for instance)
|
|
737
|
+
outputPath: [...member.path.slice(0, -1), translateToId],
|
|
738
|
+
}))
|
|
739
|
+
}
|
|
740
|
+
// all other member paths are skipped
|
|
741
|
+
return undefined
|
|
722
742
|
},
|
|
723
|
-
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
})
|
|
724
746
|
```
|
|
725
747
|
|
|
726
748
|
## Adding translation actions to fields
|
|
@@ -740,31 +762,29 @@ For document types configured for full document translations, a **Translate** ac
|
|
|
740
762
|
|
|
741
763
|
For document types configured for field translations, a **Translate fields...** action will be added. Running it will open a dialog with language selectors.
|
|
742
764
|
|
|
743
|
-
|
|
744
765
|
#### Example
|
|
745
766
|
|
|
746
767
|
```ts
|
|
747
768
|
defineField({
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
769
|
+
name: 'subtitle',
|
|
770
|
+
type: 'internationalizedArrayString',
|
|
771
|
+
title: 'Subtitle',
|
|
772
|
+
options: {
|
|
773
|
+
aiAssist: {
|
|
774
|
+
translateAction: true,
|
|
755
775
|
},
|
|
776
|
+
},
|
|
756
777
|
})
|
|
757
778
|
```
|
|
758
779
|
|
|
759
780
|
## Caveats
|
|
760
781
|
|
|
761
|
-
Large Language Models (LLMs) are a new technology. Constraints and limitations are still being explored,
|
|
782
|
+
Large Language Models (LLMs) are a new technology. Constraints and limitations are still being explored,
|
|
762
783
|
but some common caveats to the field that you may run into using AI Assist are:
|
|
763
784
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
785
|
+
- Limits to instruction length: Long instructions on deep content structures may exhaust model context
|
|
786
|
+
- Timeouts: To be able to write structured content, we're using the largest language models - long-running results may time out or intermittently fail
|
|
787
|
+
- Limited capacity: The underlying LLM APIs used by AI Assist are resource-constrained
|
|
768
788
|
|
|
769
789
|
## Third party sub-processors
|
|
770
790
|
|
|
@@ -782,7 +802,6 @@ with default configuration for build & watch scripts.
|
|
|
782
802
|
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
783
803
|
on how to run this plugin with hotreload in the studio.
|
|
784
804
|
|
|
785
|
-
|
|
786
805
|
### Release new version
|
|
787
806
|
|
|
788
807
|
Run ["CI & Release" workflow](https://github.com/sanity-io/sanity/actions/workflows/main.yml).
|