jev-recipes 0.0.1 → 0.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/CHANGELOG.md +7 -2
- package/README.md +95 -105
- package/dist/catalog/index.d.ts +211 -0
- package/dist/catalog/index.js +31 -0
- package/dist/catalog/recipes.d.ts +351 -0
- package/dist/catalog/recipes.js +50 -0
- package/dist/catalog/schema.d.ts +19 -0
- package/dist/catalog/schema.js +9 -0
- package/dist/cli/index.js +32 -16
- package/dist/cli/schema.d.ts +23 -8
- package/dist/cli/schema.js +10 -10
- package/dist/examples/support/index.d.ts +1 -0
- package/dist/examples/support/index.js +37 -0
- package/dist/examples/support/schema.d.ts +566 -0
- package/dist/examples/support/schema.js +76 -0
- package/dist/examples/support/workflow.d.ts +3 -0
- package/dist/examples/support/workflow.js +85 -0
- package/dist/recipes/answerability/index.d.ts +5 -0
- package/dist/recipes/answerability/index.js +32 -0
- package/dist/recipes/answerability/metadata.d.ts +8 -0
- package/dist/recipes/answerability/metadata.js +11 -0
- package/dist/recipes/answerability/schema.d.ts +47 -0
- package/dist/recipes/answerability/schema.js +25 -0
- package/dist/recipes/clarify/index.d.ts +5 -0
- package/dist/recipes/clarify/index.js +46 -0
- package/dist/recipes/clarify/metadata.d.ts +8 -0
- package/dist/recipes/clarify/metadata.js +11 -0
- package/dist/recipes/clarify/schema.d.ts +55 -0
- package/dist/recipes/clarify/schema.js +27 -0
- package/dist/recipes/handoff/index.d.ts +5 -0
- package/dist/recipes/handoff/index.js +50 -0
- package/dist/recipes/handoff/metadata.d.ts +8 -0
- package/dist/recipes/handoff/metadata.js +11 -0
- package/dist/recipes/handoff/schema.d.ts +59 -0
- package/dist/recipes/handoff/schema.js +27 -0
- package/dist/recipes/rerank/metadata.d.ts +8 -0
- package/dist/recipes/rerank/metadata.js +11 -0
- package/dist/recipes/route/metadata.d.ts +8 -0
- package/dist/recipes/route/metadata.js +11 -0
- package/dist/recipes/verify/metadata.d.ts +8 -0
- package/dist/recipes/verify/metadata.js +11 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +4 -0
- package/dist/src/schema.d.ts +19 -0
- package/dist/src/schema.js +9 -0
- package/examples/support/README.md +60 -0
- package/examples/support/demo.json +156 -0
- package/package.json +26 -7
- package/recipes/answerability/README.md +29 -0
- package/recipes/answerability/demo.json +31 -0
- package/recipes/clarify/README.md +32 -0
- package/recipes/clarify/demo.json +45 -0
- package/recipes/handoff/README.md +35 -0
- package/recipes/handoff/demo.json +30 -0
- package/dist/cli/recipes.d.ts +0 -53
- package/dist/cli/recipes.js +0 -20
package/dist/src/schema.d.ts
CHANGED
|
@@ -34,6 +34,25 @@ export declare const recipeOptionsSchema: z.ZodObject<{
|
|
|
34
34
|
model: z.ZodOptional<z.ZodString>;
|
|
35
35
|
signal: z.ZodOptional<z.ZodInstanceOf<AbortSignal>>;
|
|
36
36
|
}, z.core.$strip>;
|
|
37
|
+
export declare const recipeCategorySchema: z.ZodEnum<{
|
|
38
|
+
retrieval: "retrieval";
|
|
39
|
+
conversation: "conversation";
|
|
40
|
+
workflow: "workflow";
|
|
41
|
+
}>;
|
|
42
|
+
export declare const recipeMetadataSchema: z.ZodObject<{
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
title: z.ZodString;
|
|
45
|
+
description: z.ZodString;
|
|
46
|
+
category: z.ZodEnum<{
|
|
47
|
+
retrieval: "retrieval";
|
|
48
|
+
conversation: "conversation";
|
|
49
|
+
workflow: "workflow";
|
|
50
|
+
}>;
|
|
51
|
+
tags: z.ZodArray<z.ZodString>;
|
|
52
|
+
limitations: z.ZodArray<z.ZodString>;
|
|
53
|
+
}, z.core.$strip>;
|
|
54
|
+
export type RecipeMetadata = z.infer<typeof recipeMetadataSchema>;
|
|
55
|
+
export type RecipeCategory = z.infer<typeof recipeCategorySchema>;
|
|
37
56
|
export type DecisionClient = z.infer<typeof decisionClientSchema>;
|
|
38
57
|
export type DecisionStatus = z.infer<typeof decisionStatusSchema>;
|
|
39
58
|
export type RecipeOptions = z.infer<typeof recipeOptionsSchema>;
|
package/dist/src/schema.js
CHANGED
|
@@ -20,6 +20,15 @@ export const recipeOptionsSchema = z.object({
|
|
|
20
20
|
model: nonEmptyText.optional(),
|
|
21
21
|
signal: z.instanceof(AbortSignal).optional(),
|
|
22
22
|
});
|
|
23
|
+
export const recipeCategorySchema = z.enum(['retrieval', 'conversation', 'workflow']);
|
|
24
|
+
export const recipeMetadataSchema = z.object({
|
|
25
|
+
id: nonEmptyText,
|
|
26
|
+
title: nonEmptyText,
|
|
27
|
+
description: nonEmptyText,
|
|
28
|
+
category: recipeCategorySchema,
|
|
29
|
+
tags: z.array(nonEmptyText),
|
|
30
|
+
limitations: z.array(nonEmptyText),
|
|
31
|
+
});
|
|
23
32
|
function hasDecisionMethod(client) {
|
|
24
33
|
return (typeof client === 'object' &&
|
|
25
34
|
client !== null &&
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Support assistant
|
|
2
|
+
|
|
3
|
+
A complete decision flow using all six recipes. The workflow lives here so each recipe remains independently usable.
|
|
4
|
+
|
|
5
|
+
From the repository, after `npm run build`:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm run example:support
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The default run uses hand-authored Jev responses and a saved draft from [demo.json](demo.json). It requires no API key and makes no model calls.
|
|
12
|
+
|
|
13
|
+
To use live Jev decisions with the same saved draft:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm run example:support -- --live
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This loads `TYPESAFE_API_KEY` from `.env`, sends synthetic fixture inputs to TypeSafe, and makes up to six Jev requests with retries disabled. There is no generative model call. The output identifies the mode, outcome, and decisions made before stopping.
|
|
20
|
+
|
|
21
|
+
After installing a packed or released package, you can run the compiled example directly:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
node node_modules/jev-recipes/dist/examples/support/index.js
|
|
25
|
+
node --env-file=.env node_modules/jev-recipes/dist/examples/support/index.js --live
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Follow the flow
|
|
29
|
+
|
|
30
|
+
Read [workflow.ts](https://github.com/agencyenterprise/jev-recipes/blob/main/examples/support/workflow.ts) from top to bottom:
|
|
31
|
+
|
|
32
|
+
1. `handoff` checks escalation rules first. A human request or uncertainty stops here.
|
|
33
|
+
2. `clarify` checks the information needed to proceed.
|
|
34
|
+
3. `route` selects the support handler. Its name is passed to the draft callback.
|
|
35
|
+
4. `rerank` selects useful passages from evidence the application already retrieved.
|
|
36
|
+
5. `answerability` checks whether those passages answer the question.
|
|
37
|
+
6. Your `draftAnswer` callback creates claims with references to selected evidence IDs.
|
|
38
|
+
7. `verify` checks each claim against the referenced original passages.
|
|
39
|
+
8. The final answer joins only those checked claims after all are supported.
|
|
40
|
+
|
|
41
|
+
The workflow rejects references to evidence that was not selected. The callback supplies IDs, never replacement evidence text. The final response contains no unchecked prose outside the supplied claims. Claim verification still does not establish source truth or guarantee that the answer covers every part of the question.
|
|
42
|
+
|
|
43
|
+
## Adapt it
|
|
44
|
+
|
|
45
|
+
Copy `workflow.ts` and `schema.ts` into your application. Replace their repository-relative imports with `jev-recipes` or individual recipe exports. The example schemas also use lower-level recipe schemas; either define your application's shapes locally or derive them from each exported input schema's `.shape`.
|
|
46
|
+
|
|
47
|
+
Supply your own routes, requirements, handoff rules, and retrieved evidence. Replace the runner's saved-draft callback with your existing generation code, returning `{ claims: [{ id, claim, evidenceIds }] }`. The callback receives the original request, optional conversation context, selected route, and selected passages. You choose the model and framework.
|
|
48
|
+
|
|
49
|
+
A caller can import the source workflow within this repository:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { answerSupportRequest } from './workflow.js';
|
|
53
|
+
|
|
54
|
+
const result = await answerSupportRequest(input, draftAnswer, { client });
|
|
55
|
+
if (result.outcome === 'answered') console.log(result.answer);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The possible outcomes are `answered`, `human`, `review`, `clarification`, `no_evidence`, `cannot_answer`, and `unverified`. Stopped results identify the `stage`. Results include completed `decisions` for inspection. Only `answered` includes an answer; errors in inputs, draft references, model responses, or transport throw.
|
|
59
|
+
|
|
60
|
+
This example does not retrieve documents, send responses, open tickets, or make account changes. Your application handles those actions and follows its own review policy. The example is not an accuracy benchmark.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input": {
|
|
3
|
+
"request": "How do I reset my password?",
|
|
4
|
+
"routes": {
|
|
5
|
+
"account": "Sign-in, passwords, and account access",
|
|
6
|
+
"billing": "Invoices, payments, and subscriptions"
|
|
7
|
+
},
|
|
8
|
+
"requirements": [
|
|
9
|
+
{
|
|
10
|
+
"id": "intent",
|
|
11
|
+
"description": "The customer identifies what they need help with."
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"handoffRules": [
|
|
15
|
+
{
|
|
16
|
+
"id": "requested-human",
|
|
17
|
+
"description": "The customer explicitly asks to speak with a human support representative."
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"evidence": [
|
|
21
|
+
{
|
|
22
|
+
"id": "password-reset",
|
|
23
|
+
"text": "Select Forgot password on the sign-in page to receive a password reset link."
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "invoices",
|
|
27
|
+
"text": "Download invoices from the Billing page."
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"topK": 1
|
|
31
|
+
},
|
|
32
|
+
"draft": {
|
|
33
|
+
"claims": [
|
|
34
|
+
{
|
|
35
|
+
"id": "reset-instructions",
|
|
36
|
+
"claim": "Select Forgot password on the sign-in page to receive a password reset link.",
|
|
37
|
+
"evidenceIds": ["password-reset"]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"responses": {
|
|
42
|
+
"rule_0": {
|
|
43
|
+
"model": "demo-fixture",
|
|
44
|
+
"usage": {
|
|
45
|
+
"input_tokens": 0,
|
|
46
|
+
"output_tokens": 0
|
|
47
|
+
},
|
|
48
|
+
"answers": {
|
|
49
|
+
"rule_0": {
|
|
50
|
+
"type": "choice",
|
|
51
|
+
"choice": "does_not_match",
|
|
52
|
+
"confidence": 0.96,
|
|
53
|
+
"probabilities": {
|
|
54
|
+
"matches": 0.015,
|
|
55
|
+
"does_not_match": 0.97,
|
|
56
|
+
"unclear": 0.015
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"requirement_0": {
|
|
62
|
+
"model": "demo-fixture",
|
|
63
|
+
"usage": {
|
|
64
|
+
"input_tokens": 0,
|
|
65
|
+
"output_tokens": 0
|
|
66
|
+
},
|
|
67
|
+
"answers": {
|
|
68
|
+
"requirement_0": {
|
|
69
|
+
"type": "choice",
|
|
70
|
+
"choice": "present",
|
|
71
|
+
"confidence": 0.96,
|
|
72
|
+
"probabilities": {
|
|
73
|
+
"present": 0.97,
|
|
74
|
+
"missing": 0.015,
|
|
75
|
+
"ambiguous": 0.015
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"route": {
|
|
81
|
+
"model": "demo-fixture",
|
|
82
|
+
"usage": {
|
|
83
|
+
"input_tokens": 0,
|
|
84
|
+
"output_tokens": 0
|
|
85
|
+
},
|
|
86
|
+
"answers": {
|
|
87
|
+
"route": {
|
|
88
|
+
"type": "choice",
|
|
89
|
+
"choice": "account",
|
|
90
|
+
"confidence": 0.96,
|
|
91
|
+
"probabilities": {
|
|
92
|
+
"account": 0.97,
|
|
93
|
+
"billing": 0.015,
|
|
94
|
+
"__review__": 0.015
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"item_0,item_1": {
|
|
100
|
+
"model": "demo-fixture",
|
|
101
|
+
"usage": {
|
|
102
|
+
"input_tokens": 0,
|
|
103
|
+
"output_tokens": 0
|
|
104
|
+
},
|
|
105
|
+
"answers": {
|
|
106
|
+
"item_0": {
|
|
107
|
+
"type": "noul",
|
|
108
|
+
"noul": 0.97
|
|
109
|
+
},
|
|
110
|
+
"item_1": {
|
|
111
|
+
"type": "noul",
|
|
112
|
+
"noul": 0.04
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"answerability": {
|
|
117
|
+
"model": "demo-fixture",
|
|
118
|
+
"usage": {
|
|
119
|
+
"input_tokens": 0,
|
|
120
|
+
"output_tokens": 0
|
|
121
|
+
},
|
|
122
|
+
"answers": {
|
|
123
|
+
"answerability": {
|
|
124
|
+
"type": "choice",
|
|
125
|
+
"choice": "sufficient",
|
|
126
|
+
"confidence": 0.96,
|
|
127
|
+
"probabilities": {
|
|
128
|
+
"sufficient": 0.97,
|
|
129
|
+
"partial": 0.01,
|
|
130
|
+
"insufficient": 0.01,
|
|
131
|
+
"conflicting": 0.01
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"claim_0": {
|
|
137
|
+
"model": "demo-fixture",
|
|
138
|
+
"usage": {
|
|
139
|
+
"input_tokens": 0,
|
|
140
|
+
"output_tokens": 0
|
|
141
|
+
},
|
|
142
|
+
"answers": {
|
|
143
|
+
"claim_0": {
|
|
144
|
+
"type": "choice",
|
|
145
|
+
"choice": "supported",
|
|
146
|
+
"confidence": 0.96,
|
|
147
|
+
"probabilities": {
|
|
148
|
+
"supported": 0.97,
|
|
149
|
+
"contradicted": 0.015,
|
|
150
|
+
"unsupported": 0.015
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jev-recipes",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Small, composable Jev
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"description": "Small, composable Jev decisions for retrieval, conversations, and agent workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -31,7 +31,23 @@
|
|
|
31
31
|
"types": "./dist/recipes/verify/index.d.ts",
|
|
32
32
|
"import": "./dist/recipes/verify/index.js"
|
|
33
33
|
},
|
|
34
|
-
"./package.json": "./package.json"
|
|
34
|
+
"./package.json": "./package.json",
|
|
35
|
+
"./answerability": {
|
|
36
|
+
"types": "./dist/recipes/answerability/index.d.ts",
|
|
37
|
+
"import": "./dist/recipes/answerability/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./clarify": {
|
|
40
|
+
"types": "./dist/recipes/clarify/index.d.ts",
|
|
41
|
+
"import": "./dist/recipes/clarify/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./handoff": {
|
|
44
|
+
"types": "./dist/recipes/handoff/index.d.ts",
|
|
45
|
+
"import": "./dist/recipes/handoff/index.js"
|
|
46
|
+
},
|
|
47
|
+
"./catalog": {
|
|
48
|
+
"types": "./dist/catalog/index.d.ts",
|
|
49
|
+
"import": "./dist/catalog/index.js"
|
|
50
|
+
}
|
|
35
51
|
},
|
|
36
52
|
"bin": {
|
|
37
53
|
"jev-recipes": "dist/cli/index.js"
|
|
@@ -42,7 +58,9 @@
|
|
|
42
58
|
"recipes/*/README.md",
|
|
43
59
|
"README.md",
|
|
44
60
|
"CHANGELOG.md",
|
|
45
|
-
"LICENSE"
|
|
61
|
+
"LICENSE",
|
|
62
|
+
"examples/support/demo.json",
|
|
63
|
+
"examples/support/README.md"
|
|
46
64
|
],
|
|
47
65
|
"scripts": {
|
|
48
66
|
"clean": "rimraf dist",
|
|
@@ -50,12 +68,13 @@
|
|
|
50
68
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
51
69
|
"format": "prettier --write .",
|
|
52
70
|
"format:check": "prettier --check .",
|
|
53
|
-
"demo": "node dist/cli/index.js demo
|
|
71
|
+
"demo": "node dist/cli/index.js demo all",
|
|
54
72
|
"jev": "node --env-file-if-exists=.env dist/cli/index.js",
|
|
55
|
-
"ci": "npm run format:check && npm run typecheck && npm run build && npm run demo",
|
|
73
|
+
"ci": "npm run format:check && npm run typecheck && npm run build && npm run demo && npm run example:support",
|
|
56
74
|
"pack:check": "npm pack --dry-run",
|
|
57
75
|
"prepack": "npm run build",
|
|
58
|
-
"prepublishOnly": "npm run ci"
|
|
76
|
+
"prepublishOnly": "npm run ci",
|
|
77
|
+
"example:support": "node --env-file-if-exists=.env dist/examples/support/index.js"
|
|
59
78
|
},
|
|
60
79
|
"dependencies": {
|
|
61
80
|
"@typesafe-ai/sdk": "0.6.0",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Answerability
|
|
2
|
+
|
|
3
|
+
Decide whether supplied evidence can answer an entire question before generating a response.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { answerability } from 'jev-recipes/answerability';
|
|
7
|
+
|
|
8
|
+
const result = await answerability({
|
|
9
|
+
question: 'How do I reset my password, and when does the link expire?',
|
|
10
|
+
evidence: [{ id: 'reset', text: 'Select Forgot password. Reset links expire after 30 minutes.' }],
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (result.canAnswer) console.log('Evidence is sufficient for drafting.');
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Provide 1 to 50 passages with unique, non-empty IDs. Handle empty retrieval in your application before calling this recipe. `minConfidence` defaults to `0.8`.
|
|
17
|
+
|
|
18
|
+
| Verdict | Meaning |
|
|
19
|
+
| -------------- | ------------------------------------------------------- |
|
|
20
|
+
| `sufficient` | Every material part of the question can be answered. |
|
|
21
|
+
| `partial` | Some material parts can be answered, but others cannot. |
|
|
22
|
+
| `insufficient` | No material part can be answered. |
|
|
23
|
+
| `conflicting` | Incompatible evidence prevents a consistent answer. |
|
|
24
|
+
|
|
25
|
+
`canAnswer` is true only for `sufficient` at or above the confidence threshold. `status: "ready"` means the assessment passed the threshold; it can still describe insufficient or conflicting evidence. Below the threshold, `status` is `review` and `canAnswer` is false.
|
|
26
|
+
|
|
27
|
+
The result includes the verdict, confidence, all choice probabilities, model, and token usage. The recipe assesses only supplied evidence. It does not verify source truth, generate an answer, or guarantee that a later draft uses the evidence correctly. Use `verify` on that draft's claims.
|
|
28
|
+
|
|
29
|
+
Run `npm run jev -- demo answerability` for the hand-authored offline [fixture](demo.json). Fixtures exercise decision handling; they do not measure accuracy. Calibrate thresholds with your own labeled examples.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input": {
|
|
3
|
+
"question": "How do I reset my password, and when does the link expire?",
|
|
4
|
+
"evidence": [
|
|
5
|
+
{
|
|
6
|
+
"id": "reset",
|
|
7
|
+
"text": "Select Forgot password on the sign-in page. Reset links expire after 30 minutes."
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"response": {
|
|
12
|
+
"model": "demo-fixture",
|
|
13
|
+
"usage": {
|
|
14
|
+
"input_tokens": 0,
|
|
15
|
+
"output_tokens": 0
|
|
16
|
+
},
|
|
17
|
+
"answers": {
|
|
18
|
+
"answerability": {
|
|
19
|
+
"type": "choice",
|
|
20
|
+
"choice": "sufficient",
|
|
21
|
+
"confidence": 0.96,
|
|
22
|
+
"probabilities": {
|
|
23
|
+
"sufficient": 0.97,
|
|
24
|
+
"partial": 0.01,
|
|
25
|
+
"insufficient": 0.01,
|
|
26
|
+
"conflicting": 0.01
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Clarify
|
|
2
|
+
|
|
3
|
+
Check whether a request supplies the information your application needs. You define the requirements; the recipe identifies which ones are present, missing, or ambiguous.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { clarify } from 'jev-recipes/clarify';
|
|
7
|
+
|
|
8
|
+
const result = await clarify({
|
|
9
|
+
request: 'Please cancel it.',
|
|
10
|
+
context: 'The customer has a storage subscription and a pending hardware order.',
|
|
11
|
+
requirements: [
|
|
12
|
+
{ id: 'target', description: 'Which product or order should be changed' },
|
|
13
|
+
{ id: 'action', description: 'What change the customer wants' },
|
|
14
|
+
],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
console.log(result.canProceed, result.missing, result.ambiguous);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Provide 1 to 50 requirements with unique, non-empty IDs. Optional `context` supplies conversation history or application facts. `minConfidence` defaults to `0.8` and applies independently to each check.
|
|
21
|
+
|
|
22
|
+
- `present`: the required information is explicit or unambiguously implied.
|
|
23
|
+
- `missing`: the required information is absent.
|
|
24
|
+
- `ambiguous`: relevant information has multiple plausible meanings or conflicting values.
|
|
25
|
+
|
|
26
|
+
`canProceed` is true only when every requirement is confidently present. `missing` and `ambiguous` contain confidently classified requirement IDs. Low-confidence checks remain in `checks` with `status: "review"`; they block proceeding even if both arrays are empty. Overall `status` is `review` when any check falls below the threshold.
|
|
27
|
+
|
|
28
|
+
A `ready` result can still require clarification. Each check includes its verdict, confidence, and probabilities; the result also includes model and token usage.
|
|
29
|
+
|
|
30
|
+
Your application maps requirement IDs to follow-up questions or asks its generative model to phrase them. This recipe neither discovers requirements nor generates questions. It does not validate identity, permissions, or exact field formats.
|
|
31
|
+
|
|
32
|
+
Run `npm run jev -- demo clarify` for the hand-authored offline [fixture](demo.json). Evaluate your own requirements and thresholds on labeled examples before relying on them.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input": {
|
|
3
|
+
"request": "Please cancel it.",
|
|
4
|
+
"context": "The customer has a monthly storage subscription and a pending hardware order.",
|
|
5
|
+
"requirements": [
|
|
6
|
+
{
|
|
7
|
+
"id": "target",
|
|
8
|
+
"description": "Which product or order should be changed"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "action",
|
|
12
|
+
"description": "What change the customer wants"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"response": {
|
|
17
|
+
"model": "demo-fixture",
|
|
18
|
+
"usage": {
|
|
19
|
+
"input_tokens": 0,
|
|
20
|
+
"output_tokens": 0
|
|
21
|
+
},
|
|
22
|
+
"answers": {
|
|
23
|
+
"requirement_0": {
|
|
24
|
+
"type": "choice",
|
|
25
|
+
"choice": "ambiguous",
|
|
26
|
+
"confidence": 0.96,
|
|
27
|
+
"probabilities": {
|
|
28
|
+
"present": 0.015,
|
|
29
|
+
"missing": 0.015,
|
|
30
|
+
"ambiguous": 0.97
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"requirement_1": {
|
|
34
|
+
"type": "choice",
|
|
35
|
+
"choice": "present",
|
|
36
|
+
"confidence": 0.96,
|
|
37
|
+
"probabilities": {
|
|
38
|
+
"present": 0.97,
|
|
39
|
+
"missing": 0.015,
|
|
40
|
+
"ambiguous": 0.015
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Handoff
|
|
2
|
+
|
|
3
|
+
Check a request against your rules for involving a human. The function returns a decision; your application handles the transfer.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { handoff } from 'jev-recipes/handoff';
|
|
7
|
+
|
|
8
|
+
const result = await handoff({
|
|
9
|
+
request: 'Please connect me with a person.',
|
|
10
|
+
rules: [
|
|
11
|
+
{
|
|
12
|
+
id: 'requested-human',
|
|
13
|
+
description: 'The customer explicitly asks to speak with a human support representative.',
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
console.log(result.decision, result.matchedRules);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Provide 1 to 50 rules with unique, non-empty IDs. Optional `context` supplies conversation history or application facts. `minConfidence` defaults to `0.8` and applies independently to each check.
|
|
22
|
+
|
|
23
|
+
| Decision | Condition |
|
|
24
|
+
| ---------- | ----------------------------------------------------------------------- |
|
|
25
|
+
| `human` | At least one rule confidently `matches`. |
|
|
26
|
+
| `review` | No confident match, and at least one `unclear` or low-confidence check. |
|
|
27
|
+
| `continue` | Every rule confidently `does_not_match`. |
|
|
28
|
+
|
|
29
|
+
`matchedRules` contains confident matches. `uncertainRules` contains unclear and low-confidence checks. A confident match is enough to return `human` with `status: "ready"`, even if other rules are uncertain. A `review` decision always has `status: "review"`.
|
|
30
|
+
|
|
31
|
+
The result includes every rule's verdict, confidence, and probabilities, plus model and token usage. An `unclear` verdict can have high confidence: the model is confident that it cannot decide from the supplied facts.
|
|
32
|
+
|
|
33
|
+
Write observable rules and provide the facts needed to evaluate them. Compute exact amounts, dates, permissions, and hard limits in code. This semantic check does not contact anyone, enforce authorization, or provide a security boundary.
|
|
34
|
+
|
|
35
|
+
Run `npm run jev -- demo handoff` for the hand-authored offline [fixture](demo.json). Fixtures do not measure live model accuracy.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"input": {
|
|
3
|
+
"request": "Please connect me with a person.",
|
|
4
|
+
"rules": [
|
|
5
|
+
{
|
|
6
|
+
"id": "requested-human",
|
|
7
|
+
"description": "The customer explicitly asks to speak with a human support representative."
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"response": {
|
|
12
|
+
"model": "demo-fixture",
|
|
13
|
+
"usage": {
|
|
14
|
+
"input_tokens": 0,
|
|
15
|
+
"output_tokens": 0
|
|
16
|
+
},
|
|
17
|
+
"answers": {
|
|
18
|
+
"rule_0": {
|
|
19
|
+
"type": "choice",
|
|
20
|
+
"choice": "matches",
|
|
21
|
+
"confidence": 0.96,
|
|
22
|
+
"probabilities": {
|
|
23
|
+
"matches": 0.97,
|
|
24
|
+
"does_not_match": 0.015,
|
|
25
|
+
"unclear": 0.015
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/cli/recipes.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { RecipeOptions } from '../src/schema.js';
|
|
2
|
-
export declare const recipes: {
|
|
3
|
-
route: {
|
|
4
|
-
description: string;
|
|
5
|
-
run: (input: unknown, options?: RecipeOptions) => Promise<{
|
|
6
|
-
model: string;
|
|
7
|
-
usage: {
|
|
8
|
-
input_tokens: number;
|
|
9
|
-
output_tokens: number;
|
|
10
|
-
};
|
|
11
|
-
status: "ready" | "review";
|
|
12
|
-
route: string | null;
|
|
13
|
-
suggestedRoute: string | null;
|
|
14
|
-
confidence: number;
|
|
15
|
-
probabilities: Record<string, number>;
|
|
16
|
-
}>;
|
|
17
|
-
};
|
|
18
|
-
rerank: {
|
|
19
|
-
description: string;
|
|
20
|
-
run: (input: unknown, options?: RecipeOptions) => Promise<{
|
|
21
|
-
model: string;
|
|
22
|
-
usage: {
|
|
23
|
-
input_tokens: number;
|
|
24
|
-
output_tokens: number;
|
|
25
|
-
};
|
|
26
|
-
status: "ready" | "review";
|
|
27
|
-
items: {
|
|
28
|
-
id: string;
|
|
29
|
-
text: string;
|
|
30
|
-
relevance: number;
|
|
31
|
-
}[];
|
|
32
|
-
evaluated: number;
|
|
33
|
-
}>;
|
|
34
|
-
};
|
|
35
|
-
verify: {
|
|
36
|
-
description: string;
|
|
37
|
-
run: (input: unknown, options?: RecipeOptions) => Promise<{
|
|
38
|
-
model: string;
|
|
39
|
-
usage: {
|
|
40
|
-
input_tokens: number;
|
|
41
|
-
output_tokens: number;
|
|
42
|
-
};
|
|
43
|
-
checks: {
|
|
44
|
-
id: string;
|
|
45
|
-
status: "ready" | "review";
|
|
46
|
-
verdict: "supported" | "contradicted" | "unsupported";
|
|
47
|
-
confidence: number;
|
|
48
|
-
probabilities: Record<"supported" | "contradicted" | "unsupported", number>;
|
|
49
|
-
}[];
|
|
50
|
-
allSupported: boolean;
|
|
51
|
-
}>;
|
|
52
|
-
};
|
|
53
|
-
};
|
package/dist/cli/recipes.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { route } from '../recipes/route/index.js';
|
|
2
|
-
import { routeInputSchema } from '../recipes/route/schema.js';
|
|
3
|
-
import { rerank } from '../recipes/rerank/index.js';
|
|
4
|
-
import { rerankInputSchema } from '../recipes/rerank/schema.js';
|
|
5
|
-
import { verify } from '../recipes/verify/index.js';
|
|
6
|
-
import { verifyInputSchema } from '../recipes/verify/schema.js';
|
|
7
|
-
export const recipes = {
|
|
8
|
-
route: {
|
|
9
|
-
description: 'Choose a handler or request review.',
|
|
10
|
-
run: (input, options) => route(routeInputSchema.parse(input), options),
|
|
11
|
-
},
|
|
12
|
-
rerank: {
|
|
13
|
-
description: 'Rank passages by relevance to a query.',
|
|
14
|
-
run: (input, options) => rerank(rerankInputSchema.parse(input), options),
|
|
15
|
-
},
|
|
16
|
-
verify: {
|
|
17
|
-
description: 'Check claims against supplied evidence.',
|
|
18
|
-
run: (input, options) => verify(verifyInputSchema.parse(input), options),
|
|
19
|
-
},
|
|
20
|
-
};
|