@independo/inderun-web 0.3.0-dev.9 → 0.3.1-dev.1
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/README.md +12 -10
- package/dist/index.d.ts +63 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -2
- package/dist/onnx.d.ts +21 -0
- package/dist/onnx.d.ts.map +1 -1
- package/dist/onnx.js +15 -0
- package/dist/openai.d.ts +7 -0
- package/dist/openai.d.ts.map +1 -1
- package/dist/openai.js +7 -0
- package/dist/system-model.d.ts +13 -0
- package/dist/system-model.d.ts.map +1 -1
- package/dist/system-model.js +13 -0
- package/package.json +22 -8
- package/src/core/browser-host.ts +339 -0
- package/src/core/engine.ts +540 -0
- package/src/core/errors.ts +220 -0
- package/src/core/event-gate.ts +71 -0
- package/src/core/generated/inderun-api.ts +19 -0
- package/src/core/host.ts +15 -0
- package/src/core/provider.ts +231 -0
- package/src/core/registry.ts +48 -0
- package/src/core/route-planner.ts +261 -0
- package/src/core/router.ts +149 -0
- package/src/core/sse.ts +134 -0
- package/src/core/telemetry.ts +20 -0
- package/src/index.ts +147 -0
- package/src/onnx.ts +57 -0
- package/src/openai.ts +21 -0
- package/src/providers/onnx/provider.ts +300 -0
- package/src/providers/onnx/runtime.ts +188 -0
- package/src/providers/onnx/transformers-runtime.ts +345 -0
- package/src/providers/openai/provider.ts +765 -0
- package/src/providers/system-model/chrome-runtime.ts +221 -0
- package/src/providers/system-model/provider.ts +260 -0
- package/src/providers/system-model/runtime.ts +202 -0
- package/src/system-model.ts +49 -0
- package/src/web-sdk.ts +114 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { IndeRunError, IndeRunErrorClass } from "@independo/inderun-contracts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parameter structure required to initialize an IndeRunException.
|
|
5
|
+
*/
|
|
6
|
+
export interface IndeRunExceptionParams {
|
|
7
|
+
/**
|
|
8
|
+
* The standardized error class (e.g. 'CapabilityMismatch', 'Offline').
|
|
9
|
+
*/
|
|
10
|
+
errorClass: IndeRunErrorClass;
|
|
11
|
+
/**
|
|
12
|
+
* Explanatory human-readable message.
|
|
13
|
+
*/
|
|
14
|
+
message: string;
|
|
15
|
+
/**
|
|
16
|
+
* The unique run ID where this exception occurred.
|
|
17
|
+
*/
|
|
18
|
+
runId?: string;
|
|
19
|
+
/**
|
|
20
|
+
* The provider ID that triggered the exception, if execution reached a provider.
|
|
21
|
+
*/
|
|
22
|
+
providerId?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Whether the client should retry this request.
|
|
25
|
+
*/
|
|
26
|
+
retryable?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Backoff time suggestion in milliseconds before retrying.
|
|
29
|
+
*/
|
|
30
|
+
retryAfterMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Rich contextual metadata regarding the error.
|
|
33
|
+
*/
|
|
34
|
+
details?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Standardized framework exception thrown when route selection or provider execution fails.
|
|
39
|
+
* Extends the native JavaScript Error class and exposes normalized error schema fields.
|
|
40
|
+
*/
|
|
41
|
+
export class IndeRunException extends Error {
|
|
42
|
+
/** The schema version matching the API contracts. */
|
|
43
|
+
readonly schemaVersion = "1.0";
|
|
44
|
+
/** The categorized class of error. */
|
|
45
|
+
readonly errorClass: IndeRunErrorClass;
|
|
46
|
+
/** The unique execution run ID. */
|
|
47
|
+
readonly runId?: string;
|
|
48
|
+
/** The provider ID responsible for the error. */
|
|
49
|
+
readonly providerId?: string;
|
|
50
|
+
/** Whether the error is transient and retryable. */
|
|
51
|
+
readonly retryable?: boolean;
|
|
52
|
+
/** Suggested cooling-down time in milliseconds before retry. */
|
|
53
|
+
readonly retryAfterMs?: number;
|
|
54
|
+
/** Custom details and metadata context. */
|
|
55
|
+
readonly details?: Record<string, unknown>;
|
|
56
|
+
|
|
57
|
+
constructor(params: IndeRunExceptionParams) {
|
|
58
|
+
super(params.message);
|
|
59
|
+
this.name = "IndeRunException";
|
|
60
|
+
this.errorClass = params.errorClass;
|
|
61
|
+
if (params.runId !== undefined) this.runId = params.runId;
|
|
62
|
+
if (params.providerId !== undefined) this.providerId = params.providerId;
|
|
63
|
+
if (params.retryable !== undefined) this.retryable = params.retryable;
|
|
64
|
+
if (params.retryAfterMs !== undefined) this.retryAfterMs = params.retryAfterMs;
|
|
65
|
+
if (params.details !== undefined) this.details = params.details;
|
|
66
|
+
|
|
67
|
+
Object.setPrototypeOf(this, IndeRunException.prototype);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Converts the exception into a JSON-compatible contract-compliant IndeRunError.
|
|
72
|
+
*/
|
|
73
|
+
toContractError(): IndeRunError {
|
|
74
|
+
const err: IndeRunError = {
|
|
75
|
+
schemaVersion: this.schemaVersion,
|
|
76
|
+
errorClass: this.errorClass,
|
|
77
|
+
message: this.message
|
|
78
|
+
};
|
|
79
|
+
if (this.runId !== undefined) err.runId = this.runId;
|
|
80
|
+
if (this.providerId !== undefined) err.providerId = this.providerId;
|
|
81
|
+
if (this.retryable !== undefined) err.retryable = this.retryable;
|
|
82
|
+
if (this.retryAfterMs !== undefined) err.retryAfterMs = this.retryAfterMs;
|
|
83
|
+
if (this.details !== undefined) err.details = this.details;
|
|
84
|
+
return err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Factory helper for CapabilityMismatch error class.
|
|
90
|
+
*/
|
|
91
|
+
export function createCapabilityMismatch(
|
|
92
|
+
message: string,
|
|
93
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
94
|
+
): IndeRunException {
|
|
95
|
+
return new IndeRunException({ errorClass: "CapabilityMismatch", message, ...extra });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Factory helper for Offline error class.
|
|
100
|
+
*/
|
|
101
|
+
export function createOffline(
|
|
102
|
+
message: string,
|
|
103
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
104
|
+
): IndeRunException {
|
|
105
|
+
return new IndeRunException({ errorClass: "Offline", message, ...extra });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Factory helper for AuthError error class.
|
|
110
|
+
*/
|
|
111
|
+
export function createAuthError(
|
|
112
|
+
message: string,
|
|
113
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
114
|
+
): IndeRunException {
|
|
115
|
+
return new IndeRunException({ errorClass: "AuthError", message, ...extra });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Factory helper for RateLimited error class.
|
|
120
|
+
*/
|
|
121
|
+
export function createRateLimited(
|
|
122
|
+
message: string,
|
|
123
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
124
|
+
): IndeRunException {
|
|
125
|
+
return new IndeRunException({ errorClass: "RateLimited", message, ...extra });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Factory helper for Timeout error class.
|
|
130
|
+
*/
|
|
131
|
+
export function createTimeout(
|
|
132
|
+
message: string,
|
|
133
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
134
|
+
): IndeRunException {
|
|
135
|
+
return new IndeRunException({ errorClass: "Timeout", message, ...extra });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Factory helper for Unavailable error class.
|
|
140
|
+
*/
|
|
141
|
+
export function createUnavailable(
|
|
142
|
+
message: string,
|
|
143
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
144
|
+
): IndeRunException {
|
|
145
|
+
return new IndeRunException({ errorClass: "Unavailable", message, ...extra });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Factory helper for Internal error class.
|
|
150
|
+
*/
|
|
151
|
+
export function createInternal(
|
|
152
|
+
message: string,
|
|
153
|
+
extra?: Omit<IndeRunExceptionParams, "errorClass" | "message">
|
|
154
|
+
): IndeRunException {
|
|
155
|
+
return new IndeRunException({ errorClass: "Internal", message, ...extra });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Standardizes any caught error into an IndeRunException.
|
|
160
|
+
* If the input error is already an IndeRunException, merges additional fallback parameters (like totalMs).
|
|
161
|
+
*/
|
|
162
|
+
export function toIndeRunException(
|
|
163
|
+
error: unknown,
|
|
164
|
+
fallbackParams?: Partial<Omit<IndeRunExceptionParams, "message">>
|
|
165
|
+
): IndeRunException {
|
|
166
|
+
if (error instanceof IndeRunException) {
|
|
167
|
+
if (fallbackParams) {
|
|
168
|
+
const mergedParams: IndeRunExceptionParams = {
|
|
169
|
+
errorClass: error.errorClass,
|
|
170
|
+
message: error.message
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const runId = error.runId ?? fallbackParams.runId;
|
|
174
|
+
if (runId !== undefined) mergedParams.runId = runId;
|
|
175
|
+
|
|
176
|
+
const providerId = error.providerId ?? fallbackParams.providerId;
|
|
177
|
+
if (providerId !== undefined) mergedParams.providerId = providerId;
|
|
178
|
+
|
|
179
|
+
const retryable = error.retryable ?? fallbackParams.retryable;
|
|
180
|
+
if (retryable !== undefined) mergedParams.retryable = retryable;
|
|
181
|
+
|
|
182
|
+
const retryAfterMs = error.retryAfterMs ?? fallbackParams.retryAfterMs;
|
|
183
|
+
if (retryAfterMs !== undefined) mergedParams.retryAfterMs = retryAfterMs;
|
|
184
|
+
|
|
185
|
+
if (error.details !== undefined || fallbackParams.details !== undefined) {
|
|
186
|
+
mergedParams.details = {
|
|
187
|
+
...(error.details || {}),
|
|
188
|
+
...(fallbackParams.details || {})
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return new IndeRunException(mergedParams);
|
|
193
|
+
}
|
|
194
|
+
return error;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
198
|
+
const details: Record<string, unknown> = {
|
|
199
|
+
...(fallbackParams?.details || {})
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (error instanceof Error) {
|
|
203
|
+
details.originalError = {
|
|
204
|
+
name: error.name,
|
|
205
|
+
message: error.message,
|
|
206
|
+
stack: error.stack
|
|
207
|
+
};
|
|
208
|
+
} else {
|
|
209
|
+
details.originalError = {
|
|
210
|
+
message: String(error)
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return new IndeRunException({
|
|
215
|
+
errorClass: "Internal",
|
|
216
|
+
message,
|
|
217
|
+
...fallbackParams,
|
|
218
|
+
details
|
|
219
|
+
});
|
|
220
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { StreamEvent, StreamTerminalOutcome } from "@independo/inderun-contracts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-run gatekeeper for Mode 2 streaming. Enforces the two structural guarantees
|
|
5
|
+
* required by the streaming orchestrator: exactly one terminal outcome is ever
|
|
6
|
+
* produced for a run, and no event is admitted after that terminal outcome fires.
|
|
7
|
+
*
|
|
8
|
+
* Correctness relies on `admit`/`terminate` performing their check-and-set
|
|
9
|
+
* synchronously with no `await` in between — safe under JS's single-threaded,
|
|
10
|
+
* run-to-completion semantics even with interleaved `await` resumption elsewhere
|
|
11
|
+
* (e.g. a provider's async iterator and a caller's `cancel()` racing).
|
|
12
|
+
*/
|
|
13
|
+
export class EventGate {
|
|
14
|
+
private sequence = 0;
|
|
15
|
+
private terminated = false;
|
|
16
|
+
private terminalOutcome: StreamTerminalOutcome | undefined;
|
|
17
|
+
|
|
18
|
+
constructor(private readonly runId: string) {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Admits a non-terminal StreamEvent, assigning it the next monotonic sequence
|
|
22
|
+
* number. Returns null (suppressing the event) if the run has already terminated.
|
|
23
|
+
*/
|
|
24
|
+
admit(event: {
|
|
25
|
+
timestamp: number;
|
|
26
|
+
type: string;
|
|
27
|
+
payload?: StreamEvent["payload"];
|
|
28
|
+
}): StreamEvent | null {
|
|
29
|
+
if (this.terminated) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
runId: this.runId,
|
|
34
|
+
schemaVersion: "1.0",
|
|
35
|
+
sequence: this.sequence++,
|
|
36
|
+
timestamp: event.timestamp,
|
|
37
|
+
type: event.type,
|
|
38
|
+
...(event.payload !== undefined ? { payload: event.payload } : {})
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Terminates the run with the given outcome. First writer wins: only the first
|
|
44
|
+
* call produces a terminal StreamEvent and stores the outcome; every subsequent
|
|
45
|
+
* call (duplicate provider "done", late error after cancel, concurrent cancel()
|
|
46
|
+
* calls) is a silent no-op that returns null, making repeated cancellation safe.
|
|
47
|
+
*/
|
|
48
|
+
terminate(outcome: StreamTerminalOutcome, timestamp: number): StreamEvent | null {
|
|
49
|
+
if (this.terminated) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
this.terminated = true;
|
|
53
|
+
this.terminalOutcome = outcome;
|
|
54
|
+
return {
|
|
55
|
+
runId: this.runId,
|
|
56
|
+
schemaVersion: "1.0",
|
|
57
|
+
sequence: this.sequence++,
|
|
58
|
+
timestamp,
|
|
59
|
+
type: "terminal",
|
|
60
|
+
payload: outcome
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
isTerminated(): boolean {
|
|
65
|
+
return this.terminated;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getOutcome(): StreamTerminalOutcome | undefined {
|
|
69
|
+
return this.terminalOutcome;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* This file was generated from contracts/api/inderun-api.json. Do not edit by hand. */
|
|
2
|
+
|
|
3
|
+
import type { TaskRequest, TaskResult } from "@independo/inderun-contracts";
|
|
4
|
+
import type { StreamRun, ProviderCapabilitySnapshot } from "../provider.js";
|
|
5
|
+
|
|
6
|
+
export interface IndeRunApi {
|
|
7
|
+
/**
|
|
8
|
+
* Executes a Mode 1 (request/response) text-to-text task against a routed provider and returns the completed TaskResult. Routing (local vs. cloud, which provider) is decided internally from TaskRequest.constraints/preferences — the caller does not select a provider directly. Rejects the request up front on validation failure; otherwise throws an IndeRunException (carrying an IndeRunError) if no eligible provider is found or every attempted provider fails. There is no partial or streaming result in this mode: run() either resolves with a complete TaskResult or throws.
|
|
9
|
+
*/
|
|
10
|
+
run(request: TaskRequest): Promise<TaskResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Executes a Mode 2 (streaming) text-to-text task and returns a handle to the run, its canonical StreamEvent sequence, and a cancel hook. The requested interaction mode is a routing input rather than a filter applied after routing: providers that cannot stream are rejected during planning with a normalized reason, so a stream and a run over the same registry may legitimately resolve to different provider chains. Two failure surfaces exist deliberately — validation and route-selection failures reject this call, since there is no handle to correlate them with yet, while provider failure, cancellation, and completion all arrive as the single terminal StreamEvent in the returned sequence. Fallback is narrower than run()'s: a provider failure is only fallback-eligible before the first content event has been delivered; once one has, the run is committed to that provider and a later failure becomes a terminal error rather than a silent provider swap. Cancellation forecloses both at any commit state and always produces exactly one cancelled outcome.
|
|
13
|
+
*/
|
|
14
|
+
stream(request: TaskRequest): Promise<StreamRun>;
|
|
15
|
+
/**
|
|
16
|
+
* Returns a live snapshot of every registered provider's static descriptor and current dynamic availability, without executing a task or producing any side effects. Call this before run() when you need to show which providers/models are currently available — for example a settings screen or provider picker. Availability can change between calls (a local model can unload, cloud credentials can expire), so don't cache the result across a run() call.
|
|
17
|
+
*/
|
|
18
|
+
checkCapabilities(): Promise<ProviderCapabilitySnapshot[]>;
|
|
19
|
+
}
|
package/src/core/host.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
AbortSignalLike,
|
|
3
|
+
ClockService,
|
|
4
|
+
ConnectivityService,
|
|
5
|
+
DeviceConstraintsService,
|
|
6
|
+
HostServices,
|
|
7
|
+
HttpClientService,
|
|
8
|
+
HttpRequest,
|
|
9
|
+
HttpResponse,
|
|
10
|
+
HttpStreamResponse,
|
|
11
|
+
HttpStreamingClientService,
|
|
12
|
+
SecureStorageService,
|
|
13
|
+
TelemetryService,
|
|
14
|
+
ThermalState
|
|
15
|
+
} from "@independo/inderun-contracts";
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
StreamEvent,
|
|
3
|
+
StreamRunHandle,
|
|
4
|
+
TaskRequest,
|
|
5
|
+
TaskResult
|
|
6
|
+
} from "@independo/inderun-contracts";
|
|
7
|
+
import type { HostServices } from "./host.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Static metadata descriptor defining the capabilities, requirements, and constraints
|
|
11
|
+
* of a provider. This is used by the routing engine and Router to select candidate providers.
|
|
12
|
+
*/
|
|
13
|
+
export interface ProviderDescriptor {
|
|
14
|
+
/**
|
|
15
|
+
* Unique identifier of the provider (e.g., 'openai', 'apple-foundation-models').
|
|
16
|
+
*/
|
|
17
|
+
id: string;
|
|
18
|
+
/**
|
|
19
|
+
* Execution target type: 'local' (on-device), 'edge', or 'cloud'.
|
|
20
|
+
*/
|
|
21
|
+
type: "local" | "edge" | "cloud";
|
|
22
|
+
/**
|
|
23
|
+
* Connection transport layer protocol.
|
|
24
|
+
*/
|
|
25
|
+
transport: "in_process" | "system_service" | "http" | "sse" | "realtime";
|
|
26
|
+
/**
|
|
27
|
+
* Optional streaming style, if token or chunk streaming is supported.
|
|
28
|
+
*/
|
|
29
|
+
streamingStyle?: "tokens" | "chunks" | "snapshots";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Flags representing the features and modes supported by this provider.
|
|
33
|
+
*/
|
|
34
|
+
supports: {
|
|
35
|
+
/** Whether the provider supports Mode 1 run (request/response). */
|
|
36
|
+
run: boolean;
|
|
37
|
+
/** Whether the provider supports Mode 2 stream. */
|
|
38
|
+
streaming: boolean;
|
|
39
|
+
/** Whether the provider supports Mode 3 realtime session. */
|
|
40
|
+
realtime: boolean;
|
|
41
|
+
/** Whether the provider supports tool calling definition. */
|
|
42
|
+
tools: boolean;
|
|
43
|
+
/** Whether the provider outputs reasoning events alongside contents. */
|
|
44
|
+
reasoningEvents: boolean;
|
|
45
|
+
/** Whether the provider supports structured outputs (JSON schema constraints). */
|
|
46
|
+
structuredOutput: boolean;
|
|
47
|
+
/** Whether the provider supports multimodal inputs (images, audio, etc.). */
|
|
48
|
+
multimodal: boolean;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Cancellation capabilities support:
|
|
53
|
+
* - 'hard': fully stops processing and interrupts remote operations.
|
|
54
|
+
* - 'soft': stops reading the stream locally, but the remote execution continues.
|
|
55
|
+
* - 'none': cancellation is unsupported.
|
|
56
|
+
*/
|
|
57
|
+
cancel: "hard" | "soft" | "none";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* List of supported task kinds (e.g., 'text_to_text', 'embeddings').
|
|
61
|
+
*/
|
|
62
|
+
tasks: string[];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Optional input/output resource boundaries.
|
|
66
|
+
*/
|
|
67
|
+
limits?: {
|
|
68
|
+
maxInputTokens?: number;
|
|
69
|
+
maxOutputTokens?: number;
|
|
70
|
+
maxImageBytes?: number;
|
|
71
|
+
maxAudioSeconds?: number;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Privacy settings regarding data egress.
|
|
76
|
+
*/
|
|
77
|
+
privacy?: {
|
|
78
|
+
/** Set to true if the model input leaves the host device. */
|
|
79
|
+
dataLeavesDevice: boolean;
|
|
80
|
+
/** Geographic regions where the cloud service executes. */
|
|
81
|
+
regions?: string[];
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Dynamic capability snapshot evaluated at runtime.
|
|
87
|
+
*/
|
|
88
|
+
export interface ProviderDynamicCapabilities {
|
|
89
|
+
/**
|
|
90
|
+
* Whether the provider is available and functional on the host device now.
|
|
91
|
+
*/
|
|
92
|
+
available: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Detail message if availability check fails.
|
|
95
|
+
*/
|
|
96
|
+
reason?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Whether the provider can stream (Mode 2) right now on this host. Leave
|
|
99
|
+
* undefined to inherit the static `descriptor.supports.streaming` value —
|
|
100
|
+
* only set it when the runtime environment can take streaming away from a
|
|
101
|
+
* provider that otherwise declares it (e.g. the host has no chunked HTTP
|
|
102
|
+
* capability).
|
|
103
|
+
*/
|
|
104
|
+
streamingAvailable?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Detail message used when `streamingAvailable` is false. Omit to let the
|
|
107
|
+
* route planner synthesize a generic message.
|
|
108
|
+
*/
|
|
109
|
+
streamingUnavailableReason?: string;
|
|
110
|
+
/**
|
|
111
|
+
* Whether cancellation is honored right now on this host. Leave undefined to
|
|
112
|
+
* inherit the static `descriptor.cancel` value (anything other than `"none"`
|
|
113
|
+
* means available).
|
|
114
|
+
*/
|
|
115
|
+
cancellationAvailable?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* One registered provider's identity, fixed capabilities, and live availability,
|
|
120
|
+
* as of the moment checkCapabilities() was called.
|
|
121
|
+
*/
|
|
122
|
+
export interface ProviderCapabilitySnapshot {
|
|
123
|
+
/**
|
|
124
|
+
* Stable identifier for this provider, matching the value used in routing
|
|
125
|
+
* preferences/constraints.
|
|
126
|
+
*/
|
|
127
|
+
providerId: string;
|
|
128
|
+
/**
|
|
129
|
+
* The provider's static, unchanging capability declaration (supported modes,
|
|
130
|
+
* privacy posture, model info).
|
|
131
|
+
*/
|
|
132
|
+
descriptor: ProviderDescriptor;
|
|
133
|
+
/**
|
|
134
|
+
* The provider's current dynamic availability (e.g. whether it's reachable/loaded
|
|
135
|
+
* right now) — this is the part that can change between calls.
|
|
136
|
+
*/
|
|
137
|
+
capabilities: ProviderDynamicCapabilities;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Execution context passed to provider run commands.
|
|
142
|
+
*/
|
|
143
|
+
export interface RunContext {
|
|
144
|
+
/**
|
|
145
|
+
* Unique execution run ID.
|
|
146
|
+
*/
|
|
147
|
+
runId: string;
|
|
148
|
+
/**
|
|
149
|
+
* Host service boundary for provider adapters. Cloud adapters use this to
|
|
150
|
+
* resolve authContextRef through secure storage and dispatch HTTP requests.
|
|
151
|
+
*/
|
|
152
|
+
hostServices: HostServices;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Execution context passed to provider stream commands. Extends RunContext with the
|
|
157
|
+
* caller-driven cancellation signal; the Engine core owns the AbortController and
|
|
158
|
+
* threads its signal here, so providers never need to construct their own.
|
|
159
|
+
*/
|
|
160
|
+
export interface ProviderStreamContext extends RunContext {
|
|
161
|
+
/**
|
|
162
|
+
* Signals caller-driven cancellation of this stream. Providers with `cancel: "hard"`
|
|
163
|
+
* should tear down their connection immediately on abort; `cancel: "soft"` providers
|
|
164
|
+
* may stop relaying events without interrupting underlying local work; `cancel:
|
|
165
|
+
* "none"` providers may ignore it entirely; the engine enforces the caller-visible
|
|
166
|
+
* cancellation guarantee at the consumer loop regardless of what the provider does.
|
|
167
|
+
*/
|
|
168
|
+
signal: AbortSignal;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A raw, provider-shaped streaming event yielded by ProviderAdapter.stream(). This is
|
|
173
|
+
* intentionally distinct from the canonical StreamEvent contract: providers emit
|
|
174
|
+
* provider-shaped deltas here, and the Engine core's Event Gate normalizes these into
|
|
175
|
+
* StreamEvent/StreamTerminalOutcome, keeping provider-specific mechanics from leaking
|
|
176
|
+
* through the public API.
|
|
177
|
+
*/
|
|
178
|
+
export type ProviderStreamEvent =
|
|
179
|
+
| { kind: "delta"; text: string }
|
|
180
|
+
| { kind: "snapshot"; text: string }
|
|
181
|
+
| {
|
|
182
|
+
kind: "done";
|
|
183
|
+
finalText: string;
|
|
184
|
+
/**
|
|
185
|
+
* How generation ended, when the provider reports it. Mirrors
|
|
186
|
+
* `TaskResult.finishReason` for Mode 1 and surfaces on the `completed`
|
|
187
|
+
* terminal outcome; omit when the provider gives no signal.
|
|
188
|
+
*/
|
|
189
|
+
finishReason?: TaskResult["finishReason"];
|
|
190
|
+
usage?: TaskResult["usage"];
|
|
191
|
+
}
|
|
192
|
+
| { kind: "error"; error: unknown };
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Pluggable execution adapter contract that wraps a specific model runtime
|
|
196
|
+
* (system frameworks, local runtimes, or cloud APIs) and exposes normalized APIs.
|
|
197
|
+
*/
|
|
198
|
+
export interface ProviderAdapter {
|
|
199
|
+
/**
|
|
200
|
+
* Returns static provider descriptor metadata.
|
|
201
|
+
*/
|
|
202
|
+
describe(): ProviderDescriptor;
|
|
203
|
+
/**
|
|
204
|
+
* Performs dynamic capability check to determine if the provider can execute now.
|
|
205
|
+
*/
|
|
206
|
+
capabilities(host: HostServices): Promise<ProviderDynamicCapabilities>;
|
|
207
|
+
/**
|
|
208
|
+
* Executes a task request in Mode 1 (request/response).
|
|
209
|
+
*/
|
|
210
|
+
run(req: TaskRequest, ctx: RunContext): Promise<TaskResult>;
|
|
211
|
+
/**
|
|
212
|
+
* Executes a task request in Mode 2 (streaming). Optional: only providers declaring
|
|
213
|
+
* `describe().supports.streaming === true` are expected to implement this.
|
|
214
|
+
*/
|
|
215
|
+
stream?(req: TaskRequest, ctx: ProviderStreamContext): AsyncIterable<ProviderStreamEvent>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* What `IndeRun.stream()` returns: the run's identity, its canonical event
|
|
220
|
+
* sequence, and a cancel hook.
|
|
221
|
+
*
|
|
222
|
+
* `events` terminates in exactly one terminal `StreamEvent`, per the Event
|
|
223
|
+
* Gate's guarantee, and is single-use — the run is driven once, not restarted
|
|
224
|
+
* per consumer. `cancel()` is idempotent: repeated or concurrent calls produce
|
|
225
|
+
* exactly one `cancelled` terminal outcome.
|
|
226
|
+
*/
|
|
227
|
+
export interface StreamRun {
|
|
228
|
+
handle: StreamRunHandle;
|
|
229
|
+
events: AsyncIterable<StreamEvent>;
|
|
230
|
+
cancel(reason?: string): void;
|
|
231
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ProviderAdapter } from "./provider.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Registry class that holds and manages all active ProviderAdapter instances.
|
|
5
|
+
* Loaded adapters are queried by the Router to choose appropriate routes.
|
|
6
|
+
*/
|
|
7
|
+
export class ProviderRegistry {
|
|
8
|
+
private providers = new Map<string, ProviderAdapter>();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registers a provider adapter in the registry.
|
|
12
|
+
* @param provider - The provider adapter instance to register.
|
|
13
|
+
* @throws {Error} If the provider does not have a valid ID.
|
|
14
|
+
* @throws {Error} If another provider is already registered with the same ID.
|
|
15
|
+
*/
|
|
16
|
+
register(provider: ProviderAdapter): void {
|
|
17
|
+
const descriptor = provider.describe();
|
|
18
|
+
if (!descriptor.id) {
|
|
19
|
+
throw new Error("Provider must have a non-empty ID.");
|
|
20
|
+
}
|
|
21
|
+
if (this.providers.has(descriptor.id)) {
|
|
22
|
+
throw new Error(`Provider with ID "${descriptor.id}" is already registered.`);
|
|
23
|
+
}
|
|
24
|
+
this.providers.set(descriptor.id, provider);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves a registered provider by its ID.
|
|
29
|
+
* @param id - Unique provider identifier.
|
|
30
|
+
*/
|
|
31
|
+
get(id: string): ProviderAdapter | undefined {
|
|
32
|
+
return this.providers.get(id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns a list of all registered provider adapters.
|
|
37
|
+
*/
|
|
38
|
+
list(): ProviderAdapter[] {
|
|
39
|
+
return Array.from(this.providers.values());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Clears all registered providers.
|
|
44
|
+
*/
|
|
45
|
+
clear(): void {
|
|
46
|
+
this.providers.clear();
|
|
47
|
+
}
|
|
48
|
+
}
|