@tangle-network/agent-eval 0.205.2 → 0.206.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/analyst/index.d.ts +1 -1
  3. package/dist/analyst/index.js +1 -1
  4. package/dist/{benchmark-command-DuDTN5ID.js → benchmark-command-DtP94cOB.js} +2 -2
  5. package/dist/{benchmark-command-DuDTN5ID.js.map → benchmark-command-DtP94cOB.js.map} +1 -1
  6. package/dist/cli.js +1 -1
  7. package/dist/meta-eval/index.d.ts +6 -0
  8. package/dist/meta-eval/index.d.ts.map +1 -1
  9. package/dist/meta-eval/index.js +15 -1
  10. package/dist/meta-eval/index.js.map +1 -1
  11. package/dist/openapi.json +1 -1
  12. package/dist/rl.d.ts +12 -12
  13. package/dist/rl.d.ts.map +1 -1
  14. package/dist/rl.js +17 -17
  15. package/dist/rl.js.map +1 -1
  16. package/dist/systemone-protocol-DKoi1NdK.d.ts +132 -0
  17. package/dist/systemone-protocol-DKoi1NdK.d.ts.map +1 -0
  18. package/dist/systemone-protocol.d.ts +2 -0
  19. package/dist/systemone-protocol.js +214 -0
  20. package/dist/systemone-protocol.js.map +1 -0
  21. package/dist/{jev-review-Cw06AKuF.d.ts → systemone-review-DGTKkLMG.d.ts} +16 -16
  22. package/dist/systemone-review-DGTKkLMG.d.ts.map +1 -0
  23. package/dist/{jev-review-DZEjDzlF.js → systemone-review-DTClwgmE.js} +11 -11
  24. package/dist/systemone-review-DTClwgmE.js.map +1 -0
  25. package/dist/systemone.d.ts +60 -0
  26. package/dist/systemone.d.ts.map +1 -0
  27. package/dist/{jev.js → systemone.js} +16 -16
  28. package/dist/systemone.js.map +1 -0
  29. package/docs/changes/jev-evidence-review.md +1 -1
  30. package/docs/changes/systemone-provider-neutral.md +36 -0
  31. package/docs/public-api.md +93 -68
  32. package/docs/{jev-behavior-review.md → systemone-behavior-review.md} +17 -17
  33. package/docs/{jev-integrations.md → systemone-integrations.md} +4 -4
  34. package/docs/{jev-replay.md → systemone-replay.md} +6 -6
  35. package/docs/{jev.md → systemone.md} +84 -33
  36. package/package.json +9 -9
  37. package/dist/jev-protocol-BQLxwbqD.d.ts +0 -99
  38. package/dist/jev-protocol-BQLxwbqD.d.ts.map +0 -1
  39. package/dist/jev-protocol.d.ts +0 -2
  40. package/dist/jev-protocol.js +0 -114
  41. package/dist/jev-protocol.js.map +0 -1
  42. package/dist/jev-review-Cw06AKuF.d.ts.map +0 -1
  43. package/dist/jev-review-DZEjDzlF.js.map +0 -1
  44. package/dist/jev.d.ts +0 -60
  45. package/dist/jev.d.ts.map +0 -1
  46. package/dist/jev.js.map +0 -1
@@ -1,35 +1,50 @@
1
- # Typed evaluations: products, judges, and analysts
1
+ # System One evaluations: products, judges, and analysts
2
+
3
+ System One is a typed decision protocol: a request carries `state` and named questions (noul
4
+ yes/no, choice, or score rubric), and the answer is a probability for every allowed option.
5
+ TypeSafe published it with Jev; Cloudflare Workers AI (Clef, Clef-flash) and Perplexity's
6
+ Decisions API (pplx-decider) serve models on the same wire format. This package owns what a
7
+ valid result means. It names no endpoint, credential or model. Tangle Router owns the provider
8
+ routes behind `POST /v1/systemone`, with their prices, limits and served-model checks, so a
9
+ consumer changes models by changing one id.
2
10
 
3
11
  Configure a transport once. Supply your own model, state, and questions on every
4
12
  call. Interpret the answers in ordinary code; no campaign or agent is required.
5
13
 
6
14
  ```ts
7
- import { TypeSafeClient, noul, choice } from '@typesafe-ai/sdk'
8
- import { jevEvaluator } from '@tangle-network/agent-eval/jev'
9
-
10
- const client = new TypeSafeClient({
11
- baseURL: routerOrigin, // Origin without /v1; omit to use TypeSafe directly.
12
- apiKey: scopedRouterKey,
13
- retry: { maxRetries: 0 },
14
- })
15
- const evaluate = jevEvaluator({
16
- evaluate: (request, { signal, idempotencyKey }) => client.systemOne(request, {
17
- signal,
18
- retry: { maxRetries: 0 },
19
- headers: { 'Idempotency-Key': idempotencyKey },
20
- }),
15
+ import { systemOneEvaluator } from '@tangle-network/agent-eval/systemone'
16
+
17
+ const evaluate = systemOneEvaluator({
18
+ evaluate: async (request, { signal, idempotencyKey }) => {
19
+ const response = await fetch(`${routerOrigin}/v1/systemone`, {
20
+ method: 'POST',
21
+ signal,
22
+ headers: {
23
+ Authorization: `Bearer ${scopedRouterKey}`,
24
+ 'Content-Type': 'application/json',
25
+ 'Idempotency-Key': idempotencyKey,
26
+ },
27
+ body: JSON.stringify(request),
28
+ })
29
+ if (!response.ok) throw new Error(`System One request failed: ${response.status}`)
30
+ return response.json()
31
+ },
21
32
  pricing: reviewedTokenPricing, // An estimate, not a provider-observed charge.
22
33
  })
23
34
 
24
35
  const result = await evaluate({
25
- model: selectedModel,
36
+ model: selectedModel, // a native_evaluation id from Router's /v1/models
26
37
  state: { requirements, evidence },
27
38
  questions: {
28
- supported: noul('Does the evidence support the claim?'),
29
- next: choice({ task: 'Choose the next step using the evidence' }, {
30
- inspect: { when: 'Evidence is missing or contradictory' },
31
- finish: { when: 'The requirements are satisfied' },
32
- }),
39
+ supported: { type: 'noul', instructions: 'Does the evidence support the claim?' },
40
+ next: {
41
+ type: 'choice',
42
+ instructions: 'Choose the next step using the evidence',
43
+ criteria: {
44
+ inspect: 'Evidence is missing or contradictory',
45
+ finish: 'The requirements are satisfied',
46
+ },
47
+ },
33
48
  },
34
49
  }, { signal, costLedger })
35
50
 
@@ -38,21 +53,56 @@ result.value.answers.next.probabilities.inspect // number
38
53
  result.receipt // Existing CostLedger receipt, separate from native answers.
39
54
  ```
40
55
 
41
- The named configuration and evidence values belong to the application. Plain JSON
42
- questions work too; SDK builders are optional. Structured criteria, null instructions and choice or noul
56
+ The named configuration and evidence values belong to the application. TypeSafe's SDK
57
+ builders produce the same JSON and are optional. Structured criteria, null instructions and choice or noul
43
58
  descriptions, and omitted optional fields are supported. `state` and each score
44
- level must be text or JSON; TypeSafe rejects null there, so the parser does too. Static TypeScript questions preserve
59
+ level must be text or JSON; TypeSafe rejects null there, so the parser does too. Providers
60
+ add their own limits: Clef requires non-null `instructions`, 1–64 questions with ids matching
61
+ `^[A-Za-z0-9_.-]{1,100}$`, and 2–255 choice options. Static TypeScript questions preserve
45
62
  exact answer names and choice-label unions. Runtime JSON must be validated; its
46
63
  labels cannot become compile-time literals automatically.
47
64
 
48
- ## Any classifier, not just Jev
65
+ ## Providers, rounding and confidence
66
+
67
+ The wire format is the same for every provider; how each reports its numbers is not. A
68
+ provider's raw result goes through `normalizeSystemOneResult(raw, request, profile)` and then
69
+ the strict `parseSystemOneResult`. The route owner supplies the profile.
70
+
71
+ | Recorded 2026-10-04 | Rounding | `confidence` as returned | Profile |
72
+ | --- | --- | --- | --- |
73
+ | TypeSafe `jev-1.13.0` | 2 decimals. Probabilities sum to 1; the score is not always their expectation (8.79 against 8.86). | `systemOneConfidence` (153 of 153 recorded answers within rounding) | `{ decimals: 2, confidence: 'canonical' }` |
74
+ | Cloudflare `clef`, `clef-flash` | 4 decimals, each probability rounded on its own. Wrapped in Workers AI's `{ result, success, errors, messages }`. | (Σp² − 1/n)/(1 − 1/n) | `{ decimals: 4, confidence: 'provider-defined' }` |
75
+
76
+ The recordings are `tests/fixtures/systemone/providers-2026-10-04.json`, checked by
77
+ `tests/systemone-providers.test.ts`. Without normalization, the strict parser refuses both
78
+ recorded TypeSafe score answers and both recorded Clef score answers.
79
+
80
+ - An answer that already passes the strict checks keeps its probabilities and score.
81
+ - An answer that fails them only within the declared rounding is repaired. A distribution whose
82
+ sum is more than 1e-4 from 1 is divided by its sum. A score becomes the expectation of the
83
+ distribution.
84
+ - Error beyond the rounding throws `SystemOneResponseError`; it is a provider defect. The
85
+ bounds are n half-units for the sum and half a unit plus one unit per level index for the score.
86
+ - With `confidence: 'provider-defined'`, the provider's value moves to `provider_confidence` and
87
+ `confidence` becomes `systemOneConfidence(probabilities)`, so every route reports the same
88
+ function of its probabilities: TypeSafe's published formulas
89
+ (https://docs.typesafe.ai/confidence).
90
+ - Noul answers never change. `noul` is the probability of yes for every provider.
91
+
92
+ Probabilities are not made equivalent across models. A Clef 0.8 and a Jev 0.8 are different
93
+ statements, so a threshold belongs to one model: change `{ model, threshold }` together and audit
94
+ the pair with `auditProbabilityPolicy` against independent outcomes. `brierScore` and
95
+ `calibrationFromPairs` (with `range: { lo: 0, hi: 1 }`; `binning: 'equal-frequency'` for
96
+ equal-mass bins) in `/meta-eval` report calibration on the same pairs.
97
+
98
+ ## Any classifier, not only System One
49
99
 
50
100
  `createEvaluator`, `asJudge`, and `asAnalyst` are provider-independent exports from
51
- `@tangle-network/agent-eval/evaluation` (also re-exported from `/jev`).
101
+ `@tangle-network/agent-eval/evaluation` (also re-exported from `/systemone`).
52
102
  `createEvaluator({ execute, receipt })` meters a typed asynchronous function.
53
- It does not require a Jev response, probability output, prompt, or provider SDK.
103
+ It does not require a System One response, probability output, prompt, or provider SDK.
54
104
 
55
- `jevEvaluator` adds native protocol validation and optional `acceptModel(requested,
105
+ `systemOneEvaluator` adds native protocol validation and optional `acceptModel(requested,
56
106
  served)` policy. It preserves the reported model instead of guessing alias rules.
57
107
  Router independently enforces its supported model and pricing policy.
58
108
 
@@ -69,13 +119,13 @@ and analyst context. `map(value, input, analystContext)` creates evidence-backed
69
119
  findings. Use the existing `makeFinding` factory and real event/span/artifact references.
70
120
  An empty finding list is valid; an inference failure is not a clean review.
71
121
 
72
- The optional `jevJudge` and `jevAnalyst` conveniences accept static questions or a
122
+ The optional `systemOneJudge` and `systemOneAnalyst` conveniences accept static questions or a
73
123
  question-producing function. State renderers may be async. Judge renderers receive
74
124
  the evaluation context; analyst renderers and finding mappers receive the effective
75
125
  cancellation/deadline signal. Rendering must honor that signal during external work.
76
126
  Both conveniences accept a `record` callback for the complete observation.
77
127
 
78
- For a numeric rubric, `jevJudge` defaults to `normalizedJevScore`: equally spaced
128
+ For a numeric rubric, `systemOneJudge` defaults to `normalizedSystemOneScore`: equally spaced
79
129
  score expectations on [0,1], noul values unchanged, and an optional weighted mean.
80
130
  It emits no canned notes. Supply `map` for choice utilities, different scales, or
81
131
  critical-failure policies. Dynamic questions and custom mappings require explicit
@@ -98,7 +148,7 @@ none is supplied, an explicit `budgetUsd` creates an account for that invocation
98
148
  Otherwise the evaluator keeps its configured ledger, including its cumulative cap
99
149
  across direct calls, judges, and analysts. An absent analyst budget never creates a
100
150
  fresh unlimited account that overrides the configured one. This precedence is the
101
- same for Jev and other classifiers. A per-analysis budget replaces the default
151
+ same for System One and other classifiers. A per-analysis budget replaces the default
102
152
  account; it is not an additional child limit. Pass the shared run ledger explicitly
103
153
  when the whole run must conserve one authority. Only trusted host code should choose
104
154
  or replace that authority.
@@ -132,10 +182,11 @@ confidence as demonstrated calibration or a classification as proof of root caus
132
182
 
133
183
  ## Checks
134
184
 
135
- Run `pnpm exec vitest run tests/jev*.test.ts tests/evaluation-authority.test.ts`,
185
+ Run `pnpm exec vitest run tests/systemone*.test.ts tests/evaluation-authority.test.ts`,
136
186
  `pnpm typecheck`, `pnpm build`, and `pnpm verify:package`. These are contract and
137
187
  execution checks, not live-provider quality or deployment evidence. The product-integrity
138
188
  rubric lives in `examples/`, not the generic implementation, and is not a default policy.
139
189
 
140
190
  Protocol: https://docs.typesafe.ai/api
141
- SDK: https://github.com/typesafe-ai/typesafe-sdk-js
191
+ Clef: https://developers.cloudflare.com/workers-ai/models/clef/
192
+ SDK (optional builders): https://github.com/typesafe-ai/typesafe-sdk-js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-eval",
3
- "version": "0.205.2",
3
+ "version": "0.206.0",
4
4
  "description": "Evaluate and improve AI agents from runs, traces, judges, and feedback. Compare candidates, cluster failures, measure lift, and gate releases.",
5
5
  "homepage": "https://github.com/tangle-network/agent-eval#readme",
6
6
  "repository": {
@@ -19,10 +19,10 @@
19
19
  "import": "./dist/index.js",
20
20
  "default": "./dist/index.js"
21
21
  },
22
- "./jev": {
23
- "types": "./dist/jev.d.ts",
24
- "import": "./dist/jev.js",
25
- "default": "./dist/jev.js"
22
+ "./systemone": {
23
+ "types": "./dist/systemone.d.ts",
24
+ "import": "./dist/systemone.js",
25
+ "default": "./dist/systemone.js"
26
26
  },
27
27
  "./profile-cell": {
28
28
  "types": "./dist/profile-cell.d.ts",
@@ -177,10 +177,10 @@
177
177
  "import": "./dist/evaluation.js",
178
178
  "default": "./dist/evaluation.js"
179
179
  },
180
- "./jev/protocol": {
181
- "types": "./dist/jev-protocol.d.ts",
182
- "import": "./dist/jev-protocol.js",
183
- "default": "./dist/jev-protocol.js"
180
+ "./systemone/protocol": {
181
+ "types": "./dist/systemone-protocol.d.ts",
182
+ "import": "./dist/systemone-protocol.js",
183
+ "default": "./dist/systemone-protocol.js"
184
184
  }
185
185
  },
186
186
  "bin": {
@@ -1,99 +0,0 @@
1
- //#region src/jev-protocol.d.ts
2
- /**
3
- * Structural equivalents of TypeSafe's published request schema
4
- * (https://api.typesafe.ai/openapi.json); callers can use its builders unchanged.
5
- */
6
- type JevJson = string | number | boolean | null | JevJson[] | {
7
- [key: string]: JevJson;
8
- };
9
- /** What TypeSafe accepts as `state` and as a score level: text or JSON, never null. */
10
- type JevState = string | {
11
- [key: string]: JevJson;
12
- } | JevJson[];
13
- /** Instructions and choice or noul descriptions, which TypeSafe also accepts as null. */
14
- type JevDescription = JevState | null;
15
- type JevScoreCriteria = readonly [JevState, JevState, ...JevState[]];
16
- type JevQuestion = {
17
- type: 'noul';
18
- instructions?: JevDescription;
19
- criteria?: {
20
- true?: JevDescription;
21
- false?: JevDescription;
22
- } | null;
23
- } | {
24
- type: 'score';
25
- instructions?: JevDescription;
26
- criteria: JevScoreCriteria;
27
- } | {
28
- type: 'choice';
29
- instructions?: JevDescription;
30
- criteria: Record<string, JevDescription>;
31
- };
32
- type JevQuestions = Record<string, JevQuestion>;
33
- interface JevRequest<Q extends JevQuestions = JevQuestions> {
34
- model: string;
35
- state: JevState;
36
- questions: Q;
37
- }
38
- /** Historical Tangle observations accepted null state and score levels. Not a live request. */
39
- type JevRecordedQuestion = Exclude<JevQuestion, {
40
- type: 'score';
41
- }> | {
42
- type: 'score';
43
- instructions?: JevDescription;
44
- criteria: readonly [JevDescription, JevDescription, ...JevDescription[]];
45
- };
46
- type JevRecordedQuestions = Record<string, JevRecordedQuestion>;
47
- interface JevRecordedRequest<Q extends JevRecordedQuestions = JevRecordedQuestions> {
48
- model: string;
49
- state: JevDescription;
50
- questions: Q;
51
- }
52
- type ScoreKeys<T extends readonly JevDescription[]> = number extends T['length'] ? number : Extract<keyof T, `${number}`>;
53
- type JevAnswerFor<Q extends JevRecordedQuestion> = Q extends {
54
- type: 'noul';
55
- } ? {
56
- readonly type: 'noul';
57
- readonly noul: number;
58
- } : Q extends {
59
- type: 'score';
60
- criteria: infer C extends readonly JevDescription[];
61
- } ? {
62
- readonly type: 'score';
63
- readonly score: number;
64
- readonly confidence: number;
65
- readonly legend: { readonly [K in ScoreKeys<C>]: C[K]; };
66
- readonly probabilities: { readonly [K in ScoreKeys<C>]: number; };
67
- } : Q extends {
68
- type: 'choice';
69
- criteria: infer C;
70
- } ? {
71
- readonly type: 'choice';
72
- readonly choice: keyof C & string;
73
- readonly confidence: number;
74
- readonly probabilities: { readonly [K in keyof C]: number; };
75
- } : never;
76
- type JevAnswer = JevAnswerFor<JevQuestion>;
77
- interface JevResult<Q extends JevRecordedQuestions = JevQuestions> {
78
- readonly model: string;
79
- readonly answers: { readonly [K in keyof Q]: JevAnswerFor<Q[K]>; };
80
- readonly usage: {
81
- readonly input_tokens: number;
82
- readonly output_tokens: number;
83
- };
84
- }
85
- declare class JevResponseError extends Error {
86
- constructor(message: string);
87
- }
88
- /** Validate named questions without a state, as a judge does before it has input. */
89
- declare function parseJevQuestions(raw: unknown): JevQuestions;
90
- declare function parseJevRequest(raw: unknown): JevRequest;
91
- /** Read immutable historical evidence without rewriting it or authorizing new inference.
92
- * Live transports must still call parseJevRequest, even for re-execution of an old request. */
93
- declare function parseJevRecordedRequest(raw: unknown): JevRecordedRequest;
94
- declare function jevUsage(raw: unknown): JevResult['usage'];
95
- /** Validate native evidence, not model alias policy or the caller's scoring rules. */
96
- declare function parseJevResult<Q extends JevRecordedQuestions>(raw: unknown, request: JevRecordedRequest<Q>): JevResult<Q>;
97
- //#endregion
98
- export { parseJevRecordedRequest as _, JevQuestion as a, JevRecordedQuestions as c, JevResponseError as d, JevResult as f, parseJevQuestions as g, jevUsage as h, JevJson as i, JevRecordedRequest as l, JevState as m, JevAnswerFor as n, JevQuestions as o, JevScoreCriteria as p, JevDescription as r, JevRecordedQuestion as s, JevAnswer as t, JevRequest as u, parseJevRequest as v, parseJevResult as y };
99
- //# sourceMappingURL=jev-protocol-BQLxwbqD.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev-protocol-BQLxwbqD.d.ts","names":[],"sources":["../src/jev-protocol.ts"],"mappings":";;;;;KAOY,6CAA6C;GAAe,cAAc;;;KAE1E;GAAuB,cAAc;IAAY;;KAEjD,iBAAiB;KACjB,6BAA6B,UAAU,aAAa;KACpD;EAEN;EACA,eAAe;EACf;IAAa,OAAO;IAAgB,QAAQ;;;EAE5C;EAAe,eAAe;EAAgB,UAAU;;EACxD;EAAgB,eAAe;EAAgB,UAAU,eAAe;;KAClE,eAAe,eAAe;UACzB,WAAW,UAAU,eAAe;EACnD;EACA,OAAO;EACP,WAAW;;;KAID,sBACR,QAAQ;EAAe;;EAErB;EACA,eAAe;EACf,oBAAoB,gBAAgB,mBAAmB;;KAEjD,uBAAuB,eAAe;UACjC,mBAAmB,UAAU,uBAAuB;EACnE;EACA,OAAO;EACP,WAAW;;KAGR,UAAU,mBAAmB,mCAAmC,uBAEjE,cAAc;KACN,aAAa,UAAU,uBAAuB;EAAY;;WACvD;WAAuB;IAClC;EAAY;EAAe,gBAAgB,mBAAmB;;WAEjD;WACA;WACA;WACA,oBAAoB,KAAK,UAAU,KAAK,EAAE;WAC1C,2BAA2B,KAAK,UAAU;IAErD;EAAY;EAAgB,gBAAgB;;WAE/B;WACA,cAAc;WACd;WACA,2BAA2B,WAAW;;KAG7C,YAAY,aAAa;UACpB,UAAU,UAAU,uBAAuB;WACjD;WACA,qBAAqB,WAAW,IAAI,aAAa,EAAE;WACnD;aAAkB;aAA+B;;;cAG/C,yBAAyB;EACpC,YAAY;;;iBA6BE,kBAAkB,eAAe;iBAyCjC,gBAAgB,eAAe;;;iBAM/B,wBAAwB,eAAe;iBAevC,SAAS,eAAe;;iBA2BxB,eAAe,UAAU,sBACvC,cACA,SAAS,mBAAmB,KAC3B,UAAU"}
@@ -1,2 +0,0 @@
1
- import { _ as parseJevRecordedRequest, a as JevQuestion, c as JevRecordedQuestions, d as JevResponseError, f as JevResult, g as parseJevQuestions, h as jevUsage, i as JevJson, l as JevRecordedRequest, m as JevState, n as JevAnswerFor, o as JevQuestions, p as JevScoreCriteria, r as JevDescription, s as JevRecordedQuestion, t as JevAnswer, u as JevRequest, v as parseJevRequest, y as parseJevResult } from "./jev-protocol-BQLxwbqD.js";
2
- export { JevAnswer, JevAnswerFor, JevDescription, JevJson, JevQuestion, JevQuestions, JevRecordedQuestion, JevRecordedQuestions, JevRecordedRequest, JevRequest, JevResponseError, JevResult, JevScoreCriteria, JevState, jevUsage, parseJevQuestions, parseJevRecordedRequest, parseJevRequest, parseJevResult };
@@ -1,114 +0,0 @@
1
- import { r as canonicalString } from "./canonical-BsZVgBhW.js";
2
- import { isDeepStrictEqual } from "node:util";
3
- //#region src/jev-protocol.ts
4
- var JevResponseError = class extends Error {
5
- constructor(message) {
6
- super(`Jev: ${message}`);
7
- this.name = "JevResponseError";
8
- }
9
- };
10
- function object(value) {
11
- if (value === null || typeof value !== "object" || Array.isArray(value)) throw new JevResponseError("Expected an object");
12
- return value;
13
- }
14
- function textOrJson(value, name) {
15
- if (typeof value === "string") return;
16
- if (value === null || typeof value !== "object") throw new TypeError(`${name} must be text or a JSON object/array`);
17
- canonicalString(value);
18
- }
19
- function optionalTextOrJson(value, name) {
20
- if (value !== null) textOrJson(value, name);
21
- }
22
- /** Validate named questions without a state, as a judge does before it has input. */
23
- function parseJevQuestions(raw) {
24
- return readQuestions(raw, false);
25
- }
26
- function readQuestions(raw, recorded) {
27
- const questions = object(raw);
28
- if (!Object.keys(questions).length) throw new TypeError("At least one question is required");
29
- for (const [name, rawQuestion] of Object.entries(questions)) {
30
- const question = object(rawQuestion);
31
- if (question.instructions !== void 0) optionalTextOrJson(question.instructions, `${name} instructions`);
32
- if (question.type === "noul") {
33
- if (question.criteria != null) {
34
- const criteria = object(question.criteria);
35
- if (Object.keys(criteria).some((key) => key !== "true" && key !== "false")) throw new TypeError("Noul criteria must describe true and/or false");
36
- for (const value of Object.values(criteria)) if (value !== void 0) optionalTextOrJson(value, `${name} criteria`);
37
- }
38
- } else if (question.type === "score") {
39
- if (!Array.isArray(question.criteria) || question.criteria.length < 2) throw new TypeError("Score criteria require at least two ordered entries");
40
- for (const value of question.criteria) {
41
- if (recorded && value === null) continue;
42
- textOrJson(value, `${name} score level`);
43
- }
44
- } else if (question.type === "choice") {
45
- const criteria = object(question.criteria);
46
- if (!Object.keys(criteria).length) throw new TypeError("Choice criteria cannot be empty");
47
- for (const value of Object.values(criteria)) optionalTextOrJson(value, `${name} choice`);
48
- } else throw new TypeError("Unsupported native question type");
49
- }
50
- return raw;
51
- }
52
- function parseJevRequest(raw) {
53
- return readRequest(raw, false);
54
- }
55
- /** Read immutable historical evidence without rewriting it or authorizing new inference.
56
- * Live transports must still call parseJevRequest, even for re-execution of an old request. */
57
- function parseJevRecordedRequest(raw) {
58
- canonicalString(raw);
59
- return readRequest(raw, true);
60
- }
61
- function readRequest(raw, recorded) {
62
- const request = object(raw);
63
- if (typeof request.model !== "string" || !request.model.trim()) throw new TypeError("Evaluation requires a model");
64
- if (!recorded || request.state !== null) textOrJson(request.state, "Evaluation state");
65
- readQuestions(request.questions, recorded);
66
- return raw;
67
- }
68
- function jevUsage(raw) {
69
- const usage = object(object(raw).usage);
70
- for (const key of ["input_tokens", "output_tokens"]) if (!Number.isSafeInteger(usage[key]) || Number(usage[key]) < 0) throw new JevResponseError("Missing or invalid usage");
71
- return usage;
72
- }
73
- function probability(value) {
74
- if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) throw new JevResponseError("Invalid probability");
75
- return value;
76
- }
77
- function exactKeys(value, names) {
78
- if (Object.keys(value).length !== names.length || names.some((key) => !Object.hasOwn(value, key))) throw new JevResponseError("Response keys differ from the request");
79
- }
80
- /** Validate native evidence, not model alias policy or the caller's scoring rules. */
81
- function parseJevResult(raw, request) {
82
- canonicalString(raw);
83
- const response = object(raw);
84
- if (typeof response.model !== "string" || !response.model.trim()) throw new JevResponseError("Missing served model");
85
- const answers = object(response.answers);
86
- exactKeys(answers, Object.keys(request.questions));
87
- for (const [name, question] of Object.entries(request.questions)) {
88
- const answer = object(answers[name]);
89
- if (answer.type !== question.type) throw new JevResponseError("Wrong answer type");
90
- if (question.type === "noul") {
91
- probability(answer.noul);
92
- continue;
93
- }
94
- probability(answer.confidence);
95
- const levels = question.type === "score" ? question.criteria.map((_, index) => String(index)) : Object.keys(question.criteria);
96
- const probabilities = object(answer.probabilities);
97
- exactKeys(probabilities, levels);
98
- const sum = levels.reduce((total, level) => total + probability(probabilities[level]), 0);
99
- if (Math.abs(sum - 1) > 1e-4) throw new JevResponseError("Invalid probability distribution");
100
- if (question.type === "score") {
101
- const legend = object(answer.legend);
102
- exactKeys(legend, levels);
103
- if (levels.some((level) => !isDeepStrictEqual(legend[level], question.criteria[Number(level)]))) throw new JevResponseError("Provider changed the rubric");
104
- const expected = levels.reduce((total, level) => total + Number(level) * Number(probabilities[level]), 0);
105
- if (typeof answer.score !== "number" || !Number.isFinite(answer.score) || Math.abs(answer.score - expected) > 1e-4) throw new JevResponseError("Score differs from its distribution");
106
- } else if (typeof answer.choice !== "string" || !Object.hasOwn(probabilities, answer.choice) || levels.some((level) => Number(probabilities[level]) > Number(probabilities[String(answer.choice)]) + 1e-4)) throw new JevResponseError("Choice is not a highest-probability option");
107
- }
108
- jevUsage(raw);
109
- return raw;
110
- }
111
- //#endregion
112
- export { JevResponseError, jevUsage, parseJevQuestions, parseJevRecordedRequest, parseJevRequest, parseJevResult };
113
-
114
- //# sourceMappingURL=jev-protocol.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev-protocol.js","names":[],"sources":["../src/jev-protocol.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util'\nimport { canonicalString } from './ledger-core/canonical'\n\n/**\n * Structural equivalents of TypeSafe's published request schema\n * (https://api.typesafe.ai/openapi.json); callers can use its builders unchanged.\n */\nexport type JevJson = string | number | boolean | null | JevJson[] | { [key: string]: JevJson }\n/** What TypeSafe accepts as `state` and as a score level: text or JSON, never null. */\nexport type JevState = string | { [key: string]: JevJson } | JevJson[]\n/** Instructions and choice or noul descriptions, which TypeSafe also accepts as null. */\nexport type JevDescription = JevState | null\nexport type JevScoreCriteria = readonly [JevState, JevState, ...JevState[]]\nexport type JevQuestion =\n | {\n type: 'noul'\n instructions?: JevDescription\n criteria?: { true?: JevDescription; false?: JevDescription } | null\n }\n | { type: 'score'; instructions?: JevDescription; criteria: JevScoreCriteria }\n | { type: 'choice'; instructions?: JevDescription; criteria: Record<string, JevDescription> }\nexport type JevQuestions = Record<string, JevQuestion>\nexport interface JevRequest<Q extends JevQuestions = JevQuestions> {\n model: string\n state: JevState\n questions: Q\n}\n\n/** Historical Tangle observations accepted null state and score levels. Not a live request. */\nexport type JevRecordedQuestion =\n | Exclude<JevQuestion, { type: 'score' }>\n | {\n type: 'score'\n instructions?: JevDescription\n criteria: readonly [JevDescription, JevDescription, ...JevDescription[]]\n }\nexport type JevRecordedQuestions = Record<string, JevRecordedQuestion>\nexport interface JevRecordedRequest<Q extends JevRecordedQuestions = JevRecordedQuestions> {\n model: string\n state: JevDescription\n questions: Q\n}\n\ntype ScoreKeys<T extends readonly JevDescription[]> = number extends T['length']\n ? number\n : Extract<keyof T, `${number}`>\nexport type JevAnswerFor<Q extends JevRecordedQuestion> = Q extends { type: 'noul' }\n ? { readonly type: 'noul'; readonly noul: number }\n : Q extends { type: 'score'; criteria: infer C extends readonly JevDescription[] }\n ? {\n readonly type: 'score'\n readonly score: number\n readonly confidence: number\n readonly legend: { readonly [K in ScoreKeys<C>]: C[K] }\n readonly probabilities: { readonly [K in ScoreKeys<C>]: number }\n }\n : Q extends { type: 'choice'; criteria: infer C }\n ? {\n readonly type: 'choice'\n readonly choice: keyof C & string\n readonly confidence: number\n readonly probabilities: { readonly [K in keyof C]: number }\n }\n : never\nexport type JevAnswer = JevAnswerFor<JevQuestion>\nexport interface JevResult<Q extends JevRecordedQuestions = JevQuestions> {\n readonly model: string\n readonly answers: { readonly [K in keyof Q]: JevAnswerFor<Q[K]> }\n readonly usage: { readonly input_tokens: number; readonly output_tokens: number }\n}\n\nexport class JevResponseError extends Error {\n constructor(message: string) {\n super(`Jev: ${message}`)\n this.name = 'JevResponseError'\n }\n}\n\nfunction object(value: unknown): Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new JevResponseError('Expected an object')\n }\n return value as Record<string, unknown>\n}\n\n// TypeSafe answers 422 for a null state or score level. Refusing it here reports the\n// caller's mistake before a paid round trip, not as a provider failure after one.\nfunction textOrJson(value: unknown, name: string): void {\n if (typeof value === 'string') return\n if (value === null || typeof value !== 'object') {\n throw new TypeError(`${name} must be text or a JSON object/array`)\n }\n // Reuse the canonical JSON validator rather than accepting lossy Map/Date coercions.\n canonicalString(value)\n}\n\nfunction optionalTextOrJson(value: unknown, name: string): void {\n if (value !== null) textOrJson(value, name)\n}\n\n/** Validate named questions without a state, as a judge does before it has input. */\nexport function parseJevQuestions(raw: unknown): JevQuestions {\n return readQuestions(raw, false) as JevQuestions\n}\n\nfunction readQuestions(raw: unknown, recorded: boolean): JevRecordedQuestions {\n const questions = object(raw)\n if (!Object.keys(questions).length) throw new TypeError('At least one question is required')\n for (const [name, rawQuestion] of Object.entries(questions)) {\n const question = object(rawQuestion)\n if (question.instructions !== undefined) {\n optionalTextOrJson(question.instructions, `${name} instructions`)\n }\n if (question.type === 'noul') {\n if (question.criteria != null) {\n const criteria = object(question.criteria)\n if (Object.keys(criteria).some((key) => key !== 'true' && key !== 'false')) {\n throw new TypeError('Noul criteria must describe true and/or false')\n }\n for (const value of Object.values(criteria)) {\n if (value !== undefined) optionalTextOrJson(value, `${name} criteria`)\n }\n }\n } else if (question.type === 'score') {\n if (!Array.isArray(question.criteria) || question.criteria.length < 2) {\n throw new TypeError('Score criteria require at least two ordered entries')\n }\n for (const value of question.criteria) {\n if (recorded && value === null) continue\n textOrJson(value, `${name} score level`)\n }\n } else if (question.type === 'choice') {\n const criteria = object(question.criteria)\n if (!Object.keys(criteria).length) throw new TypeError('Choice criteria cannot be empty')\n for (const value of Object.values(criteria)) optionalTextOrJson(value, `${name} choice`)\n } else {\n throw new TypeError('Unsupported native question type')\n }\n }\n return raw as JevRecordedQuestions\n}\n\nexport function parseJevRequest(raw: unknown): JevRequest {\n return readRequest(raw, false) as JevRequest\n}\n\n/** Read immutable historical evidence without rewriting it or authorizing new inference.\n * Live transports must still call parseJevRequest, even for re-execution of an old request. */\nexport function parseJevRecordedRequest(raw: unknown): JevRecordedRequest {\n canonicalString(raw)\n return readRequest(raw, true)\n}\n\nfunction readRequest(raw: unknown, recorded: boolean): JevRecordedRequest {\n const request = object(raw)\n if (typeof request.model !== 'string' || !request.model.trim()) {\n throw new TypeError('Evaluation requires a model')\n }\n if (!recorded || request.state !== null) textOrJson(request.state, 'Evaluation state')\n readQuestions(request.questions, recorded)\n return raw as JevRecordedRequest\n}\n\nexport function jevUsage(raw: unknown): JevResult['usage'] {\n const usage = object(object(raw).usage)\n for (const key of ['input_tokens', 'output_tokens']) {\n if (!Number.isSafeInteger(usage[key]) || Number(usage[key]) < 0) {\n throw new JevResponseError('Missing or invalid usage')\n }\n }\n return usage as unknown as JevResult['usage']\n}\n\nfunction probability(value: unknown): number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1) {\n throw new JevResponseError('Invalid probability')\n }\n return value\n}\n\nfunction exactKeys(value: Record<string, unknown>, names: string[]): void {\n if (\n Object.keys(value).length !== names.length ||\n names.some((key) => !Object.hasOwn(value, key))\n ) {\n throw new JevResponseError('Response keys differ from the request')\n }\n}\n\n/** Validate native evidence, not model alias policy or the caller's scoring rules. */\nexport function parseJevResult<Q extends JevRecordedQuestions>(\n raw: unknown,\n request: JevRecordedRequest<Q>,\n): JevResult<Q> {\n // Preserve extension metadata, rejecting values that change when persisted as JSON.\n canonicalString(raw)\n const response = object(raw)\n if (typeof response.model !== 'string' || !response.model.trim()) {\n throw new JevResponseError('Missing served model')\n }\n const answers = object(response.answers)\n exactKeys(answers, Object.keys(request.questions))\n for (const [name, question] of Object.entries(request.questions)) {\n const answer = object(answers[name])\n if (answer.type !== question.type) throw new JevResponseError('Wrong answer type')\n if (question.type === 'noul') {\n probability(answer.noul)\n continue\n }\n probability(answer.confidence)\n const levels =\n question.type === 'score'\n ? question.criteria.map((_, index) => String(index))\n : Object.keys(question.criteria)\n const probabilities = object(answer.probabilities)\n exactKeys(probabilities, levels)\n const sum = levels.reduce((total, level) => total + probability(probabilities[level]), 0)\n if (Math.abs(sum - 1) > 0.0001) throw new JevResponseError('Invalid probability distribution')\n if (question.type === 'score') {\n const legend = object(answer.legend)\n exactKeys(legend, levels)\n if (\n levels.some((level) => !isDeepStrictEqual(legend[level], question.criteria[Number(level)]))\n ) {\n throw new JevResponseError('Provider changed the rubric')\n }\n const expected = levels.reduce(\n (total, level) => total + Number(level) * Number(probabilities[level]),\n 0,\n )\n if (\n typeof answer.score !== 'number' ||\n !Number.isFinite(answer.score) ||\n Math.abs(answer.score - expected) > 0.0001\n ) {\n throw new JevResponseError('Score differs from its distribution')\n }\n } else if (\n typeof answer.choice !== 'string' ||\n !Object.hasOwn(probabilities, answer.choice) ||\n levels.some(\n (level) =>\n Number(probabilities[level]) > Number(probabilities[String(answer.choice)]) + 0.0001,\n )\n ) {\n throw new JevResponseError('Choice is not a highest-probability option')\n }\n }\n jevUsage(raw)\n return raw as JevResult<Q>\n}\n"],"mappings":";;;AAuEA,IAAa,mBAAb,cAAsC,MAAM;CAC1C,YAAY,SAAiB;EAC3B,MAAM,QAAQ,SAAS;EACvB,KAAK,OAAO;CACd;AACF;AAEA,SAAS,OAAO,OAAyC;CACvD,IAAI,UAAU,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GACpE,MAAM,IAAI,iBAAiB,oBAAoB;CAEjD,OAAO;AACT;AAIA,SAAS,WAAW,OAAgB,MAAoB;CACtD,IAAI,OAAO,UAAU,UAAU;CAC/B,IAAI,UAAU,QAAQ,OAAO,UAAU,UACrC,MAAM,IAAI,UAAU,GAAG,KAAK,qCAAqC;CAGnE,gBAAgB,KAAK;AACvB;AAEA,SAAS,mBAAmB,OAAgB,MAAoB;CAC9D,IAAI,UAAU,MAAM,WAAW,OAAO,IAAI;AAC5C;;AAGA,SAAgB,kBAAkB,KAA4B;CAC5D,OAAO,cAAc,KAAK,KAAK;AACjC;AAEA,SAAS,cAAc,KAAc,UAAyC;CAC5E,MAAM,YAAY,OAAO,GAAG;CAC5B,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,QAAQ,MAAM,IAAI,UAAU,mCAAmC;CAC3F,KAAK,MAAM,CAAC,MAAM,gBAAgB,OAAO,QAAQ,SAAS,GAAG;EAC3D,MAAM,WAAW,OAAO,WAAW;EACnC,IAAI,SAAS,iBAAiB,KAAA,GAC5B,mBAAmB,SAAS,cAAc,GAAG,KAAK,cAAc;EAElE,IAAI,SAAS,SAAS,QAChB;OAAA,SAAS,YAAY,MAAM;IAC7B,MAAM,WAAW,OAAO,SAAS,QAAQ;IACzC,IAAI,OAAO,KAAK,QAAQ,CAAC,CAAC,MAAM,QAAQ,QAAQ,UAAU,QAAQ,OAAO,GACvE,MAAM,IAAI,UAAU,+CAA+C;IAErE,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GACxC,IAAI,UAAU,KAAA,GAAW,mBAAmB,OAAO,GAAG,KAAK,UAAU;GAEzE;SACK,IAAI,SAAS,SAAS,SAAS;GACpC,IAAI,CAAC,MAAM,QAAQ,SAAS,QAAQ,KAAK,SAAS,SAAS,SAAS,GAClE,MAAM,IAAI,UAAU,qDAAqD;GAE3E,KAAK,MAAM,SAAS,SAAS,UAAU;IACrC,IAAI,YAAY,UAAU,MAAM;IAChC,WAAW,OAAO,GAAG,KAAK,aAAa;GACzC;EACF,OAAO,IAAI,SAAS,SAAS,UAAU;GACrC,MAAM,WAAW,OAAO,SAAS,QAAQ;GACzC,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,QAAQ,MAAM,IAAI,UAAU,iCAAiC;GACxF,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,GAAG,mBAAmB,OAAO,GAAG,KAAK,QAAQ;EACzF,OACE,MAAM,IAAI,UAAU,kCAAkC;CAE1D;CACA,OAAO;AACT;AAEA,SAAgB,gBAAgB,KAA0B;CACxD,OAAO,YAAY,KAAK,KAAK;AAC/B;;;AAIA,SAAgB,wBAAwB,KAAkC;CACxE,gBAAgB,GAAG;CACnB,OAAO,YAAY,KAAK,IAAI;AAC9B;AAEA,SAAS,YAAY,KAAc,UAAuC;CACxE,MAAM,UAAU,OAAO,GAAG;CAC1B,IAAI,OAAO,QAAQ,UAAU,YAAY,CAAC,QAAQ,MAAM,KAAK,GAC3D,MAAM,IAAI,UAAU,6BAA6B;CAEnD,IAAI,CAAC,YAAY,QAAQ,UAAU,MAAM,WAAW,QAAQ,OAAO,kBAAkB;CACrF,cAAc,QAAQ,WAAW,QAAQ;CACzC,OAAO;AACT;AAEA,SAAgB,SAAS,KAAkC;CACzD,MAAM,QAAQ,OAAO,OAAO,GAAG,CAAC,CAAC,KAAK;CACtC,KAAK,MAAM,OAAO,CAAC,gBAAgB,eAAe,GAChD,IAAI,CAAC,OAAO,cAAc,MAAM,IAAI,KAAK,OAAO,MAAM,IAAI,IAAI,GAC5D,MAAM,IAAI,iBAAiB,0BAA0B;CAGzD,OAAO;AACT;AAEA,SAAS,YAAY,OAAwB;CAC3C,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,KAAK,QAAQ,GAC/E,MAAM,IAAI,iBAAiB,qBAAqB;CAElD,OAAO;AACT;AAEA,SAAS,UAAU,OAAgC,OAAuB;CACxE,IACE,OAAO,KAAK,KAAK,CAAC,CAAC,WAAW,MAAM,UACpC,MAAM,MAAM,QAAQ,CAAC,OAAO,OAAO,OAAO,GAAG,CAAC,GAE9C,MAAM,IAAI,iBAAiB,uCAAuC;AAEtE;;AAGA,SAAgB,eACd,KACA,SACc;CAEd,gBAAgB,GAAG;CACnB,MAAM,WAAW,OAAO,GAAG;CAC3B,IAAI,OAAO,SAAS,UAAU,YAAY,CAAC,SAAS,MAAM,KAAK,GAC7D,MAAM,IAAI,iBAAiB,sBAAsB;CAEnD,MAAM,UAAU,OAAO,SAAS,OAAO;CACvC,UAAU,SAAS,OAAO,KAAK,QAAQ,SAAS,CAAC;CACjD,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,QAAQ,SAAS,GAAG;EAChE,MAAM,SAAS,OAAO,QAAQ,KAAK;EACnC,IAAI,OAAO,SAAS,SAAS,MAAM,MAAM,IAAI,iBAAiB,mBAAmB;EACjF,IAAI,SAAS,SAAS,QAAQ;GAC5B,YAAY,OAAO,IAAI;GACvB;EACF;EACA,YAAY,OAAO,UAAU;EAC7B,MAAM,SACJ,SAAS,SAAS,UACd,SAAS,SAAS,KAAK,GAAG,UAAU,OAAO,KAAK,CAAC,IACjD,OAAO,KAAK,SAAS,QAAQ;EACnC,MAAM,gBAAgB,OAAO,OAAO,aAAa;EACjD,UAAU,eAAe,MAAM;EAC/B,MAAM,MAAM,OAAO,QAAQ,OAAO,UAAU,QAAQ,YAAY,cAAc,MAAM,GAAG,CAAC;EACxF,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,MAAQ,MAAM,IAAI,iBAAiB,kCAAkC;EAC7F,IAAI,SAAS,SAAS,SAAS;GAC7B,MAAM,SAAS,OAAO,OAAO,MAAM;GACnC,UAAU,QAAQ,MAAM;GACxB,IACE,OAAO,MAAM,UAAU,CAAC,kBAAkB,OAAO,QAAQ,SAAS,SAAS,OAAO,KAAK,EAAE,CAAC,GAE1F,MAAM,IAAI,iBAAiB,6BAA6B;GAE1D,MAAM,WAAW,OAAO,QACrB,OAAO,UAAU,QAAQ,OAAO,KAAK,IAAI,OAAO,cAAc,MAAM,GACrE,CACF;GACA,IACE,OAAO,OAAO,UAAU,YACxB,CAAC,OAAO,SAAS,OAAO,KAAK,KAC7B,KAAK,IAAI,OAAO,QAAQ,QAAQ,IAAI,MAEpC,MAAM,IAAI,iBAAiB,qCAAqC;EAEpE,OAAO,IACL,OAAO,OAAO,WAAW,YACzB,CAAC,OAAO,OAAO,eAAe,OAAO,MAAM,KAC3C,OAAO,MACJ,UACC,OAAO,cAAc,MAAM,IAAI,OAAO,cAAc,OAAO,OAAO,MAAM,EAAE,IAAI,IAClF,GAEA,MAAM,IAAI,iBAAiB,4CAA4C;CAE3E;CACA,SAAS,GAAG;CACZ,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev-review-Cw06AKuF.d.ts","names":[],"sources":["../src/jev-review.ts"],"mappings":";;;;cAcM,aAAW,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0Bb,EAAA,KAAA;;KAGQ,iBAAiB,EAAE,aAAa;UAC3B,eAAe,UAAU,eAAe;EACvD;EACA,SAAS,WAAW;EACpB,WAAW,WAAW,IAAI;;UAGX,kBAAkB,UAAU,eAAe,sBAClD,eAAe;;EAEvB;;UAGe;EACf;EACA;EACA;;EAQA;EACA;EACA;EACA;EACA,OAAO;;UAGQ;EACf;EACA;EACA;EACA,aAAa;;;iBAWC,uBAAuB,UAAU,cAC/C,OAAO,eAAe,KACrB,kBAAkB;;iBAuCL,gBAAgB,UAAU,cACxC,QAAQ,kBAAkB,IAC1B,eACC;;iBAyDa,kBAAkB,UAAU,cAC1C,QAAQ,kBAAkB,IAC1B,QAAQ,UAAU,IAClB;EAAW;EAAmB;IAC7B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev-review-DZEjDzlF.js","names":[],"sources":["../src/jev-review.ts"],"sourcesContent":["import { z } from 'zod'\nimport {\n ANALYST_SEVERITIES,\n type AnalystFinding,\n type EvidenceRef,\n makeFinding,\n} from './analyst/types'\nimport type { JevQuestions, JevRequest, JevResult } from './jev-protocol'\nimport { parseJevRequest, parseJevResult } from './jev-protocol'\nimport { canonicalString, hashCanonical, jsonDocument } from './ledger-core/canonical'\nimport { deepFreezeCanonicalJson } from './ledger-core/deep-freeze'\n\nconst name = z.string().trim().min(1)\nconst probability = z.number().finite().gt(0).max(1)\nconst checkSchema = z\n .object({\n claim: name,\n area: name,\n subject: name,\n severity: z.enum(ANALYST_SEVERITIES),\n /** Choice labels or score-level keys, never scores invented from category order. */\n supports: z.array(z.string()).min(1),\n refutes: z.array(z.string()).min(1),\n supportAtLeast: probability,\n refuteAtLeast: probability,\n /** Host-supplied coverage of the question's required evidence, not model self-assessment. */\n coverage: z.enum(['complete', 'partial', 'missing']),\n evidence: z.array(\n z\n .object({\n kind: z.enum(['span', 'event', 'artifact', 'finding', 'metric']),\n uri: name,\n excerpt: z.string().optional(),\n })\n .strict(),\n ),\n })\n .strict()\n .refine((value) => value.supportAtLeast + value.refuteAtLeast > 1, {\n message: 'Support and refutation thresholds must not overlap',\n })\n\n/** Optional interpretation of native choices/scores. Policy and evidence belong to the caller. */\nexport type JevReviewCheck = z.infer<typeof checkSchema>\nexport interface JevReviewInput<Q extends JevQuestions = JevQuestions> {\n version: string\n request: JevRequest<Q>\n checks: { [K in keyof Q]: JevReviewCheck }\n}\n\nexport interface PreparedJevReview<Q extends JevQuestions = JevQuestions>\n extends JevReviewInput<Q> {\n /** Binds questions, option order, context, evidence, thresholds and definition version. */\n digest: string\n}\n\nexport interface JevReviewAssessment {\n question: string\n status: 'supported' | 'refuted' | 'unresolved'\n reason:\n | 'support-threshold'\n | 'refutation-threshold'\n | 'missing-evidence'\n | 'partial-negative-evidence'\n | 'below-threshold'\n | 'ambiguous-thresholds'\n /** Unnormalized marginal mass of caller-designated native alternatives. Not calibrated risk. */\n supportProbability: number\n refutationProbability: number\n unresolvedProbability: number\n modelConfidence: number | null\n check: JevReviewCheck\n}\n\nexport interface JevReviewReport {\n reviewDigest: string\n requestedModel: string\n servedModel: string\n assessments: JevReviewAssessment[]\n}\n\nfunction alternatives(question: JevQuestions[string]): string[] {\n if (question.type === 'noul') return ['true', 'false']\n return question.type === 'choice'\n ? Object.keys(question.criteria)\n : question.criteria.map((_, index) => String(index))\n}\n\n/** Validate before paid admission; make a detached, frozen snapshot without rewriting native JSON. */\nexport function prepareJevReview<const Q extends JevQuestions>(\n input: JevReviewInput<Q>,\n): PreparedJevReview<Q> {\n if (!input.version?.trim()) throw new TypeError('Review version is required')\n parseJevRequest(input.request)\n // Document form drops only optional undefined object fields; canonicalization rejects loss.\n const document = jsonDocument(input)\n canonicalString(document)\n // Canonical bytes are for hashing, not dispatch: option order can affect a classifier.\n const snapshot = structuredClone(document) as JevReviewInput<Q>\n const keys = Object.keys(snapshot.request.questions)\n if (\n !snapshot.checks ||\n Object.keys(snapshot.checks).length !== keys.length ||\n keys.some((key) => !Object.hasOwn(snapshot.checks, key))\n ) {\n throw new TypeError('Review checks must match the exact question names')\n }\n for (const key of keys) {\n const check = checkSchema.parse(snapshot.checks[key])\n const labels = [...check.supports, ...check.refutes]\n const allowed = alternatives(snapshot.request.questions[key]!)\n if (\n new Set(labels).size !== labels.length ||\n labels.some((label) => !allowed.includes(label))\n ) {\n throw new TypeError('Review alternatives must exist and be disjoint')\n }\n if (check.coverage !== 'missing' && check.evidence.length === 0) {\n throw new TypeError('Review evidence references are required for non-missing coverage')\n }\n // Validation must not normalize a label into a different native option.\n if (canonicalString(jsonDocument(check)) !== canonicalString(snapshot.checks[key])) {\n throw new TypeError('Review metadata must not require normalization')\n }\n }\n const order = keys.map((key) => [key, alternatives(snapshot.request.questions[key]!)])\n return deepFreezeCanonicalJson({ ...snapshot, digest: hashCanonical({ snapshot, order }) })\n}\n\n/** Re-score the same retained distribution without new inference or any authorization effect. */\nexport function assessJevReview<Q extends JevQuestions>(\n review: PreparedJevReview<Q>,\n raw: unknown,\n): JevReviewReport {\n const { digest, ...input } = review\n const prepared = prepareJevReview(input)\n if (prepared.digest !== digest) throw new TypeError('Review definition changed after preparation')\n const result = parseJevResult(raw, prepared.request)\n const assessments = Object.entries(prepared.checks).map(([question, check]) => {\n const answer = result.answers[question]!\n const probabilities: Record<string, number> =\n answer.type === 'noul' ? { true: answer.noul, false: 1 - answer.noul } : answer.probabilities\n const sum = (labels: string[]) =>\n labels.reduce((total, label) => total + probabilities[label]!, 0)\n const supportProbability = sum(check.supports)\n const refutationProbability = sum(check.refutes)\n const used = new Set([...check.supports, ...check.refutes])\n const unresolvedProbability = sum(\n Object.keys(probabilities).filter((label) => !used.has(label)),\n )\n let status: JevReviewAssessment['status'] = 'unresolved'\n let reason: JevReviewAssessment['reason'] = 'below-threshold'\n if (supportProbability > 1 || refutationProbability > 1 || unresolvedProbability > 1) {\n throw new TypeError('Review probability mass exceeds one; do not silently normalize it')\n }\n if (check.coverage === 'missing') reason = 'missing-evidence'\n else if (\n supportProbability >= check.supportAtLeast &&\n refutationProbability >= check.refuteAtLeast\n ) {\n reason = 'ambiguous-thresholds'\n } else if (supportProbability >= check.supportAtLeast) {\n status = 'supported'\n reason = 'support-threshold'\n } else if (refutationProbability >= check.refuteAtLeast) {\n if (check.coverage === 'complete') {\n status = 'refuted'\n reason = 'refutation-threshold'\n } else reason = 'partial-negative-evidence'\n }\n return {\n question,\n status,\n reason,\n supportProbability,\n refutationProbability,\n unresolvedProbability,\n modelConfidence: answer.type === 'noul' ? null : answer.confidence,\n check: structuredClone(check),\n }\n })\n return {\n reviewDigest: digest,\n requestedModel: review.request.model,\n servedModel: result.model,\n assessments,\n }\n}\n\n/** Ordinary analyst findings; a negative review is not a global safety certificate. */\nexport function jevReviewFindings<Q extends JevQuestions>(\n review: PreparedJevReview<Q>,\n result: JevResult<Q>,\n options: { analystId: string; producedAt?: string },\n): AnalystFinding[] {\n if (!options.analystId.trim()) throw new TypeError('Analyst id is required')\n const report = assessJevReview(review, result)\n return report.assessments\n .filter((item) => item.status !== 'refuted')\n .map((item) =>\n makeFinding({\n analyst_id: options.analystId,\n ...(options.producedAt === undefined ? {} : { produced_at: options.producedAt }),\n subject: item.check.subject,\n area: item.status === 'unresolved' ? 'assessment-coverage' : item.check.area,\n severity: item.status === 'unresolved' ? 'info' : item.check.severity,\n claim:\n item.status === 'supported'\n ? `Model-supported hypothesis: ${item.check.claim}`\n : `Unresolved review: ${item.check.claim}`,\n evidence_refs: structuredClone(item.check.evidence) as EvidenceRef[],\n // For unresolved findings, confidence describes the deterministic unresolved status only.\n confidence: item.status === 'supported' ? item.supportProbability : 1,\n derived_from_judge: true,\n id_basis: hashCanonical([item.question, item.status, review.version, item.check.claim]),\n metadata: {\n review_digest: report.reviewDigest,\n question: item.question,\n requested_model: report.requestedModel,\n served_model: report.servedModel,\n assessment: item.status,\n assessment_reason: item.reason,\n evidence_coverage: item.check.coverage,\n support_probability: item.supportProbability,\n refutation_probability: item.refutationProbability,\n unresolved_probability: item.unresolvedProbability,\n model_confidence: item.modelConfidence,\n calibration: 'not-established',\n confidence_basis:\n item.status === 'supported' ? 'model-support-mass' : 'deterministic-status',\n native_answer: structuredClone(result.answers[item.question]),\n },\n }),\n )\n}\n"],"mappings":";;;;;;AAYA,MAAM,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;AACpC,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,MAAM,cAAc,EACjB,OAAO;CACN,OAAO;CACP,MAAM;CACN,SAAS;CACT,UAAU,EAAE,KAAK,kBAAkB;;CAEnC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;CACnC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;CAClC,gBAAgB;CAChB,eAAe;;CAEf,UAAU,EAAE,KAAK;EAAC;EAAY;EAAW;CAAS,CAAC;CACnD,UAAU,EAAE,MACV,EACG,OAAO;EACN,MAAM,EAAE,KAAK;GAAC;GAAQ;GAAS;GAAY;GAAW;EAAQ,CAAC;EAC/D,KAAK;EACL,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,CAAC,CAAC,CACD,OAAO,CACZ;AACF,CAAC,CAAC,CACD,OAAO,CAAC,CACR,QAAQ,UAAU,MAAM,iBAAiB,MAAM,gBAAgB,GAAG,EACjE,SAAS,qDACX,CAAC;AAyCH,SAAS,aAAa,UAA0C;CAC9D,IAAI,SAAS,SAAS,QAAQ,OAAO,CAAC,QAAQ,OAAO;CACrD,OAAO,SAAS,SAAS,WACrB,OAAO,KAAK,SAAS,QAAQ,IAC7B,SAAS,SAAS,KAAK,GAAG,UAAU,OAAO,KAAK,CAAC;AACvD;;AAGA,SAAgB,iBACd,OACsB;CACtB,IAAI,CAAC,MAAM,SAAS,KAAK,GAAG,MAAM,IAAI,UAAU,4BAA4B;CAC5E,gBAAgB,MAAM,OAAO;CAE7B,MAAM,WAAW,aAAa,KAAK;CACnC,gBAAgB,QAAQ;CAExB,MAAM,WAAW,gBAAgB,QAAQ;CACzC,MAAM,OAAO,OAAO,KAAK,SAAS,QAAQ,SAAS;CACnD,IACE,CAAC,SAAS,UACV,OAAO,KAAK,SAAS,MAAM,CAAC,CAAC,WAAW,KAAK,UAC7C,KAAK,MAAM,QAAQ,CAAC,OAAO,OAAO,SAAS,QAAQ,GAAG,CAAC,GAEvD,MAAM,IAAI,UAAU,mDAAmD;CAEzE,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,YAAY,MAAM,SAAS,OAAO,IAAI;EACpD,MAAM,SAAS,CAAC,GAAG,MAAM,UAAU,GAAG,MAAM,OAAO;EACnD,MAAM,UAAU,aAAa,SAAS,QAAQ,UAAU,IAAK;EAC7D,IACE,IAAI,IAAI,MAAM,CAAC,CAAC,SAAS,OAAO,UAChC,OAAO,MAAM,UAAU,CAAC,QAAQ,SAAS,KAAK,CAAC,GAE/C,MAAM,IAAI,UAAU,gDAAgD;EAEtE,IAAI,MAAM,aAAa,aAAa,MAAM,SAAS,WAAW,GAC5D,MAAM,IAAI,UAAU,kEAAkE;EAGxF,IAAI,gBAAgB,aAAa,KAAK,CAAC,MAAM,gBAAgB,SAAS,OAAO,IAAI,GAC/E,MAAM,IAAI,UAAU,gDAAgD;CAExE;CACA,MAAM,QAAQ,KAAK,KAAK,QAAQ,CAAC,KAAK,aAAa,SAAS,QAAQ,UAAU,IAAK,CAAC,CAAC;CACrF,OAAO,wBAAwB;EAAE,GAAG;EAAU,QAAQ,cAAc;GAAE;GAAU;EAAM,CAAC;CAAE,CAAC;AAC5F;;AAGA,SAAgB,gBACd,QACA,KACiB;CACjB,MAAM,EAAE,QAAQ,GAAG,UAAU;CAC7B,MAAM,WAAW,iBAAiB,KAAK;CACvC,IAAI,SAAS,WAAW,QAAQ,MAAM,IAAI,UAAU,6CAA6C;CACjG,MAAM,SAAS,eAAe,KAAK,SAAS,OAAO;CACnD,MAAM,cAAc,OAAO,QAAQ,SAAS,MAAM,CAAC,CAAC,KAAK,CAAC,UAAU,WAAW;EAC7E,MAAM,SAAS,OAAO,QAAQ;EAC9B,MAAM,gBACJ,OAAO,SAAS,SAAS;GAAE,MAAM,OAAO;GAAM,OAAO,IAAI,OAAO;EAAK,IAAI,OAAO;EAClF,MAAM,OAAO,WACX,OAAO,QAAQ,OAAO,UAAU,QAAQ,cAAc,QAAS,CAAC;EAClE,MAAM,qBAAqB,IAAI,MAAM,QAAQ;EAC7C,MAAM,wBAAwB,IAAI,MAAM,OAAO;EAC/C,MAAM,uBAAO,IAAI,IAAI,CAAC,GAAG,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC;EAC1D,MAAM,wBAAwB,IAC5B,OAAO,KAAK,aAAa,CAAC,CAAC,QAAQ,UAAU,CAAC,KAAK,IAAI,KAAK,CAAC,CAC/D;EACA,IAAI,SAAwC;EAC5C,IAAI,SAAwC;EAC5C,IAAI,qBAAqB,KAAK,wBAAwB,KAAK,wBAAwB,GACjF,MAAM,IAAI,UAAU,mEAAmE;EAEzF,IAAI,MAAM,aAAa,WAAW,SAAS;OACtC,IACH,sBAAsB,MAAM,kBAC5B,yBAAyB,MAAM,eAE/B,SAAS;OACJ,IAAI,sBAAsB,MAAM,gBAAgB;GACrD,SAAS;GACT,SAAS;EACX,OAAO,IAAI,yBAAyB,MAAM,eAAe;GACvD,IAAI,MAAM,aAAa,YAAY;IACjC,SAAS;IACT,SAAS;GACX,OAAO,SAAS;EAClB;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA,iBAAiB,OAAO,SAAS,SAAS,OAAO,OAAO;GACxD,OAAO,gBAAgB,KAAK;EAC9B;CACF,CAAC;CACD,OAAO;EACL,cAAc;EACd,gBAAgB,OAAO,QAAQ;EAC/B,aAAa,OAAO;EACpB;CACF;AACF;;AAGA,SAAgB,kBACd,QACA,QACA,SACkB;CAClB,IAAI,CAAC,QAAQ,UAAU,KAAK,GAAG,MAAM,IAAI,UAAU,wBAAwB;CAC3E,MAAM,SAAS,gBAAgB,QAAQ,MAAM;CAC7C,OAAO,OAAO,YACX,QAAQ,SAAS,KAAK,WAAW,SAAS,CAAC,CAC3C,KAAK,SACJ,YAAY;EACV,YAAY,QAAQ;EACpB,GAAI,QAAQ,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,QAAQ,WAAW;EAC9E,SAAS,KAAK,MAAM;EACpB,MAAM,KAAK,WAAW,eAAe,wBAAwB,KAAK,MAAM;EACxE,UAAU,KAAK,WAAW,eAAe,SAAS,KAAK,MAAM;EAC7D,OACE,KAAK,WAAW,cACZ,+BAA+B,KAAK,MAAM,UAC1C,sBAAsB,KAAK,MAAM;EACvC,eAAe,gBAAgB,KAAK,MAAM,QAAQ;EAElD,YAAY,KAAK,WAAW,cAAc,KAAK,qBAAqB;EACpE,oBAAoB;EACpB,UAAU,cAAc;GAAC,KAAK;GAAU,KAAK;GAAQ,OAAO;GAAS,KAAK,MAAM;EAAK,CAAC;EACtF,UAAU;GACR,eAAe,OAAO;GACtB,UAAU,KAAK;GACf,iBAAiB,OAAO;GACxB,cAAc,OAAO;GACrB,YAAY,KAAK;GACjB,mBAAmB,KAAK;GACxB,mBAAmB,KAAK,MAAM;GAC9B,qBAAqB,KAAK;GAC1B,wBAAwB,KAAK;GAC7B,wBAAwB,KAAK;GAC7B,kBAAkB,KAAK;GACvB,aAAa;GACb,kBACE,KAAK,WAAW,cAAc,uBAAuB;GACvD,eAAe,gBAAgB,OAAO,QAAQ,KAAK,SAAS;EAC9D;CACF,CAAC,CACH;AACJ"}
package/dist/jev.d.ts DELETED
@@ -1,60 +0,0 @@
1
- import { b as CustomTokenPricing, g as CostReceiptInput } from "./cost-ledger-DbQdN3nO.js";
2
- import { i as AnalystFinding, n as AnalystContext, t as Analyst } from "./types-6VEehmEg.js";
3
- import { R as Scenario, S as JudgeConfig, w as JudgeScore } from "./types-ZUy5HGY5.js";
4
- import { EvaluationAnalystOptions, EvaluationContext, EvaluationJudgeOptions, EvaluationResult, Evaluator, EvaluatorOptions, asAnalyst, asJudge, createEvaluator } from "./evaluation.js";
5
- import { _ as parseJevRecordedRequest, a as JevQuestion, c as JevRecordedQuestions, d as JevResponseError, f as JevResult, g as parseJevQuestions, h as jevUsage, i as JevJson, l as JevRecordedRequest, m as JevState, n as JevAnswerFor, o as JevQuestions, p as JevScoreCriteria, r as JevDescription, s as JevRecordedQuestion, t as JevAnswer, u as JevRequest, v as parseJevRequest, y as parseJevResult } from "./jev-protocol-BQLxwbqD.js";
6
- import { a as PreparedJevReview, c as prepareJevReview, i as JevReviewReport, n as JevReviewCheck, o as assessJevReview, r as JevReviewInput, s as jevReviewFindings, t as JevReviewAssessment } from "./jev-review-Cw06AKuF.js";
7
- //#region src/jev.d.ts
8
- export type JevEvaluate = EvaluatorOptions<JevRequest, unknown>['execute'];
9
- export interface JevEvaluatorOptions {
10
- evaluate: JevEvaluate;
11
- costLedger?: EvaluatorOptions<JevRequest, unknown>['costLedger'];
12
- maximumCharge?: EvaluatorOptions<JevRequest, unknown>['maximumCharge'];
13
- pricing?: CustomTokenPricing;
14
- receipt?: (response: unknown) => CostReceiptInput;
15
- receiptFromError?: (error: Error) => CostReceiptInput | undefined;
16
- /** Optional deployment policy; the protocol adapter does not hardcode model aliases. */
17
- acceptModel?: (requested: string, served: string) => boolean;
18
- }
19
- /** Configure transport once; supply native state, questions, and model on every invocation. */
20
- export declare function jevEvaluator(options: JevEvaluatorOptions): <const Q extends JevQuestions>(request: JevRequest<Q>, context?: EvaluationContext) => Promise<EvaluationResult<JevResult<Q>>>;
21
- type Input<A, S extends Scenario> = {
22
- artifact: A;
23
- scenario: S;
24
- };
25
- type QuestionSource<I, Q extends JevQuestions> = Q | ((input: I) => Q | Promise<Q>);
26
- export interface JevOptions extends JevEvaluatorOptions {
27
- model: string;
28
- version: string;
29
- questions: JevQuestions;
30
- }
31
- export interface JevJudgeOptions<A, S extends Scenario = Scenario, Q extends JevQuestions = JevQuestions> extends JevEvaluatorOptions {
32
- model: string;
33
- version: string;
34
- questions: QuestionSource<Input<A, S>, Q>;
35
- renderState: (input: Input<A, S>, context: EvaluationContext) => JevState | Promise<JevState>;
36
- dimensions?: JudgeConfig<A, S>['dimensions'];
37
- weights?: Record<string, number>;
38
- map?: (value: JevResult<Q>, input: Input<A, S>) => JudgeScore | Promise<JudgeScore>;
39
- record?: (result: EvaluationResult<JevResult<Q>>, input: Input<A, S>) => void | Promise<void>;
40
- appliesTo?: (scenario: S) => boolean;
41
- }
42
- /** Optional convention. Choice utilities and non-uniform scales belong in an explicit map. */
43
- export declare function normalizedJevScore(result: JevResult, weights?: Record<string, number>): JudgeScore;
44
- /** Convenience around the public evaluator. No domain questions, thresholds, or prose. */
45
- export declare function jevJudge<A, S extends Scenario = Scenario, Q extends JevQuestions = JevQuestions>(name: string, options: JevJudgeOptions<A, S, Q>): JudgeConfig<A, S>;
46
- export interface JevAnalystOptions<I, Q extends JevQuestions = JevQuestions> extends JevEvaluatorOptions {
47
- id: string;
48
- description: string;
49
- inputKind: Analyst<I>['inputKind'];
50
- model: string;
51
- version: string;
52
- questions: Q | ((input: I, context: AnalystContext) => Q | Promise<Q>);
53
- renderState: (input: I, context: AnalystContext) => JevState | Promise<JevState>;
54
- findings: (value: JevResult<Q>, input: I, context: AnalystContext) => AnalystFinding[] | Promise<AnalystFinding[]>;
55
- record?: (result: EvaluationResult<JevResult<Q>>, input: I, context: AnalystContext) => void | Promise<void>;
56
- }
57
- export declare function jevAnalyst<I, Q extends JevQuestions = JevQuestions>(options: JevAnalystOptions<I, Q>): Analyst<I>;
58
- //#endregion
59
- export { EvaluationAnalystOptions, EvaluationContext, EvaluationJudgeOptions, EvaluationResult, Evaluator, EvaluatorOptions, JevAnswer, JevAnswerFor, JevDescription, JevJson, JevQuestion, JevQuestions, JevRecordedQuestion, JevRecordedQuestions, JevRecordedRequest, JevRequest, JevResponseError, JevResult, JevReviewAssessment, JevReviewCheck, JevReviewInput, JevReviewReport, JevScoreCriteria, JevState, PreparedJevReview, asAnalyst, asJudge, assessJevReview, createEvaluator, jevReviewFindings, jevUsage, parseJevQuestions, parseJevRecordedRequest, parseJevRequest, parseJevResult, prepareJevReview };
60
- //# sourceMappingURL=jev.d.ts.map
package/dist/jev.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev.d.ts","names":[],"sources":["../src/jev.ts"],"mappings":";;;;;;;YAeY,cAAc,iBAAiB;iBAC1B;EACf,UAAU;EACV,aAAa,iBAAiB;EAC9B,gBAAgB,iBAAiB;EACjC,UAAU;EACV,WAAW,sBAAsB;EACjC,oBAAoB,OAAO,UAAU;;EAErC,eAAe,mBAAmB;;;wBAIpB,aAAa,SAAS,6BA2BhB,UAAU,cAAY,SAC/B,WAAW,IAAE,UACZ,sBACT,QAAQ,iBAAiB,UAAU;KAUnC,MAAM,GAAG,UAAU;EAAc,UAAU;EAAG,UAAU;;KACxD,eAAe,GAAG,UAAU,gBAAgB,MAAM,OAAO,MAAM,IAAI,QAAQ;iBAC/D,mBAAmB;EAClC;EACA;EACA,WAAW;;iBAEI,gBACf,GACA,UAAU,WAAW,UACrB,UAAU,eAAe,sBACjB;EACR;EACA;EACA,WAAW,eAAe,MAAM,GAAG,IAAI;EACvC,cAAc,OAAO,MAAM,GAAG,IAAI,SAAS,sBAAsB,WAAW,QAAQ;EACpF,aAAa,YAAY,GAAG;EAC5B,UAAU;EACV,OAAO,OAAO,UAAU,IAAI,OAAO,MAAM,GAAG,OAAO,aAAa,QAAQ;EACxE,UAAU,QAAQ,iBAAiB,UAAU,KAAK,OAAO,MAAM,GAAG,cAAc;EAChF,aAAa,UAAU;;;wBA0BT,mBACd,QAAQ,WACR,UAAU,yBACT;;wBAgCa,SAAS,GAAG,UAAU,WAAW,UAAU,UAAU,eAAe,cAClF,cACA,SAAS,gBAAgB,GAAG,GAAG,KAC9B,YAAY,GAAG;iBAqDD,kBAAkB,GAAG,UAAU,eAAe,sBACrD;EACR;EACA;EACA,WAAW,QAAQ;EACnB;EACA;EACA,WAAW,MAAM,OAAO,GAAG,SAAS,mBAAmB,IAAI,QAAQ;EACnE,cAAc,OAAO,GAAG,SAAS,mBAAmB,WAAW,QAAQ;EACvE,WACE,OAAO,UAAU,IACjB,OAAO,GACP,SAAS,mBACN,mBAAmB,QAAQ;EAChC,UACE,QAAQ,iBAAiB,UAAU,KACnC,OAAO,GACP,SAAS,0BACC;;wBAGE,WAAW,GAAG,UAAU,eAAe,cACrD,SAAS,kBAAkB,GAAG,KAC7B,QAAQ"}
package/dist/jev.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"jev.js","names":[],"sources":["../src/jev.ts"],"sourcesContent":["import type { Analyst, AnalystContext, AnalystFinding } from './analyst/types'\nimport type { JudgeConfig, JudgeScore, Scenario } from './campaign/types'\nimport type { CostReceiptInput, CustomTokenPricing } from './cost-ledger'\nimport type { EvaluationContext, EvaluationResult, EvaluatorOptions } from './evaluation'\nimport { asAnalyst, asJudge, createEvaluator } from './evaluation'\nimport type { JevQuestions, JevRequest, JevResult, JevState } from './jev-protocol'\nimport { jevUsage, parseJevQuestions, parseJevRequest, parseJevResult } from './jev-protocol'\nimport { jsonDocument } from './ledger-core/canonical'\nimport { weightedComposite } from './statistics'\nimport { contentHash } from './verdict-cache'\n\nexport * from './evaluation'\nexport * from './jev-protocol'\nexport * from './jev-review'\n\nexport type JevEvaluate = EvaluatorOptions<JevRequest, unknown>['execute']\nexport interface JevEvaluatorOptions {\n evaluate: JevEvaluate\n costLedger?: EvaluatorOptions<JevRequest, unknown>['costLedger']\n maximumCharge?: EvaluatorOptions<JevRequest, unknown>['maximumCharge']\n pricing?: CustomTokenPricing\n receipt?: (response: unknown) => CostReceiptInput\n receiptFromError?: (error: Error) => CostReceiptInput | undefined\n /** Optional deployment policy; the protocol adapter does not hardcode model aliases. */\n acceptModel?: (requested: string, served: string) => boolean\n}\n\n/** Configure transport once; supply native state, questions, and model on every invocation. */\nexport function jevEvaluator(options: JevEvaluatorOptions) {\n const config = { ...options, pricing: options.pricing && { ...options.pricing } }\n const run = createEvaluator<JevRequest, unknown>({\n execute: (request, context) => config.evaluate(structuredClone(request), context),\n model: (request) => request.model,\n costLedger: config.costLedger,\n maximumCharge: config.maximumCharge,\n receiptFromError: config.receiptFromError,\n receipt:\n config.receipt ??\n ((raw) => {\n const usage = jevUsage(raw)\n const { model } = raw as JevResult\n return {\n model,\n inputTokens: usage.input_tokens,\n outputTokens: usage.output_tokens,\n customTokenPricing: config.pricing,\n }\n }),\n validate: (raw, request) => {\n const result = parseJevResult(raw, request)\n if (config.acceptModel && !config.acceptModel(request.model, result.model)) {\n throw new Error('Evaluation served a model rejected by the caller policy')\n }\n },\n })\n return async <const Q extends JevQuestions>(\n request: JevRequest<Q>,\n context?: EvaluationContext,\n ): Promise<EvaluationResult<JevResult<Q>>> => {\n context?.signal?.throwIfAborted()\n parseJevRequest(request)\n const snapshot = structuredClone(request)\n const result = await run(snapshot, context)\n // The validator checked these exact questions after recording the paid work.\n return { ...result, value: result.value as JevResult<Q> }\n }\n}\n\ntype Input<A, S extends Scenario> = { artifact: A; scenario: S }\ntype QuestionSource<I, Q extends JevQuestions> = Q | ((input: I) => Q | Promise<Q>)\nexport interface JevOptions extends JevEvaluatorOptions {\n model: string\n version: string\n questions: JevQuestions\n}\nexport interface JevJudgeOptions<\n A,\n S extends Scenario = Scenario,\n Q extends JevQuestions = JevQuestions,\n> extends JevEvaluatorOptions {\n model: string\n version: string\n questions: QuestionSource<Input<A, S>, Q>\n renderState: (input: Input<A, S>, context: EvaluationContext) => JevState | Promise<JevState>\n dimensions?: JudgeConfig<A, S>['dimensions']\n weights?: Record<string, number>\n map?: (value: JevResult<Q>, input: Input<A, S>) => JudgeScore | Promise<JudgeScore>\n record?: (result: EvaluationResult<JevResult<Q>>, input: Input<A, S>) => void | Promise<void>\n appliesTo?: (scenario: S) => boolean\n}\n\n/** Canonical identity also retains native question/alternative order; dispatch stays untouched. */\nfunction questionIdentity(questions: JevQuestions) {\n return {\n document: jsonDocument(questions),\n order: Object.entries(questions).map(([name, question]) => [\n name,\n question.type === 'choice' ? Object.keys(question.criteria) : null,\n ]),\n }\n}\n\nfunction validateWeights(weights: Record<string, number>, keys: string[]): void {\n if (\n Object.entries(weights).some(\n ([key, weight]) => !keys.includes(key) || !Number.isFinite(weight) || weight < 0,\n ) ||\n Object.values(weights).reduce((sum, weight) => sum + weight, 0) <= 0\n ) {\n throw new TypeError('Invalid judge weights')\n }\n}\n\n/** Optional convention. Choice utilities and non-uniform scales belong in an explicit map. */\nexport function normalizedJevScore(\n result: JevResult,\n weights?: Record<string, number>,\n): JudgeScore {\n const dimensions: Record<string, number> = Object.create(null)\n const distribution: NonNullable<JudgeScore['distribution']> = Object.create(null)\n for (const [key, answer] of Object.entries(result.answers)) {\n if (answer.type === 'choice') throw new TypeError('Choice scoring requires an explicit map')\n if (answer.type === 'noul') {\n dimensions[key] = answer.noul\n distribution[key] = [\n { score: 0, probability: 1 - answer.noul },\n { score: 1, probability: answer.noul },\n ]\n } else {\n const scale = Object.keys(answer.legend).length - 1\n dimensions[key] = answer.score / scale\n distribution[key] = Object.entries(answer.probabilities).map(([level, probability]) => ({\n score: Number(level) / scale,\n probability,\n }))\n }\n }\n const effective = weights ?? Object.fromEntries(Object.keys(dimensions).map((key) => [key, 1]))\n validateWeights(effective, Object.keys(dimensions))\n return {\n dimensions,\n composite: weightedComposite({ dims: dimensions, weights: effective }).composite,\n notes: '',\n scoringMethod: 'expectation',\n distribution,\n }\n}\n\n/** Convenience around the public evaluator. No domain questions, thresholds, or prose. */\nexport function jevJudge<A, S extends Scenario = Scenario, Q extends JevQuestions = JevQuestions>(\n name: string,\n options: JevJudgeOptions<A, S, Q>,\n): JudgeConfig<A, S> {\n const config = { ...options }\n const source =\n typeof options.questions === 'function' ? options.questions : structuredClone(options.questions)\n const weights = options.weights && { ...options.weights }\n if (!name.trim() || !options.version.trim()) {\n throw new TypeError('Judge name and version are required')\n }\n if ((typeof source === 'function' || config.map) && !config.dimensions) {\n throw new TypeError('Dynamic questions and custom mappings require stable judge dimensions')\n }\n if (!config.map && typeof source !== 'function') {\n parseJevQuestions(source)\n if (Object.values(source).some((question) => question.type === 'choice')) {\n throw new TypeError(\n 'Choice scoring requires an explicit map; labels have no numeric ordering',\n )\n }\n if (weights) validateWeights(weights, Object.keys(source))\n }\n const dimensions =\n config.dimensions ??\n Object.entries(source).map(([key, question]) => ({\n key,\n description: typeof question.instructions === 'string' ? question.instructions : key,\n }))\n const evaluate = jevEvaluator(config)\n return asJudge<A, S, JevResult<Q>>({\n name,\n version: contentHash({\n model: config.model,\n version: config.version,\n questions: typeof source === 'function' ? 'dynamic' : questionIdentity(source),\n dimensions,\n // Hash the JSON document: optional undefined fields are absent on the wire.\n ...(weights ? { weights } : {}),\n }),\n dimensions,\n appliesTo: config.appliesTo,\n evaluate: async (input, context = {}) =>\n evaluate(\n {\n model: config.model,\n state: await config.renderState(input, context),\n questions: typeof source === 'function' ? await source(input) : source,\n },\n context,\n ),\n map: config.map ?? ((value) => normalizedJevScore(value, weights)),\n record: config.record,\n })\n}\n\nexport interface JevAnalystOptions<I, Q extends JevQuestions = JevQuestions>\n extends JevEvaluatorOptions {\n id: string\n description: string\n inputKind: Analyst<I>['inputKind']\n model: string\n version: string\n questions: Q | ((input: I, context: AnalystContext) => Q | Promise<Q>)\n renderState: (input: I, context: AnalystContext) => JevState | Promise<JevState>\n findings: (\n value: JevResult<Q>,\n input: I,\n context: AnalystContext,\n ) => AnalystFinding[] | Promise<AnalystFinding[]>\n record?: (\n result: EvaluationResult<JevResult<Q>>,\n input: I,\n context: AnalystContext,\n ) => void | Promise<void>\n}\n\nexport function jevAnalyst<I, Q extends JevQuestions = JevQuestions>(\n options: JevAnalystOptions<I, Q>,\n): Analyst<I> {\n const config = { ...options }\n const source =\n typeof options.questions === 'function' ? options.questions : structuredClone(options.questions)\n if (!config.id.trim() || !config.version.trim() || !config.description.trim()) {\n throw new TypeError('Analyst id, version, and description are required')\n }\n const evaluate = jevEvaluator(config)\n return asAnalyst<I, JevResult<Q>>({\n id: config.id,\n description: config.description,\n inputKind: config.inputKind,\n version: contentHash({\n model: config.model,\n version: config.version,\n questions: typeof source === 'function' ? 'dynamic' : questionIdentity(source),\n }),\n cost: { kind: 'llm', models: [config.model] },\n evaluate: async (input, context, analystContext) =>\n evaluate(\n {\n model: config.model,\n state: await config.renderState(input, analystContext),\n questions: typeof source === 'function' ? await source(input, analystContext) : source,\n },\n context,\n ),\n map: config.findings,\n record: config.record,\n })\n}\n"],"mappings":"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"}