@byokit/decide 0.4.6 → 0.5.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/CHANGELOG.md +20 -0
- package/README.md +199 -8
- package/dist/claude-code.d.ts +19 -0
- package/dist/claude-code.js +189 -0
- package/dist/cli.js +3 -2
- package/dist/config.d.ts +7 -3
- package/dist/config.js +7 -4
- package/dist/eval.d.ts +8 -2
- package/dist/eval.js +12 -5
- package/dist/generate.d.ts +65 -0
- package/dist/generate.js +112 -0
- package/dist/generation-images.d.ts +3 -0
- package/dist/generation-images.js +11 -0
- package/dist/images.d.ts +28 -0
- package/dist/images.js +67 -0
- package/dist/index.d.ts +33 -14
- package/dist/index.js +51 -29
- package/dist/jev.js +4 -1
- package/dist/openai.d.ts +2 -0
- package/dist/openai.js +18 -6
- package/dist/schema.d.ts +73 -0
- package/dist/schema.js +179 -0
- package/package.json +6 -2
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
2
|
+
readonly [key: string]: JsonValue;
|
|
3
|
+
};
|
|
4
|
+
type JsonType = 'null' | 'boolean' | 'number' | 'integer' | 'string' | 'array' | 'object';
|
|
5
|
+
type SchemaNode = boolean | OutputSchema;
|
|
6
|
+
export type OutputSchema = {
|
|
7
|
+
readonly type?: JsonType | readonly JsonType[];
|
|
8
|
+
readonly properties?: Readonly<Record<string, SchemaNode>>;
|
|
9
|
+
readonly required?: readonly string[];
|
|
10
|
+
readonly additionalProperties?: SchemaNode;
|
|
11
|
+
readonly items?: SchemaNode;
|
|
12
|
+
readonly enum?: readonly JsonValue[];
|
|
13
|
+
readonly const?: JsonValue;
|
|
14
|
+
readonly anyOf?: readonly SchemaNode[];
|
|
15
|
+
readonly oneOf?: readonly SchemaNode[];
|
|
16
|
+
readonly allOf?: readonly SchemaNode[];
|
|
17
|
+
readonly not?: SchemaNode;
|
|
18
|
+
readonly minimum?: number;
|
|
19
|
+
readonly maximum?: number;
|
|
20
|
+
readonly exclusiveMinimum?: number;
|
|
21
|
+
readonly exclusiveMaximum?: number;
|
|
22
|
+
readonly minLength?: number;
|
|
23
|
+
readonly maxLength?: number;
|
|
24
|
+
readonly minItems?: number;
|
|
25
|
+
readonly maxItems?: number;
|
|
26
|
+
readonly uniqueItems?: boolean;
|
|
27
|
+
readonly minProperties?: number;
|
|
28
|
+
readonly maxProperties?: number;
|
|
29
|
+
readonly title?: string;
|
|
30
|
+
readonly description?: string;
|
|
31
|
+
readonly default?: JsonValue;
|
|
32
|
+
readonly examples?: readonly JsonValue[];
|
|
33
|
+
readonly $schema?: 'http://json-schema.org/draft-07/schema#';
|
|
34
|
+
};
|
|
35
|
+
type KeysInEveryList<R extends readonly string[]> = (R extends unknown ? (key: number extends R['length'] ? never : R[number]) => void : never) extends (key: infer K) => void ? Extract<K, string> : never;
|
|
36
|
+
type RequiredKeys<S> = S extends {
|
|
37
|
+
readonly required: infer R extends readonly string[];
|
|
38
|
+
} ? KeysInEveryList<R> : never;
|
|
39
|
+
type ObjectOutput<S> = S extends {
|
|
40
|
+
readonly properties: infer P extends Record<string, SchemaNode>;
|
|
41
|
+
} ? {
|
|
42
|
+
-readonly [K in keyof P as K extends RequiredKeys<S> ? K : never]: SchemaOutput<P[K]>;
|
|
43
|
+
} & {
|
|
44
|
+
-readonly [K in keyof P as K extends RequiredKeys<S> ? never : K]?: SchemaOutput<P[K]>;
|
|
45
|
+
} : Record<string, unknown>;
|
|
46
|
+
type OutputType<T, S> = T extends 'null' ? null : T extends 'boolean' ? boolean : T extends 'number' | 'integer' ? number : T extends 'string' ? string : T extends 'array' ? (S extends {
|
|
47
|
+
readonly items: infer I;
|
|
48
|
+
} ? SchemaOutput<I> : unknown)[] : T extends 'object' ? ObjectOutput<S> : unknown;
|
|
49
|
+
/** Literal schemas infer their output; broad/dynamic schemas and unsupported inference shapes remain unknown. */
|
|
50
|
+
export type SchemaOutput<S> = S extends {
|
|
51
|
+
readonly const: infer V;
|
|
52
|
+
} ? V : S extends {
|
|
53
|
+
readonly enum: readonly (infer V)[];
|
|
54
|
+
} ? V : S extends {
|
|
55
|
+
readonly type: infer T;
|
|
56
|
+
} ? OutputType<T extends readonly unknown[] ? T[number] : T, S> : S extends {
|
|
57
|
+
readonly anyOf: readonly (infer B)[];
|
|
58
|
+
} | {
|
|
59
|
+
readonly oneOf: readonly (infer B)[];
|
|
60
|
+
} ? SchemaOutput<B> : unknown;
|
|
61
|
+
export declare class InvalidSchemaError extends Error {
|
|
62
|
+
readonly code = "invalid_schema";
|
|
63
|
+
constructor();
|
|
64
|
+
}
|
|
65
|
+
/** Snapshot a JSON-only schema before any await; no mutation can change the prompt or its validation. */
|
|
66
|
+
export declare function outputSchema(value: unknown): {
|
|
67
|
+
json: string;
|
|
68
|
+
prompt: string;
|
|
69
|
+
parse(text: string): {
|
|
70
|
+
data: unknown;
|
|
71
|
+
} | undefined;
|
|
72
|
+
};
|
|
73
|
+
export {};
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const record = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
2
|
+
const own = (v, k) => Object.hasOwn(v, k);
|
|
3
|
+
const types = new Set(['null', 'boolean', 'number', 'integer', 'string', 'array', 'object']);
|
|
4
|
+
const bounds = ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'];
|
|
5
|
+
const counts = ['minLength', 'maxLength', 'minItems', 'maxItems', 'minProperties', 'maxProperties'];
|
|
6
|
+
const annotations = ['title', 'description', 'default', 'examples', '$schema'];
|
|
7
|
+
const keywords = new Set(['type', 'properties', 'required', 'additionalProperties', 'items', 'enum', 'const',
|
|
8
|
+
'anyOf', 'oneOf', 'allOf', 'not', 'uniqueItems', ...bounds, ...counts, ...annotations]);
|
|
9
|
+
export class InvalidSchemaError extends Error {
|
|
10
|
+
code = 'invalid_schema';
|
|
11
|
+
constructor() { super('The output schema is invalid or unsupported.'); this.name = 'InvalidSchemaError'; }
|
|
12
|
+
}
|
|
13
|
+
function invalid() { throw new InvalidSchemaError(); }
|
|
14
|
+
function checkSchema(s, depth = 0) {
|
|
15
|
+
if (depth > 32)
|
|
16
|
+
invalid();
|
|
17
|
+
if (typeof s === 'boolean')
|
|
18
|
+
return;
|
|
19
|
+
if (!record(s) || Object.keys(s).some((k) => !keywords.has(k)))
|
|
20
|
+
invalid();
|
|
21
|
+
if (own(s, 'type')) {
|
|
22
|
+
const ts = Array.isArray(s.type) ? s.type : [s.type];
|
|
23
|
+
if (!ts.length || ts.some((t) => !types.has(t)) || new Set(ts).size !== ts.length)
|
|
24
|
+
invalid();
|
|
25
|
+
}
|
|
26
|
+
for (const k of bounds)
|
|
27
|
+
if (own(s, k) && (typeof s[k] !== 'number' || !Number.isFinite(s[k])))
|
|
28
|
+
invalid();
|
|
29
|
+
for (const k of counts)
|
|
30
|
+
if (own(s, k) && (typeof s[k] !== 'number' || !Number.isSafeInteger(s[k]) || s[k] < 0))
|
|
31
|
+
invalid();
|
|
32
|
+
for (const k of ['title', 'description'])
|
|
33
|
+
if (own(s, k) && typeof s[k] !== 'string')
|
|
34
|
+
invalid();
|
|
35
|
+
if (own(s, '$schema') && s.$schema !== 'http://json-schema.org/draft-07/schema#')
|
|
36
|
+
invalid();
|
|
37
|
+
if (own(s, 'examples') && !Array.isArray(s.examples))
|
|
38
|
+
invalid();
|
|
39
|
+
if (own(s, 'enum') && (!Array.isArray(s.enum) || !s.enum.length))
|
|
40
|
+
invalid();
|
|
41
|
+
if (own(s, 'uniqueItems') && typeof s.uniqueItems !== 'boolean')
|
|
42
|
+
invalid();
|
|
43
|
+
if (own(s, 'required') && (!Array.isArray(s.required) || s.required.some((k) => typeof k !== 'string')
|
|
44
|
+
|| new Set(s.required).size !== s.required.length))
|
|
45
|
+
invalid();
|
|
46
|
+
if (own(s, 'properties')) {
|
|
47
|
+
if (!record(s.properties))
|
|
48
|
+
invalid();
|
|
49
|
+
for (const sub of Object.values(s.properties))
|
|
50
|
+
checkSchema(sub, depth + 1);
|
|
51
|
+
}
|
|
52
|
+
for (const k of ['items', 'additionalProperties', 'not'])
|
|
53
|
+
if (own(s, k))
|
|
54
|
+
checkSchema(s[k], depth + 1);
|
|
55
|
+
for (const k of ['anyOf', 'oneOf', 'allOf'])
|
|
56
|
+
if (own(s, k)) {
|
|
57
|
+
if (!Array.isArray(s[k]) || !s[k].length)
|
|
58
|
+
invalid();
|
|
59
|
+
for (const sub of s[k])
|
|
60
|
+
checkSchema(sub, depth + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Equality is structural: JSON objects have no key order, including enum/const values and unique array items.
|
|
64
|
+
function equal(a, b) {
|
|
65
|
+
if (a === b)
|
|
66
|
+
return true;
|
|
67
|
+
if (Array.isArray(a) && Array.isArray(b))
|
|
68
|
+
return a.length === b.length && a.every((v, i) => equal(v, b[i]));
|
|
69
|
+
if (record(a) && record(b))
|
|
70
|
+
return Object.keys(a).length === Object.keys(b).length
|
|
71
|
+
&& Object.keys(a).every((k) => own(b, k) && equal(a[k], b[k]));
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function hasType(v, t) {
|
|
75
|
+
if (t === 'null')
|
|
76
|
+
return v === null;
|
|
77
|
+
if (t === 'array')
|
|
78
|
+
return Array.isArray(v);
|
|
79
|
+
if (t === 'object')
|
|
80
|
+
return record(v);
|
|
81
|
+
if (t === 'integer')
|
|
82
|
+
return typeof v === 'number' && Number.isInteger(v);
|
|
83
|
+
return typeof v === t && (t !== 'number' || Number.isFinite(v));
|
|
84
|
+
}
|
|
85
|
+
function matches(s, v) {
|
|
86
|
+
if (typeof s === 'boolean')
|
|
87
|
+
return s;
|
|
88
|
+
if (s.type !== undefined && !(Array.isArray(s.type) ? s.type : [s.type]).some((t) => hasType(v, t)))
|
|
89
|
+
return false;
|
|
90
|
+
if (own(s, 'const') && !equal(s.const, v))
|
|
91
|
+
return false;
|
|
92
|
+
if (s.enum && !s.enum.some((e) => equal(e, v)))
|
|
93
|
+
return false;
|
|
94
|
+
if (s.allOf && !s.allOf.every((sub) => matches(sub, v)))
|
|
95
|
+
return false;
|
|
96
|
+
if (s.anyOf && !s.anyOf.some((sub) => matches(sub, v)))
|
|
97
|
+
return false;
|
|
98
|
+
if (s.oneOf && s.oneOf.filter((sub) => matches(sub, v)).length !== 1)
|
|
99
|
+
return false;
|
|
100
|
+
if (s.not !== undefined && matches(s.not, v))
|
|
101
|
+
return false;
|
|
102
|
+
if (typeof v === 'number') {
|
|
103
|
+
if (!Number.isFinite(v) || (s.minimum !== undefined && v < s.minimum) || (s.maximum !== undefined && v > s.maximum)
|
|
104
|
+
|| (s.exclusiveMinimum !== undefined && v <= s.exclusiveMinimum) || (s.exclusiveMaximum !== undefined && v >= s.exclusiveMaximum))
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (typeof v === 'string') {
|
|
108
|
+
const length = [...v].length;
|
|
109
|
+
if ((s.minLength !== undefined && length < s.minLength) || (s.maxLength !== undefined && length > s.maxLength))
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
if (Array.isArray(v)) {
|
|
113
|
+
if ((s.minItems !== undefined && v.length < s.minItems) || (s.maxItems !== undefined && v.length > s.maxItems))
|
|
114
|
+
return false;
|
|
115
|
+
if (s.items !== undefined && !v.every((item) => matches(s.items, item)))
|
|
116
|
+
return false;
|
|
117
|
+
if (s.uniqueItems && v.some((item, i) => v.slice(0, i).some((other) => equal(item, other))))
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (record(v)) {
|
|
121
|
+
const keys = Object.keys(v);
|
|
122
|
+
if ((s.minProperties !== undefined && keys.length < s.minProperties) || (s.maxProperties !== undefined && keys.length > s.maxProperties))
|
|
123
|
+
return false;
|
|
124
|
+
if (s.required?.some((k) => !own(v, k)))
|
|
125
|
+
return false;
|
|
126
|
+
for (const k of keys) {
|
|
127
|
+
const sub = s.properties && own(s.properties, k) ? s.properties[k] : s.additionalProperties;
|
|
128
|
+
if (sub !== undefined && !matches(sub, v[k]))
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
function utf8Length(text) {
|
|
135
|
+
let length = 0;
|
|
136
|
+
for (const char of text) {
|
|
137
|
+
const code = char.codePointAt(0);
|
|
138
|
+
length += code < 0x80 ? 1 : code < 0x800 ? 2 : code < 0x10000 ? 3 : 4;
|
|
139
|
+
}
|
|
140
|
+
return length;
|
|
141
|
+
}
|
|
142
|
+
/** Snapshot a JSON-only schema before any await; no mutation can change the prompt or its validation. */
|
|
143
|
+
export function outputSchema(value) {
|
|
144
|
+
let json;
|
|
145
|
+
try {
|
|
146
|
+
json = JSON.stringify(value, (_key, v) => {
|
|
147
|
+
if (v === undefined || typeof v === 'function' || typeof v === 'symbol' || typeof v === 'bigint'
|
|
148
|
+
|| (typeof v === 'number' && !Number.isFinite(v)))
|
|
149
|
+
invalid();
|
|
150
|
+
return v;
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
invalid();
|
|
155
|
+
}
|
|
156
|
+
if (!json || json.length > 65_536 || utf8Length(json) > 65_536)
|
|
157
|
+
invalid();
|
|
158
|
+
const schema = JSON.parse(json);
|
|
159
|
+
if (!record(schema))
|
|
160
|
+
invalid(); // the public run contract requires an object at its root
|
|
161
|
+
checkSchema(schema);
|
|
162
|
+
return {
|
|
163
|
+
json,
|
|
164
|
+
prompt: `Return only one JSON value matching this JSON Schema, with no markdown or surrounding text.\n${json}`,
|
|
165
|
+
parse: (text) => {
|
|
166
|
+
try {
|
|
167
|
+
const data = JSON.parse(text, (_key, value) => {
|
|
168
|
+
if (typeof value === 'number' && !Number.isFinite(value))
|
|
169
|
+
throw new Error('invalid number');
|
|
170
|
+
return value;
|
|
171
|
+
});
|
|
172
|
+
return matches(schema, data) ? { data } : undefined;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byokit/decide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Typed questions in, a typed answer with confidence out, abstaining below a floor. Rules, Jev and configurable OpenAI backends, and an eval runner.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"./eval": {
|
|
21
21
|
"types": "./dist/eval.d.ts",
|
|
22
22
|
"default": "./dist/eval.js"
|
|
23
|
+
},
|
|
24
|
+
"./claude-code": {
|
|
25
|
+
"types": "./dist/claude-code.d.ts",
|
|
26
|
+
"default": "./dist/claude-code.js"
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
"bin": {
|
|
@@ -37,7 +41,7 @@
|
|
|
37
41
|
"access": "public"
|
|
38
42
|
},
|
|
39
43
|
"dependencies": {
|
|
40
|
-
"@byokit/accounts": "0.
|
|
44
|
+
"@byokit/accounts": "0.14.0",
|
|
41
45
|
"openai": "7.25.0"
|
|
42
46
|
}
|
|
43
47
|
}
|