@outputai/evals 0.1.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 +201 -0
- package/README.md +11 -0
- package/dist/aggregate.d.ts +2 -0
- package/dist/aggregate.js +11 -0
- package/dist/aggregate.spec.d.ts +1 -0
- package/dist/aggregate.spec.js +48 -0
- package/dist/eval_workflow.d.ts +20 -0
- package/dist/eval_workflow.js +88 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +9 -0
- package/dist/index_sandbox.d.ts +16 -0
- package/dist/index_sandbox.js +51 -0
- package/dist/interpret.d.ts +3 -0
- package/dist/interpret.js +40 -0
- package/dist/interpret.spec.d.ts +1 -0
- package/dist/interpret.spec.js +86 -0
- package/dist/judge.d.ts +10 -0
- package/dist/judge.js +31 -0
- package/dist/naming.d.ts +3 -0
- package/dist/naming.js +4 -0
- package/dist/naming.spec.d.ts +1 -0
- package/dist/naming.spec.js +30 -0
- package/dist/render.d.ts +3 -0
- package/dist/render.js +75 -0
- package/dist/render.spec.d.ts +1 -0
- package/dist/render.spec.js +171 -0
- package/dist/schemas.d.ts +95 -0
- package/dist/schemas.js +60 -0
- package/dist/verdict.d.ts +27 -0
- package/dist/verdict.js +109 -0
- package/dist/verdict.spec.d.ts +1 -0
- package/dist/verdict.spec.js +181 -0
- package/dist/verify.d.ts +41 -0
- package/dist/verify.js +18 -0
- package/dist/verify.spec.d.ts +1 -0
- package/dist/verify.spec.js +70 -0
- package/package.json +29 -0
package/dist/judge.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { EvaluationVerdictResult, EvaluationNumberResult, EvaluationBooleanResult, EvaluationStringResult, z } from '@outputai/core';
|
|
2
|
+
import { resolveInvocationDir } from '@outputai/core/sdk_utils';
|
|
3
|
+
import { generateText, Output } from '@outputai/llm';
|
|
4
|
+
const verdictSchema = z.object({ verdict: z.enum(['pass', 'partial', 'fail']), reasoning: z.string() });
|
|
5
|
+
const scoreSchema = z.object({ score: z.number(), reasoning: z.string() });
|
|
6
|
+
const booleanSchema = z.object({ result: z.boolean(), reasoning: z.string() });
|
|
7
|
+
const labelSchema = z.object({ label: z.string(), reasoning: z.string() });
|
|
8
|
+
export async function judgeVerdict({ prompt, variables, schema = verdictSchema }) {
|
|
9
|
+
const promptDir = resolveInvocationDir();
|
|
10
|
+
const response = await generateText({ prompt, variables, promptDir, output: Output.object({ schema }) });
|
|
11
|
+
const result = response.output;
|
|
12
|
+
return new EvaluationVerdictResult({ value: result.verdict, confidence: 0.9, reasoning: result.reasoning });
|
|
13
|
+
}
|
|
14
|
+
export async function judgeScore({ prompt, variables, schema = scoreSchema }) {
|
|
15
|
+
const promptDir = resolveInvocationDir();
|
|
16
|
+
const response = await generateText({ prompt, variables, promptDir, output: Output.object({ schema }) });
|
|
17
|
+
const result = response.output;
|
|
18
|
+
return new EvaluationNumberResult({ value: result.score, confidence: 0.9, reasoning: result.reasoning });
|
|
19
|
+
}
|
|
20
|
+
export async function judgeBoolean({ prompt, variables, schema = booleanSchema }) {
|
|
21
|
+
const promptDir = resolveInvocationDir();
|
|
22
|
+
const response = await generateText({ prompt, variables, promptDir, output: Output.object({ schema }) });
|
|
23
|
+
const result = response.output;
|
|
24
|
+
return new EvaluationBooleanResult({ value: result.result, confidence: 0.9, reasoning: result.reasoning });
|
|
25
|
+
}
|
|
26
|
+
export async function judgeLabel({ prompt, variables, schema = labelSchema }) {
|
|
27
|
+
const promptDir = resolveInvocationDir();
|
|
28
|
+
const response = await generateText({ prompt, variables, promptDir, output: Output.object({ schema }) });
|
|
29
|
+
const result = response.output;
|
|
30
|
+
return new EvaluationStringResult({ value: result.label, confidence: 0.9, reasoning: result.reasoning });
|
|
31
|
+
}
|
package/dist/naming.d.ts
ADDED
package/dist/naming.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const EVAL_SUFFIX = '_eval';
|
|
2
|
+
export const getEvalWorkflowName = (workflowName) => `${workflowName}${EVAL_SUFFIX}`;
|
|
3
|
+
export const isEvalWorkflow = (name) => name.endsWith(EVAL_SUFFIX);
|
|
4
|
+
export const getParentWorkflowName = (evalName) => evalName.slice(0, -EVAL_SUFFIX.length);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getEvalWorkflowName, isEvalWorkflow, getParentWorkflowName } from './naming.js';
|
|
3
|
+
describe('naming', () => {
|
|
4
|
+
describe('getEvalWorkflowName', () => {
|
|
5
|
+
it('appends _eval suffix', () => {
|
|
6
|
+
expect(getEvalWorkflowName('simple')).toBe('simple_eval');
|
|
7
|
+
expect(getEvalWorkflowName('my_workflow')).toBe('my_workflow_eval');
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
describe('isEvalWorkflow', () => {
|
|
11
|
+
it('returns true for names ending with _eval', () => {
|
|
12
|
+
expect(isEvalWorkflow('simple_eval')).toBe(true);
|
|
13
|
+
expect(isEvalWorkflow('my_workflow_eval')).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('returns false for regular workflow names', () => {
|
|
16
|
+
expect(isEvalWorkflow('simple')).toBe(false);
|
|
17
|
+
expect(isEvalWorkflow('eval_runner')).toBe(false);
|
|
18
|
+
expect(isEvalWorkflow('evaluation')).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe('getParentWorkflowName', () => {
|
|
22
|
+
it('strips the _eval suffix', () => {
|
|
23
|
+
expect(getParentWorkflowName('simple_eval')).toBe('simple');
|
|
24
|
+
expect(getParentWorkflowName('my_workflow_eval')).toBe('my_workflow');
|
|
25
|
+
});
|
|
26
|
+
it('returns empty string for bare _eval', () => {
|
|
27
|
+
expect(getParentWorkflowName('_eval')).toBe('');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
package/dist/render.d.ts
ADDED
package/dist/render.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { VERDICT, CRITICALITY } from './schemas.js';
|
|
2
|
+
const COLORS = {
|
|
3
|
+
reset: '\x1b[0m',
|
|
4
|
+
green: '\x1b[32m',
|
|
5
|
+
yellow: '\x1b[33m',
|
|
6
|
+
red: '\x1b[31m',
|
|
7
|
+
dim: '\x1b[2m',
|
|
8
|
+
bold: '\x1b[1m',
|
|
9
|
+
cyan: '\x1b[36m'
|
|
10
|
+
};
|
|
11
|
+
const VERDICT_STYLE = {
|
|
12
|
+
[VERDICT.PASS]: { label: 'PASS', color: COLORS.green },
|
|
13
|
+
[VERDICT.PARTIAL]: { label: 'PARTIAL', color: COLORS.yellow },
|
|
14
|
+
[VERDICT.FAIL]: { label: 'FAIL', color: COLORS.red }
|
|
15
|
+
};
|
|
16
|
+
function colorize(text, color) {
|
|
17
|
+
return `${color}${text}${COLORS.reset}`;
|
|
18
|
+
}
|
|
19
|
+
function padWithDots(label, width = 30) {
|
|
20
|
+
const dotCount = Math.max(2, width - label.length);
|
|
21
|
+
return ` ${'.'.repeat(dotCount)} `;
|
|
22
|
+
}
|
|
23
|
+
function renderEvaluator(evaluator) {
|
|
24
|
+
const lines = [];
|
|
25
|
+
const style = VERDICT_STYLE[evaluator.verdict];
|
|
26
|
+
const prefix = evaluator.criticality === CRITICALITY.INFORMATIONAL ?
|
|
27
|
+
`${COLORS.dim}(info)${COLORS.reset} ` :
|
|
28
|
+
'';
|
|
29
|
+
lines.push(` ${prefix}${evaluator.name}${padWithDots(evaluator.name, 24)}${colorize(evaluator.verdict, style.color)}`);
|
|
30
|
+
if (evaluator.reasoning && evaluator.verdict !== VERDICT.PASS) {
|
|
31
|
+
lines.push(` ${COLORS.dim}-> ${evaluator.reasoning}${COLORS.reset}`);
|
|
32
|
+
}
|
|
33
|
+
if (evaluator.feedback) {
|
|
34
|
+
for (const fb of evaluator.feedback) {
|
|
35
|
+
const item = fb;
|
|
36
|
+
if (item.issue) {
|
|
37
|
+
lines.push(` ${COLORS.dim}-> ${item.issue}${COLORS.reset}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
43
|
+
function renderCase(evalCase) {
|
|
44
|
+
const caseStyle = VERDICT_STYLE[evalCase.verdict];
|
|
45
|
+
const lines = [
|
|
46
|
+
` ${evalCase.datasetName}${padWithDots(evalCase.datasetName)}${colorize(caseStyle.label, caseStyle.color)}`
|
|
47
|
+
];
|
|
48
|
+
evalCase.evaluators.forEach(evaluator => lines.push(...renderEvaluator(evaluator)));
|
|
49
|
+
lines.push('');
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
export function renderEvalOutput(evalOutput, evalName) {
|
|
53
|
+
const lines = [];
|
|
54
|
+
if (evalName) {
|
|
55
|
+
lines.push(colorize(evalName, COLORS.bold));
|
|
56
|
+
lines.push('');
|
|
57
|
+
}
|
|
58
|
+
evalOutput.cases.forEach(evalCase => lines.push(...renderCase(evalCase)));
|
|
59
|
+
const { summary } = evalOutput;
|
|
60
|
+
const parts = [];
|
|
61
|
+
if (summary.passed > 0) {
|
|
62
|
+
parts.push(colorize(`${summary.passed} passed`, COLORS.green));
|
|
63
|
+
}
|
|
64
|
+
if (summary.partial > 0) {
|
|
65
|
+
parts.push(colorize(`${summary.partial} partial`, COLORS.yellow));
|
|
66
|
+
}
|
|
67
|
+
if (summary.failed > 0) {
|
|
68
|
+
parts.push(colorize(`${summary.failed} failed`, COLORS.red));
|
|
69
|
+
}
|
|
70
|
+
lines.push(`${parts.join(', ')} (${Math.round(summary.acceptableRate * 100)}% acceptable)`);
|
|
71
|
+
return lines.join('\n');
|
|
72
|
+
}
|
|
73
|
+
export function computeExitCode(evalOutput) {
|
|
74
|
+
return evalOutput.cases.some(c => c.verdict === VERDICT.FAIL) ? 1 : 0;
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderEvalOutput, computeExitCode } from './render.js';
|
|
3
|
+
const makeOutput = (cases, overrides) => {
|
|
4
|
+
const passed = cases.filter(c => c.verdict === 'pass').length;
|
|
5
|
+
const partial = cases.filter(c => c.verdict === 'partial').length;
|
|
6
|
+
const failed = cases.filter(c => c.verdict === 'fail').length;
|
|
7
|
+
const total = cases.length;
|
|
8
|
+
return {
|
|
9
|
+
cases,
|
|
10
|
+
summary: {
|
|
11
|
+
total,
|
|
12
|
+
passed,
|
|
13
|
+
partial,
|
|
14
|
+
failed,
|
|
15
|
+
acceptableRate: total > 0 ? (passed + partial) / total : 0,
|
|
16
|
+
...overrides
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
describe('computeExitCode', () => {
|
|
21
|
+
it('returns 0 when all cases pass', () => {
|
|
22
|
+
const output = makeOutput([
|
|
23
|
+
{ datasetName: 'a', verdict: 'pass', evaluators: [] }
|
|
24
|
+
]);
|
|
25
|
+
expect(computeExitCode(output)).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
it('returns 0 when cases are partial but none fail', () => {
|
|
28
|
+
const output = makeOutput([
|
|
29
|
+
{ datasetName: 'a', verdict: 'partial', evaluators: [] }
|
|
30
|
+
]);
|
|
31
|
+
expect(computeExitCode(output)).toBe(0);
|
|
32
|
+
});
|
|
33
|
+
it('returns 1 when any case fails', () => {
|
|
34
|
+
const output = makeOutput([
|
|
35
|
+
{ datasetName: 'a', verdict: 'pass', evaluators: [] },
|
|
36
|
+
{ datasetName: 'b', verdict: 'fail', evaluators: [] }
|
|
37
|
+
]);
|
|
38
|
+
expect(computeExitCode(output)).toBe(1);
|
|
39
|
+
});
|
|
40
|
+
it('returns 0 for empty cases', () => {
|
|
41
|
+
const output = makeOutput([]);
|
|
42
|
+
expect(computeExitCode(output)).toBe(0);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('renderEvalOutput', () => {
|
|
46
|
+
it('includes eval name when provided', () => {
|
|
47
|
+
const output = makeOutput([
|
|
48
|
+
{ datasetName: 'test', verdict: 'pass', evaluators: [] }
|
|
49
|
+
]);
|
|
50
|
+
const rendered = renderEvalOutput(output, 'my_eval');
|
|
51
|
+
expect(rendered).toContain('my_eval');
|
|
52
|
+
});
|
|
53
|
+
it('includes dataset names', () => {
|
|
54
|
+
const output = makeOutput([
|
|
55
|
+
{ datasetName: 'basic_input', verdict: 'pass', evaluators: [] },
|
|
56
|
+
{ datasetName: 'edge_case', verdict: 'fail', evaluators: [] }
|
|
57
|
+
]);
|
|
58
|
+
const rendered = renderEvalOutput(output);
|
|
59
|
+
expect(rendered).toContain('basic_input');
|
|
60
|
+
expect(rendered).toContain('edge_case');
|
|
61
|
+
});
|
|
62
|
+
it('includes verdict labels', () => {
|
|
63
|
+
const output = makeOutput([
|
|
64
|
+
{ datasetName: 'a', verdict: 'pass', evaluators: [] },
|
|
65
|
+
{ datasetName: 'b', verdict: 'fail', evaluators: [] }
|
|
66
|
+
]);
|
|
67
|
+
const rendered = renderEvalOutput(output);
|
|
68
|
+
expect(rendered).toContain('PASS');
|
|
69
|
+
expect(rendered).toContain('FAIL');
|
|
70
|
+
});
|
|
71
|
+
it('shows reasoning for failed evaluators', () => {
|
|
72
|
+
const output = makeOutput([
|
|
73
|
+
{
|
|
74
|
+
datasetName: 'test',
|
|
75
|
+
verdict: 'fail',
|
|
76
|
+
evaluators: [{
|
|
77
|
+
name: 'check',
|
|
78
|
+
verdict: 'fail',
|
|
79
|
+
criticality: 'required',
|
|
80
|
+
reasoning: 'Expected 15, got null'
|
|
81
|
+
}]
|
|
82
|
+
}
|
|
83
|
+
]);
|
|
84
|
+
const rendered = renderEvalOutput(output);
|
|
85
|
+
expect(rendered).toContain('Expected 15, got null');
|
|
86
|
+
});
|
|
87
|
+
it('hides reasoning for passing evaluators', () => {
|
|
88
|
+
const output = makeOutput([
|
|
89
|
+
{
|
|
90
|
+
datasetName: 'test',
|
|
91
|
+
verdict: 'pass',
|
|
92
|
+
evaluators: [{
|
|
93
|
+
name: 'check',
|
|
94
|
+
verdict: 'pass',
|
|
95
|
+
criticality: 'required',
|
|
96
|
+
reasoning: 'All good'
|
|
97
|
+
}]
|
|
98
|
+
}
|
|
99
|
+
]);
|
|
100
|
+
const rendered = renderEvalOutput(output);
|
|
101
|
+
expect(rendered).not.toContain('All good');
|
|
102
|
+
});
|
|
103
|
+
it('shows summary with acceptable rate', () => {
|
|
104
|
+
const output = makeOutput([
|
|
105
|
+
{ datasetName: 'a', verdict: 'pass', evaluators: [] },
|
|
106
|
+
{ datasetName: 'b', verdict: 'fail', evaluators: [] }
|
|
107
|
+
]);
|
|
108
|
+
const rendered = renderEvalOutput(output);
|
|
109
|
+
expect(rendered).toContain('1 passed');
|
|
110
|
+
expect(rendered).toContain('1 failed');
|
|
111
|
+
expect(rendered).toContain('50% acceptable');
|
|
112
|
+
});
|
|
113
|
+
it('includes PARTIAL verdict label', () => {
|
|
114
|
+
const output = makeOutput([
|
|
115
|
+
{ datasetName: 'a', verdict: 'partial', evaluators: [] }
|
|
116
|
+
]);
|
|
117
|
+
const rendered = renderEvalOutput(output);
|
|
118
|
+
expect(rendered).toContain('PARTIAL');
|
|
119
|
+
});
|
|
120
|
+
it('shows reasoning for partial evaluators', () => {
|
|
121
|
+
const output = makeOutput([
|
|
122
|
+
{
|
|
123
|
+
datasetName: 'test',
|
|
124
|
+
verdict: 'partial',
|
|
125
|
+
evaluators: [{
|
|
126
|
+
name: 'check',
|
|
127
|
+
verdict: 'partial',
|
|
128
|
+
criticality: 'required',
|
|
129
|
+
reasoning: 'Close but not quite'
|
|
130
|
+
}]
|
|
131
|
+
}
|
|
132
|
+
]);
|
|
133
|
+
const rendered = renderEvalOutput(output);
|
|
134
|
+
expect(rendered).toContain('Close but not quite');
|
|
135
|
+
});
|
|
136
|
+
it('renders feedback issues', () => {
|
|
137
|
+
const output = makeOutput([
|
|
138
|
+
{
|
|
139
|
+
datasetName: 'test',
|
|
140
|
+
verdict: 'fail',
|
|
141
|
+
evaluators: [{
|
|
142
|
+
name: 'check',
|
|
143
|
+
verdict: 'fail',
|
|
144
|
+
criticality: 'required',
|
|
145
|
+
feedback: [
|
|
146
|
+
{ issue: 'Missing required field' },
|
|
147
|
+
{ issue: 'Invalid format' }
|
|
148
|
+
]
|
|
149
|
+
}]
|
|
150
|
+
}
|
|
151
|
+
]);
|
|
152
|
+
const rendered = renderEvalOutput(output);
|
|
153
|
+
expect(rendered).toContain('Missing required field');
|
|
154
|
+
expect(rendered).toContain('Invalid format');
|
|
155
|
+
});
|
|
156
|
+
it('marks informational evaluators with (info) prefix', () => {
|
|
157
|
+
const output = makeOutput([
|
|
158
|
+
{
|
|
159
|
+
datasetName: 'test',
|
|
160
|
+
verdict: 'pass',
|
|
161
|
+
evaluators: [{
|
|
162
|
+
name: 'info_check',
|
|
163
|
+
verdict: 'fail',
|
|
164
|
+
criticality: 'informational'
|
|
165
|
+
}]
|
|
166
|
+
}
|
|
167
|
+
]);
|
|
168
|
+
const rendered = renderEvalOutput(output);
|
|
169
|
+
expect(rendered).toContain('(info)');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from '@outputai/core';
|
|
2
|
+
export declare const VERDICT: {
|
|
3
|
+
readonly PASS: "pass";
|
|
4
|
+
readonly PARTIAL: "partial";
|
|
5
|
+
readonly FAIL: "fail";
|
|
6
|
+
};
|
|
7
|
+
export declare const CRITICALITY: {
|
|
8
|
+
readonly REQUIRED: "required";
|
|
9
|
+
readonly INFORMATIONAL: "informational";
|
|
10
|
+
};
|
|
11
|
+
export declare const VerdictSchema: z.ZodEnum<{
|
|
12
|
+
pass: "pass";
|
|
13
|
+
partial: "partial";
|
|
14
|
+
fail: "fail";
|
|
15
|
+
}>;
|
|
16
|
+
export type Verdict = z.infer<typeof VerdictSchema>;
|
|
17
|
+
export declare const CriticalitySchema: z.ZodEnum<{
|
|
18
|
+
required: "required";
|
|
19
|
+
informational: "informational";
|
|
20
|
+
}>;
|
|
21
|
+
export type Criticality = z.infer<typeof CriticalitySchema>;
|
|
22
|
+
export type InterpretConfig = {
|
|
23
|
+
type: 'verdict';
|
|
24
|
+
} | {
|
|
25
|
+
type: 'boolean';
|
|
26
|
+
} | {
|
|
27
|
+
type: 'number';
|
|
28
|
+
pass: number;
|
|
29
|
+
partial?: number;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'string';
|
|
32
|
+
pass: string[];
|
|
33
|
+
partial?: string[];
|
|
34
|
+
};
|
|
35
|
+
export declare const InterpretConfigSchema: z.ZodType<InterpretConfig>;
|
|
36
|
+
export interface EvaluatorResult {
|
|
37
|
+
name: string;
|
|
38
|
+
verdict: Verdict;
|
|
39
|
+
criticality: Criticality;
|
|
40
|
+
confidence?: number;
|
|
41
|
+
reasoning?: string;
|
|
42
|
+
feedback?: unknown[];
|
|
43
|
+
}
|
|
44
|
+
export declare const EvaluatorResultSchema: z.ZodType<EvaluatorResult>;
|
|
45
|
+
export interface EvalCase {
|
|
46
|
+
datasetName: string;
|
|
47
|
+
verdict: Verdict;
|
|
48
|
+
evaluators: EvaluatorResult[];
|
|
49
|
+
}
|
|
50
|
+
export declare const EvalCaseSchema: z.ZodType<EvalCase>;
|
|
51
|
+
export interface EvalSummary {
|
|
52
|
+
total: number;
|
|
53
|
+
passed: number;
|
|
54
|
+
partial: number;
|
|
55
|
+
failed: number;
|
|
56
|
+
acceptableRate: number;
|
|
57
|
+
}
|
|
58
|
+
export interface EvalOutput {
|
|
59
|
+
cases: EvalCase[];
|
|
60
|
+
summary: EvalSummary;
|
|
61
|
+
}
|
|
62
|
+
export declare const EvalOutputSchema: z.ZodType<EvalOutput>;
|
|
63
|
+
export interface GroundTruth {
|
|
64
|
+
evals?: Record<string, Record<string, unknown>>;
|
|
65
|
+
[key: string]: unknown;
|
|
66
|
+
}
|
|
67
|
+
export interface LastOutput {
|
|
68
|
+
output: unknown;
|
|
69
|
+
executionTimeMs?: number;
|
|
70
|
+
date: string;
|
|
71
|
+
}
|
|
72
|
+
export interface LastEval {
|
|
73
|
+
output: EvalCase;
|
|
74
|
+
executionTimeMs?: number;
|
|
75
|
+
date: string;
|
|
76
|
+
}
|
|
77
|
+
export declare const LastOutputSchema: z.ZodType<LastOutput>;
|
|
78
|
+
export declare const LastEvalSchema: z.ZodType<LastEval>;
|
|
79
|
+
export interface Dataset {
|
|
80
|
+
name: string;
|
|
81
|
+
input: Record<string, unknown>;
|
|
82
|
+
ground_truth?: GroundTruth;
|
|
83
|
+
last_output?: LastOutput;
|
|
84
|
+
last_eval?: LastEval;
|
|
85
|
+
[key: string]: unknown;
|
|
86
|
+
}
|
|
87
|
+
export declare const DatasetSchema: z.ZodObject<{
|
|
88
|
+
name: z.ZodString;
|
|
89
|
+
input: z.ZodRecord<z.ZodString, z.ZodAny>;
|
|
90
|
+
ground_truth: z.ZodOptional<z.ZodObject<{
|
|
91
|
+
evals: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodAny>>>;
|
|
92
|
+
}, z.core.$loose>>;
|
|
93
|
+
last_output: z.ZodOptional<z.ZodType<LastOutput, unknown, z.core.$ZodTypeInternals<LastOutput, unknown>>>;
|
|
94
|
+
last_eval: z.ZodOptional<z.ZodType<LastEval, unknown, z.core.$ZodTypeInternals<LastEval, unknown>>>;
|
|
95
|
+
}, z.core.$loose>;
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from '@outputai/core';
|
|
2
|
+
export const VERDICT = {
|
|
3
|
+
PASS: 'pass',
|
|
4
|
+
PARTIAL: 'partial',
|
|
5
|
+
FAIL: 'fail'
|
|
6
|
+
};
|
|
7
|
+
export const CRITICALITY = {
|
|
8
|
+
REQUIRED: 'required',
|
|
9
|
+
INFORMATIONAL: 'informational'
|
|
10
|
+
};
|
|
11
|
+
export const VerdictSchema = z.enum([VERDICT.PASS, VERDICT.PARTIAL, VERDICT.FAIL]);
|
|
12
|
+
export const CriticalitySchema = z.enum([CRITICALITY.REQUIRED, CRITICALITY.INFORMATIONAL]);
|
|
13
|
+
export const InterpretConfigSchema = z.discriminatedUnion('type', [
|
|
14
|
+
z.object({ type: z.literal('verdict') }),
|
|
15
|
+
z.object({ type: z.literal('boolean') }),
|
|
16
|
+
z.object({ type: z.literal('number'), pass: z.number(), partial: z.number().optional() }),
|
|
17
|
+
z.object({ type: z.literal('string'), pass: z.array(z.string()), partial: z.array(z.string()).optional() })
|
|
18
|
+
]);
|
|
19
|
+
export const EvaluatorResultSchema = z.object({
|
|
20
|
+
name: z.string(),
|
|
21
|
+
verdict: VerdictSchema,
|
|
22
|
+
criticality: CriticalitySchema,
|
|
23
|
+
confidence: z.number().optional(),
|
|
24
|
+
reasoning: z.string().optional(),
|
|
25
|
+
feedback: z.array(z.any()).optional()
|
|
26
|
+
});
|
|
27
|
+
export const EvalCaseSchema = z.object({
|
|
28
|
+
datasetName: z.string(),
|
|
29
|
+
verdict: VerdictSchema,
|
|
30
|
+
evaluators: z.array(EvaluatorResultSchema)
|
|
31
|
+
});
|
|
32
|
+
export const EvalOutputSchema = z.object({
|
|
33
|
+
cases: z.array(EvalCaseSchema),
|
|
34
|
+
summary: z.object({
|
|
35
|
+
total: z.number(),
|
|
36
|
+
passed: z.number(),
|
|
37
|
+
partial: z.number(),
|
|
38
|
+
failed: z.number(),
|
|
39
|
+
acceptableRate: z.number()
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
export const LastOutputSchema = z.object({
|
|
43
|
+
output: z.any(),
|
|
44
|
+
executionTimeMs: z.number().optional(),
|
|
45
|
+
date: z.string()
|
|
46
|
+
});
|
|
47
|
+
export const LastEvalSchema = z.object({
|
|
48
|
+
output: EvalCaseSchema,
|
|
49
|
+
executionTimeMs: z.number().optional(),
|
|
50
|
+
date: z.string()
|
|
51
|
+
});
|
|
52
|
+
export const DatasetSchema = z.object({
|
|
53
|
+
name: z.string(),
|
|
54
|
+
input: z.record(z.string(), z.any()),
|
|
55
|
+
ground_truth: z.object({
|
|
56
|
+
evals: z.record(z.string(), z.record(z.string(), z.any())).optional()
|
|
57
|
+
}).passthrough().optional(),
|
|
58
|
+
last_output: LastOutputSchema.optional(),
|
|
59
|
+
last_eval: LastEvalSchema.optional()
|
|
60
|
+
}).passthrough();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EvaluationVerdictResult, EvaluationBooleanResult, EvaluationNumberResult, EvaluationStringResult, EvaluationFeedback } from '@outputai/core';
|
|
2
|
+
type FeedbackArg = ConstructorParameters<typeof EvaluationFeedback>[0];
|
|
3
|
+
export declare const Verdict: {
|
|
4
|
+
pass(reasoning?: string): EvaluationVerdictResult;
|
|
5
|
+
partial(confidence: number, reasoning?: string, feedback?: FeedbackArg[]): EvaluationVerdictResult;
|
|
6
|
+
fail(reasoning: string, feedback?: FeedbackArg[]): EvaluationVerdictResult;
|
|
7
|
+
equals(actual: unknown, expected: unknown): EvaluationBooleanResult;
|
|
8
|
+
closeTo(actual: number, expected: number, tolerance: number): EvaluationBooleanResult;
|
|
9
|
+
gt(actual: number, threshold: number): EvaluationBooleanResult;
|
|
10
|
+
gte(actual: number, threshold: number): EvaluationBooleanResult;
|
|
11
|
+
lt(actual: number, threshold: number): EvaluationBooleanResult;
|
|
12
|
+
lte(actual: number, threshold: number): EvaluationBooleanResult;
|
|
13
|
+
inRange(actual: number, min: number, max: number): EvaluationBooleanResult;
|
|
14
|
+
contains(haystack: string, needle: string): EvaluationBooleanResult;
|
|
15
|
+
matches(value: string, pattern: RegExp): EvaluationBooleanResult;
|
|
16
|
+
includesAll(actual: unknown[], expected: unknown[]): EvaluationBooleanResult;
|
|
17
|
+
includesAny(actual: unknown[], expected: unknown[]): EvaluationBooleanResult;
|
|
18
|
+
isTrue(value: boolean): EvaluationBooleanResult;
|
|
19
|
+
isFalse(value: boolean): EvaluationBooleanResult;
|
|
20
|
+
fromJudge({ verdict, reasoning }: {
|
|
21
|
+
verdict: "pass" | "partial" | "fail";
|
|
22
|
+
reasoning: string;
|
|
23
|
+
}): EvaluationVerdictResult;
|
|
24
|
+
score(value: number, reasoning?: string): EvaluationNumberResult;
|
|
25
|
+
label(value: string, reasoning?: string): EvaluationStringResult;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
package/dist/verdict.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { EvaluationVerdictResult, EvaluationBooleanResult, EvaluationNumberResult, EvaluationStringResult, EvaluationFeedback } from '@outputai/core';
|
|
2
|
+
const boolResult = (value, reasoning) => new EvaluationBooleanResult({ value, confidence: 1.0, reasoning });
|
|
3
|
+
export const Verdict = {
|
|
4
|
+
// --- Original helpers ---
|
|
5
|
+
pass(reasoning) {
|
|
6
|
+
return new EvaluationVerdictResult({ value: 'pass', confidence: 1.0, reasoning });
|
|
7
|
+
},
|
|
8
|
+
partial(confidence, reasoning, feedback) {
|
|
9
|
+
return new EvaluationVerdictResult({
|
|
10
|
+
value: 'partial',
|
|
11
|
+
confidence,
|
|
12
|
+
reasoning,
|
|
13
|
+
feedback: feedback ? feedback.map(f => new EvaluationFeedback(f)) : undefined
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
fail(reasoning, feedback) {
|
|
17
|
+
return new EvaluationVerdictResult({
|
|
18
|
+
value: 'fail',
|
|
19
|
+
confidence: 0.0,
|
|
20
|
+
reasoning,
|
|
21
|
+
feedback: feedback ? feedback.map(f => new EvaluationFeedback(f)) : undefined
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
// --- Deterministic assertions (confidence 1.0 for both pass and fail) ---
|
|
25
|
+
equals(actual, expected) {
|
|
26
|
+
const passed = actual === expected;
|
|
27
|
+
return boolResult(passed, passed ?
|
|
28
|
+
`Value equals expected: ${JSON.stringify(expected)}` :
|
|
29
|
+
`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
|
30
|
+
},
|
|
31
|
+
closeTo(actual, expected, tolerance) {
|
|
32
|
+
const passed = Math.abs(actual - expected) <= tolerance;
|
|
33
|
+
return boolResult(passed, passed ?
|
|
34
|
+
`${actual} is within ${tolerance} of ${expected}` :
|
|
35
|
+
`${actual} is not within ${tolerance} of ${expected} (diff: ${Math.abs(actual - expected)})`);
|
|
36
|
+
},
|
|
37
|
+
gt(actual, threshold) {
|
|
38
|
+
const passed = actual > threshold;
|
|
39
|
+
return boolResult(passed, passed ?
|
|
40
|
+
`${actual} > ${threshold}` :
|
|
41
|
+
`${actual} is not greater than ${threshold}`);
|
|
42
|
+
},
|
|
43
|
+
gte(actual, threshold) {
|
|
44
|
+
const passed = actual >= threshold;
|
|
45
|
+
return boolResult(passed, passed ?
|
|
46
|
+
`${actual} >= ${threshold}` :
|
|
47
|
+
`${actual} is not greater than or equal to ${threshold}`);
|
|
48
|
+
},
|
|
49
|
+
lt(actual, threshold) {
|
|
50
|
+
const passed = actual < threshold;
|
|
51
|
+
return boolResult(passed, passed ?
|
|
52
|
+
`${actual} < ${threshold}` :
|
|
53
|
+
`${actual} is not less than ${threshold}`);
|
|
54
|
+
},
|
|
55
|
+
lte(actual, threshold) {
|
|
56
|
+
const passed = actual <= threshold;
|
|
57
|
+
return boolResult(passed, passed ?
|
|
58
|
+
`${actual} <= ${threshold}` :
|
|
59
|
+
`${actual} is not less than or equal to ${threshold}`);
|
|
60
|
+
},
|
|
61
|
+
inRange(actual, min, max) {
|
|
62
|
+
const passed = actual >= min && actual <= max;
|
|
63
|
+
return boolResult(passed, passed ?
|
|
64
|
+
`${actual} is in range [${min}, ${max}]` :
|
|
65
|
+
`${actual} is not in range [${min}, ${max}]`);
|
|
66
|
+
},
|
|
67
|
+
contains(haystack, needle) {
|
|
68
|
+
const passed = haystack.includes(needle);
|
|
69
|
+
return boolResult(passed, passed ?
|
|
70
|
+
`String contains "${needle}"` :
|
|
71
|
+
`String does not contain "${needle}"`);
|
|
72
|
+
},
|
|
73
|
+
matches(value, pattern) {
|
|
74
|
+
const passed = pattern.test(value);
|
|
75
|
+
return boolResult(passed, passed ?
|
|
76
|
+
`Value matches ${pattern}` :
|
|
77
|
+
`Value does not match ${pattern}`);
|
|
78
|
+
},
|
|
79
|
+
includesAll(actual, expected) {
|
|
80
|
+
const missing = expected.filter(e => !actual.includes(e));
|
|
81
|
+
const passed = missing.length === 0;
|
|
82
|
+
return boolResult(passed, passed ?
|
|
83
|
+
'Array includes all expected values' :
|
|
84
|
+
`Array is missing: ${JSON.stringify(missing)}`);
|
|
85
|
+
},
|
|
86
|
+
includesAny(actual, expected) {
|
|
87
|
+
const found = expected.filter(e => actual.includes(e));
|
|
88
|
+
const passed = found.length > 0;
|
|
89
|
+
return boolResult(passed, passed ?
|
|
90
|
+
`Array includes: ${JSON.stringify(found)}` :
|
|
91
|
+
`Array includes none of: ${JSON.stringify(expected)}`);
|
|
92
|
+
},
|
|
93
|
+
isTrue(value) {
|
|
94
|
+
return boolResult(value === true, value === true ? 'Value is true' : `Expected true, got ${value}`);
|
|
95
|
+
},
|
|
96
|
+
isFalse(value) {
|
|
97
|
+
return boolResult(value === false, value === false ? 'Value is false' : `Expected false, got ${value}`);
|
|
98
|
+
},
|
|
99
|
+
// --- LLM judge helpers ---
|
|
100
|
+
fromJudge({ verdict, reasoning }) {
|
|
101
|
+
return new EvaluationVerdictResult({ value: verdict, confidence: 0.9, reasoning });
|
|
102
|
+
},
|
|
103
|
+
score(value, reasoning) {
|
|
104
|
+
return new EvaluationNumberResult({ value, confidence: 0.9, reasoning });
|
|
105
|
+
},
|
|
106
|
+
label(value, reasoning) {
|
|
107
|
+
return new EvaluationStringResult({ value, confidence: 0.9, reasoning });
|
|
108
|
+
}
|
|
109
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|