@independo/inderun-web 0.3.0-dev.4 → 0.3.0-dev.6
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 +39 -0
- package/dist/core/browser-host.d.ts +29 -2
- package/dist/core/browser-host.d.ts.map +1 -1
- package/dist/core/browser-host.js +157 -0
- package/dist/core/engine.d.ts +19 -19
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +28 -11
- package/dist/core/generated/inderun-api.d.ts +5 -1
- package/dist/core/generated/inderun-api.d.ts.map +1 -1
- package/dist/core/host.d.ts +1 -1
- package/dist/core/host.d.ts.map +1 -1
- package/dist/core/provider.d.ts +40 -1
- package/dist/core/provider.d.ts.map +1 -1
- package/dist/core/route-planner.d.ts +6 -1
- package/dist/core/route-planner.d.ts.map +1 -1
- package/dist/core/route-planner.js +29 -5
- package/dist/core/router.d.ts +36 -2
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +158 -45
- package/dist/core/sse.d.ts +40 -0
- package/dist/core/sse.d.ts.map +1 -0
- package/dist/core/sse.js +118 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/providers/openai/provider.d.ts +44 -1
- package/dist/providers/openai/provider.d.ts.map +1 -1
- package/dist/providers/openai/provider.js +192 -30
- package/package.json +3 -3
package/dist/core/router.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { TaskRequest } from "@independo/inderun-contracts";
|
|
|
2
2
|
import type { HostServices } from "./host.js";
|
|
3
3
|
import type { ProviderAdapter } from "./provider.js";
|
|
4
4
|
import type { ProviderRegistry } from "./registry.js";
|
|
5
|
-
import { type RoutePlanner, type SharedPlannerRoutePlan, type WasmUnavailableReason } from "./route-planner.js";
|
|
5
|
+
import { type InteractionMode, type RoutePlanner, type SharedPlannerRoutePlan, type WasmUnavailableReason } from "./route-planner.js";
|
|
6
6
|
/**
|
|
7
7
|
* Output structure from the Router containing the chosen provider and a route selection description.
|
|
8
8
|
*/
|
|
@@ -52,13 +52,47 @@ export declare class Router {
|
|
|
52
52
|
* - `CapabilityMismatch` when `execution === 'on_device'` but no local provider is registered or available.
|
|
53
53
|
* - `Offline` when `execution === 'cloud'` but the device lacks internet connectivity.
|
|
54
54
|
* - `Unavailable` when `execution === 'cloud'` but no cloud provider is registered or functional.
|
|
55
|
+
*
|
|
56
|
+
* The requested `interactionMode` is a planning input, not a post-filter: the
|
|
57
|
+
* shared planner rejects providers that cannot satisfy it and says why, so the
|
|
58
|
+
* whole returned chain is mode-compatible.
|
|
55
59
|
*/
|
|
56
|
-
selectRoute(request: TaskRequest, hostServices: HostServices): Promise<RouteSelection>;
|
|
60
|
+
selectRoute(request: TaskRequest, hostServices: HostServices, interactionMode?: InteractionMode): Promise<RouteSelection>;
|
|
57
61
|
private selectFromSharedPlan;
|
|
58
62
|
private selectFallbackRoute;
|
|
59
63
|
private buildSelectionFromRoutePlan;
|
|
64
|
+
/**
|
|
65
|
+
* Hand-written mirror of the shared Rust planner (`rust/inderun-route-core/src/planner.rs`),
|
|
66
|
+
* used when the WASM core cannot be loaded. It reproduces the same rejection
|
|
67
|
+
* codes and messages so route explanations stay comparable across both paths.
|
|
68
|
+
*
|
|
69
|
+
* Known, deliberate divergence: this mirror keeps the id-sorted snapshot order
|
|
70
|
+
* instead of reproducing the Rust placement/preference ranks, so `optimizeFor`
|
|
71
|
+
* does not influence ordering here.
|
|
72
|
+
*/
|
|
60
73
|
private createFallbackPlan;
|
|
74
|
+
/**
|
|
75
|
+
* Mirrors `infer_failure_code` in the shared Rust planner. The offline class is
|
|
76
|
+
* derived from the rejection reasons rather than from the connectivity flag
|
|
77
|
+
* alone: an offline host whose only provider is local and non-streaming failed
|
|
78
|
+
* on capability, not on connectivity, and reporting `Offline` there would
|
|
79
|
+
* contradict the explanation shipped alongside it.
|
|
80
|
+
*/
|
|
81
|
+
private inferFallbackFailureCode;
|
|
82
|
+
/**
|
|
83
|
+
* Mirrors `evaluate_provider` in the shared Rust planner: it accumulates every
|
|
84
|
+
* violated rule rather than short-circuiting, so a rejected provider carries a
|
|
85
|
+
* complete explanation. The streaming checks are ordered static-before-dynamic
|
|
86
|
+
* for the same reason as in Rust — a provider that never declared streaming
|
|
87
|
+
* must not also be reported as dynamically unavailable.
|
|
88
|
+
*/
|
|
89
|
+
private evaluateFallbackCandidate;
|
|
61
90
|
private buildFallbackFailureSummary;
|
|
91
|
+
/**
|
|
92
|
+
* Routing failure throws before any `route_decided` telemetry is emitted, so the
|
|
93
|
+
* plan's diagnostics are attached to the exception — that is the only channel
|
|
94
|
+
* through which a caller learns *why* each provider was rejected.
|
|
95
|
+
*/
|
|
62
96
|
private routePlanFailure;
|
|
63
97
|
}
|
|
64
98
|
//# sourceMappingURL=router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtD,OAAO,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAOtD,OAAO,EAGL,KAAK,eAAe,EAEpB,KAAK,YAAY,EAEjB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAE3B,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,QAAQ,EAAE,eAAe,CAAC;IAC1B;;OAEG;IACH,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC;;OAEG;IACH,SAAS,EAAE,sBAAsB,CAAC;IAClC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,qBAAqB,CAAC;CAClD;AAED;;;;GAIG;AACH,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;gBADP,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,GAAE,YAAqC;IAGxD;;;;;;;;;;;;;OAaG;IACG,WAAW,CACf,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,YAAY,EAC1B,eAAe,GAAE,eAAuB,GACvC,OAAO,CAAC,cAAc,CAAC;IAoB1B,OAAO,CAAC,oBAAoB;IAW5B,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,2BAA2B;IA2BnC;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAwE1B;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IAsBhC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IA4FjC,OAAO,CAAC,2BAA2B;IAyCnC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;CAkBzB"}
|
package/dist/core/router.js
CHANGED
|
@@ -21,16 +21,20 @@ export class Router {
|
|
|
21
21
|
* - `CapabilityMismatch` when `execution === 'on_device'` but no local provider is registered or available.
|
|
22
22
|
* - `Offline` when `execution === 'cloud'` but the device lacks internet connectivity.
|
|
23
23
|
* - `Unavailable` when `execution === 'cloud'` but no cloud provider is registered or functional.
|
|
24
|
+
*
|
|
25
|
+
* The requested `interactionMode` is a planning input, not a post-filter: the
|
|
26
|
+
* shared planner rejects providers that cannot satisfy it and says why, so the
|
|
27
|
+
* whole returned chain is mode-compatible.
|
|
24
28
|
*/
|
|
25
|
-
async selectRoute(request, hostServices) {
|
|
29
|
+
async selectRoute(request, hostServices, interactionMode = "run") {
|
|
26
30
|
const online = await hostServices.connectivity.isOnline();
|
|
27
31
|
const snapshots = await collectProviderRuntimeSnapshots(this.registry.list(), hostServices);
|
|
28
|
-
const planInput = buildSharedPlannerInput(request, snapshots, online);
|
|
32
|
+
const planInput = buildSharedPlannerInput(request, snapshots, online, interactionMode);
|
|
29
33
|
const outcome = await this.planner.planRoute(planInput);
|
|
30
34
|
if (outcome.routePlan) {
|
|
31
35
|
return this.selectFromSharedPlan(snapshots, outcome.routePlan);
|
|
32
36
|
}
|
|
33
|
-
return this.selectFallbackRoute(request, snapshots, online, outcome.unavailableReason);
|
|
37
|
+
return this.selectFallbackRoute(request, snapshots, online, interactionMode, outcome.unavailableReason);
|
|
34
38
|
}
|
|
35
39
|
selectFromSharedPlan(snapshots, routePlan) {
|
|
36
40
|
if (!routePlan.selectedProviderId) {
|
|
@@ -38,8 +42,8 @@ export class Router {
|
|
|
38
42
|
}
|
|
39
43
|
return this.buildSelectionFromRoutePlan(snapshots, routePlan, "wasm");
|
|
40
44
|
}
|
|
41
|
-
selectFallbackRoute(request, snapshots, online, plannerUnavailableReason) {
|
|
42
|
-
const plan = this.createFallbackPlan(request, snapshots, online);
|
|
45
|
+
selectFallbackRoute(request, snapshots, online, interactionMode, plannerUnavailableReason) {
|
|
46
|
+
const plan = this.createFallbackPlan(request, snapshots, online, interactionMode);
|
|
43
47
|
if (!plan.selectedProviderId) {
|
|
44
48
|
throw this.routePlanFailure(plan);
|
|
45
49
|
}
|
|
@@ -62,58 +66,48 @@ export class Router {
|
|
|
62
66
|
...(plannerUnavailableReason ? { plannerUnavailableReason } : {})
|
|
63
67
|
};
|
|
64
68
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Hand-written mirror of the shared Rust planner (`rust/inderun-route-core/src/planner.rs`),
|
|
71
|
+
* used when the WASM core cannot be loaded. It reproduces the same rejection
|
|
72
|
+
* codes and messages so route explanations stay comparable across both paths.
|
|
73
|
+
*
|
|
74
|
+
* Known, deliberate divergence: this mirror keeps the id-sorted snapshot order
|
|
75
|
+
* instead of reproducing the Rust placement/preference ranks, so `optimizeFor`
|
|
76
|
+
* does not influence ordering here.
|
|
77
|
+
*/
|
|
78
|
+
createFallbackPlan(request, snapshots, online, interactionMode) {
|
|
79
|
+
const planInput = buildSharedPlannerInput(request, snapshots, online, interactionMode);
|
|
80
|
+
const eligible = snapshots.filter((candidate) => candidate.descriptor.tasks.includes(planInput.task.kind));
|
|
70
81
|
const localCandidates = eligible.filter((candidate) => candidate.descriptor.type !== "cloud");
|
|
71
82
|
const cloudCandidates = eligible.filter((candidate) => candidate.descriptor.type === "cloud");
|
|
72
83
|
// Constraint filtering must apply to the whole candidate list, not just the selected provider:
|
|
73
84
|
// the remainder becomes the engine's fallback chain, and a fallback that violates the request
|
|
74
85
|
// constraints would let a `local_required` run be retried against a cloud provider.
|
|
75
|
-
const admissible =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? !descriptor.privacy.dataLeavesDevice
|
|
82
|
-
: descriptor.type !== "cloud";
|
|
83
|
-
if (constraints.cloud === "forbidden" && descriptor.type === "cloud") {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
if (constraints.cloud === "required" && descriptor.type !== "cloud") {
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
|
-
if (constraints.privacy === "local_required" && !isDataPrivate) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
if (constraints.privacy === "cloud_required" && descriptor.type !== "cloud") {
|
|
93
|
-
return false;
|
|
86
|
+
const admissible = [];
|
|
87
|
+
const rejectedProviders = [];
|
|
88
|
+
for (const candidate of snapshots) {
|
|
89
|
+
const reasons = this.evaluateFallbackCandidate(candidate, planInput, interactionMode);
|
|
90
|
+
if (reasons.length === 0) {
|
|
91
|
+
admissible.push(candidate);
|
|
94
92
|
}
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
else {
|
|
94
|
+
rejectedProviders.push({ providerId: candidate.descriptor.id, reasons });
|
|
97
95
|
}
|
|
98
|
-
|
|
99
|
-
});
|
|
96
|
+
}
|
|
100
97
|
if (admissible.length === 0) {
|
|
101
98
|
const failureSummary = this.buildFallbackFailureSummary({
|
|
102
99
|
online,
|
|
100
|
+
interactionMode,
|
|
101
|
+
taskKind: planInput.task.kind,
|
|
103
102
|
constraints: planInput.constraints,
|
|
104
103
|
localCandidates,
|
|
105
104
|
cloudCandidates
|
|
106
105
|
});
|
|
107
|
-
const failureCode =
|
|
108
|
-
? "offline"
|
|
109
|
-
: planInput.constraints.cloud === "required" ||
|
|
110
|
-
planInput.constraints.privacy === "cloud_required"
|
|
111
|
-
? "unavailable"
|
|
112
|
-
: "capability_mismatch";
|
|
106
|
+
const failureCode = this.inferFallbackFailureCode(planInput, rejectedProviders);
|
|
113
107
|
return {
|
|
114
108
|
fallbackProviderIds: [],
|
|
115
109
|
candidates: [],
|
|
116
|
-
rejectedProviders
|
|
110
|
+
rejectedProviders,
|
|
117
111
|
failureCode,
|
|
118
112
|
explanation: {
|
|
119
113
|
summary: failureSummary
|
|
@@ -121,6 +115,9 @@ export class Router {
|
|
|
121
115
|
};
|
|
122
116
|
}
|
|
123
117
|
const selectedProviderId = admissible[0]?.descriptor.id;
|
|
118
|
+
const summary = interactionMode === "stream"
|
|
119
|
+
? `Selected streaming provider '${selectedProviderId}' deterministically from ${admissible.length} eligible candidate(s).`
|
|
120
|
+
: `Selected provider '${selectedProviderId}' deterministically from ${admissible.length} eligible candidate(s).`;
|
|
124
121
|
return {
|
|
125
122
|
selectedProviderId,
|
|
126
123
|
fallbackProviderIds: admissible.slice(1).map((candidate) => candidate.descriptor.id),
|
|
@@ -128,13 +125,113 @@ export class Router {
|
|
|
128
125
|
providerId: candidate.descriptor.id,
|
|
129
126
|
order: index
|
|
130
127
|
})),
|
|
131
|
-
rejectedProviders
|
|
128
|
+
rejectedProviders,
|
|
132
129
|
explanation: {
|
|
133
|
-
summary
|
|
130
|
+
summary,
|
|
134
131
|
selectedProviderId
|
|
135
132
|
}
|
|
136
133
|
};
|
|
137
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Mirrors `infer_failure_code` in the shared Rust planner. The offline class is
|
|
137
|
+
* derived from the rejection reasons rather than from the connectivity flag
|
|
138
|
+
* alone: an offline host whose only provider is local and non-streaming failed
|
|
139
|
+
* on capability, not on connectivity, and reporting `Offline` there would
|
|
140
|
+
* contradict the explanation shipped alongside it.
|
|
141
|
+
*/
|
|
142
|
+
inferFallbackFailureCode(planInput, rejectedProviders) {
|
|
143
|
+
if (rejectedProviders.some((rejected) => rejected.reasons.some((reason) => reason.code === "offline"))) {
|
|
144
|
+
return "offline";
|
|
145
|
+
}
|
|
146
|
+
if (planInput.constraints.cloud === "required" ||
|
|
147
|
+
planInput.constraints.privacy === "cloud_required") {
|
|
148
|
+
return "unavailable";
|
|
149
|
+
}
|
|
150
|
+
return "capability_mismatch";
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Mirrors `evaluate_provider` in the shared Rust planner: it accumulates every
|
|
154
|
+
* violated rule rather than short-circuiting, so a rejected provider carries a
|
|
155
|
+
* complete explanation. The streaming checks are ordered static-before-dynamic
|
|
156
|
+
* for the same reason as in Rust — a provider that never declared streaming
|
|
157
|
+
* must not also be reported as dynamically unavailable.
|
|
158
|
+
*/
|
|
159
|
+
evaluateFallbackCandidate(candidate, planInput, interactionMode) {
|
|
160
|
+
const descriptor = candidate.descriptor;
|
|
161
|
+
const capabilities = candidate.capabilities;
|
|
162
|
+
const constraints = planInput.constraints;
|
|
163
|
+
const wantsStream = interactionMode === "stream";
|
|
164
|
+
const reasons = [];
|
|
165
|
+
// Mirrors `is_data_private` in the shared Rust planner: a provider is private when it
|
|
166
|
+
// declares that data does not leave the device, defaulting to non-cloud providers.
|
|
167
|
+
const isDataPrivate = descriptor.privacy
|
|
168
|
+
? !descriptor.privacy.dataLeavesDevice
|
|
169
|
+
: descriptor.type !== "cloud";
|
|
170
|
+
if (!descriptor.tasks.includes(planInput.task.kind)) {
|
|
171
|
+
reasons.push({
|
|
172
|
+
code: "task_not_supported",
|
|
173
|
+
message: `Provider '${descriptor.id}' does not support task '${planInput.task.kind}'.`
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (!wantsStream && !descriptor.supports.run) {
|
|
177
|
+
reasons.push({
|
|
178
|
+
code: "run_not_supported",
|
|
179
|
+
message: `Provider '${descriptor.id}' does not support run().`
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (wantsStream) {
|
|
183
|
+
if (!descriptor.supports.streaming) {
|
|
184
|
+
reasons.push({
|
|
185
|
+
code: "streaming_not_supported",
|
|
186
|
+
message: `Provider '${descriptor.id}' does not support streaming (Mode 2).`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
else if (capabilities.streamingAvailable === false) {
|
|
190
|
+
reasons.push({
|
|
191
|
+
code: "streaming_unavailable",
|
|
192
|
+
message: capabilities.streamingUnavailableReason ??
|
|
193
|
+
`Provider '${descriptor.id}' cannot stream in the current host environment.`
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (constraints.privacy === "local_required" && !isDataPrivate) {
|
|
198
|
+
reasons.push({
|
|
199
|
+
code: "privacy_constraint",
|
|
200
|
+
message: `Provider '${descriptor.id}' does not satisfy local-required privacy.`
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (constraints.privacy === "cloud_required" && descriptor.type !== "cloud") {
|
|
204
|
+
reasons.push({
|
|
205
|
+
code: "privacy_constraint",
|
|
206
|
+
message: `Provider '${descriptor.id}' does not satisfy cloud-required privacy.`
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (constraints.cloud === "forbidden" && descriptor.type === "cloud") {
|
|
210
|
+
reasons.push({
|
|
211
|
+
code: "cloud_constraint",
|
|
212
|
+
message: `Provider '${descriptor.id}' is cloud-based but cloud execution is forbidden.`
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (constraints.cloud === "required" && descriptor.type !== "cloud") {
|
|
216
|
+
reasons.push({
|
|
217
|
+
code: "cloud_constraint",
|
|
218
|
+
message: `Provider '${descriptor.id}' is not cloud-based but cloud execution is required.`
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (constraints.networkOnline === false && descriptor.type === "cloud") {
|
|
222
|
+
reasons.push({
|
|
223
|
+
code: "offline",
|
|
224
|
+
message: `Provider '${descriptor.id}' requires cloud connectivity, but the host is offline.`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (!capabilities.available) {
|
|
228
|
+
reasons.push({
|
|
229
|
+
code: "capability_unavailable",
|
|
230
|
+
message: capabilities.reason ?? `Provider '${descriptor.id}' is currently unavailable.`
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return reasons;
|
|
234
|
+
}
|
|
138
235
|
buildFallbackFailureSummary(input) {
|
|
139
236
|
const wantsCloud = input.constraints.cloud === "required" || input.constraints.privacy === "cloud_required";
|
|
140
237
|
const wantsLocal = input.constraints.privacy === "local_required";
|
|
@@ -153,18 +250,34 @@ export class Router {
|
|
|
153
250
|
}
|
|
154
251
|
return "No on-device provider is currently available.";
|
|
155
252
|
}
|
|
253
|
+
if (input.interactionMode === "stream") {
|
|
254
|
+
// Kept verbatim in sync with the shared Rust planner's stream-mode failure
|
|
255
|
+
// summary so the message a caller sees does not depend on which planner ran.
|
|
256
|
+
return `No provider capable of streaming was found for task '${input.taskKind}'.`;
|
|
257
|
+
}
|
|
156
258
|
return "No eligible provider found for the current routing constraints.";
|
|
157
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Routing failure throws before any `route_decided` telemetry is emitted, so the
|
|
262
|
+
* plan's diagnostics are attached to the exception — that is the only channel
|
|
263
|
+
* through which a caller learns *why* each provider was rejected.
|
|
264
|
+
*/
|
|
158
265
|
routePlanFailure(routePlan) {
|
|
159
266
|
const message = routePlan.explanation.summary;
|
|
267
|
+
const details = {
|
|
268
|
+
details: {
|
|
269
|
+
failureCode: routePlan.failureCode,
|
|
270
|
+
rejectedProviders: routePlan.rejectedProviders
|
|
271
|
+
}
|
|
272
|
+
};
|
|
160
273
|
switch (routePlan.failureCode) {
|
|
161
274
|
case "offline":
|
|
162
|
-
throw createOffline(message);
|
|
275
|
+
throw createOffline(message, details);
|
|
163
276
|
case "unavailable":
|
|
164
|
-
throw createUnavailable(message);
|
|
277
|
+
throw createUnavailable(message, details);
|
|
165
278
|
case "capability_mismatch":
|
|
166
279
|
default:
|
|
167
|
-
throw createCapabilityMismatch(message);
|
|
280
|
+
throw createCapabilityMismatch(message, details);
|
|
168
281
|
}
|
|
169
282
|
}
|
|
170
283
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single dispatched server-sent event.
|
|
3
|
+
*
|
|
4
|
+
* `event` and `id` are `undefined` when the stream did not carry those fields.
|
|
5
|
+
* `data` is the accumulated data payload with its trailing newline removed, and
|
|
6
|
+
* is passed through verbatim — sentinels such as OpenAI's `[DONE]` are the
|
|
7
|
+
* caller's concern, not the framing layer's.
|
|
8
|
+
*/
|
|
9
|
+
export interface SseEvent {
|
|
10
|
+
event?: string;
|
|
11
|
+
data: string;
|
|
12
|
+
id?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Frames a raw byte stream into server-sent events.
|
|
16
|
+
*
|
|
17
|
+
* Follows the WHATWG server-sent events stream parsing rules, minus
|
|
18
|
+
* reconnection: `retry` is parsed as a field and ignored, and there is no
|
|
19
|
+
* Last-Event-ID reconnect logic. Concretely that means:
|
|
20
|
+
*
|
|
21
|
+
* - lines end with LF, CRLF, or bare CR;
|
|
22
|
+
* - a line beginning with `:` is a comment (servers send these as keep-alives);
|
|
23
|
+
* - `field: value` strips exactly one leading space from the value, and a line
|
|
24
|
+
* with no colon is that field with an empty value;
|
|
25
|
+
* - `data` fields accumulate, joined by newlines;
|
|
26
|
+
* - a blank line dispatches the accumulated event, or resets without
|
|
27
|
+
* dispatching if no data was accumulated;
|
|
28
|
+
* - a stream that ends mid-event does **not** dispatch the partial event, since
|
|
29
|
+
* a truncated event must never be reported as a complete one.
|
|
30
|
+
*
|
|
31
|
+
* Chunk boundaries are meaningless: this decodes incrementally (`stream: true`)
|
|
32
|
+
* so a boundary may fall inside a multi-byte UTF-8 sequence or mid-line.
|
|
33
|
+
*
|
|
34
|
+
* Held to `contracts/fixtures/streaming/sse-framing.json` together with the
|
|
35
|
+
* Swift and Kotlin implementations of the same protocol.
|
|
36
|
+
*
|
|
37
|
+
* @param chunks - Raw response body chunks, in arrival order.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseSseStream(chunks: AsyncIterable<Uint8Array>): AsyncGenerator<SseEvent>;
|
|
40
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/core/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAuB,cAAc,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAsFjG"}
|
package/dist/core/sse.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frames a raw byte stream into server-sent events.
|
|
3
|
+
*
|
|
4
|
+
* Follows the WHATWG server-sent events stream parsing rules, minus
|
|
5
|
+
* reconnection: `retry` is parsed as a field and ignored, and there is no
|
|
6
|
+
* Last-Event-ID reconnect logic. Concretely that means:
|
|
7
|
+
*
|
|
8
|
+
* - lines end with LF, CRLF, or bare CR;
|
|
9
|
+
* - a line beginning with `:` is a comment (servers send these as keep-alives);
|
|
10
|
+
* - `field: value` strips exactly one leading space from the value, and a line
|
|
11
|
+
* with no colon is that field with an empty value;
|
|
12
|
+
* - `data` fields accumulate, joined by newlines;
|
|
13
|
+
* - a blank line dispatches the accumulated event, or resets without
|
|
14
|
+
* dispatching if no data was accumulated;
|
|
15
|
+
* - a stream that ends mid-event does **not** dispatch the partial event, since
|
|
16
|
+
* a truncated event must never be reported as a complete one.
|
|
17
|
+
*
|
|
18
|
+
* Chunk boundaries are meaningless: this decodes incrementally (`stream: true`)
|
|
19
|
+
* so a boundary may fall inside a multi-byte UTF-8 sequence or mid-line.
|
|
20
|
+
*
|
|
21
|
+
* Held to `contracts/fixtures/streaming/sse-framing.json` together with the
|
|
22
|
+
* Swift and Kotlin implementations of the same protocol.
|
|
23
|
+
*
|
|
24
|
+
* @param chunks - Raw response body chunks, in arrival order.
|
|
25
|
+
*/
|
|
26
|
+
export async function* parseSseStream(chunks) {
|
|
27
|
+
const decoder = new TextDecoder("utf-8");
|
|
28
|
+
let buffer = "";
|
|
29
|
+
let eventType;
|
|
30
|
+
let data;
|
|
31
|
+
let id;
|
|
32
|
+
const takeEvent = () => {
|
|
33
|
+
const dispatched = data === undefined ? undefined : buildEvent(eventType, data, id);
|
|
34
|
+
eventType = undefined;
|
|
35
|
+
data = undefined;
|
|
36
|
+
id = undefined;
|
|
37
|
+
return dispatched;
|
|
38
|
+
};
|
|
39
|
+
const handleLine = (line) => {
|
|
40
|
+
if (line.length === 0)
|
|
41
|
+
return takeEvent();
|
|
42
|
+
if (line.startsWith(":"))
|
|
43
|
+
return undefined;
|
|
44
|
+
const colon = line.indexOf(":");
|
|
45
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
46
|
+
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
47
|
+
if (value.startsWith(" "))
|
|
48
|
+
value = value.slice(1);
|
|
49
|
+
switch (field) {
|
|
50
|
+
case "event":
|
|
51
|
+
eventType = value;
|
|
52
|
+
break;
|
|
53
|
+
case "data":
|
|
54
|
+
data = data === undefined ? value : `${data}\n${value}`;
|
|
55
|
+
break;
|
|
56
|
+
case "id":
|
|
57
|
+
// Per the SSE spec an id containing a NUL is ignored outright.
|
|
58
|
+
if (!value.includes("\0"))
|
|
59
|
+
id = value;
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
// `retry` and anything unrecognized are dropped rather than erroring.
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
};
|
|
67
|
+
for await (const chunk of chunks) {
|
|
68
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
69
|
+
for (;;) {
|
|
70
|
+
const line = takeLine();
|
|
71
|
+
if (line === undefined)
|
|
72
|
+
break;
|
|
73
|
+
const dispatched = handleLine(line);
|
|
74
|
+
if (dispatched)
|
|
75
|
+
yield dispatched;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// A trailing bare CR is ambiguous until the stream ends: it could still have
|
|
79
|
+
// been the first half of a CRLF.
|
|
80
|
+
buffer += decoder.decode();
|
|
81
|
+
for (;;) {
|
|
82
|
+
const line = takeLine(true);
|
|
83
|
+
if (line === undefined)
|
|
84
|
+
break;
|
|
85
|
+
const dispatched = handleLine(line);
|
|
86
|
+
if (dispatched)
|
|
87
|
+
yield dispatched;
|
|
88
|
+
}
|
|
89
|
+
function takeLine(atEnd = false) {
|
|
90
|
+
for (let index = 0; index < buffer.length; index += 1) {
|
|
91
|
+
const char = buffer[index];
|
|
92
|
+
if (char === "\n") {
|
|
93
|
+
const line = buffer.slice(0, index);
|
|
94
|
+
buffer = buffer.slice(index + 1);
|
|
95
|
+
return line;
|
|
96
|
+
}
|
|
97
|
+
if (char === "\r") {
|
|
98
|
+
const isLast = index === buffer.length - 1;
|
|
99
|
+
// Withhold a trailing CR until we know whether an LF follows, unless
|
|
100
|
+
// the stream is over and no LF can arrive.
|
|
101
|
+
if (isLast && !atEnd)
|
|
102
|
+
return undefined;
|
|
103
|
+
const skip = buffer[index + 1] === "\n" ? 2 : 1;
|
|
104
|
+
const line = buffer.slice(0, index);
|
|
105
|
+
buffer = buffer.slice(index + skip);
|
|
106
|
+
return line;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function buildEvent(eventType, data, id) {
|
|
113
|
+
return {
|
|
114
|
+
...(eventType !== undefined ? { event: eventType } : {}),
|
|
115
|
+
data,
|
|
116
|
+
...(id !== undefined ? { id } : {})
|
|
117
|
+
};
|
|
118
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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, type ProviderStreamContext, type ProviderStreamEvent } from "./core/provider.js";
|
|
1
|
+
export { type AbortSignalLike, type ConnectivityService, type DeviceConstraintsService, type SecureStorageService, type ClockService, type HttpClientService, type HttpRequest, type HttpResponse, type HttpStreamResponse, type HttpStreamingClientService, type HostServices, type ThermalState } from "./core/host.js";
|
|
2
|
+
export { type ProviderDescriptor, type ProviderDynamicCapabilities, type ProviderCapabilitySnapshot, type RunContext, type ProviderAdapter, type ProviderStreamContext, type ProviderStreamEvent, type StreamRun } from "./core/provider.js";
|
|
3
3
|
export { EventGate } from "./core/event-gate.js";
|
|
4
4
|
export { ProviderRegistry } from "./core/registry.js";
|
|
5
5
|
export { type RouteSelection, Router } from "./core/router.js";
|
|
@@ -8,6 +8,6 @@ export { type IndeRunExceptionParams, IndeRunException, createCapabilityMismatch
|
|
|
8
8
|
export { IndeRun, type StreamHandleResult } from "./core/engine.js";
|
|
9
9
|
export type { IndeRunApi } from "./core/generated/inderun-api.js";
|
|
10
10
|
export { type TelemetryEventType, type TelemetryEvent, type TelemetryService, NoOpTelemetryService } from "./core/telemetry.js";
|
|
11
|
-
export { BrowserConnectivityService, FetchHttpClient, SystemClockService, createBrowserHostServices, type CreateBrowserHostServicesOptions, type FetchHttpClientOptions } from "./core/browser-host.js";
|
|
11
|
+
export { BrowserConnectivityService, FetchHttpClient, FetchStreamingHttpClient, SystemClockService, createBrowserHostServices, type CreateBrowserHostServicesOptions, type FetchHttpClientOptions } from "./core/browser-host.js";
|
|
12
12
|
export { createIndeRunWeb, type CreateIndeRunWebOptions } from "./web-sdk.js";
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
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,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,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,EACxB,KAAK,SAAS,EACf,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,wBAAwB,EACxB,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
|
@@ -7,7 +7,7 @@ export { WasmRoutePlanner } from "./core/route-planner.js";
|
|
|
7
7
|
export { IndeRunException, createCapabilityMismatch, createOffline, createAuthError, createRateLimited, createTimeout, createUnavailable, createInternal, toIndeRunException } from "./core/errors.js";
|
|
8
8
|
export { IndeRun } from "./core/engine.js";
|
|
9
9
|
export { NoOpTelemetryService } from "./core/telemetry.js";
|
|
10
|
-
export { BrowserConnectivityService, FetchHttpClient, SystemClockService, createBrowserHostServices } from "./core/browser-host.js";
|
|
10
|
+
export { BrowserConnectivityService, FetchHttpClient, FetchStreamingHttpClient, SystemClockService, createBrowserHostServices } from "./core/browser-host.js";
|
|
11
11
|
export { createIndeRunWeb } from "./web-sdk.js";
|
|
12
12
|
// The OpenAI Responses adapter is provider-specific and intentionally excluded
|
|
13
13
|
// from this unified index. Import it from "@independo/inderun-web/openai".
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TaskRequest, TaskResult } from "@independo/inderun-contracts";
|
|
2
2
|
import type { HostServices } from "../../core/host.js";
|
|
3
|
-
import type { ProviderAdapter, ProviderDescriptor, ProviderDynamicCapabilities, RunContext } from "../../core/provider.js";
|
|
3
|
+
import type { ProviderAdapter, ProviderDescriptor, ProviderDynamicCapabilities, ProviderStreamContext, ProviderStreamEvent, RunContext } from "../../core/provider.js";
|
|
4
4
|
export declare const DEFAULT_OPENAI_RESPONSES_ENDPOINT = "https://api.openai.com/v1/responses";
|
|
5
5
|
/**
|
|
6
6
|
* Configuration for the OpenAI Responses provider.
|
|
@@ -84,6 +84,49 @@ export declare class OpenAIResponsesProvider implements ProviderAdapter {
|
|
|
84
84
|
* @throws IndeRunException mapped to the standard error taxonomy.
|
|
85
85
|
*/
|
|
86
86
|
run(request: TaskRequest, context: RunContext): Promise<TaskResult>;
|
|
87
|
+
/**
|
|
88
|
+
* Executes a normalized text-to-text task in Mode 2 against the OpenAI
|
|
89
|
+
* Responses API's server-sent event stream.
|
|
90
|
+
*
|
|
91
|
+
* The request is the Mode 1 body plus `"stream": true`, so both modes stay a
|
|
92
|
+
* single code path up to the transport. The response head is classified
|
|
93
|
+
* before the body is read: a non-2xx is drained to text and run through the
|
|
94
|
+
* same `mapHttpError` as Mode 1, because a 429 must surface as RateLimited
|
|
95
|
+
* rather than as a malformed event stream.
|
|
96
|
+
*
|
|
97
|
+
* Event mapping, from the Responses API's typed SSE events to the canonical
|
|
98
|
+
* provider vocabulary:
|
|
99
|
+
*
|
|
100
|
+
* - `response.output_text.delta` becomes a delta;
|
|
101
|
+
* - `response.completed` and `response.incomplete` become the terminal done,
|
|
102
|
+
* with `finalText`, `usage`, and `finishReason` read off the embedded
|
|
103
|
+
* response object by the same helpers Mode 1 uses — it has the same shape;
|
|
104
|
+
* - `response.failed` and `error` become a terminal error;
|
|
105
|
+
* - every other event type is ignored, since the set is open and additive.
|
|
106
|
+
*
|
|
107
|
+
* @param request - Canonical IndeRun task request.
|
|
108
|
+
* @param context - Engine stream context: run id, host services, abort signal.
|
|
109
|
+
*/
|
|
110
|
+
stream(request: TaskRequest, context: ProviderStreamContext): AsyncGenerator<ProviderStreamEvent>;
|
|
111
|
+
/**
|
|
112
|
+
* Maps a `response.failed` / `error` stream event onto the error taxonomy.
|
|
113
|
+
* These arrive over an HTTP 200 body, so there is no status code to classify
|
|
114
|
+
* from; OpenAI reports rate limiting and auth failures here with the same
|
|
115
|
+
* `code` values it uses in unary error bodies.
|
|
116
|
+
*
|
|
117
|
+
* Two payload shapes exist and both are accepted: the standalone `error`
|
|
118
|
+
* event carries `code`/`message` at its root, while `response.failed` nests
|
|
119
|
+
* them under `response.error`. The root `type` is the event name rather than
|
|
120
|
+
* an error type, so it is never read as one.
|
|
121
|
+
*/
|
|
122
|
+
private mapStreamError;
|
|
123
|
+
/**
|
|
124
|
+
* Resolves the request headers, including the bearer token behind
|
|
125
|
+
* `authContextRef`. Shared by `run` and `stream`: the credential never travels
|
|
126
|
+
* in the request payload, only the slot id does, so both modes must resolve it
|
|
127
|
+
* the same way.
|
|
128
|
+
*/
|
|
129
|
+
private resolveHeaders;
|
|
87
130
|
private createRequestBody;
|
|
88
131
|
/**
|
|
89
132
|
* Maps an OpenAI HTTP error response onto the IndeRun error taxonomy:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAQ5E,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,2BAA2B,EAC3B,UAAU,EACX,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAQ5E,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,2BAA2B,EAC3B,qBAAqB,EACrB,mBAAmB,EACnB,UAAU,EACX,MAAM,wBAAwB,CAAC;AA4BhC,eAAO,MAAM,iCAAiC,wCAAwC,CAAC;AAKvF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IACjC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA6BD;;;;;;GAMG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAUjD,OAAO,CAAC,QAAQ,CAAC,OAAO;IATpC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,YAAY,CAAC,CAA6D;IAElF;;;OAGG;gBAC0B,OAAO,EAAE,qBAAqB;IAM3D;;OAEG;IACH,QAAQ,IAAI,kBAAkB;IAuB9B;;;;;;;;OAQG;IACG,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,2BAA2B,CAAC;YA+B9D,sBAAsB;IAuCpC;;;;;;OAMG;IACG,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAiFzE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,MAAM,CACX,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAAC,mBAAmB,CAAC;IAiGtC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,cAAc;IAmBtB;;;;;OAKG;YACW,cAAc;IA2C5B,OAAO,CAAC,iBAAiB;IA2BzB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;CAmErB"}
|