sanity-plugin-jev-fields 0.1.1 → 0.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/README.md +27 -27
- package/dist/index.d.ts +25 -25
- package/dist/index.js +127 -127
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,16 +8,16 @@ next to it for you to query, and can warn or block publishing.
|
|
|
8
8
|
|
|
9
9
|
The questions are answered by [TypeSafe's Jev](https://typesafe.ai), a decision model that
|
|
10
10
|
answers a question with a probability instead of writing text, through
|
|
11
|
-
[Vercel AI Gateway](https://vercel.com/ai-gateway). Each question
|
|
12
|
-
|
|
11
|
+
[Vercel AI Gateway](https://vercel.com/ai-gateway). Each question shows as a chip under the field
|
|
12
|
+
it judges; click it for the details. There are three kinds:
|
|
13
13
|
|
|
14
14
|
- `noul`: a yes/no question, answered with the probability that the answer is yes
|
|
15
15
|
- `score`: a position on an ordered scale you define
|
|
16
16
|
- `choice`: one option from a named set, with a probability for each option
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Questions are re-evaluated shortly after the field is edited. Opening a document never writes to it.
|
|
19
19
|
|
|
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
|
|
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 question 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".">
|
|
21
21
|
|
|
22
22
|
## Install
|
|
23
23
|
|
|
@@ -38,7 +38,7 @@ Requires Sanity Studio 6. Its peer dependencies, `react` and `react-dom` 19.2 or
|
|
|
38
38
|
|
|
39
39
|
## Usage
|
|
40
40
|
|
|
41
|
-
Add the plugin, and wrap your schema types with `withJevAnswers` so every
|
|
41
|
+
Add the plugin, and wrap your schema types with `withJevAnswers` so every question gets a field to
|
|
42
42
|
store its answer in:
|
|
43
43
|
|
|
44
44
|
```ts
|
|
@@ -53,7 +53,7 @@ export default defineConfig({
|
|
|
53
53
|
})
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Then attach
|
|
56
|
+
Then attach questions to any field with `options.jev`:
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
59
|
import {defineArrayMember, defineField} from 'sanity'
|
|
@@ -94,11 +94,11 @@ defineField({
|
|
|
94
94
|
})
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
A
|
|
97
|
+
A question reads only the field it is attached to. Portable Text, slugs and nested objects are
|
|
98
98
|
flattened to plain text. Each key (`readable`, `evidence`, `tone`) becomes the name of a field
|
|
99
99
|
next to it that stores the answer; `withJevAnswers` adds those fields and stops with an error if a
|
|
100
100
|
name is already taken. A `score` takes 2 to 10 criteria and a `choice` 2 to 255 options; see
|
|
101
|
-
[
|
|
101
|
+
[Question options](#question-options) for the rest.
|
|
102
102
|
|
|
103
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
104
|
|
|
@@ -106,16 +106,16 @@ When a field changes, its answers are marked out of date until they are evaluate
|
|
|
106
106
|
|
|
107
107
|
## Rules
|
|
108
108
|
|
|
109
|
-
Give a
|
|
109
|
+
Give a question a `warn` or `require` rule to act on its answer: `warn` shows a warning on the
|
|
110
110
|
attached field, `require` an error that blocks publishing.
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
options: {
|
|
114
114
|
jev: {
|
|
115
|
-
readable: noul({...
|
|
116
|
-
evidence: score({...
|
|
117
|
-
risk: score({...
|
|
118
|
-
tone: choice({...
|
|
115
|
+
readable: noul({...question, warn: {atLeast: 0.6}}), // probability, 0–1
|
|
116
|
+
evidence: score({...question, require: {atLeast: 2}}), // position on the scale
|
|
117
|
+
risk: score({...question, colors: 'reverse', warn: {atMost: 1}}), // lower is better
|
|
118
|
+
tone: choice({...question, warn: {oneOf: ['formal', 'casual']}}),
|
|
119
119
|
},
|
|
120
120
|
}
|
|
121
121
|
```
|
|
@@ -123,14 +123,14 @@ options: {
|
|
|
123
123
|
`noul` and `score` rules take `atLeast`, `atMost` or both. `choice` rules take `oneOf`, the options
|
|
124
124
|
the answer must be one of.
|
|
125
125
|
|
|
126
|
-
Rules judge the stored answer, so they say nothing until a
|
|
126
|
+
Rules judge the stored answer, so they say nothing until a question has been evaluated, and an
|
|
127
127
|
answer that is out of date is still judged as it is.
|
|
128
128
|
|
|
129
129
|
## What Jev is good at
|
|
130
130
|
|
|
131
131
|
Jev judges meaning in text: tone, clarity, whether claims are backed up. It reads only text, so
|
|
132
132
|
images and other media in a field are ignored. It is not built for counting or arithmetic: ask
|
|
133
|
-
"Is this under 150 words?" in a validation rule instead. Test
|
|
133
|
+
"Is this under 150 words?" in a validation rule instead. Test questions on content in your own
|
|
134
134
|
languages before relying on them. A field's text may be up to about 32k tokens; the Studio warns
|
|
135
135
|
when a field gets close.
|
|
136
136
|
|
|
@@ -210,24 +210,24 @@ and config problems are meant for schema authors.
|
|
|
210
210
|
|
|
211
211
|
Passed to `jev({...})`. All are optional.
|
|
212
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
|
|
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 question, e.g. `jev.noul:article.readable`. |
|
|
222
222
|
|
|
223
|
-
###
|
|
223
|
+
### Question options
|
|
224
224
|
|
|
225
|
-
Every
|
|
225
|
+
Every question takes:
|
|
226
226
|
|
|
227
227
|
| Option | Description |
|
|
228
228
|
| -------------- | ----------------------------------------------------------------------------------- |
|
|
229
229
|
| `instructions` | Required. The question Jev answers about the attached field. |
|
|
230
|
-
| `title` | Chip and detail heading. Defaults to the
|
|
230
|
+
| `title` | Chip and detail heading. Defaults to the question's key: `readable` → "Readable". |
|
|
231
231
|
| `warn` | Shows a warning on the field when the answer breaks this rule. See [Rules](#rules). |
|
|
232
232
|
| `require` | Blocks publishing when the answer breaks this rule. |
|
|
233
233
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SchemaTypeDefinition } from "sanity";
|
|
2
2
|
import "react";
|
|
3
3
|
/** @public */
|
|
4
|
-
type
|
|
4
|
+
type GatewayQuestion = {
|
|
5
5
|
type: 'boolean';
|
|
6
6
|
instructions: string;
|
|
7
7
|
criteria: {
|
|
@@ -18,7 +18,7 @@ type JevQuestion = {
|
|
|
18
18
|
criteria: Record<string, string>;
|
|
19
19
|
};
|
|
20
20
|
/** @public */
|
|
21
|
-
type
|
|
21
|
+
type GatewayAnswer = {
|
|
22
22
|
type: 'boolean';
|
|
23
23
|
probability: number;
|
|
24
24
|
} | {
|
|
@@ -41,7 +41,7 @@ interface JevRequest {
|
|
|
41
41
|
model: string;
|
|
42
42
|
state: string | Record<string, string>;
|
|
43
43
|
questions: {
|
|
44
|
-
q:
|
|
44
|
+
q: GatewayQuestion;
|
|
45
45
|
};
|
|
46
46
|
providerOptions: {
|
|
47
47
|
gateway: {
|
|
@@ -78,12 +78,12 @@ interface JevPluginConfig {
|
|
|
78
78
|
tool?: boolean;
|
|
79
79
|
/**
|
|
80
80
|
* AI Gateway reporting tags, for cost attribution. Defaults to `['feature:jev-fields']`.
|
|
81
|
-
* Each request also gets a per-
|
|
81
|
+
* Each request also gets a per-question tag, e.g. `jev.noul:article.readable`.
|
|
82
82
|
*/
|
|
83
83
|
tags?: string[];
|
|
84
84
|
}
|
|
85
85
|
/** @public */
|
|
86
|
-
interface
|
|
86
|
+
interface ChoiceQuestion extends QuestionBase {
|
|
87
87
|
type: 'choice';
|
|
88
88
|
/** Two to 255 options: option name → what it means. */
|
|
89
89
|
criteria: Record<string, string>;
|
|
@@ -97,7 +97,7 @@ interface ChoiceRule {
|
|
|
97
97
|
oneOf: string[];
|
|
98
98
|
}
|
|
99
99
|
/** One option from a named set. @public */
|
|
100
|
-
export declare const choice: (
|
|
100
|
+
export declare const choice: (question: Omit<ChoiceQuestion, "type">) => ChoiceQuestion;
|
|
101
101
|
interface ChoiceProbability {
|
|
102
102
|
_key: string;
|
|
103
103
|
_type?: 'jev.choiceProbability';
|
|
@@ -113,7 +113,7 @@ interface ChoiceValue extends EvaluatedValue {
|
|
|
113
113
|
probabilities?: ChoiceProbability[];
|
|
114
114
|
}
|
|
115
115
|
/** @public */
|
|
116
|
-
interface
|
|
116
|
+
interface NoulQuestion extends QuestionBase {
|
|
117
117
|
type: 'noul';
|
|
118
118
|
/** What a "yes" means. Shown when the probability is 50% or higher. */
|
|
119
119
|
true: string;
|
|
@@ -126,8 +126,8 @@ interface NoulSignal extends SignalBase {
|
|
|
126
126
|
/** Block publishing when the probability is outside these bounds (0–1). */
|
|
127
127
|
require?: RangeRule;
|
|
128
128
|
}
|
|
129
|
-
/** A yes/no
|
|
130
|
-
export declare const noul: (
|
|
129
|
+
/** A yes/no question: the chip shows the probability that the answer is yes. @public */
|
|
130
|
+
export declare const noul: (question: Omit<NoulQuestion, "type">) => NoulQuestion;
|
|
131
131
|
/** @public */
|
|
132
132
|
interface NoulValue extends EvaluatedValue {
|
|
133
133
|
_type?: 'jev.noul';
|
|
@@ -135,7 +135,7 @@ interface NoulValue extends EvaluatedValue {
|
|
|
135
135
|
probability?: number;
|
|
136
136
|
}
|
|
137
137
|
/** @public */
|
|
138
|
-
interface
|
|
138
|
+
interface ScoreQuestion extends QuestionBase {
|
|
139
139
|
type: 'score';
|
|
140
140
|
/**
|
|
141
141
|
* Two to ten criteria, lowest first. Text before a colon becomes the short label,
|
|
@@ -154,7 +154,7 @@ interface ScoreSignal extends SignalBase {
|
|
|
154
154
|
require?: RangeRule;
|
|
155
155
|
}
|
|
156
156
|
/** A position on an ordered scale. @public */
|
|
157
|
-
export declare const score: (
|
|
157
|
+
export declare const score: (question: Omit<ScoreQuestion, "type">) => ScoreQuestion;
|
|
158
158
|
/** @public */
|
|
159
159
|
interface ScoreValue extends EvaluatedValue {
|
|
160
160
|
_type?: 'jev.score';
|
|
@@ -167,8 +167,8 @@ interface ScoreValue extends EvaluatedValue {
|
|
|
167
167
|
/** The model's confidence in this score (0–1). */
|
|
168
168
|
confidence?: number;
|
|
169
169
|
}
|
|
170
|
-
interface
|
|
171
|
-
/** Chip and detail heading. Defaults to the
|
|
170
|
+
interface QuestionBase {
|
|
171
|
+
/** Chip and detail heading. Defaults to the question's key, e.g. `readable` → "Readable". */
|
|
172
172
|
title?: string;
|
|
173
173
|
/** The question Jev answers about the attached field. */
|
|
174
174
|
instructions: string;
|
|
@@ -186,11 +186,11 @@ interface RangeRule {
|
|
|
186
186
|
atMost?: number;
|
|
187
187
|
}
|
|
188
188
|
/** @public */
|
|
189
|
-
type
|
|
190
|
-
/**
|
|
191
|
-
type
|
|
189
|
+
type JevQuestion = NoulQuestion | ScoreQuestion | ChoiceQuestion;
|
|
190
|
+
/** Questions attached to a field, keyed by the name of the field that stores each answer. */
|
|
191
|
+
type JevQuestions = Record<string, JevQuestion>;
|
|
192
192
|
/**
|
|
193
|
-
* Adds a field that stores each
|
|
193
|
+
* Adds a field that stores each question's answer next to every field with `options.jev`.
|
|
194
194
|
* Plugins can't see the Studio's own schema types, so wrap them in `sanity.config`:
|
|
195
195
|
*
|
|
196
196
|
* ```ts
|
|
@@ -209,7 +209,7 @@ export declare const JEV_NAMESPACE: "jev";
|
|
|
209
209
|
declare const resources: {
|
|
210
210
|
readonly 'strip.label': "Jev";
|
|
211
211
|
readonly 'strip.set-up': "Set up Jev";
|
|
212
|
-
readonly 'strip.evaluate-all': "Evaluate all
|
|
212
|
+
readonly 'strip.evaluate-all': "Evaluate all questions now";
|
|
213
213
|
readonly 'chip.empty': "–";
|
|
214
214
|
readonly 'chip.error': "Error";
|
|
215
215
|
readonly 'chip.out-of-date': "Out of date";
|
|
@@ -239,7 +239,7 @@ declare const resources: {
|
|
|
239
239
|
readonly 'key-dialog.saving': "Saving…";
|
|
240
240
|
readonly 'key-dialog.save-failed': "Could not save the key: {{error}}";
|
|
241
241
|
readonly 'tool.title': "Jev";
|
|
242
|
-
readonly 'tool.intro': "Jev
|
|
242
|
+
readonly 'tool.intro': "Jev questions are answered by TypeSafe’s Jev model through Vercel AI Gateway.";
|
|
243
243
|
readonly 'tool.key.heading': "AI Gateway API key";
|
|
244
244
|
readonly 'tool.key.status.config': "From plugin config";
|
|
245
245
|
readonly 'tool.key.status.checking': "Checking";
|
|
@@ -250,11 +250,11 @@ declare const resources: {
|
|
|
250
250
|
readonly 'tool.key.ends-with': "Key ending in <Code>{{last4}}</Code>";
|
|
251
251
|
readonly 'tool.key.changed': "Last changed {{date}}";
|
|
252
252
|
readonly 'tool.key.checking': "Checking for a stored key…";
|
|
253
|
-
readonly 'tool.key.none': "No key is stored, so Jev
|
|
253
|
+
readonly 'tool.key.none': "No key is stored, so Jev questions can’t be evaluated yet.";
|
|
254
254
|
readonly 'tool.key.set': "Set key";
|
|
255
255
|
readonly 'tool.key.change': "Change key";
|
|
256
256
|
readonly 'tool.key.remove': "Remove key";
|
|
257
|
-
readonly 'tool.key.remove-confirm': "Remove the key? Jev
|
|
257
|
+
readonly 'tool.key.remove-confirm': "Remove the key? Jev questions stop being evaluated until a new one is set.";
|
|
258
258
|
readonly 'tool.key.remove-cancel': "Cancel";
|
|
259
259
|
readonly 'tool.key.remove-confirm-button': "Remove";
|
|
260
260
|
readonly 'tool.key.remove-failed': "Could not remove the key: {{error}}";
|
|
@@ -268,16 +268,16 @@ declare const resources: {
|
|
|
268
268
|
type JevTranslationKey = keyof typeof resources;
|
|
269
269
|
declare module 'sanity' {
|
|
270
270
|
interface BaseSchemaTypeOptions {
|
|
271
|
-
/** Jev
|
|
272
|
-
jev?:
|
|
271
|
+
/** Jev questions shown on this field, keyed by the name of the field storing each answer. */
|
|
272
|
+
jev?: JevQuestions;
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
/**
|
|
276
|
-
* Jev
|
|
276
|
+
* Jev questions for Sanity Studio: yes/no, score and choice questions attached to a field with
|
|
277
277
|
* `options.jev`, answered by TypeSafe's Jev model through Vercel AI Gateway. Pair it with
|
|
278
278
|
* `withJevAnswers(schemaTypes)` in `schema.types`.
|
|
279
279
|
* @public
|
|
280
280
|
*/
|
|
281
281
|
export declare const jev: import("sanity").Plugin<void | JevPluginConfig>;
|
|
282
|
-
export type {
|
|
282
|
+
export type { ChoiceQuestion, ChoiceRule, ChoiceValue, GatewayAnswer, GatewayQuestion, JevPluginConfig, JevQuestion, JevQuestions, JevRequest, JevTranslationKey, JevTransport, NoulQuestion, NoulValue, RangeRule, ScoreQuestion, ScoreValue };
|
|
283
283
|
//# sourceMappingURL=index.d.ts.map
|