@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 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
- if (!schema || typeof schema !== "object")
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
- await this.refuse(null, "this function takes no binary frames");
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
- if (Object.prototype.hasOwnProperty.call(this.inLive, key)) {
267
- if (key === this.inBinary) {
268
- await this.refuse(key, "send this field's items as binary frames");
269
- continue;
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
- else if (Object.prototype.hasOwnProperty.call(this.inShape, key)) {
279
- const result = this.inShape[key].safeParse(raw);
280
- if (!result.success) {
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
- else {
288
- await this.refuse(key, "no such field");
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.socket.send({ error: { field, message } });
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;
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inferencesh/app",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "App framework for building inference.sh apps — File handling, output metadata, storage utilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",