@inferencesh/app 0.1.14 → 0.1.16
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/schema.d.ts +2 -1
- package/dist/schema.js +4 -11
- package/dist/stream.d.ts +19 -0
- package/dist/stream.js +53 -58
- package/dist/zod-walk.d.ts +9 -0
- package/dist/zod-walk.js +40 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { File } from "./file.js";
|
|
|
2
2
|
export type { FileOptions, FileData } from "./file.js";
|
|
3
3
|
export { createFileSchema, isFileSchema, FILE_SCHEMA_MARKER } from "./schema.js";
|
|
4
4
|
export type { Socket, Frame } from "./socket.js";
|
|
5
|
-
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
5
|
+
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, CLEAR_KEY, ERROR_KEY, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
6
6
|
export type { Update } from "./stream.js";
|
|
7
7
|
export { StorageDir, ensureDir } from "./storage.js";
|
|
8
8
|
export type { StorageDirValue } from "./storage.js";
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export { File } from "./file.js";
|
|
3
3
|
// Zod schema utilities
|
|
4
4
|
export { createFileSchema, isFileSchema, FILE_SCHEMA_MARKER } from "./schema.js";
|
|
5
|
-
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
5
|
+
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, CLEAR_KEY, ERROR_KEY, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
6
6
|
// Storage directories
|
|
7
7
|
export { StorageDir, ensureDir } from "./storage.js";
|
|
8
8
|
// Download utility
|
package/dist/schema.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare const FILE_SCHEMA_MARKER: unique symbol;
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function createFileSchema(z: any): any;
|
|
30
30
|
/**
|
|
31
|
-
* Check if a Zod schema is a file schema (created by createFileSchema)
|
|
31
|
+
* Check if a Zod schema is a file schema (created by createFileSchema),
|
|
32
|
+
* through any wrapper (optional, default, nullable, ...) in zod v3 or v4.
|
|
32
33
|
*/
|
|
33
34
|
export declare function isFileSchema(schema: any): boolean;
|
package/dist/schema.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - Generates JSON Schema with format: "file"
|
|
8
8
|
*/
|
|
9
9
|
import { File } from "./file.js";
|
|
10
|
+
import { findMarker } from "./zod-walk.js";
|
|
10
11
|
// We don't import zod directly to avoid version conflicts.
|
|
11
12
|
// Instead, apps provide their own zod and we work with the schema structure.
|
|
12
13
|
/**
|
|
@@ -36,17 +37,9 @@ export function createFileSchema(z) {
|
|
|
36
37
|
return schema;
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
39
|
-
* Check if a Zod schema is a file schema (created by createFileSchema)
|
|
40
|
+
* Check if a Zod schema is a file schema (created by createFileSchema),
|
|
41
|
+
* through any wrapper (optional, default, nullable, ...) in zod v3 or v4.
|
|
40
42
|
*/
|
|
41
43
|
export function isFileSchema(schema) {
|
|
42
|
-
|
|
43
|
-
return false;
|
|
44
|
-
// Check for our marker
|
|
45
|
-
if (schema._def && schema._def[FILE_SCHEMA_MARKER])
|
|
46
|
-
return true;
|
|
47
|
-
// Check inner type for transforms/effects
|
|
48
|
-
if (schema._def?.typeName === "ZodEffects" && schema._def.schema) {
|
|
49
|
-
return isFileSchema(schema._def.schema);
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
44
|
+
return findMarker(schema, FILE_SCHEMA_MARKER) === true;
|
|
52
45
|
}
|
package/dist/stream.d.ts
CHANGED
|
@@ -51,6 +51,13 @@ import type { Socket } from "./socket.js";
|
|
|
51
51
|
* The kernel's zodToJsonSchema reads this and emits `format: "stream"`.
|
|
52
52
|
*/
|
|
53
53
|
export declare const STREAM_SCHEMA_MARKER: unique symbol;
|
|
54
|
+
/** `{"$clear": "audio"}`: drop what has been buffered of a live output field. */
|
|
55
|
+
export declare const CLEAR_KEY = "$clear";
|
|
56
|
+
/**
|
|
57
|
+
* `{"$error": {"field": ..., "message": ...}}`: a refused frame, or anything
|
|
58
|
+
* else the caller should be told went wrong. The stream goes on.
|
|
59
|
+
*/
|
|
60
|
+
export declare const ERROR_KEY = "$error";
|
|
54
61
|
/**
|
|
55
62
|
* Marks a binary item schema. The value on `_def` is the content type.
|
|
56
63
|
* The kernel's zodToJsonSchema reads this and emits
|
|
@@ -127,11 +134,18 @@ export declare class Live {
|
|
|
127
134
|
private readonly inBinary;
|
|
128
135
|
private readonly outShape;
|
|
129
136
|
private readonly outBinary;
|
|
137
|
+
private readonly outLive;
|
|
138
|
+
private refusedBinary;
|
|
130
139
|
constructor(socket: Socket, inputData: Record<string, unknown>, inputSchema: any, outputSchema: any);
|
|
131
140
|
[Symbol.asyncIterator](): AsyncGenerator<Update, void, undefined>;
|
|
132
141
|
private frames;
|
|
133
142
|
private read;
|
|
134
143
|
private refuse;
|
|
144
|
+
/**
|
|
145
|
+
* Tell the caller something went wrong without ending the stream: a
|
|
146
|
+
* provider error, or why the session is about to end.
|
|
147
|
+
*/
|
|
148
|
+
error(message: string, field?: string | null): Promise<void>;
|
|
135
149
|
/**
|
|
136
150
|
* Send items of live output fields, or new values of ordinary ones.
|
|
137
151
|
*
|
|
@@ -140,5 +154,10 @@ export declare class Live {
|
|
|
140
154
|
* `await live.send({ effect: "echo" })` are JSON frames. Keys in one call
|
|
141
155
|
* other than the binary field go out together as one JSON frame.
|
|
142
156
|
*/
|
|
157
|
+
/**
|
|
158
|
+
* Tell the caller to drop what it has buffered of a live output field:
|
|
159
|
+
* the queued audio of an answer the user just talked over.
|
|
160
|
+
*/
|
|
161
|
+
clear(field: string): Promise<void>;
|
|
143
162
|
send(fields: Record<string, unknown>): Promise<void>;
|
|
144
163
|
}
|
package/dist/stream.js
CHANGED
|
@@ -45,11 +45,21 @@
|
|
|
45
45
|
* Zod is never imported: the app's own zod (v3 or v4) is passed in, and the
|
|
46
46
|
* schemas are read by duck-typing `_def`, the same way the kernel does.
|
|
47
47
|
*/
|
|
48
|
+
import { findMarker } from "./zod-walk.js";
|
|
48
49
|
/**
|
|
49
50
|
* Marks a live field's schema. The value on `_def` is the item schema.
|
|
50
51
|
* The kernel's zodToJsonSchema reads this and emits `format: "stream"`.
|
|
51
52
|
*/
|
|
52
53
|
export const STREAM_SCHEMA_MARKER = Symbol.for("inferencesh.streamSchema");
|
|
54
|
+
// Control frames. Reserved keys start with `$`, which no field name can, so a
|
|
55
|
+
// control frame is never mistaken for an output field.
|
|
56
|
+
/** `{"$clear": "audio"}`: drop what has been buffered of a live output field. */
|
|
57
|
+
export const CLEAR_KEY = "$clear";
|
|
58
|
+
/**
|
|
59
|
+
* `{"$error": {"field": ..., "message": ...}}`: a refused frame, or anything
|
|
60
|
+
* else the caller should be told went wrong. The stream goes on.
|
|
61
|
+
*/
|
|
62
|
+
export const ERROR_KEY = "$error";
|
|
53
63
|
/**
|
|
54
64
|
* Marks a binary item schema. The value on `_def` is the content type.
|
|
55
65
|
* The kernel's zodToJsonSchema reads this and emits
|
|
@@ -90,41 +100,6 @@ export function pcm16(z, rate = 16000, channels = 1) {
|
|
|
90
100
|
return media(z, `audio/pcm;format=s16le;rate=${rate};channels=${channels}`);
|
|
91
101
|
}
|
|
92
102
|
// --- schema walking (zod v3 and v4 `_def` shapes) ---------------------------
|
|
93
|
-
/** Inner schemas of a wrapper def, in the order worth checking. */
|
|
94
|
-
function innerSchemas(def) {
|
|
95
|
-
const out = [];
|
|
96
|
-
// v3 + v4: optional / default / nullable / catch / readonly
|
|
97
|
-
if (def.innerType)
|
|
98
|
-
out.push(def.innerType);
|
|
99
|
-
// v3: effects (preprocess / transform / refine), v4: some wrappers
|
|
100
|
-
if (def.schema)
|
|
101
|
-
out.push(def.schema);
|
|
102
|
-
if (def.inner)
|
|
103
|
-
out.push(def.inner);
|
|
104
|
-
// v4: pipe (preprocess, transform) — in, then out
|
|
105
|
-
if (def.in)
|
|
106
|
-
out.push(def.in);
|
|
107
|
-
if (def.out)
|
|
108
|
-
out.push(def.out);
|
|
109
|
-
return out;
|
|
110
|
-
}
|
|
111
|
-
/** The marker's value on the schema or any schema it wraps, or undefined. */
|
|
112
|
-
function findMarker(schema, marker, seen = new Set()) {
|
|
113
|
-
if (!schema || typeof schema !== "object" || seen.has(schema))
|
|
114
|
-
return undefined;
|
|
115
|
-
seen.add(schema);
|
|
116
|
-
const def = schema._def;
|
|
117
|
-
if (!def || typeof def !== "object")
|
|
118
|
-
return undefined;
|
|
119
|
-
if (Object.prototype.hasOwnProperty.call(def, marker))
|
|
120
|
-
return def[marker];
|
|
121
|
-
for (const inner of innerSchemas(def)) {
|
|
122
|
-
const found = findMarker(inner, marker, seen);
|
|
123
|
-
if (found !== undefined)
|
|
124
|
-
return found;
|
|
125
|
-
}
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
103
|
/** The fields of a zod object schema. v3: `_def.shape()`, v4: `_def.shape`. */
|
|
129
104
|
function shapeOf(objectSchema) {
|
|
130
105
|
const def = objectSchema?._def;
|
|
@@ -213,6 +188,8 @@ export class Live {
|
|
|
213
188
|
inBinary;
|
|
214
189
|
outShape;
|
|
215
190
|
outBinary;
|
|
191
|
+
outLive;
|
|
192
|
+
refusedBinary = false;
|
|
216
193
|
constructor(socket, inputData, inputSchema, outputSchema) {
|
|
217
194
|
this.socket = socket;
|
|
218
195
|
this.input = inputData;
|
|
@@ -221,6 +198,7 @@ export class Live {
|
|
|
221
198
|
this.inBinary = binaryField(inputSchema);
|
|
222
199
|
this.outShape = shapeOf(outputSchema);
|
|
223
200
|
this.outBinary = binaryField(outputSchema);
|
|
201
|
+
this.outLive = liveFields(outputSchema);
|
|
224
202
|
}
|
|
225
203
|
async *[Symbol.asyncIterator]() {
|
|
226
204
|
for await (const frame of this.frames()) {
|
|
@@ -245,7 +223,12 @@ export class Live {
|
|
|
245
223
|
async read(frame) {
|
|
246
224
|
if (isBinaryFrame(frame)) {
|
|
247
225
|
if (this.inBinary === null) {
|
|
248
|
-
|
|
226
|
+
// Once: a caller streaming audio to the wrong function would
|
|
227
|
+
// otherwise get an error frame for every frame it sends.
|
|
228
|
+
if (!this.refusedBinary) {
|
|
229
|
+
this.refusedBinary = true;
|
|
230
|
+
await this.refuse(null, "this function takes no binary frames");
|
|
231
|
+
}
|
|
249
232
|
return [];
|
|
250
233
|
}
|
|
251
234
|
return [{ field: this.inBinary, value: toBuffer(frame) }];
|
|
@@ -263,35 +246,37 @@ export class Live {
|
|
|
263
246
|
}
|
|
264
247
|
const updates = [];
|
|
265
248
|
for (const [key, raw] of Object.entries(patch)) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
const result = this.inLive[key].safeParse(raw);
|
|
272
|
-
if (!result.success) {
|
|
273
|
-
await this.refuse(key, firstIssue(result));
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
updates.push({ field: key, value: result.data });
|
|
249
|
+
const live = Object.prototype.hasOwnProperty.call(this.inLive, key);
|
|
250
|
+
if (!live && !Object.prototype.hasOwnProperty.call(this.inShape, key)) {
|
|
251
|
+
await this.refuse(key, "no such field");
|
|
252
|
+
continue;
|
|
277
253
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
await this.refuse(key, firstIssue(result));
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
this.input[key] = result.data;
|
|
285
|
-
updates.push({ field: key, value: result.data });
|
|
254
|
+
if (key === this.inBinary) {
|
|
255
|
+
await this.refuse(key, "send this field's items as binary frames");
|
|
256
|
+
continue;
|
|
286
257
|
}
|
|
287
|
-
|
|
288
|
-
|
|
258
|
+
// A live field's items are validated against the item schema, an
|
|
259
|
+
// ordinary field against its own and then applied to the input.
|
|
260
|
+
const result = (live ? this.inLive[key] : this.inShape[key]).safeParse(raw);
|
|
261
|
+
if (!result.success) {
|
|
262
|
+
await this.refuse(key, firstIssue(result));
|
|
263
|
+
continue;
|
|
289
264
|
}
|
|
265
|
+
if (!live)
|
|
266
|
+
this.input[key] = result.data;
|
|
267
|
+
updates.push({ field: key, value: result.data });
|
|
290
268
|
}
|
|
291
269
|
return updates;
|
|
292
270
|
}
|
|
293
271
|
async refuse(field, message) {
|
|
294
|
-
await this.
|
|
272
|
+
await this.error(message, field);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Tell the caller something went wrong without ending the stream: a
|
|
276
|
+
* provider error, or why the session is about to end.
|
|
277
|
+
*/
|
|
278
|
+
async error(message, field = null) {
|
|
279
|
+
await this.socket.send({ [ERROR_KEY]: { field, message } });
|
|
295
280
|
}
|
|
296
281
|
/**
|
|
297
282
|
* Send items of live output fields, or new values of ordinary ones.
|
|
@@ -301,6 +286,16 @@ export class Live {
|
|
|
301
286
|
* `await live.send({ effect: "echo" })` are JSON frames. Keys in one call
|
|
302
287
|
* other than the binary field go out together as one JSON frame.
|
|
303
288
|
*/
|
|
289
|
+
/**
|
|
290
|
+
* Tell the caller to drop what it has buffered of a live output field:
|
|
291
|
+
* the queued audio of an answer the user just talked over.
|
|
292
|
+
*/
|
|
293
|
+
async clear(field) {
|
|
294
|
+
if (!Object.prototype.hasOwnProperty.call(this.outLive, field)) {
|
|
295
|
+
throw new Error(`the output schema has no live field "${field}"`);
|
|
296
|
+
}
|
|
297
|
+
await this.socket.send({ [CLEAR_KEY]: field });
|
|
298
|
+
}
|
|
304
299
|
async send(fields) {
|
|
305
300
|
const patch = {};
|
|
306
301
|
for (const [key, value] of Object.entries(fields)) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walking a zod schema through its wrappers, the same way for zod v3 and v4:
|
|
3
|
+
* the app's own zod is passed in and never imported here, so schemas are read
|
|
4
|
+
* by duck-typing `_def`, as the kernel does.
|
|
5
|
+
*/
|
|
6
|
+
/** The schemas a wrapper (optional, default, nullable, effects, pipe, ...) wraps. */
|
|
7
|
+
export declare function innerSchemas(def: any): any[];
|
|
8
|
+
/** The value of a marker on a schema's `_def`, looking through its wrappers. */
|
|
9
|
+
export declare function findMarker(schema: any, marker: symbol, seen?: Set<any>): unknown;
|
package/dist/zod-walk.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walking a zod schema through its wrappers, the same way for zod v3 and v4:
|
|
3
|
+
* the app's own zod is passed in and never imported here, so schemas are read
|
|
4
|
+
* by duck-typing `_def`, as the kernel does.
|
|
5
|
+
*/
|
|
6
|
+
/** The schemas a wrapper (optional, default, nullable, effects, pipe, ...) wraps. */
|
|
7
|
+
export function innerSchemas(def) {
|
|
8
|
+
const out = [];
|
|
9
|
+
// v3 + v4: optional / default / nullable / catch / readonly
|
|
10
|
+
if (def.innerType)
|
|
11
|
+
out.push(def.innerType);
|
|
12
|
+
// v3: effects (preprocess / transform / refine), v4: some wrappers
|
|
13
|
+
if (def.schema)
|
|
14
|
+
out.push(def.schema);
|
|
15
|
+
if (def.inner)
|
|
16
|
+
out.push(def.inner);
|
|
17
|
+
// v4: pipe (preprocess, transform) — in, then out
|
|
18
|
+
if (def.in)
|
|
19
|
+
out.push(def.in);
|
|
20
|
+
if (def.out)
|
|
21
|
+
out.push(def.out);
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
/** The value of a marker on a schema's `_def`, looking through its wrappers. */
|
|
25
|
+
export function findMarker(schema, marker, seen = new Set()) {
|
|
26
|
+
if (!schema || typeof schema !== "object" || seen.has(schema))
|
|
27
|
+
return undefined;
|
|
28
|
+
seen.add(schema);
|
|
29
|
+
const def = schema._def;
|
|
30
|
+
if (!def || typeof def !== "object")
|
|
31
|
+
return undefined;
|
|
32
|
+
if (Object.prototype.hasOwnProperty.call(def, marker))
|
|
33
|
+
return def[marker];
|
|
34
|
+
for (const inner of innerSchemas(def)) {
|
|
35
|
+
const found = findMarker(inner, marker, seen);
|
|
36
|
+
if (found !== undefined)
|
|
37
|
+
return found;
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
package/package.json
CHANGED