@sanity/assist 1.2.15 → 2.0.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/LICENSE +1 -1
- package/README.md +551 -30
- package/dist/index.cjs.mjs +1 -0
- package/dist/index.d.ts +253 -11
- package/dist/index.esm.js +2405 -392
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +2399 -385
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
- package/src/_lib/form/DocumentForm.tsx +3 -2
- package/src/_lib/form/constants.ts +1 -0
- package/src/assistDocument/AssistDocumentInput.tsx +24 -4
- package/src/assistDocument/RequestRunInstructionProvider.tsx +37 -21
- package/src/assistDocument/components/instruction/InstructionInput.tsx +5 -4
- package/src/assistDocument/components/instruction/InstructionOutputField.tsx +45 -0
- package/src/assistDocument/components/instruction/InstructionOutputInput.tsx +205 -0
- package/src/assistDocument/hooks/useStudioAssistDocument.ts +5 -32
- package/src/assistFormComponents/AssistField.tsx +11 -5
- package/src/assistFormComponents/AssistFormBlock.tsx +2 -3
- package/src/assistFormComponents/validation/listItem.tsx +2 -2
- package/src/assistInspector/AssistInspector.tsx +6 -0
- package/src/assistInspector/FieldAutocomplete.tsx +1 -0
- package/src/assistInspector/InstructionTaskHistoryButton.tsx +2 -3
- package/src/assistInspector/helpers.ts +9 -11
- package/src/assistLayout/AssistLayout.tsx +9 -9
- package/src/components/ImageContext.tsx +19 -9
- package/src/components/SafeValueInput.tsx +4 -1
- package/src/fieldActions/assistFieldActions.tsx +42 -13
- package/src/fieldActions/generateCaptionActions.tsx +2 -2
- package/src/fieldActions/generateImageActions.tsx +57 -0
- package/src/helpers/assistSupported.ts +10 -16
- package/src/helpers/conditionalMembers.test.ts +200 -0
- package/src/helpers/conditionalMembers.ts +127 -0
- package/src/helpers/typeUtils.ts +19 -5
- package/src/index.ts +3 -0
- package/src/plugin.tsx +14 -5
- package/src/presence/AssistAvatar.tsx +1 -1
- package/src/schemas/assistDocumentSchema.tsx +40 -1
- package/src/schemas/serialize/serializeSchema.test.ts +239 -8
- package/src/schemas/serialize/serializeSchema.ts +77 -10
- package/src/schemas/typeDefExtensions.ts +89 -5
- package/src/translate/FieldTranslationProvider.tsx +360 -0
- package/src/translate/getLanguageParams.ts +26 -0
- package/src/translate/languageStore.ts +18 -0
- package/src/translate/paths.test.ts +133 -0
- package/src/translate/paths.ts +175 -0
- package/src/translate/translateActions.tsx +188 -0
- package/src/translate/types.ts +160 -0
- package/src/types.ts +33 -12
- package/src/useApiClient.ts +130 -2
- package/src/assistLayout/AlphaMigration.tsx +0 -310
- package/src/legacy-types.ts +0 -72
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SanityDocument
|
|
1
|
+
import {SanityDocument} from 'sanity'
|
|
2
2
|
import {PortableTextBlock, PortableTextMarkDefinition, PortableTextSpan} from '@portabletext/types'
|
|
3
3
|
|
|
4
4
|
//id prefixes
|
|
@@ -26,6 +26,9 @@ export const fieldPresenceTypeName = 'sanity.assist.instructionTask.presence' as
|
|
|
26
26
|
export const assistSerializedTypeName = 'sanity.assist.serialized.type' as const
|
|
27
27
|
export const assistSerializedFieldTypeName = 'sanity.assist.serialized.field' as const
|
|
28
28
|
|
|
29
|
+
export const outputFieldTypeName = 'sanity.assist.output.field' as const
|
|
30
|
+
export const outputTypeTypeName = 'sanity.assist.output.type' as const
|
|
31
|
+
|
|
29
32
|
//url params
|
|
30
33
|
export const inspectParam = 'inspect' as const
|
|
31
34
|
export const fieldPathParam = 'pathKey' as const
|
|
@@ -34,27 +37,29 @@ export const instructionParam = 'instruction' as const
|
|
|
34
37
|
// other constants
|
|
35
38
|
export const documentRootKey = '<document>'
|
|
36
39
|
|
|
37
|
-
export
|
|
40
|
+
export type SerializedSchemaMember = Omit<SerializedSchemaType, 'name' | '_type'> & {
|
|
38
41
|
_type?: typeof assistSerializedFieldTypeName
|
|
39
|
-
|
|
40
|
-
name: string
|
|
41
|
-
title?: string
|
|
42
|
-
values?: string[]
|
|
43
|
-
of?: SerializedSchemaMember[]
|
|
44
|
-
to?: {type: string}[]
|
|
42
|
+
name?: string
|
|
45
43
|
}
|
|
46
44
|
|
|
47
45
|
export interface SerializedSchemaType {
|
|
48
46
|
_type?: typeof assistSerializedTypeName
|
|
49
47
|
_id?: string
|
|
50
48
|
type: string
|
|
49
|
+
name: string
|
|
51
50
|
title?: string
|
|
52
|
-
name?: string
|
|
53
51
|
fields?: SerializedSchemaMember[]
|
|
54
|
-
of?:
|
|
55
|
-
to?:
|
|
52
|
+
of?: SerializedSchemaMember[]
|
|
53
|
+
to?: SerializedSchemaMember[]
|
|
54
|
+
annotations?: SerializedSchemaMember[]
|
|
55
|
+
inlineOf?: SerializedSchemaMember[]
|
|
56
|
+
values?: string[] | {value: string; title?: string}[]
|
|
57
|
+
hidden?: boolean | 'function'
|
|
58
|
+
readOnly?: boolean | 'function'
|
|
56
59
|
options?: {
|
|
60
|
+
/** equivalent to options.aiAssist.imageDescriptionField - not renamed in the api for backwards compatability */
|
|
57
61
|
imagePromptField?: string
|
|
62
|
+
embeddingsIndex?: string
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -149,10 +154,10 @@ export interface StudioInstruction {
|
|
|
149
154
|
userId?: string
|
|
150
155
|
title?: string
|
|
151
156
|
placeholder?: string
|
|
157
|
+
output?: (OutputFieldItem | OutputTypeItem)[]
|
|
152
158
|
|
|
153
159
|
//added after query / synthetic fields
|
|
154
160
|
tasks?: InstructionTask[]
|
|
155
|
-
validation?: ValidationMarker[]
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
export interface AssistTasksStatus {
|
|
@@ -166,3 +171,19 @@ export interface AssistInspectorRouteParams {
|
|
|
166
171
|
[fieldPathParam]?: string
|
|
167
172
|
[instructionParam]?: string
|
|
168
173
|
}
|
|
174
|
+
|
|
175
|
+
export interface OutputFieldItem {
|
|
176
|
+
_type: typeof outputFieldTypeName
|
|
177
|
+
_key: string
|
|
178
|
+
//path relative to the field the instruction is for (same as _key)
|
|
179
|
+
relativePath?: string
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface OutputTypeItem {
|
|
183
|
+
_type: typeof outputTypeTypeName
|
|
184
|
+
_key: string
|
|
185
|
+
/* array item type name */
|
|
186
|
+
type?: string
|
|
187
|
+
//path relative to the array-field the instruction is for, can be empty string (the array itself, same as _key)
|
|
188
|
+
relativePath?: string
|
|
189
|
+
}
|
package/src/useApiClient.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {useClient, useCurrentUser, useSchema} from 'sanity'
|
|
1
|
+
import {Path, pathToString, useClient, useCurrentUser, useSchema} from 'sanity'
|
|
2
2
|
import {useCallback, useMemo, useState} from 'react'
|
|
3
3
|
import {serializeSchema} from './schemas/serialize/serializeSchema'
|
|
4
4
|
import {useToast} from '@sanity/ui'
|
|
5
5
|
import {SanityClient} from '@sanity/client'
|
|
6
|
+
import {FieldLanguageMap} from './translate/paths'
|
|
7
|
+
import {documentRootKey} from './types'
|
|
8
|
+
import {ConditionalMemberState} from './helpers/conditionalMembers'
|
|
6
9
|
|
|
7
10
|
export interface UserTextInstance {
|
|
8
11
|
blockKey: string
|
|
@@ -17,6 +20,7 @@ export interface RunInstructionRequest {
|
|
|
17
20
|
instructionKey: string
|
|
18
21
|
userId?: string
|
|
19
22
|
userTexts?: UserTextInstance[]
|
|
23
|
+
conditionalMembers?: ConditionalMemberState[]
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
export interface InstructStatus {
|
|
@@ -25,6 +29,14 @@ export interface InstructStatus {
|
|
|
25
29
|
validToken: boolean
|
|
26
30
|
}
|
|
27
31
|
|
|
32
|
+
export interface TranslateRequest {
|
|
33
|
+
documentId: string
|
|
34
|
+
translatePath: Path
|
|
35
|
+
languagePath?: string
|
|
36
|
+
fieldLanguageMap?: FieldLanguageMap[]
|
|
37
|
+
conditionalMembers?: ConditionalMemberState[]
|
|
38
|
+
}
|
|
39
|
+
|
|
28
40
|
const basePath = '/assist/tasks/instruction'
|
|
29
41
|
|
|
30
42
|
export function useApiClient(customApiClient?: (defaultClient: SanityClient) => SanityClient) {
|
|
@@ -35,6 +47,69 @@ export function useApiClient(customApiClient?: (defaultClient: SanityClient) =>
|
|
|
35
47
|
)
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
export function useTranslate(apiClient: SanityClient) {
|
|
51
|
+
const [loading, setLoading] = useState(false)
|
|
52
|
+
const user = useCurrentUser()
|
|
53
|
+
const schema = useSchema()
|
|
54
|
+
const types = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
|
|
55
|
+
const toast = useToast()
|
|
56
|
+
|
|
57
|
+
const translate = useCallback(
|
|
58
|
+
({
|
|
59
|
+
documentId,
|
|
60
|
+
languagePath,
|
|
61
|
+
translatePath,
|
|
62
|
+
fieldLanguageMap,
|
|
63
|
+
conditionalMembers,
|
|
64
|
+
}: TranslateRequest) => {
|
|
65
|
+
setLoading(true)
|
|
66
|
+
|
|
67
|
+
return apiClient
|
|
68
|
+
.request({
|
|
69
|
+
method: 'POST',
|
|
70
|
+
url: `/assist/tasks/translate/${apiClient.config().dataset}?projectId=${
|
|
71
|
+
apiClient.config().projectId
|
|
72
|
+
}`,
|
|
73
|
+
body: {
|
|
74
|
+
documentId,
|
|
75
|
+
types,
|
|
76
|
+
languagePath,
|
|
77
|
+
fieldLanguageMap,
|
|
78
|
+
conditionalMembers,
|
|
79
|
+
translatePath:
|
|
80
|
+
translatePath.length === 0 ? documentRootKey : pathToString(translatePath),
|
|
81
|
+
userId: user?.id,
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
.catch((e) => {
|
|
85
|
+
toast.push({
|
|
86
|
+
status: 'error',
|
|
87
|
+
title: 'Translate failed',
|
|
88
|
+
description: e.message,
|
|
89
|
+
})
|
|
90
|
+
setLoading(false)
|
|
91
|
+
throw e
|
|
92
|
+
})
|
|
93
|
+
.finally(() => {
|
|
94
|
+
// adding some artificial delay here
|
|
95
|
+
// server responds with 201 then proceeds; we dont need to allow spamming the button
|
|
96
|
+
setTimeout(() => {
|
|
97
|
+
setLoading(false)
|
|
98
|
+
}, 2000)
|
|
99
|
+
})
|
|
100
|
+
},
|
|
101
|
+
[setLoading, apiClient, toast, user, types]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return useMemo(
|
|
105
|
+
() => ({
|
|
106
|
+
translate,
|
|
107
|
+
loading,
|
|
108
|
+
}),
|
|
109
|
+
[translate, loading]
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
38
113
|
export function useGenerateCaption(apiClient: SanityClient) {
|
|
39
114
|
const [loading, setLoading] = useState(false)
|
|
40
115
|
const user = useCurrentUser()
|
|
@@ -62,7 +137,7 @@ export function useGenerateCaption(apiClient: SanityClient) {
|
|
|
62
137
|
.catch((e) => {
|
|
63
138
|
toast.push({
|
|
64
139
|
status: 'error',
|
|
65
|
-
title: 'Generate
|
|
140
|
+
title: 'Generate image description failed',
|
|
66
141
|
description: e.message,
|
|
67
142
|
})
|
|
68
143
|
setLoading(false)
|
|
@@ -88,6 +163,59 @@ export function useGenerateCaption(apiClient: SanityClient) {
|
|
|
88
163
|
)
|
|
89
164
|
}
|
|
90
165
|
|
|
166
|
+
export function useGenerateImage(apiClient: SanityClient) {
|
|
167
|
+
const [loading, setLoading] = useState(false)
|
|
168
|
+
const user = useCurrentUser()
|
|
169
|
+
const schema = useSchema()
|
|
170
|
+
const types = useMemo(() => serializeSchema(schema, {leanFormat: true}), [schema])
|
|
171
|
+
const toast = useToast()
|
|
172
|
+
|
|
173
|
+
const generateImage = useCallback(
|
|
174
|
+
({path, documentId}: {path: string; documentId: string}) => {
|
|
175
|
+
setLoading(true)
|
|
176
|
+
|
|
177
|
+
return apiClient
|
|
178
|
+
.request({
|
|
179
|
+
method: 'POST',
|
|
180
|
+
url: `/assist/tasks/generate-image/${apiClient.config().dataset}?projectId=${
|
|
181
|
+
apiClient.config().projectId
|
|
182
|
+
}`,
|
|
183
|
+
body: {
|
|
184
|
+
path,
|
|
185
|
+
documentId,
|
|
186
|
+
types,
|
|
187
|
+
userId: user?.id,
|
|
188
|
+
},
|
|
189
|
+
})
|
|
190
|
+
.catch((e) => {
|
|
191
|
+
toast.push({
|
|
192
|
+
status: 'error',
|
|
193
|
+
title: 'Generate image from prompt failed',
|
|
194
|
+
description: e.message,
|
|
195
|
+
})
|
|
196
|
+
setLoading(false)
|
|
197
|
+
throw e
|
|
198
|
+
})
|
|
199
|
+
.finally(() => {
|
|
200
|
+
// adding some artificial delay here
|
|
201
|
+
// server responds with 201 then proceeds; we dont need to allow spamming the button
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
setLoading(false)
|
|
204
|
+
}, 2000)
|
|
205
|
+
})
|
|
206
|
+
},
|
|
207
|
+
[setLoading, apiClient, toast, user, types]
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
return useMemo(
|
|
211
|
+
() => ({
|
|
212
|
+
generateImage,
|
|
213
|
+
loading,
|
|
214
|
+
}),
|
|
215
|
+
[generateImage, loading]
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
91
219
|
export function useGetInstructStatus(apiClient: SanityClient) {
|
|
92
220
|
const [loading, setLoading] = useState(true)
|
|
93
221
|
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
import {SanityClient, SanityDocument, useClient} from 'sanity'
|
|
2
|
-
import {useCallback, useEffect, useState} from 'react'
|
|
3
|
-
import {Box, Button, Card, Dialog, Spinner, Stack, Text, useToast} from '@sanity/ui'
|
|
4
|
-
import {CheckmarkIcon} from '@sanity/icons'
|
|
5
|
-
import {
|
|
6
|
-
LegacyAssistDocument,
|
|
7
|
-
legacyAssistDocumentIdPrefix,
|
|
8
|
-
legacyAssistDocumentTypeName,
|
|
9
|
-
legacyAssistStatusDocumentTypeName,
|
|
10
|
-
LegacyContextBlock,
|
|
11
|
-
legacyContextDocumentTypeName,
|
|
12
|
-
LegacyFieldRef,
|
|
13
|
-
LegacyPromptBlock,
|
|
14
|
-
LegacyPromptTextBlock,
|
|
15
|
-
LegacyUserInputBlock,
|
|
16
|
-
} from '../legacy-types'
|
|
17
|
-
import {assistDocumentId} from '../helpers/ids'
|
|
18
|
-
import {
|
|
19
|
-
assistDocumentIdPrefix,
|
|
20
|
-
assistDocumentTypeName,
|
|
21
|
-
AssistField,
|
|
22
|
-
assistFieldTypeName,
|
|
23
|
-
contextDocumentTypeName,
|
|
24
|
-
fieldReferenceTypeName,
|
|
25
|
-
InlinePromptBlock,
|
|
26
|
-
instructionContextTypeName,
|
|
27
|
-
instructionTypeName,
|
|
28
|
-
PromptBlock,
|
|
29
|
-
userInputTypeName,
|
|
30
|
-
} from '../types'
|
|
31
|
-
import {PortableTextSpan} from '@portabletext/types'
|
|
32
|
-
import {pluginTitle} from '../constants'
|
|
33
|
-
|
|
34
|
-
const NO_ASSIST_DOCS: LegacyAssistDocument[] = []
|
|
35
|
-
const NO_CONTEXT_DOCS: SanityDocument[] = []
|
|
36
|
-
const NO_IDS: string[] = []
|
|
37
|
-
|
|
38
|
-
interface MigratedContextDoc {
|
|
39
|
-
_id: string
|
|
40
|
-
_alphaId: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type Task = (subtaskProgress: (percentage: number) => void) => Promise<void>
|
|
44
|
-
|
|
45
|
-
export function AlphaMigration() {
|
|
46
|
-
const [alphaAssistDocs, setAlphaAssistDocs] = useState(NO_ASSIST_DOCS)
|
|
47
|
-
const [contextDocs, setContextDocs] = useState(NO_CONTEXT_DOCS)
|
|
48
|
-
const [staleStatusDocIds, setStaleStatusDocs] = useState(NO_IDS)
|
|
49
|
-
const [error, setError] = useState<Error | undefined>(undefined)
|
|
50
|
-
const [progress, setProgress] = useState<number | undefined>(undefined)
|
|
51
|
-
const toast = useToast()
|
|
52
|
-
const client = useClient({apiVersion: '2023-06-01'})
|
|
53
|
-
|
|
54
|
-
useEffect(() => {
|
|
55
|
-
let canUpdate = true
|
|
56
|
-
client
|
|
57
|
-
.fetch<{
|
|
58
|
-
assistDocs?: LegacyAssistDocument[]
|
|
59
|
-
staleStatusDocIds?: string[]
|
|
60
|
-
contextDocs?: SanityDocument[]
|
|
61
|
-
}>(
|
|
62
|
-
`
|
|
63
|
-
{
|
|
64
|
-
"assistDocs": *[_type=="${legacyAssistDocumentTypeName}"],
|
|
65
|
-
"staleStatusDocIds": *[_type=="${legacyAssistStatusDocumentTypeName}"]._id,
|
|
66
|
-
"contextDocs": *[_type=="${legacyContextDocumentTypeName}"],
|
|
67
|
-
}
|
|
68
|
-
`
|
|
69
|
-
)
|
|
70
|
-
.then((result) => {
|
|
71
|
-
if (!canUpdate || !result) {
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
setAlphaAssistDocs(result?.assistDocs ?? NO_ASSIST_DOCS)
|
|
75
|
-
setStaleStatusDocs(result?.staleStatusDocIds ?? NO_IDS)
|
|
76
|
-
setContextDocs(result?.contextDocs ?? NO_CONTEXT_DOCS)
|
|
77
|
-
})
|
|
78
|
-
return () => {
|
|
79
|
-
canUpdate = false
|
|
80
|
-
}
|
|
81
|
-
}, [client, setAlphaAssistDocs, setStaleStatusDocs, setContextDocs])
|
|
82
|
-
|
|
83
|
-
const convert = useCallback(async () => {
|
|
84
|
-
try {
|
|
85
|
-
setProgress(0.0001)
|
|
86
|
-
|
|
87
|
-
const tasks: Task[] = [
|
|
88
|
-
() => convertContextDocs(client, contextDocs),
|
|
89
|
-
(subtaskProgress) => deleteDocs(client, staleStatusDocIds, subtaskProgress),
|
|
90
|
-
(subtaskProgress) => convertDocs(client, alphaAssistDocs, subtaskProgress),
|
|
91
|
-
(subtaskProgress) =>
|
|
92
|
-
deleteDocs(
|
|
93
|
-
client,
|
|
94
|
-
contextDocs.map((d) => d._id),
|
|
95
|
-
subtaskProgress
|
|
96
|
-
),
|
|
97
|
-
]
|
|
98
|
-
|
|
99
|
-
const taskSize = 1 / tasks.length
|
|
100
|
-
for (let i = 0; i < tasks.length; i++) {
|
|
101
|
-
const startProgress = i / tasks.length
|
|
102
|
-
await tasks[i]((subProgress) => setProgress(startProgress + subProgress * taskSize))
|
|
103
|
-
setProgress((i + 1) / tasks.length)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
setProgress(1)
|
|
107
|
-
setAlphaAssistDocs(NO_ASSIST_DOCS)
|
|
108
|
-
setContextDocs(NO_CONTEXT_DOCS)
|
|
109
|
-
setStaleStatusDocs(NO_IDS)
|
|
110
|
-
toast.push({
|
|
111
|
-
title: `Converted instructions to new format.`,
|
|
112
|
-
status: 'success',
|
|
113
|
-
closable: true,
|
|
114
|
-
})
|
|
115
|
-
} catch (e: any) {
|
|
116
|
-
console.error(e)
|
|
117
|
-
toast.push({
|
|
118
|
-
title: `An error occurred`,
|
|
119
|
-
status: 'error',
|
|
120
|
-
closable: true,
|
|
121
|
-
})
|
|
122
|
-
setError(e)
|
|
123
|
-
setProgress(undefined)
|
|
124
|
-
}
|
|
125
|
-
}, [contextDocs, client, alphaAssistDocs, staleStatusDocIds, setProgress, toast])
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
(alphaAssistDocs.length || staleStatusDocIds.length || contextDocs.length) &&
|
|
129
|
-
(!progress || progress < 1)
|
|
130
|
-
) {
|
|
131
|
-
return (
|
|
132
|
-
<Dialog id="outdated-assist-docs" header={pluginTitle}>
|
|
133
|
-
<Card padding={3}>
|
|
134
|
-
<Stack space={4} style={{maxWidth: 500}}>
|
|
135
|
-
<Text size={1}>
|
|
136
|
-
It seems like this workspace contains documents from an{' '}
|
|
137
|
-
<strong>older version of {pluginTitle}</strong>.
|
|
138
|
-
</Text>
|
|
139
|
-
<Text size={1}>Cleanup is required.</Text>
|
|
140
|
-
{error ? (
|
|
141
|
-
<Card padding={2} tone="critical" border>
|
|
142
|
-
<Text size={1}>An error occurred. See console for details.</Text>{' '}
|
|
143
|
-
</Card>
|
|
144
|
-
) : null}
|
|
145
|
-
<Button
|
|
146
|
-
icon={
|
|
147
|
-
progress ? (
|
|
148
|
-
<Box style={{marginTop: 5}}>
|
|
149
|
-
<Spinner />
|
|
150
|
-
</Box>
|
|
151
|
-
) : (
|
|
152
|
-
CheckmarkIcon
|
|
153
|
-
)
|
|
154
|
-
}
|
|
155
|
-
disabled={!!progress}
|
|
156
|
-
text={progress ? `${Math.floor(progress * 100)}%` : 'Ok, convert to new format!'}
|
|
157
|
-
tone="primary"
|
|
158
|
-
onClick={convert}
|
|
159
|
-
/>
|
|
160
|
-
</Stack>
|
|
161
|
-
</Card>
|
|
162
|
-
</Dialog>
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return null
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function deleteDocs(
|
|
170
|
-
client: SanityClient,
|
|
171
|
-
ids: string[],
|
|
172
|
-
updateProgress: (percentage: number) => void
|
|
173
|
-
) {
|
|
174
|
-
const chunkSize = 50
|
|
175
|
-
for (let i = 0; i < ids.length; i += chunkSize) {
|
|
176
|
-
const progressCount = Math.min(ids.length, i + chunkSize)
|
|
177
|
-
const chunk = ids.slice(i, progressCount)
|
|
178
|
-
const trans = client.transaction()
|
|
179
|
-
chunk.forEach((id) => trans.delete(id))
|
|
180
|
-
await trans.commit()
|
|
181
|
-
updateProgress(progressCount / ids.length)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function convertContextDocs(client: SanityClient, docs: SanityDocument[]) {
|
|
186
|
-
const trans = client.transaction()
|
|
187
|
-
|
|
188
|
-
for (const doc of docs) {
|
|
189
|
-
const {_id, _type, ...rest} = doc
|
|
190
|
-
trans.createOrReplace({
|
|
191
|
-
...rest,
|
|
192
|
-
_id: `port.${_id}`,
|
|
193
|
-
_alphaId: _id,
|
|
194
|
-
_type: contextDocumentTypeName,
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
await trans.commit()
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function convertDocs(
|
|
202
|
-
client: SanityClient,
|
|
203
|
-
docs: LegacyAssistDocument[],
|
|
204
|
-
updateProgress: (percentage: number) => void
|
|
205
|
-
) {
|
|
206
|
-
const chunkSize = 10
|
|
207
|
-
for (let i = 0; i < docs.length; i += chunkSize) {
|
|
208
|
-
const progressCount = Math.min(docs.length, i + chunkSize)
|
|
209
|
-
const chunk = docs.slice(i, progressCount)
|
|
210
|
-
|
|
211
|
-
const trans = client.transaction()
|
|
212
|
-
const contextDocs: MigratedContextDoc[] = await client.fetch(
|
|
213
|
-
`*[_type=="${contextDocumentTypeName}" && _alphaId != null]{_id, _alphaId}`
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
chunk.forEach((oldDoc) => {
|
|
217
|
-
const documentType = oldDoc._id.replace(
|
|
218
|
-
new RegExp(`^(${legacyAssistDocumentIdPrefix}|${assistDocumentIdPrefix})`),
|
|
219
|
-
''
|
|
220
|
-
)
|
|
221
|
-
|
|
222
|
-
const id = assistDocumentId(documentType)
|
|
223
|
-
|
|
224
|
-
const fields: AssistField[] = (oldDoc.fields ?? [])
|
|
225
|
-
.filter((field) => field.instructions?.length)
|
|
226
|
-
.map((oldField) => {
|
|
227
|
-
const instructions = (oldField.instructions ?? []).map((inst) => {
|
|
228
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
229
|
-
const prompt = (inst.prompt ?? []).map((block) => {
|
|
230
|
-
return mapBlock(block, contextDocs) as PromptBlock
|
|
231
|
-
})
|
|
232
|
-
return {
|
|
233
|
-
...inst,
|
|
234
|
-
_type: instructionTypeName,
|
|
235
|
-
prompt,
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
return {
|
|
239
|
-
...oldField,
|
|
240
|
-
_type: assistFieldTypeName,
|
|
241
|
-
instructions,
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
if (fields.length) {
|
|
246
|
-
trans.createOrReplace({
|
|
247
|
-
_id: id,
|
|
248
|
-
_type: assistDocumentTypeName,
|
|
249
|
-
fields,
|
|
250
|
-
})
|
|
251
|
-
}
|
|
252
|
-
trans.delete(oldDoc._id)
|
|
253
|
-
})
|
|
254
|
-
await trans.commit()
|
|
255
|
-
updateProgress(progressCount / docs.length)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
type Blocks = LegacyPromptBlock | LegacyPromptTextBlock | PortableTextSpan
|
|
260
|
-
|
|
261
|
-
function isFieldRef(block: Blocks): block is LegacyFieldRef {
|
|
262
|
-
return block._type === 'sanity.ai.prompt.fieldRef'
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function isContext(block: Blocks): block is LegacyContextBlock {
|
|
266
|
-
return block._type === 'sanity.ai.prompt.context'
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function isUserInput(block: Blocks): block is LegacyUserInputBlock {
|
|
270
|
-
return block._type === 'sanity.ai.prompt.userInput'
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function isSpan(block: Blocks): block is PortableTextSpan {
|
|
274
|
-
return block._type === 'span'
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function mapBlock(
|
|
278
|
-
block: Blocks,
|
|
279
|
-
migratedContexts: MigratedContextDoc[]
|
|
280
|
-
): PromptBlock | InlinePromptBlock {
|
|
281
|
-
if (isFieldRef(block)) {
|
|
282
|
-
return {...block, _type: fieldReferenceTypeName}
|
|
283
|
-
}
|
|
284
|
-
if (isUserInput(block)) {
|
|
285
|
-
return {...block, _type: userInputTypeName}
|
|
286
|
-
}
|
|
287
|
-
if (isContext(block)) {
|
|
288
|
-
const newBlock = {
|
|
289
|
-
...block,
|
|
290
|
-
_type: instructionContextTypeName,
|
|
291
|
-
reference: {
|
|
292
|
-
_type: 'reference',
|
|
293
|
-
_ref:
|
|
294
|
-
migratedContexts.find((c) => c._alphaId === block.reference?._ref)?._id ??
|
|
295
|
-
block.reference?._ref,
|
|
296
|
-
},
|
|
297
|
-
} as const
|
|
298
|
-
return newBlock
|
|
299
|
-
}
|
|
300
|
-
if (isSpan(block)) {
|
|
301
|
-
return block
|
|
302
|
-
}
|
|
303
|
-
const textBlock = block
|
|
304
|
-
return {
|
|
305
|
-
...textBlock,
|
|
306
|
-
children: (textBlock.children ?? []).map(
|
|
307
|
-
(child) => mapBlock(child, migratedContexts) as InlinePromptBlock
|
|
308
|
-
),
|
|
309
|
-
}
|
|
310
|
-
}
|
package/src/legacy-types.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {SanityDocument} from 'sanity'
|
|
2
|
-
import {PortableTextBlock, PortableTextMarkDefinition, PortableTextSpan} from '@portabletext/types'
|
|
3
|
-
|
|
4
|
-
//id prefixes
|
|
5
|
-
export const legacyAssistDocumentIdPrefix = 'sanity.ai.'
|
|
6
|
-
|
|
7
|
-
export const legacyAssistDocumentTypeName = 'sanity.ai.docType' as const
|
|
8
|
-
const aiFieldTypeName = 'sanity.ai.docType.field' as const
|
|
9
|
-
const instructionTypeName = 'sanity.ai.field.instruction' as const
|
|
10
|
-
|
|
11
|
-
const userInputTypeName = 'sanity.ai.prompt.userInput' as const
|
|
12
|
-
const promptContextTypeName = 'sanity.ai.prompt.context' as const
|
|
13
|
-
const fieldReferenceTypeName = 'sanity.ai.prompt.fieldRef' as const
|
|
14
|
-
|
|
15
|
-
export const legacyContextDocumentTypeName = 'ai.instruction.context' as const
|
|
16
|
-
|
|
17
|
-
export const legacyAssistStatusDocumentTypeName = 'sanity.ai.instructionStatus' as const
|
|
18
|
-
|
|
19
|
-
export interface FieldPrompts {
|
|
20
|
-
_key: string
|
|
21
|
-
_type: typeof aiFieldTypeName
|
|
22
|
-
path?: string
|
|
23
|
-
instructions?: LegacyInstruction[]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface LegacyAssistDocument extends SanityDocument {
|
|
27
|
-
fields: FieldPrompts[]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface LegacyFieldRef extends PortableTextMarkDefinition {
|
|
31
|
-
_type: typeof fieldReferenceTypeName
|
|
32
|
-
path?: string
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface LegacyContextBlock {
|
|
36
|
-
_type: typeof promptContextTypeName
|
|
37
|
-
reference?: {
|
|
38
|
-
_type: 'reference'
|
|
39
|
-
_ref?: string
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface LegacyUserInputBlock {
|
|
44
|
-
_type: typeof userInputTypeName
|
|
45
|
-
_key: string
|
|
46
|
-
message?: string
|
|
47
|
-
description?: string
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type LegacyPromptTextBlock = PortableTextBlock<
|
|
51
|
-
never,
|
|
52
|
-
PortableTextSpan | LegacyFieldRef | LegacyUserInputBlock | LegacyContextBlock,
|
|
53
|
-
'normal',
|
|
54
|
-
never
|
|
55
|
-
>
|
|
56
|
-
|
|
57
|
-
export type LegacyPromptBlock =
|
|
58
|
-
| LegacyPromptTextBlock
|
|
59
|
-
| LegacyFieldRef
|
|
60
|
-
| LegacyContextBlock
|
|
61
|
-
| LegacyUserInputBlock
|
|
62
|
-
|
|
63
|
-
export interface LegacyInstruction {
|
|
64
|
-
_key: string
|
|
65
|
-
_type: typeof instructionTypeName
|
|
66
|
-
prompt?: LegacyPromptBlock[]
|
|
67
|
-
|
|
68
|
-
icon?: string
|
|
69
|
-
userId?: string
|
|
70
|
-
title?: string
|
|
71
|
-
placeholder?: string
|
|
72
|
-
}
|