sanity-plugin-jev-fields 0.0.0 → 0.1.1
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 +100 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
> Beta: expect `0.x` releases to change until the stored value shapes are frozen at `1.0.0`.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Editorial checks that run while editors write. Ask a question about a field ("Is this easy to
|
|
6
|
+
read?", "How well are the claims backed up?") and the answer shows up under the field, is stored
|
|
7
|
+
next to it for you to query, and can warn or block publishing.
|
|
8
|
+
|
|
9
|
+
The questions are answered by [TypeSafe's Jev](https://typesafe.ai), a decision model that
|
|
10
|
+
answers a question with a probability instead of writing text, through
|
|
11
|
+
[Vercel AI Gateway](https://vercel.com/ai-gateway). Each question is a signal, shown as a chip
|
|
12
|
+
under the field it judges; click it for the details. There are three kinds:
|
|
9
13
|
|
|
10
14
|
- `noul`: a yes/no question, answered with the probability that the answer is yes
|
|
11
15
|
- `score`: a position on an ordered scale you define
|
|
@@ -13,15 +17,24 @@ the field it judges; click it for the details.
|
|
|
13
17
|
|
|
14
18
|
Signals re-evaluate shortly after the field is edited. Opening a document never writes to it.
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
<img src="https://raw.githubusercontent.com/frederikvonsperling/sanity-plugin-jev-fields/main/docs/images/readable.png" width="612" alt="A Body field in Sanity Studio with three signal chips under it: Readable 90%, Evidence 1.5/3 and Tone Casual. The Readable details are open: a High badge, 90% likely to read easily, a nearly full green bar, and the hint "Short sentences, plain words, clear structure".">
|
|
17
21
|
|
|
18
22
|
## Install
|
|
19
23
|
|
|
24
|
+
```sh
|
|
25
|
+
npm install sanity-plugin-jev-fields
|
|
26
|
+
```
|
|
27
|
+
|
|
20
28
|
```sh
|
|
21
29
|
pnpm add sanity-plugin-jev-fields
|
|
22
30
|
```
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
```sh
|
|
33
|
+
yarn add sanity-plugin-jev-fields
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requires Sanity Studio 6. Its peer dependencies, `react` and `react-dom` 19.2 or later and
|
|
37
|
+
`styled-components` 6.1 or later, come with any Studio 6 project.
|
|
25
38
|
|
|
26
39
|
## Usage
|
|
27
40
|
|
|
@@ -70,7 +83,11 @@ defineField({
|
|
|
70
83
|
}),
|
|
71
84
|
tone: choice({
|
|
72
85
|
instructions: 'What is the tone of this article?',
|
|
73
|
-
criteria: {
|
|
86
|
+
criteria: {
|
|
87
|
+
formal: 'Professional and reserved',
|
|
88
|
+
casual: 'Conversational and relaxed',
|
|
89
|
+
playful: 'Light-hearted and witty',
|
|
90
|
+
},
|
|
74
91
|
}),
|
|
75
92
|
},
|
|
76
93
|
},
|
|
@@ -80,7 +97,12 @@ defineField({
|
|
|
80
97
|
A signal reads only the field it is attached to. Portable Text, slugs and nested objects are
|
|
81
98
|
flattened to plain text. Each key (`readable`, `evidence`, `tone`) becomes the name of a field
|
|
82
99
|
next to it that stores the answer; `withJevAnswers` adds those fields and stops with an error if a
|
|
83
|
-
name is already taken.
|
|
100
|
+
name is already taken. A `score` takes 2 to 10 criteria and a `choice` 2 to 255 options; see
|
|
101
|
+
[Signal options](#signal-options) for the rest.
|
|
102
|
+
|
|
103
|
+
<img src="https://raw.githubusercontent.com/frederikvonsperling/sanity-plugin-jev-fields/main/docs/images/tone.png" width="612" alt="A Title field with a Tone Casual chip under it. The Tone details are open: bars for Formal 3%, Casual 97% and Playful 0%, the meaning "Conversational and relaxed", and the note "Out of date: the field changed since this was evaluated."">
|
|
104
|
+
|
|
105
|
+
When a field changes, its answers are marked out of date until they are evaluated again.
|
|
84
106
|
|
|
85
107
|
## Rules
|
|
86
108
|
|
|
@@ -92,11 +114,15 @@ options: {
|
|
|
92
114
|
jev: {
|
|
93
115
|
readable: noul({...signal, warn: {atLeast: 0.6}}), // probability, 0–1
|
|
94
116
|
evidence: score({...signal, require: {atLeast: 2}}), // position on the scale
|
|
117
|
+
risk: score({...signal, colors: 'reverse', warn: {atMost: 1}}), // lower is better
|
|
95
118
|
tone: choice({...signal, warn: {oneOf: ['formal', 'casual']}}),
|
|
96
119
|
},
|
|
97
120
|
}
|
|
98
121
|
```
|
|
99
122
|
|
|
123
|
+
`noul` and `score` rules take `atLeast`, `atMost` or both. `choice` rules take `oneOf`, the options
|
|
124
|
+
the answer must be one of.
|
|
125
|
+
|
|
100
126
|
Rules judge the stored answer, so they say nothing until a signal has been evaluated, and an
|
|
101
127
|
answer that is out of date is still judged as it is.
|
|
102
128
|
|
|
@@ -113,36 +139,51 @@ when a field gets close.
|
|
|
113
139
|
Open the **Jev** tool in the Studio, click **Set key** and paste an AI Gateway API key. The tool
|
|
114
140
|
shows whether a key is set, when it last changed, and can test the connection.
|
|
115
141
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
access, such as a frontend's read or preview token. It is also
|
|
119
|
-
backups. Use a dedicated key with a spend limit.
|
|
142
|
+
> **Anyone who can read the dataset can read the key.** It is stored in the document
|
|
143
|
+
> `secrets.jev`. Unauthenticated queries can't read it, but every Studio user can, and so can
|
|
144
|
+
> every API token with read access, such as a frontend's read or preview token. It is also
|
|
145
|
+
> included in dataset exports and backups. Use a dedicated key with a spend limit.
|
|
146
|
+
|
|
147
|
+
You can also pass the key as the plugin's `apiKey` option, but then it is bundled into the
|
|
148
|
+
Studio's JavaScript, where anyone who can load the Studio can read it.
|
|
120
149
|
|
|
121
150
|
If editors and tokens must never see the key, send requests through your own server instead:
|
|
122
151
|
|
|
123
152
|
```ts
|
|
124
153
|
jev({
|
|
125
154
|
transport: (request, {signal}) =>
|
|
126
|
-
fetch('/api/jev', {
|
|
155
|
+
fetch('/api/jev', {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: {'Content-Type': 'application/json'},
|
|
158
|
+
body: JSON.stringify(request),
|
|
159
|
+
signal,
|
|
160
|
+
}),
|
|
127
161
|
})
|
|
128
162
|
```
|
|
129
163
|
|
|
130
164
|
Your endpoint forwards the body unchanged to `https://ai-gateway.vercel.sh/v1/evaluate` with an
|
|
131
|
-
`Authorization: Bearer <key>` header
|
|
165
|
+
`Authorization: Bearer <key>` header and a `Content-Type: application/json` header, and returns
|
|
166
|
+
the Gateway's response with its status.
|
|
167
|
+
|
|
168
|
+
When more than one is set, `transport` wins over `apiKey`, and `apiKey` over the stored key.
|
|
132
169
|
|
|
133
170
|
## Stored values
|
|
134
171
|
|
|
135
172
|
```groq
|
|
136
173
|
*[_type == "article"]{
|
|
137
174
|
title,
|
|
138
|
-
|
|
139
|
-
"
|
|
140
|
-
|
|
175
|
+
// noul: probability that the answer is yes, 0–1
|
|
176
|
+
"readable": readable.probability,
|
|
177
|
+
// score: score runs from 0 to max; label is the nearest criterion; confidence is 0–1
|
|
178
|
+
"evidence": evidence{score, max, label, confidence},
|
|
179
|
+
// choice: the chosen option, confidence 0–1, and a probability for every option
|
|
180
|
+
"tone": tone{choice, confidence, probabilities[]{option, probability}}
|
|
141
181
|
}
|
|
142
182
|
```
|
|
143
183
|
|
|
144
184
|
Every value also stores `evaluatedAt`, `model` (e.g. `typesafe-ai/jev`) and a `sourceHash` the
|
|
145
|
-
Studio uses to show when a value is out of date.
|
|
185
|
+
Studio uses to show when a value is out of date. The types `NoulValue`, `ScoreValue` and
|
|
186
|
+
`ChoiceValue` describe the full shapes.
|
|
146
187
|
|
|
147
188
|
## Translations
|
|
148
189
|
|
|
@@ -163,21 +204,50 @@ export const jevNorwegian = defineLocaleResourceBundle({
|
|
|
163
204
|
Rule messages and config problems stay in English: Sanity's validation has no translation hook,
|
|
164
205
|
and config problems are meant for schema authors.
|
|
165
206
|
|
|
166
|
-
##
|
|
207
|
+
## Options reference
|
|
167
208
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
209
|
+
### Plugin options
|
|
210
|
+
|
|
211
|
+
Passed to `jev({...})`. All are optional.
|
|
212
|
+
|
|
213
|
+
| Option | Default | Description |
|
|
214
|
+
| ------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
215
|
+
| `transport` | | `(request, {signal}) => Promise<Response>`. Sends requests yourself, e.g. through your own server. See [API key](#api-key). |
|
|
216
|
+
| `apiKey` | | AI Gateway key. Bundled into the Studio's JavaScript; prefer the Jev tool or `transport`. |
|
|
217
|
+
| `endpoint` | `https://ai-gateway.vercel.sh/v1/evaluate` | Where requests go. Ignored when `transport` is set. |
|
|
218
|
+
| `model` | `typesafe-ai/jev` | Decision model to call. |
|
|
219
|
+
| `debounceMs` | `500` | Delay after the last edit before re-evaluating, in milliseconds. |
|
|
220
|
+
| `tool` | `true` | Adds the Jev tool for setting, testing and removing the stored key. |
|
|
221
|
+
| `tags` | `['feature:jev-fields']` | AI Gateway reporting tags, for cost attribution. Each request also gets a tag for its signal, e.g. `jev.noul:article.readable`. |
|
|
222
|
+
|
|
223
|
+
### Signal options
|
|
224
|
+
|
|
225
|
+
Every signal takes:
|
|
226
|
+
|
|
227
|
+
| Option | Description |
|
|
228
|
+
| -------------- | ----------------------------------------------------------------------------------- |
|
|
229
|
+
| `instructions` | Required. The question Jev answers about the attached field. |
|
|
230
|
+
| `title` | Chip and detail heading. Defaults to the signal's key: `readable` → "Readable". |
|
|
231
|
+
| `warn` | Shows a warning on the field when the answer breaks this rule. See [Rules](#rules). |
|
|
232
|
+
| `require` | Blocks publishing when the answer breaks this rule. |
|
|
233
|
+
|
|
234
|
+
And, per kind:
|
|
235
|
+
|
|
236
|
+
| Kind | Option | Description |
|
|
237
|
+
| -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
238
|
+
| `noul` | `true` | Required. What a yes means. Shown when the probability is 50% or higher. |
|
|
239
|
+
| `noul` | `false` | Required. What a no means. Shown when the probability is below 50%. |
|
|
240
|
+
| `noul` | `label` | Short phrase after the percentage, e.g. "likely to read easily". |
|
|
241
|
+
| `score` | `criteria` | Required. 2 to 10 criteria, lowest first. Text before a colon is the short label. |
|
|
242
|
+
| `score` | `colors` | `'traffic'` (default): red at the bottom, green at the top. `'reverse'`: green at the bottom, for scales where lower is better. `'neutral'`: one colour throughout. |
|
|
243
|
+
| `choice` | `criteria` | Required. 2 to 255 options, as option name → what it means. |
|
|
244
|
+
|
|
245
|
+
Rules on a `noul` take probabilities (0–1), rules on a `score` take positions on the scale (0 to
|
|
246
|
+
the number of criteria minus one), and rules on a `choice` take option names.
|
|
173
247
|
|
|
174
|
-
|
|
175
|
-
`AI_GATEWAY_API_KEY` in `.env`). [CONTEXT.md](./CONTEXT.md) defines the vocabulary and
|
|
176
|
-
[docs/adr](./docs/adr) records the main decisions.
|
|
248
|
+
## Contributing
|
|
177
249
|
|
|
178
|
-
|
|
179
|
-
"Version packages" PR up to date; merging it publishes to npm with trusted publishing and
|
|
180
|
-
provenance, and creates the GitHub release.
|
|
250
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
181
251
|
|
|
182
252
|
## License
|
|
183
253
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanity-plugin-jev-fields",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Jev signals for Sanity Studio: AI-evaluated yes/no, score and choice answers attached to a field, from TypeSafe's Jev model through Vercel AI Gateway",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|