jev-web 0.7.1 → 0.8.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 +21 -21
- package/README.md +625 -502
- package/package.json +60 -60
- package/src/answers.mjs +36 -36
- package/src/bekko.mjs +439 -439
- package/src/config.mjs +65 -65
- package/src/decision2.mjs +382 -382
- package/src/encode.mjs +83 -83
- package/src/index.mjs +137 -112
- package/src/kev.mjs +296 -296
- package/src/laya.mjs +630 -630
- package/src/questions.mjs +39 -39
- package/src/registry.mjs +169 -155
- package/src/session.mjs +151 -151
- package/src/strands.mjs +718 -535
- package/src/vega.mjs +636 -0
package/src/questions.mjs
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
// jev-web — question normalization for typed-decision models.
|
|
2
|
-
//
|
|
3
|
-
// The model answers three question kinds:
|
|
4
|
-
// choice — pick one of 2..255 options
|
|
5
|
-
// score — ordered levels (2..10); the answer is the expected level index
|
|
6
|
-
// noul — yes/no; the answer is p(yes)
|
|
7
|
-
|
|
8
|
-
export const QUESTION_TYPES = new Set(["choice", "score", "noul"]);
|
|
9
|
-
export const NOUL_OPTIONS = ["no", "yes"];
|
|
10
|
-
|
|
11
|
-
export function normalizeQuestions(questions) {
|
|
12
|
-
if (!Array.isArray(questions) || questions.length === 0) {
|
|
13
|
-
throw new TypeError("questions must be a non-empty array");
|
|
14
|
-
}
|
|
15
|
-
return questions.map((q, i) => {
|
|
16
|
-
const type = q?.type;
|
|
17
|
-
if (!QUESTION_TYPES.has(type)) {
|
|
18
|
-
throw new TypeError(`questions[${i}].type must be one of ${[...QUESTION_TYPES].join("|")}`);
|
|
19
|
-
}
|
|
20
|
-
const instructions = String(q?.instructions ?? "").trim();
|
|
21
|
-
if (!instructions) throw new TypeError(`questions[${i}].instructions is required`);
|
|
22
|
-
|
|
23
|
-
let options;
|
|
24
|
-
if (type === "noul") {
|
|
25
|
-
options = Array.isArray(q.options) && q.options.length ? q.options.map(String) : [...NOUL_OPTIONS];
|
|
26
|
-
if (options.length !== 2) throw new TypeError(`questions[${i}]: noul takes exactly 2 options`);
|
|
27
|
-
} else if (type === "choice") {
|
|
28
|
-
options = (q.options ?? []).map(String);
|
|
29
|
-
if (options.length < 2) throw new TypeError(`questions[${i}]: choice needs at least 2 options`);
|
|
30
|
-
if (options.length > 255) throw new TypeError(`questions[${i}]: choice supports at most 255 options`);
|
|
31
|
-
} else {
|
|
32
|
-
options = (q.options ?? []).map(String);
|
|
33
|
-
if (options.length < 2 || options.length > 10) {
|
|
34
|
-
throw new TypeError(`questions[${i}]: score needs 2-10 ordered levels`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return { type, instructions, options, ...(q?.criteria !== undefined ? { criteria: q.criteria } : {}) };
|
|
38
|
-
});
|
|
39
|
-
}
|
|
1
|
+
// jev-web — question normalization for typed-decision models.
|
|
2
|
+
//
|
|
3
|
+
// The model answers three question kinds:
|
|
4
|
+
// choice — pick one of 2..255 options
|
|
5
|
+
// score — ordered levels (2..10); the answer is the expected level index
|
|
6
|
+
// noul — yes/no; the answer is p(yes)
|
|
7
|
+
|
|
8
|
+
export const QUESTION_TYPES = new Set(["choice", "score", "noul"]);
|
|
9
|
+
export const NOUL_OPTIONS = ["no", "yes"];
|
|
10
|
+
|
|
11
|
+
export function normalizeQuestions(questions) {
|
|
12
|
+
if (!Array.isArray(questions) || questions.length === 0) {
|
|
13
|
+
throw new TypeError("questions must be a non-empty array");
|
|
14
|
+
}
|
|
15
|
+
return questions.map((q, i) => {
|
|
16
|
+
const type = q?.type;
|
|
17
|
+
if (!QUESTION_TYPES.has(type)) {
|
|
18
|
+
throw new TypeError(`questions[${i}].type must be one of ${[...QUESTION_TYPES].join("|")}`);
|
|
19
|
+
}
|
|
20
|
+
const instructions = String(q?.instructions ?? "").trim();
|
|
21
|
+
if (!instructions) throw new TypeError(`questions[${i}].instructions is required`);
|
|
22
|
+
|
|
23
|
+
let options;
|
|
24
|
+
if (type === "noul") {
|
|
25
|
+
options = Array.isArray(q.options) && q.options.length ? q.options.map(String) : [...NOUL_OPTIONS];
|
|
26
|
+
if (options.length !== 2) throw new TypeError(`questions[${i}]: noul takes exactly 2 options`);
|
|
27
|
+
} else if (type === "choice") {
|
|
28
|
+
options = (q.options ?? []).map(String);
|
|
29
|
+
if (options.length < 2) throw new TypeError(`questions[${i}]: choice needs at least 2 options`);
|
|
30
|
+
if (options.length > 255) throw new TypeError(`questions[${i}]: choice supports at most 255 options`);
|
|
31
|
+
} else {
|
|
32
|
+
options = (q.options ?? []).map(String);
|
|
33
|
+
if (options.length < 2 || options.length > 10) {
|
|
34
|
+
throw new TypeError(`questions[${i}]: score needs 2-10 ordered levels`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { type, instructions, options, ...(q?.criteria !== undefined ? { criteria: q.criteria } : {}) };
|
|
38
|
+
});
|
|
39
|
+
}
|
package/src/registry.mjs
CHANGED
|
@@ -1,156 +1,170 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* registry.mjs — multi-family runtime for JEV-style typed-decision models.
|
|
3
|
-
*
|
|
4
|
-
* jev-web ships three families today — open-jev (fused DeBERTa graph), Laya
|
|
5
|
-
* (ModernBERT encoder + typed head), Strands Decider 2B (
|
|
6
|
-
* head), Bekko System One v0 (cross-encoder), and Decision 2.0 (Qwen3 +
|
|
7
|
-
* candidate head) — and any third party can add its own without touching
|
|
8
|
-
* core:
|
|
9
|
-
*
|
|
10
|
-
* registerDecisionFamily("my-model", {
|
|
11
|
-
* defaults: { model: "org/my-typed-decision-ONNX", revision: "main" },
|
|
12
|
-
* create: async (opts) => ({ info, decide(state, questions), dispose? }),
|
|
13
|
-
* });
|
|
14
|
-
*
|
|
15
|
-
* const runtime = await createDecisionRuntime({ family: "my-model" });
|
|
16
|
-
*
|
|
17
|
-
* `createDecisionRuntime` tries the requested family, then any `fallback`
|
|
18
|
-
* families in order, reporting each failure through `onFallback`. The returned
|
|
19
|
-
* object always exposes the shared contract:
|
|
20
|
-
* { info: { family, ... }, decide(state, questions) -> DecideResult, dispose? }
|
|
21
|
-
* where DecideResult is `{ answers, truncated, length, prompts?, timings? }`.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { createDecider } from "./session.mjs";
|
|
25
|
-
import { DEFAULT_MODEL, DEFAULT_REVISION } from "./config.mjs";
|
|
26
|
-
import {
|
|
27
|
-
createLayaDecider,
|
|
28
|
-
LAYA_DEFAULT_MODEL,
|
|
29
|
-
LAYA_DEFAULT_REVISION,
|
|
30
|
-
LAYA_DEFAULT_SUBFOLDER,
|
|
31
|
-
} from "./laya.mjs";
|
|
32
|
-
import {
|
|
33
|
-
createStrandsDecider,
|
|
34
|
-
STRANDS_DEFAULT_MODEL,
|
|
35
|
-
STRANDS_DEFAULT_REVISION,
|
|
36
|
-
} from "./strands.mjs";
|
|
37
|
-
import {
|
|
38
|
-
createBekkoDecider,
|
|
39
|
-
BEKKO_DEFAULT_MODEL,
|
|
40
|
-
BEKKO_DEFAULT_REVISION,
|
|
41
|
-
} from "./bekko.mjs";
|
|
42
|
-
import {
|
|
43
|
-
createDecision2Decider,
|
|
44
|
-
DECISION2_DEFAULT_MODEL,
|
|
45
|
-
DECISION2_DEFAULT_REVISION,
|
|
46
|
-
} from "./decision2.mjs";
|
|
47
|
-
import {
|
|
48
|
-
createKevDecider,
|
|
49
|
-
KEV_DEFAULT_MODEL,
|
|
50
|
-
KEV_DEFAULT_REVISION,
|
|
51
|
-
} from "./kev.mjs";
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
1
|
+
/**
|
|
2
|
+
* registry.mjs — multi-family runtime for JEV-style typed-decision models.
|
|
3
|
+
*
|
|
4
|
+
* jev-web ships three families today — open-jev (fused DeBERTa graph), Laya
|
|
5
|
+
* (ModernBERT encoder + typed head), Strands Decider 2B (Qwen3.5/Gemma4 torso
|
|
6
|
+
* + pointer head), Bekko System One v0 (cross-encoder), and Decision 2.0 (Qwen3 +
|
|
7
|
+
* candidate head) — and any third party can add its own without touching
|
|
8
|
+
* core:
|
|
9
|
+
*
|
|
10
|
+
* registerDecisionFamily("my-model", {
|
|
11
|
+
* defaults: { model: "org/my-typed-decision-ONNX", revision: "main" },
|
|
12
|
+
* create: async (opts) => ({ info, decide(state, questions), dispose? }),
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* const runtime = await createDecisionRuntime({ family: "my-model" });
|
|
16
|
+
*
|
|
17
|
+
* `createDecisionRuntime` tries the requested family, then any `fallback`
|
|
18
|
+
* families in order, reporting each failure through `onFallback`. The returned
|
|
19
|
+
* object always exposes the shared contract:
|
|
20
|
+
* { info: { family, ... }, decide(state, questions) -> DecideResult, dispose? }
|
|
21
|
+
* where DecideResult is `{ answers, truncated, length, prompts?, timings? }`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { createDecider } from "./session.mjs";
|
|
25
|
+
import { DEFAULT_MODEL, DEFAULT_REVISION } from "./config.mjs";
|
|
26
|
+
import {
|
|
27
|
+
createLayaDecider,
|
|
28
|
+
LAYA_DEFAULT_MODEL,
|
|
29
|
+
LAYA_DEFAULT_REVISION,
|
|
30
|
+
LAYA_DEFAULT_SUBFOLDER,
|
|
31
|
+
} from "./laya.mjs";
|
|
32
|
+
import {
|
|
33
|
+
createStrandsDecider,
|
|
34
|
+
STRANDS_DEFAULT_MODEL,
|
|
35
|
+
STRANDS_DEFAULT_REVISION,
|
|
36
|
+
} from "./strands.mjs";
|
|
37
|
+
import {
|
|
38
|
+
createBekkoDecider,
|
|
39
|
+
BEKKO_DEFAULT_MODEL,
|
|
40
|
+
BEKKO_DEFAULT_REVISION,
|
|
41
|
+
} from "./bekko.mjs";
|
|
42
|
+
import {
|
|
43
|
+
createDecision2Decider,
|
|
44
|
+
DECISION2_DEFAULT_MODEL,
|
|
45
|
+
DECISION2_DEFAULT_REVISION,
|
|
46
|
+
} from "./decision2.mjs";
|
|
47
|
+
import {
|
|
48
|
+
createKevDecider,
|
|
49
|
+
KEV_DEFAULT_MODEL,
|
|
50
|
+
KEV_DEFAULT_REVISION,
|
|
51
|
+
} from "./kev.mjs";
|
|
52
|
+
|
|
53
|
+
import {
|
|
54
|
+
createVegaDecider,
|
|
55
|
+
VEGA_DEFAULT_MODEL,
|
|
56
|
+
VEGA_DEFAULT_REVISION,
|
|
57
|
+
} from "./vega.mjs";
|
|
58
|
+
|
|
59
|
+
const families = new Map();
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Register (or replace) a decision family.
|
|
63
|
+
* @param {string} id short stable identifier, e.g. "open-jev"
|
|
64
|
+
* @param {{create:(opts:object)=>Promise<object>, defaults?:object}} descriptor
|
|
65
|
+
*/
|
|
66
|
+
export function registerDecisionFamily(id, descriptor) {
|
|
67
|
+
if (!id || typeof id !== "string") throw new TypeError("registerDecisionFamily: id is required");
|
|
68
|
+
if (!descriptor || typeof descriptor.create !== "function") {
|
|
69
|
+
throw new TypeError(`registerDecisionFamily(${id}): descriptor.create is required`);
|
|
70
|
+
}
|
|
71
|
+
families.set(id, { defaults: {}, ...descriptor });
|
|
72
|
+
return id;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** @returns {string[]} registered family ids in registration order. */
|
|
76
|
+
export function listDecisionFamilies() {
|
|
77
|
+
return [...families.keys()];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function getDecisionFamily(id) {
|
|
81
|
+
return families.get(id) ?? null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
registerDecisionFamily("open-jev", {
|
|
85
|
+
defaults: { model: DEFAULT_MODEL, revision: DEFAULT_REVISION },
|
|
86
|
+
create: createDecider,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
registerDecisionFamily("laya", {
|
|
90
|
+
defaults: {
|
|
91
|
+
model: LAYA_DEFAULT_MODEL,
|
|
92
|
+
revision: LAYA_DEFAULT_REVISION,
|
|
93
|
+
subfolder: LAYA_DEFAULT_SUBFOLDER,
|
|
94
|
+
},
|
|
95
|
+
create: createLayaDecider,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
registerDecisionFamily("strands-decider", {
|
|
99
|
+
defaults: {
|
|
100
|
+
model: STRANDS_DEFAULT_MODEL,
|
|
101
|
+
revision: STRANDS_DEFAULT_REVISION,
|
|
102
|
+
},
|
|
103
|
+
create: createStrandsDecider,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
registerDecisionFamily("bekko", {
|
|
107
|
+
defaults: {
|
|
108
|
+
model: BEKKO_DEFAULT_MODEL,
|
|
109
|
+
revision: BEKKO_DEFAULT_REVISION,
|
|
110
|
+
},
|
|
111
|
+
create: createBekkoDecider,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
registerDecisionFamily("decision2", {
|
|
115
|
+
defaults: {
|
|
116
|
+
model: DECISION2_DEFAULT_MODEL,
|
|
117
|
+
revision: DECISION2_DEFAULT_REVISION,
|
|
118
|
+
},
|
|
119
|
+
create: createDecision2Decider,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
registerDecisionFamily("kev", {
|
|
123
|
+
defaults: {
|
|
124
|
+
model: KEV_DEFAULT_MODEL,
|
|
125
|
+
revision: KEV_DEFAULT_REVISION,
|
|
126
|
+
},
|
|
127
|
+
create: createKevDecider,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
registerDecisionFamily("vega", {
|
|
131
|
+
defaults: {
|
|
132
|
+
model: VEGA_DEFAULT_MODEL,
|
|
133
|
+
revision: VEGA_DEFAULT_REVISION,
|
|
134
|
+
},
|
|
135
|
+
create: createVegaDecider,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create a decision runtime, preferring `family` and falling back in order.
|
|
140
|
+
* @param {{family?:string, fallback?:string[], onFallback?:Function}} [opts]
|
|
141
|
+
* @returns {Promise<{info:object, decide:Function, dispose?:Function}>}
|
|
142
|
+
*/
|
|
143
|
+
export async function createDecisionRuntime({
|
|
144
|
+
family = "laya",
|
|
145
|
+
fallback = [],
|
|
146
|
+
onFallback = null,
|
|
147
|
+
...opts
|
|
148
|
+
} = {}) {
|
|
149
|
+
const order = [family, ...(Array.isArray(fallback) ? fallback : [])].filter(Boolean);
|
|
150
|
+
let lastError = null;
|
|
151
|
+
for (const id of order) {
|
|
152
|
+
const descriptor = families.get(id);
|
|
153
|
+
if (!descriptor) {
|
|
154
|
+
lastError = new Error(`unknown decision family "${id}"`);
|
|
155
|
+
onFallback?.({ family: id, error: lastError });
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const runtime = await descriptor.create({ ...descriptor.defaults, ...opts });
|
|
160
|
+
return {
|
|
161
|
+
...runtime,
|
|
162
|
+
info: { family: id, ...(runtime?.info ?? {}) },
|
|
163
|
+
};
|
|
164
|
+
} catch (error) {
|
|
165
|
+
lastError = error;
|
|
166
|
+
onFallback?.({ family: id, error });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw lastError ?? new Error("createDecisionRuntime: no family could be created");
|
|
156
170
|
}
|