sanity-plugin-jev-fields 0.1.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.
Files changed (2) hide show
  1. package/README.md +100 -31
  2. 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
- Jev signals for Sanity Studio: questions about a field's content, answered by
6
- [TypeSafe's Jev](https://typesafe.ai) decision model through
7
- [Vercel AI Gateway](https://vercel.com/ai-gateway) as editors write. Each signal is a chip under
8
- the field it judges; click it for the details.
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
- ![A Body field in Sanity Studio with three signal chips under it: Readable 90%, Evidence 1.5 of 3 and Tone Casual. The Evidence details are open: a four-step bar from None to Cited, filled to Anecdotal, and the hint "To move up: add a source or figure."](https://raw.githubusercontent.com/frederikvonsperling/sanity-plugin-jev-fields/main/docs/images/signals.png)
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 &quot;Short sentences, plain words, clear structure&quot;.">
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
- Requires Sanity Studio 6.
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: {formal: 'Professional and reserved', casual: 'Conversational and relaxed'},
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 &quot;Conversational and relaxed&quot;, and the note &quot;Out of date: the field changed since this was evaluated.&quot;">
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
- The key is stored in the document `secrets.jev` in your dataset. Unauthenticated queries can't
117
- read it, but anyone who can read the dataset can: every Studio user, and every API token with read
118
- access, such as a frontend's read or preview token. It is also included in dataset exports and
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', {method: 'POST', body: JSON.stringify(request), signal}),
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, and returns the Gateway's response.
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
- "readable": readable.probability, // 0–1
139
- "evidence": evidence{score, max, label}, // score runs from 0 to max
140
- "tone": tone.choice
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,22 +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
- ## Development
207
+ ## Options reference
167
208
 
168
- ```sh
169
- pnpm install
170
- pnpm test
171
- pnpm build
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
- `pnpm record-fixtures` re-records the real Gateway responses in `src/__fixtures__` (needs
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
- To release a change, add a changeset to its PR with `pnpm changeset`. The release workflow keeps a
179
- "Version packages" PR up to date; merging it stages the new version on npm (trusted publishing,
180
- no token) and creates the GitHub release. A maintainer then approves the staged version on
181
- npmjs.com, with 2FA, to publish it.
250
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
182
251
 
183
252
  ## License
184
253
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sanity-plugin-jev-fields",
3
- "version": "0.1.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",