@independo/inderun-web 0.3.0-dev.1 → 0.3.0-dev.3
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/core/engine.d.ts +26 -1
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +257 -1
- package/dist/core/event-gate.d.ts +37 -0
- package/dist/core/event-gate.d.ts.map +1 -0
- package/dist/core/event-gate.js +63 -0
- package/dist/core/provider.d.ts +41 -0
- package/dist/core/provider.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +3 -3
package/dist/core/engine.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import { type TaskRequest, type TaskResult } from "@independo/inderun-contracts";
|
|
1
|
+
import { type StreamEvent, type StreamRunHandle, type TaskRequest, type TaskResult } from "@independo/inderun-contracts";
|
|
2
2
|
import type { HostServices } from "./host.js";
|
|
3
3
|
import type { ProviderCapabilitySnapshot } from "./provider.js";
|
|
4
4
|
import type { IndeRunApi } from "./generated/inderun-api.js";
|
|
5
5
|
import { ProviderRegistry } from "./registry.js";
|
|
6
6
|
import { type TelemetryService } from "./telemetry.js";
|
|
7
|
+
/**
|
|
8
|
+
* Handle returned by IndeRun.stream(). `events` is the canonical StreamEvent
|
|
9
|
+
* sequence for this run, terminating in exactly one terminal StreamEvent per the
|
|
10
|
+
* Event Gate's guarantee. `cancel()` is idempotent: repeated or concurrent calls
|
|
11
|
+
* produce exactly one `cancelled` terminal outcome.
|
|
12
|
+
*/
|
|
13
|
+
export interface StreamHandleResult {
|
|
14
|
+
handle: StreamRunHandle;
|
|
15
|
+
events: AsyncIterable<StreamEvent>;
|
|
16
|
+
cancel(reason?: string): void;
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* Main orchestrator SDK entrypoint class.
|
|
9
20
|
* Validates tasks, executes routing rules based on request constraints and connectivity,
|
|
@@ -40,6 +51,20 @@ export declare class IndeRun implements IndeRunApi {
|
|
|
40
51
|
* @throws {IndeRunException} Standardized error indicating validation, connection, or provider failures.
|
|
41
52
|
*/
|
|
42
53
|
run(request: TaskRequest): Promise<TaskResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Orchestrates a Mode 2 streaming execution of a TaskRequest.
|
|
56
|
+
*
|
|
57
|
+
* Reuses the same route selection as run(), restricted to providers that declare
|
|
58
|
+
* `supports.streaming` and implement `stream()`. Fallback to the next provider is
|
|
59
|
+
* allowed only before the first content event has been admitted for the run; once
|
|
60
|
+
* one has been delivered, the run is committed to that provider and any later
|
|
61
|
+
* provider failure becomes a terminal error outcome rather than a silent provider
|
|
62
|
+
* swap. Cancellation always forecloses fallback, regardless of commit state, and
|
|
63
|
+
* is idempotent.
|
|
64
|
+
* @param request - The canonical task request containing prompts, tasks, and constraints.
|
|
65
|
+
* @throws {IndeRunException} If validation fails or no eligible streaming provider exists.
|
|
66
|
+
*/
|
|
67
|
+
stream(request: TaskRequest): Promise<StreamHandleResult>;
|
|
43
68
|
/**
|
|
44
69
|
* Reports each registered provider's static descriptor and current dynamic capability check,
|
|
45
70
|
* without executing a task. Useful for UI that shows live provider availability before a run.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAmB,0BAA0B,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAQjD,OAAO,EAAuB,KAAK,gBAAgB,EAAwB,MAAM,gBAAgB,CAAC;AAGlG;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,qBAAa,OAAQ,YAAW,UAAU;IAWtC,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,YAAY;IAXtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAmB;IAE3C;;;;;OAKG;gBAEO,QAAQ,EAAE,gBAAgB,EAC1B,YAAY,EAAE,YAAY,EAClC,gBAAgB,CAAC,EAAE,gBAAgB;IAOrC;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAShB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;;;;;;OAOG;IACG,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAuIpD;;;;;;;;;;;;OAYG;IACG,MAAM,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAsQ/D;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,0BAA0B,EAAE,CAAC;CASjE"}
|
package/dist/core/engine.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { getTaskRequestValidationIssues } from "@independo/inderun-contracts";
|
|
2
2
|
import { ProviderRegistry } from "./registry.js";
|
|
3
3
|
import { Router } from "./router.js";
|
|
4
|
-
import { createInternal, toIndeRunException } from "./errors.js";
|
|
4
|
+
import { createCapabilityMismatch, createInternal, createUnavailable, toIndeRunException } from "./errors.js";
|
|
5
5
|
import { NoOpTelemetryService } from "./telemetry.js";
|
|
6
|
+
import { EventGate } from "./event-gate.js";
|
|
6
7
|
/**
|
|
7
8
|
* Main orchestrator SDK entrypoint class.
|
|
8
9
|
* Validates tasks, executes routing rules based on request constraints and connectivity,
|
|
@@ -190,6 +191,261 @@ export class IndeRun {
|
|
|
190
191
|
throw exception;
|
|
191
192
|
}
|
|
192
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Orchestrates a Mode 2 streaming execution of a TaskRequest.
|
|
196
|
+
*
|
|
197
|
+
* Reuses the same route selection as run(), restricted to providers that declare
|
|
198
|
+
* `supports.streaming` and implement `stream()`. Fallback to the next provider is
|
|
199
|
+
* allowed only before the first content event has been admitted for the run; once
|
|
200
|
+
* one has been delivered, the run is committed to that provider and any later
|
|
201
|
+
* provider failure becomes a terminal error outcome rather than a silent provider
|
|
202
|
+
* swap. Cancellation always forecloses fallback, regardless of commit state, and
|
|
203
|
+
* is idempotent.
|
|
204
|
+
* @param request - The canonical task request containing prompts, tasks, and constraints.
|
|
205
|
+
* @throws {IndeRunException} If validation fails or no eligible streaming provider exists.
|
|
206
|
+
*/
|
|
207
|
+
async stream(request) {
|
|
208
|
+
const startTime = this.hostServices.clock ? this.hostServices.clock.now() : Date.now();
|
|
209
|
+
const now = () => (this.hostServices.clock ? this.hostServices.clock.now() : Date.now());
|
|
210
|
+
const runId = request.requestId || `run_${Math.random().toString(36).substring(2, 11)}`;
|
|
211
|
+
const validationIssues = getTaskRequestValidationIssues(request);
|
|
212
|
+
if (validationIssues.length > 0) {
|
|
213
|
+
const message = `Validation failed for TaskRequest: ${validationIssues
|
|
214
|
+
.map((i) => `${i.path} - ${i.message}`)
|
|
215
|
+
.join("; ")}`;
|
|
216
|
+
throw createInternal(message, { runId, details: { validationIssues } });
|
|
217
|
+
}
|
|
218
|
+
const routeSelection = await this.router.selectRoute(request, this.hostServices);
|
|
219
|
+
const allProviders = [routeSelection.provider, ...routeSelection.fallbackProviders];
|
|
220
|
+
const providers = allProviders.filter((p) => p.describe().supports.streaming && typeof p.stream === "function");
|
|
221
|
+
this.safeEmit({
|
|
222
|
+
type: "route_decided",
|
|
223
|
+
runId,
|
|
224
|
+
timestamp: now(),
|
|
225
|
+
payload: {
|
|
226
|
+
selectedProviderId: routeSelection.routePlan.selectedProviderId,
|
|
227
|
+
fallbackProviderIds: routeSelection.routePlan.fallbackProviderIds,
|
|
228
|
+
rejectedProviders: routeSelection.routePlan.rejectedProviders,
|
|
229
|
+
fallbackAvailable: providers.length > 1,
|
|
230
|
+
taskKind: request.task.kind,
|
|
231
|
+
explanation: routeSelection.explanation,
|
|
232
|
+
constraints: request.constraints ?? null,
|
|
233
|
+
preferences: request.preferences ?? null,
|
|
234
|
+
plannerSource: routeSelection.plannerSource,
|
|
235
|
+
plannerUnavailableReason: routeSelection.plannerUnavailableReason ?? null
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (providers.length === 0) {
|
|
239
|
+
throw createCapabilityMismatch("No eligible provider supports streaming for this request.", {
|
|
240
|
+
runId,
|
|
241
|
+
details: { taskKind: request.task.kind }
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const controller = new AbortController();
|
|
245
|
+
const gate = new EventGate(runId);
|
|
246
|
+
const hostServices = this.hostServices;
|
|
247
|
+
const safeEmit = this.safeEmit.bind(this);
|
|
248
|
+
const getStableMessage = this.getStableMessage.bind(this);
|
|
249
|
+
const handle = {
|
|
250
|
+
runId,
|
|
251
|
+
schemaVersion: "1.0",
|
|
252
|
+
startedAt: startTime,
|
|
253
|
+
providerId: providers[0].describe().id
|
|
254
|
+
};
|
|
255
|
+
async function* run() {
|
|
256
|
+
const attemptedProviderIds = [];
|
|
257
|
+
let committed = false;
|
|
258
|
+
const cancelledOutcome = (partialText) => {
|
|
259
|
+
const reason = typeof controller.signal.reason === "string" ? controller.signal.reason : undefined;
|
|
260
|
+
return {
|
|
261
|
+
outcome: "cancelled",
|
|
262
|
+
runId,
|
|
263
|
+
schemaVersion: "1.0",
|
|
264
|
+
partialText,
|
|
265
|
+
...(reason !== undefined ? { reason } : {})
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
for (const [index, provider] of providers.entries()) {
|
|
269
|
+
if (controller.signal.aborted) {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
const providerId = provider.describe().id;
|
|
273
|
+
attemptedProviderIds.push(providerId);
|
|
274
|
+
safeEmit({
|
|
275
|
+
type: "stream_attempt_started",
|
|
276
|
+
runId,
|
|
277
|
+
timestamp: now(),
|
|
278
|
+
payload: { providerId, fallbackOccurred: index > 0 }
|
|
279
|
+
});
|
|
280
|
+
let partialText = "";
|
|
281
|
+
try {
|
|
282
|
+
const iterable = provider.stream(request, {
|
|
283
|
+
runId,
|
|
284
|
+
hostServices: hostServices,
|
|
285
|
+
signal: controller.signal
|
|
286
|
+
});
|
|
287
|
+
let terminatedInLoop = false;
|
|
288
|
+
for await (const ev of iterable) {
|
|
289
|
+
if (controller.signal.aborted) {
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
if (ev.kind === "error") {
|
|
293
|
+
throw ev.error;
|
|
294
|
+
}
|
|
295
|
+
if (ev.kind === "delta" || ev.kind === "snapshot") {
|
|
296
|
+
partialText = ev.kind === "delta" ? partialText + ev.text : ev.text;
|
|
297
|
+
if (!committed) {
|
|
298
|
+
committed = true;
|
|
299
|
+
safeEmit({
|
|
300
|
+
type: "stream_attempt_succeeded",
|
|
301
|
+
runId,
|
|
302
|
+
timestamp: now(),
|
|
303
|
+
payload: { providerId, attemptedProviderIds }
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const streamEvent = gate.admit({
|
|
307
|
+
timestamp: now(),
|
|
308
|
+
type: ev.kind === "delta" ? "content_delta" : "content_snapshot",
|
|
309
|
+
payload: { text: ev.text }
|
|
310
|
+
});
|
|
311
|
+
if (streamEvent)
|
|
312
|
+
yield streamEvent;
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
// ev.kind === "done"
|
|
316
|
+
const outcome = {
|
|
317
|
+
outcome: "completed",
|
|
318
|
+
runId,
|
|
319
|
+
schemaVersion: "1.0",
|
|
320
|
+
finalText: ev.finalText,
|
|
321
|
+
telemetry: { providerUsed: providerId, totalMs: now() - startTime },
|
|
322
|
+
...(ev.usage !== undefined ? { usage: ev.usage } : {})
|
|
323
|
+
};
|
|
324
|
+
const terminal = gate.terminate(outcome, now());
|
|
325
|
+
safeEmit({
|
|
326
|
+
type: "stream_completed",
|
|
327
|
+
runId,
|
|
328
|
+
timestamp: now(),
|
|
329
|
+
payload: { providerId, durationMs: now() - startTime, attemptedProviderIds }
|
|
330
|
+
});
|
|
331
|
+
terminatedInLoop = true;
|
|
332
|
+
if (terminal)
|
|
333
|
+
yield terminal;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (terminatedInLoop) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (controller.signal.aborted) {
|
|
340
|
+
const terminal = gate.terminate(cancelledOutcome(partialText), now());
|
|
341
|
+
safeEmit({
|
|
342
|
+
type: "stream_cancelled",
|
|
343
|
+
runId,
|
|
344
|
+
timestamp: now(),
|
|
345
|
+
payload: { providerId, attemptedProviderIds }
|
|
346
|
+
});
|
|
347
|
+
if (terminal)
|
|
348
|
+
yield terminal;
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
// Provider's iterable ended without a terminal "done"/"error" event.
|
|
352
|
+
throw new Error(`Provider '${providerId}' stream ended without a terminal event.`);
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
if (controller.signal.aborted) {
|
|
356
|
+
const terminal = gate.terminate(cancelledOutcome(partialText), now());
|
|
357
|
+
safeEmit({
|
|
358
|
+
type: "stream_cancelled",
|
|
359
|
+
runId,
|
|
360
|
+
timestamp: now(),
|
|
361
|
+
payload: { providerId, attemptedProviderIds }
|
|
362
|
+
});
|
|
363
|
+
if (terminal)
|
|
364
|
+
yield terminal;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const exception = toIndeRunException(err, {
|
|
368
|
+
providerId,
|
|
369
|
+
runId,
|
|
370
|
+
details: { attemptedProviderIds }
|
|
371
|
+
});
|
|
372
|
+
if (!committed) {
|
|
373
|
+
safeEmit({
|
|
374
|
+
type: "stream_attempt_failed",
|
|
375
|
+
runId,
|
|
376
|
+
timestamp: now(),
|
|
377
|
+
payload: {
|
|
378
|
+
providerId,
|
|
379
|
+
errorClass: exception.errorClass,
|
|
380
|
+
message: getStableMessage(exception.errorClass)
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
const outcome = {
|
|
386
|
+
outcome: "error",
|
|
387
|
+
runId,
|
|
388
|
+
schemaVersion: "1.0",
|
|
389
|
+
error: exception.toContractError(),
|
|
390
|
+
partialText
|
|
391
|
+
};
|
|
392
|
+
const terminal = gate.terminate(outcome, now());
|
|
393
|
+
safeEmit({
|
|
394
|
+
type: "stream_failed",
|
|
395
|
+
runId,
|
|
396
|
+
timestamp: now(),
|
|
397
|
+
payload: {
|
|
398
|
+
providerId,
|
|
399
|
+
errorClass: exception.errorClass,
|
|
400
|
+
message: getStableMessage(exception.errorClass),
|
|
401
|
+
attemptedProviderIds
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
if (terminal)
|
|
405
|
+
yield terminal;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Every provider failed pre-commit, or cancellation landed before any attempt started.
|
|
410
|
+
if (controller.signal.aborted) {
|
|
411
|
+
const terminal = gate.terminate(cancelledOutcome(""), now());
|
|
412
|
+
safeEmit({
|
|
413
|
+
type: "stream_cancelled",
|
|
414
|
+
runId,
|
|
415
|
+
timestamp: now(),
|
|
416
|
+
payload: { attemptedProviderIds }
|
|
417
|
+
});
|
|
418
|
+
if (terminal)
|
|
419
|
+
yield terminal;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const exception = createUnavailable("All eligible streaming providers failed.", {
|
|
423
|
+
runId,
|
|
424
|
+
details: { attemptedProviderIds }
|
|
425
|
+
});
|
|
426
|
+
const outcome = {
|
|
427
|
+
outcome: "error",
|
|
428
|
+
runId,
|
|
429
|
+
schemaVersion: "1.0",
|
|
430
|
+
error: exception.toContractError(),
|
|
431
|
+
partialText: ""
|
|
432
|
+
};
|
|
433
|
+
const terminal = gate.terminate(outcome, now());
|
|
434
|
+
safeEmit({
|
|
435
|
+
type: "stream_failed",
|
|
436
|
+
runId,
|
|
437
|
+
timestamp: now(),
|
|
438
|
+
payload: {
|
|
439
|
+
errorClass: exception.errorClass,
|
|
440
|
+
message: getStableMessage(exception.errorClass),
|
|
441
|
+
attemptedProviderIds
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
if (terminal)
|
|
445
|
+
yield terminal;
|
|
446
|
+
}
|
|
447
|
+
return { handle, events: run(), cancel: (reason) => controller.abort(reason) };
|
|
448
|
+
}
|
|
193
449
|
/**
|
|
194
450
|
* Reports each registered provider's static descriptor and current dynamic capability check,
|
|
195
451
|
* without executing a task. Useful for UI that shows live provider availability before a run.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { StreamEvent, StreamTerminalOutcome } from "@independo/inderun-contracts";
|
|
2
|
+
/**
|
|
3
|
+
* Per-run gatekeeper for Mode 2 streaming. Enforces the two structural guarantees
|
|
4
|
+
* required by the streaming orchestrator: exactly one terminal outcome is ever
|
|
5
|
+
* produced for a run, and no event is admitted after that terminal outcome fires.
|
|
6
|
+
*
|
|
7
|
+
* Correctness relies on `admit`/`terminate` performing their check-and-set
|
|
8
|
+
* synchronously with no `await` in between — safe under JS's single-threaded,
|
|
9
|
+
* run-to-completion semantics even with interleaved `await` resumption elsewhere
|
|
10
|
+
* (e.g. a provider's async iterator and a caller's `cancel()` racing).
|
|
11
|
+
*/
|
|
12
|
+
export declare class EventGate {
|
|
13
|
+
private readonly runId;
|
|
14
|
+
private sequence;
|
|
15
|
+
private terminated;
|
|
16
|
+
private terminalOutcome;
|
|
17
|
+
constructor(runId: string);
|
|
18
|
+
/**
|
|
19
|
+
* Admits a non-terminal StreamEvent, assigning it the next monotonic sequence
|
|
20
|
+
* number. Returns null (suppressing the event) if the run has already terminated.
|
|
21
|
+
*/
|
|
22
|
+
admit(event: {
|
|
23
|
+
timestamp: number;
|
|
24
|
+
type: string;
|
|
25
|
+
payload?: StreamEvent["payload"];
|
|
26
|
+
}): StreamEvent | null;
|
|
27
|
+
/**
|
|
28
|
+
* Terminates the run with the given outcome. First writer wins: only the first
|
|
29
|
+
* call produces a terminal StreamEvent and stores the outcome; every subsequent
|
|
30
|
+
* call (duplicate provider "done", late error after cancel, concurrent cancel()
|
|
31
|
+
* calls) is a silent no-op that returns null, making repeated cancellation safe.
|
|
32
|
+
*/
|
|
33
|
+
terminate(outcome: StreamTerminalOutcome, timestamp: number): StreamEvent | null;
|
|
34
|
+
isTerminated(): boolean;
|
|
35
|
+
getOutcome(): StreamTerminalOutcome | undefined;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=event-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-gate.d.ts","sourceRoot":"","sources":["../../src/core/event-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAEvF;;;;;;;;;GASG;AACH,qBAAa,SAAS;IAKR,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAoC;gBAE9B,KAAK,EAAE,MAAM;IAE1C;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;KAClC,GAAG,WAAW,GAAG,IAAI;IActB;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAgBhF,YAAY,IAAI,OAAO;IAIvB,UAAU,IAAI,qBAAqB,GAAG,SAAS;CAGhD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-run gatekeeper for Mode 2 streaming. Enforces the two structural guarantees
|
|
3
|
+
* required by the streaming orchestrator: exactly one terminal outcome is ever
|
|
4
|
+
* produced for a run, and no event is admitted after that terminal outcome fires.
|
|
5
|
+
*
|
|
6
|
+
* Correctness relies on `admit`/`terminate` performing their check-and-set
|
|
7
|
+
* synchronously with no `await` in between — safe under JS's single-threaded,
|
|
8
|
+
* run-to-completion semantics even with interleaved `await` resumption elsewhere
|
|
9
|
+
* (e.g. a provider's async iterator and a caller's `cancel()` racing).
|
|
10
|
+
*/
|
|
11
|
+
export class EventGate {
|
|
12
|
+
runId;
|
|
13
|
+
sequence = 0;
|
|
14
|
+
terminated = false;
|
|
15
|
+
terminalOutcome;
|
|
16
|
+
constructor(runId) {
|
|
17
|
+
this.runId = runId;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Admits a non-terminal StreamEvent, assigning it the next monotonic sequence
|
|
21
|
+
* number. Returns null (suppressing the event) if the run has already terminated.
|
|
22
|
+
*/
|
|
23
|
+
admit(event) {
|
|
24
|
+
if (this.terminated) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
runId: this.runId,
|
|
29
|
+
schemaVersion: "1.0",
|
|
30
|
+
sequence: this.sequence++,
|
|
31
|
+
timestamp: event.timestamp,
|
|
32
|
+
type: event.type,
|
|
33
|
+
...(event.payload !== undefined ? { payload: event.payload } : {})
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Terminates the run with the given outcome. First writer wins: only the first
|
|
38
|
+
* call produces a terminal StreamEvent and stores the outcome; every subsequent
|
|
39
|
+
* call (duplicate provider "done", late error after cancel, concurrent cancel()
|
|
40
|
+
* calls) is a silent no-op that returns null, making repeated cancellation safe.
|
|
41
|
+
*/
|
|
42
|
+
terminate(outcome, timestamp) {
|
|
43
|
+
if (this.terminated) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
this.terminated = true;
|
|
47
|
+
this.terminalOutcome = outcome;
|
|
48
|
+
return {
|
|
49
|
+
runId: this.runId,
|
|
50
|
+
schemaVersion: "1.0",
|
|
51
|
+
sequence: this.sequence++,
|
|
52
|
+
timestamp,
|
|
53
|
+
type: "terminal",
|
|
54
|
+
payload: outcome
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
isTerminated() {
|
|
58
|
+
return this.terminated;
|
|
59
|
+
}
|
|
60
|
+
getOutcome() {
|
|
61
|
+
return this.terminalOutcome;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/core/provider.d.ts
CHANGED
|
@@ -118,6 +118,42 @@ export interface RunContext {
|
|
|
118
118
|
*/
|
|
119
119
|
hostServices: HostServices;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Execution context passed to provider stream commands. Extends RunContext with the
|
|
123
|
+
* caller-driven cancellation signal; the Engine core owns the AbortController and
|
|
124
|
+
* threads its signal here, so providers never need to construct their own.
|
|
125
|
+
*/
|
|
126
|
+
export interface ProviderStreamContext extends RunContext {
|
|
127
|
+
/**
|
|
128
|
+
* Signals caller-driven cancellation of this stream. Providers with `cancel: "hard"`
|
|
129
|
+
* should tear down their connection immediately on abort; `cancel: "soft"` providers
|
|
130
|
+
* may stop relaying events without interrupting underlying local work; `cancel:
|
|
131
|
+
* "none"` providers may ignore it entirely; the engine enforces the caller-visible
|
|
132
|
+
* cancellation guarantee at the consumer loop regardless of what the provider does.
|
|
133
|
+
*/
|
|
134
|
+
signal: AbortSignal;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* A raw, provider-shaped streaming event yielded by ProviderAdapter.stream(). This is
|
|
138
|
+
* intentionally distinct from the canonical StreamEvent contract: providers emit
|
|
139
|
+
* provider-shaped deltas here, and the Engine core's Event Gate normalizes these into
|
|
140
|
+
* StreamEvent/StreamTerminalOutcome, keeping provider-specific mechanics from leaking
|
|
141
|
+
* through the public API.
|
|
142
|
+
*/
|
|
143
|
+
export type ProviderStreamEvent = {
|
|
144
|
+
kind: "delta";
|
|
145
|
+
text: string;
|
|
146
|
+
} | {
|
|
147
|
+
kind: "snapshot";
|
|
148
|
+
text: string;
|
|
149
|
+
} | {
|
|
150
|
+
kind: "done";
|
|
151
|
+
finalText: string;
|
|
152
|
+
usage?: TaskResult["usage"];
|
|
153
|
+
} | {
|
|
154
|
+
kind: "error";
|
|
155
|
+
error: unknown;
|
|
156
|
+
};
|
|
121
157
|
/**
|
|
122
158
|
* Pluggable execution adapter contract that wraps a specific model runtime
|
|
123
159
|
* (system frameworks, local runtimes, or cloud APIs) and exposes normalized APIs.
|
|
@@ -135,5 +171,10 @@ export interface ProviderAdapter {
|
|
|
135
171
|
* Executes a task request in Mode 1 (request/response).
|
|
136
172
|
*/
|
|
137
173
|
run(req: TaskRequest, ctx: RunContext): Promise<TaskResult>;
|
|
174
|
+
/**
|
|
175
|
+
* Executes a task request in Mode 2 (streaming). Optional: only providers declaring
|
|
176
|
+
* `describe().supports.streaming === true` are expected to implement this.
|
|
177
|
+
*/
|
|
178
|
+
stream?(req: TaskRequest, ctx: ProviderStreamContext): AsyncIterable<ProviderStreamEvent>;
|
|
138
179
|
}
|
|
139
180
|
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/core/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC;;OAEG;IACH,SAAS,EAAE,YAAY,GAAG,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;IACzE;;OAEG;IACH,cAAc,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAEnD;;OAEG;IACH,QAAQ,EAAE;QACR,mEAAmE;QACnE,GAAG,EAAE,OAAO,CAAC;QACb,mDAAmD;QACnD,SAAS,EAAE,OAAO,CAAC;QACnB,6DAA6D;QAC7D,QAAQ,EAAE,OAAO,CAAC;QAClB,6DAA6D;QAC7D,KAAK,EAAE,OAAO,CAAC;QACf,wEAAwE;QACxE,eAAe,EAAE,OAAO,CAAC;QACzB,kFAAkF;QAClF,gBAAgB,EAAE,OAAO,CAAC;QAC1B,6EAA6E;QAC7E,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;IAEF;;;;;OAKG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAEjC;;OAEG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IAEF;;OAEG;IACH,OAAO,CAAC,EAAE;QACR,6DAA6D;QAC7D,gBAAgB,EAAE,OAAO,CAAC;QAC1B,2DAA2D;QAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,EAAE,kBAAkB,CAAC;IAC/B;;;OAGG;IACH,YAAY,EAAE,2BAA2B,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,IAAI,kBAAkB,CAAC;IAC/B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACvE;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/core/provider.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { type ConnectivityService, type DeviceConstraintsService, type SecureStorageService, type ClockService, type HttpClientService, type HttpRequest, type HttpResponse, type HostServices, type ThermalState } from "./core/host.js";
|
|
2
|
-
export { type ProviderDescriptor, type ProviderDynamicCapabilities, type ProviderCapabilitySnapshot, type RunContext, type ProviderAdapter } from "./core/provider.js";
|
|
2
|
+
export { type ProviderDescriptor, type ProviderDynamicCapabilities, type ProviderCapabilitySnapshot, type RunContext, type ProviderAdapter, type ProviderStreamContext, type ProviderStreamEvent } from "./core/provider.js";
|
|
3
|
+
export { EventGate } from "./core/event-gate.js";
|
|
3
4
|
export { ProviderRegistry } from "./core/registry.js";
|
|
4
5
|
export { type RouteSelection, Router } from "./core/router.js";
|
|
5
6
|
export { type RoutePlanner, type PlannerOutcome, type WasmUnavailableReason, type SharedPlannerInput, type SharedPlannerRoutePlan, WasmRoutePlanner } from "./core/route-planner.js";
|
|
6
7
|
export { type IndeRunExceptionParams, IndeRunException, createCapabilityMismatch, createOffline, createAuthError, createRateLimited, createTimeout, createUnavailable, createInternal, toIndeRunException } from "./core/errors.js";
|
|
7
|
-
export { IndeRun } from "./core/engine.js";
|
|
8
|
+
export { IndeRun, type StreamHandleResult } from "./core/engine.js";
|
|
8
9
|
export type { IndeRunApi } from "./core/generated/inderun-api.js";
|
|
9
10
|
export { type TelemetryEventType, type TelemetryEvent, type TelemetryService, NoOpTelemetryService } from "./core/telemetry.js";
|
|
10
11
|
export { BrowserConnectivityService, FetchHttpClient, SystemClockService, createBrowserHostServices, type CreateBrowserHostServicesOptions, type FetchHttpClientOptions } from "./core/browser-host.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,EAC3B,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAElE,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,0BAA0B,EAC1B,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,KAAK,gCAAgC,EACrC,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export {} from "./core/host.js";
|
|
2
2
|
export {} from "./core/provider.js";
|
|
3
|
+
export { EventGate } from "./core/event-gate.js";
|
|
3
4
|
export { ProviderRegistry } from "./core/registry.js";
|
|
4
5
|
export { Router } from "./core/router.js";
|
|
5
6
|
export { WasmRoutePlanner } from "./core/route-planner.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@independo/inderun-web",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.3",
|
|
4
4
|
"description": "TypeScript/Web SDK for IndeRun.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"README.md"
|
|
56
56
|
],
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@independo/inderun-
|
|
59
|
-
"@independo/inderun-
|
|
58
|
+
"@independo/inderun-contracts": "^0.3.0-dev.3",
|
|
59
|
+
"@independo/inderun-route-core-wasm": "^0.3.0-dev.3"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@huggingface/transformers": "^4.0.0"
|