@independo/inderun-web 0.3.3 → 0.4.0-rc.2
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 +46 -9
- package/dist/core/engine.d.ts +26 -2
- package/dist/core/engine.d.ts.map +1 -1
- package/dist/core/engine.js +78 -34
- package/dist/core/errors.d.ts +13 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +32 -0
- package/dist/core/model-profiles.d.ts +34 -0
- package/dist/core/model-profiles.d.ts.map +1 -0
- package/dist/core/model-profiles.js +49 -0
- package/dist/core/provenance.d.ts +32 -0
- package/dist/core/provenance.d.ts.map +1 -0
- package/dist/core/provenance.js +71 -0
- package/dist/core/provider.d.ts +13 -0
- package/dist/core/provider.d.ts.map +1 -1
- package/dist/core/route-planner.d.ts +1 -1
- package/dist/core/route-planner.d.ts.map +1 -1
- package/dist/core/route-planner.js +2 -1
- package/dist/core/router.d.ts +4 -3
- package/dist/core/router.d.ts.map +1 -1
- package/dist/core/router.js +4 -3
- package/dist/facade/text.d.ts +123 -0
- package/dist/facade/text.d.ts.map +1 -0
- package/dist/facade/text.js +234 -0
- package/dist/index.d.ts +36 -45
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -38
- package/dist/provider.d.ts +34 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +33 -0
- package/dist/providers/onnx/provider.d.ts.map +1 -1
- package/dist/providers/onnx/provider.js +1 -0
- package/dist/providers/openai/provider.d.ts +41 -11
- package/dist/providers/openai/provider.d.ts.map +1 -1
- package/dist/providers/openai/provider.js +168 -58
- package/dist/web-sdk.d.ts +21 -3
- package/dist/web-sdk.d.ts.map +1 -1
- package/dist/web-sdk.js +23 -6
- package/package.json +7 -3
- package/src/core/engine.ts +114 -35
- package/src/core/errors.ts +35 -1
- package/src/core/model-profiles.ts +70 -0
- package/src/core/provenance.ts +90 -0
- package/src/core/provider.ts +13 -0
- package/src/core/route-planner.ts +3 -1
- package/src/core/router.ts +12 -3
- package/src/facade/text.ts +349 -0
- package/src/index.ts +49 -95
- package/src/provider.ts +77 -0
- package/src/providers/onnx/provider.ts +1 -0
- package/src/providers/openai/provider.ts +232 -68
- package/src/web-sdk.ts +47 -10
|
@@ -42,6 +42,7 @@ export class OpenAIResponsesProvider {
|
|
|
42
42
|
return {
|
|
43
43
|
id: this.id,
|
|
44
44
|
type: "cloud",
|
|
45
|
+
model: this.options.model,
|
|
45
46
|
transport: "http",
|
|
46
47
|
streamingStyle: "tokens",
|
|
47
48
|
supports: {
|
|
@@ -173,7 +174,15 @@ export class OpenAIResponsesProvider {
|
|
|
173
174
|
throw this.mapHttpError(response.status, response.statusText, response.headers, response.body, context);
|
|
174
175
|
}
|
|
175
176
|
const responseBody = parseJson(response.body);
|
|
177
|
+
// A lenient server (OpenRouter among them) can answer 2xx for a run that
|
|
178
|
+
// failed upstream; that is a failure to classify, not an empty answer.
|
|
179
|
+
if (responseBody.status === "failed" || responseBody.status === "cancelled") {
|
|
180
|
+
throw this.mapReportedError(responseBody.error, `OpenAI Responses run ended with status '${responseBody.status}'.`, context, responseBody.error_type);
|
|
181
|
+
}
|
|
176
182
|
const outputText = extractOutputText(responseBody);
|
|
183
|
+
if (outputText === undefined && isRecord(responseBody.error)) {
|
|
184
|
+
throw this.mapReportedError(responseBody.error, "OpenAI Responses payload reported an error.", context, responseBody.error_type);
|
|
185
|
+
}
|
|
177
186
|
if (outputText === undefined) {
|
|
178
187
|
throw createInternal("OpenAI Responses payload did not contain text output.", {
|
|
179
188
|
providerId: this.id,
|
|
@@ -193,6 +202,10 @@ export class OpenAIResponsesProvider {
|
|
|
193
202
|
totalMs: 0
|
|
194
203
|
}
|
|
195
204
|
};
|
|
205
|
+
const reportedModel = getReportedModel(responseBody);
|
|
206
|
+
if (reportedModel !== undefined) {
|
|
207
|
+
result.telemetry.reportedModel = reportedModel;
|
|
208
|
+
}
|
|
196
209
|
const usage = getUsage(responseBody);
|
|
197
210
|
if (usage) {
|
|
198
211
|
result.usage = usage;
|
|
@@ -216,7 +229,9 @@ export class OpenAIResponsesProvider {
|
|
|
216
229
|
* - `response.completed` and `response.incomplete` become the terminal done,
|
|
217
230
|
* with `finalText`, `usage`, and `finishReason` read off the embedded
|
|
218
231
|
* response object by the same helpers Mode 1 uses — it has the same shape;
|
|
219
|
-
* - `response.failed` and `error` become a terminal error
|
|
232
|
+
* - `response.failed`, `response.error` and `error` become a terminal error,
|
|
233
|
+
* as does an untyped event carrying an `error` object (OpenRouter's
|
|
234
|
+
* documented mid-stream failure shape);
|
|
220
235
|
* - every other event type is ignored, since the set is open and additive.
|
|
221
236
|
*
|
|
222
237
|
* @param request - Canonical IndeRun task request.
|
|
@@ -286,10 +301,17 @@ export class OpenAIResponsesProvider {
|
|
|
286
301
|
if (usage) {
|
|
287
302
|
done.usage = usage;
|
|
288
303
|
}
|
|
304
|
+
const reportedModel = getReportedModel(body);
|
|
305
|
+
if (reportedModel !== undefined) {
|
|
306
|
+
done.reportedModel = reportedModel;
|
|
307
|
+
}
|
|
289
308
|
yield done;
|
|
290
309
|
return;
|
|
291
310
|
}
|
|
292
|
-
if (type === "response.failed" ||
|
|
311
|
+
if (type === "response.failed" ||
|
|
312
|
+
type === "response.error" ||
|
|
313
|
+
type === "error" ||
|
|
314
|
+
(type === undefined && isRecord(parsed.error))) {
|
|
293
315
|
yield { kind: "error", error: this.mapStreamError(parsed, context) };
|
|
294
316
|
return;
|
|
295
317
|
}
|
|
@@ -299,28 +321,64 @@ export class OpenAIResponsesProvider {
|
|
|
299
321
|
// whatever deltas happened to arrive would hide a truncated response.
|
|
300
322
|
}
|
|
301
323
|
/**
|
|
302
|
-
* Maps a `response.failed` / `error` stream event onto the
|
|
303
|
-
* These arrive over an HTTP 200 body, so there is no status
|
|
304
|
-
*
|
|
305
|
-
* `code` values it uses in unary error bodies.
|
|
324
|
+
* Maps a `response.failed` / `response.error` / `error` stream event onto the
|
|
325
|
+
* error taxonomy. These arrive over an HTTP 200 body, so there is no status
|
|
326
|
+
* code to classify from; the event's own `code` is classified instead.
|
|
306
327
|
*
|
|
307
328
|
* Two payload shapes exist and both are accepted: the standalone `error`
|
|
308
329
|
* event carries `code`/`message` at its root, while `response.failed` nests
|
|
309
|
-
* them under `response.error
|
|
310
|
-
* an error type, so it is never
|
|
330
|
+
* them under `response.error` and some servers nest them under `error`. The
|
|
331
|
+
* root `type` is the event name rather than an error type, so it is never
|
|
332
|
+
* read as one. OpenRouter's typed classification comes from the embedded
|
|
333
|
+
* response (`response.failed`) or the error's metadata (untyped events).
|
|
311
334
|
*/
|
|
312
335
|
mapStreamError(event, context) {
|
|
313
336
|
const nested = event.error ?? event.response?.error;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
337
|
+
return this.mapReportedError({
|
|
338
|
+
message: nested?.message ?? event.message,
|
|
339
|
+
type: nested?.type,
|
|
340
|
+
code: nested?.code ?? event.code
|
|
341
|
+
}, "OpenAI Responses stream reported a failure.", context, event.response?.error_type ?? nested?.metadata?.error_type);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Classifies a failure reported in a body rather than by the HTTP status: a
|
|
345
|
+
* failed stream event or a 2xx run body whose status is `failed`. The most
|
|
346
|
+
* specific signal wins:
|
|
347
|
+
* 1. OpenRouter's typed `error_type`, classified by the HTTP status it
|
|
348
|
+
* documents for that type (see `openRouterErrorTypeStatus`);
|
|
349
|
+
* 2. an integer `code`, which is an HTTP status (OpenRouter's convention) and
|
|
350
|
+
* is classified exactly as `mapHttpError` classifies that status;
|
|
351
|
+
* 3. a string code, by OpenAI's names.
|
|
352
|
+
* Anything unrecognized is a transient `Unavailable`, since the server did
|
|
353
|
+
* accept the request.
|
|
354
|
+
*/
|
|
355
|
+
mapReportedError(reported, fallbackMessage, context, errorType) {
|
|
356
|
+
const message = typeof reported?.message === "string" && reported.message.length > 0
|
|
357
|
+
? reported.message
|
|
358
|
+
: fallbackMessage;
|
|
359
|
+
const typedStatus = openRouterErrorTypeStatus(errorType);
|
|
360
|
+
const details = {
|
|
361
|
+
errorType: typedStatus !== undefined ? errorType : reported?.type,
|
|
362
|
+
errorCode: getErrorCode(reported?.code)
|
|
363
|
+
};
|
|
317
364
|
const params = { providerId: this.id, runId: context.runId, details };
|
|
365
|
+
if (typedStatus !== undefined) {
|
|
366
|
+
return this.errorForStatus(typedStatus, message, params);
|
|
367
|
+
}
|
|
368
|
+
const code = reported?.code;
|
|
369
|
+
if (typeof code === "number" && Number.isInteger(code)) {
|
|
370
|
+
return this.errorForStatus(code, message, params);
|
|
371
|
+
}
|
|
318
372
|
switch (code) {
|
|
319
373
|
case "rate_limit_exceeded":
|
|
320
374
|
return createRateLimited(message, { ...params, retryable: true });
|
|
321
375
|
case "invalid_api_key":
|
|
322
376
|
case "authentication_error":
|
|
323
377
|
return createAuthError(message, params);
|
|
378
|
+
// The request itself was rejected; repeating it cannot succeed.
|
|
379
|
+
case "invalid_prompt":
|
|
380
|
+
case "image_content_policy_violation":
|
|
381
|
+
return createInternal(message, params);
|
|
324
382
|
default:
|
|
325
383
|
return createUnavailable(message, { ...params, retryable: true });
|
|
326
384
|
}
|
|
@@ -386,66 +444,64 @@ export class OpenAIResponsesProvider {
|
|
|
386
444
|
return body;
|
|
387
445
|
}
|
|
388
446
|
/**
|
|
389
|
-
* Maps an OpenAI HTTP error response onto the IndeRun error taxonomy
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
* - `408` / `504` → `Timeout`
|
|
393
|
-
* - `409` / `5xx` → `Unavailable`
|
|
394
|
-
* - any other non-2xx status → `Internal`
|
|
447
|
+
* Maps an OpenAI HTTP error response onto the IndeRun error taxonomy by its
|
|
448
|
+
* status (see `errorForStatus`), honoring `Retry-After` on whichever status
|
|
449
|
+
* carries it.
|
|
395
450
|
*/
|
|
396
451
|
mapHttpError(status, statusText, headers, body, context) {
|
|
397
452
|
const error = parseJson(body);
|
|
398
453
|
const message = getOpenAIErrorMessage(error, status, statusText);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
454
|
+
const params = {
|
|
455
|
+
providerId: this.id,
|
|
456
|
+
runId: context.runId,
|
|
457
|
+
details: {
|
|
458
|
+
status,
|
|
459
|
+
statusText,
|
|
460
|
+
errorType: getString(error.error?.type),
|
|
461
|
+
errorCode: getErrorCode(error.error?.code)
|
|
462
|
+
}
|
|
404
463
|
};
|
|
464
|
+
return this.errorForStatus(status, message, params, parseRetryAfterMs(headers));
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* The status classification shared by HTTP errors and numeric in-body codes:
|
|
468
|
+
* - `401` / `403` → `AuthError`
|
|
469
|
+
* - `402` → `Unavailable`, not retryable: the account is out of credits, so
|
|
470
|
+
* this target cannot serve until someone tops it up. A 402 that carries
|
|
471
|
+
* `Retry-After` is the exception — OpenRouter documents it for its
|
|
472
|
+
* in-flight budget limit, a wait-and-retry case — and is `RateLimited`. Fallback is unaffected
|
|
473
|
+
* either way: the engine moves on to the next target whatever the class.
|
|
474
|
+
* - `429` → `RateLimited`
|
|
475
|
+
* - `408` / `504` → `Timeout`
|
|
476
|
+
* - `409` / `5xx` → `Unavailable`
|
|
477
|
+
* - any other status → `Internal`
|
|
478
|
+
*
|
|
479
|
+
* `retryAfterMs` travels on every retryable class; it is absent for numeric
|
|
480
|
+
* in-body codes, which have no header to read it from.
|
|
481
|
+
*/
|
|
482
|
+
errorForStatus(status, message, params, retryAfterMs) {
|
|
483
|
+
const retry = { ...params, retryable: true };
|
|
484
|
+
if (retryAfterMs !== undefined) {
|
|
485
|
+
retry.retryAfterMs = retryAfterMs;
|
|
486
|
+
}
|
|
405
487
|
if (status === 401 || status === 403) {
|
|
406
|
-
return createAuthError(message,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
488
|
+
return createAuthError(message, params);
|
|
489
|
+
}
|
|
490
|
+
if (status === 402) {
|
|
491
|
+
return retryAfterMs === undefined
|
|
492
|
+
? createUnavailable(message, { ...params, retryable: false })
|
|
493
|
+
: createRateLimited(message, retry);
|
|
411
494
|
}
|
|
412
495
|
if (status === 429) {
|
|
413
|
-
|
|
414
|
-
const params = {
|
|
415
|
-
providerId: this.id,
|
|
416
|
-
runId: context.runId,
|
|
417
|
-
retryable: true,
|
|
418
|
-
details
|
|
419
|
-
};
|
|
420
|
-
if (retryAfterMs !== undefined) {
|
|
421
|
-
return createRateLimited(message, {
|
|
422
|
-
...params,
|
|
423
|
-
retryAfterMs
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
return createRateLimited(message, params);
|
|
496
|
+
return createRateLimited(message, retry);
|
|
427
497
|
}
|
|
428
498
|
if (status === 408 || status === 504) {
|
|
429
|
-
return createTimeout(message,
|
|
430
|
-
providerId: this.id,
|
|
431
|
-
runId: context.runId,
|
|
432
|
-
retryable: true,
|
|
433
|
-
details
|
|
434
|
-
});
|
|
499
|
+
return createTimeout(message, retry);
|
|
435
500
|
}
|
|
436
501
|
if (status === 409 || status >= 500) {
|
|
437
|
-
return createUnavailable(message,
|
|
438
|
-
providerId: this.id,
|
|
439
|
-
runId: context.runId,
|
|
440
|
-
retryable: true,
|
|
441
|
-
details
|
|
442
|
-
});
|
|
502
|
+
return createUnavailable(message, retry);
|
|
443
503
|
}
|
|
444
|
-
return createInternal(message,
|
|
445
|
-
providerId: this.id,
|
|
446
|
-
runId: context.runId,
|
|
447
|
-
details
|
|
448
|
-
});
|
|
504
|
+
return createInternal(message, params);
|
|
449
505
|
}
|
|
450
506
|
}
|
|
451
507
|
function createInput(request) {
|
|
@@ -485,6 +541,15 @@ function getFinishReason(response) {
|
|
|
485
541
|
}
|
|
486
542
|
return "stop";
|
|
487
543
|
}
|
|
544
|
+
/**
|
|
545
|
+
* The model id the Responses API echoes on the response object. It names the model that
|
|
546
|
+
* served the request, which can be a dated snapshot of the configured alias.
|
|
547
|
+
*/
|
|
548
|
+
function getReportedModel(responseBody) {
|
|
549
|
+
return typeof responseBody.model === "string" && responseBody.model.length > 0
|
|
550
|
+
? responseBody.model
|
|
551
|
+
: undefined;
|
|
552
|
+
}
|
|
488
553
|
function getUsage(response) {
|
|
489
554
|
const usage = {};
|
|
490
555
|
if (typeof response.usage?.input_tokens === "number") {
|
|
@@ -545,3 +610,48 @@ function isRecord(value) {
|
|
|
545
610
|
function getString(value) {
|
|
546
611
|
return typeof value === "string" ? value : undefined;
|
|
547
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* The HTTP status OpenRouter documents for each typed `error_type`, so a typed
|
|
615
|
+
* failure is classified exactly like the equivalent HTTP error. Refusals and
|
|
616
|
+
* content-policy violations are documented as 403 but are not credential
|
|
617
|
+
* failures, so they classify as a rejected request (400). Unknown types return
|
|
618
|
+
* `undefined` and the native `code` decides instead.
|
|
619
|
+
*/
|
|
620
|
+
function openRouterErrorTypeStatus(errorType) {
|
|
621
|
+
return typeof errorType === "string" && Object.hasOwn(OPENROUTER_ERROR_TYPE_STATUS, errorType)
|
|
622
|
+
? OPENROUTER_ERROR_TYPE_STATUS[errorType]
|
|
623
|
+
: undefined;
|
|
624
|
+
}
|
|
625
|
+
const OPENROUTER_ERROR_TYPE_STATUS = {
|
|
626
|
+
authentication: 401,
|
|
627
|
+
payment_required: 402,
|
|
628
|
+
permission_denied: 403,
|
|
629
|
+
rate_limit_exceeded: 429,
|
|
630
|
+
timeout: 504,
|
|
631
|
+
provider_unavailable: 502,
|
|
632
|
+
provider_overloaded: 503,
|
|
633
|
+
server: 500,
|
|
634
|
+
unmapped: 500,
|
|
635
|
+
not_found: 404,
|
|
636
|
+
image_not_found: 404,
|
|
637
|
+
precondition_failed: 412,
|
|
638
|
+
payload_too_large: 413,
|
|
639
|
+
unprocessable: 422,
|
|
640
|
+
content_policy_violation: 400,
|
|
641
|
+
refusal: 400,
|
|
642
|
+
context_length_exceeded: 400,
|
|
643
|
+
max_tokens_exceeded: 400,
|
|
644
|
+
token_limit_exceeded: 400,
|
|
645
|
+
string_too_long: 400,
|
|
646
|
+
invalid_request: 400,
|
|
647
|
+
invalid_prompt: 400,
|
|
648
|
+
invalid_image: 400,
|
|
649
|
+
image_too_large: 400,
|
|
650
|
+
image_too_small: 400,
|
|
651
|
+
unsupported_image_format: 400,
|
|
652
|
+
image_download_failed: 400
|
|
653
|
+
};
|
|
654
|
+
/** An error `code` as a detail string, whether the server sent a name or a numeric status. */
|
|
655
|
+
function getErrorCode(value) {
|
|
656
|
+
return typeof value === "number" ? String(value) : getString(value);
|
|
657
|
+
}
|
package/dist/web-sdk.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type CreateBrowserHostServicesOptions } from "./core/browser-host.js";
|
|
2
2
|
import { IndeRun } from "./core/engine.js";
|
|
3
|
+
import type { ModelProfiles } from "./core/model-profiles.js";
|
|
3
4
|
import { type OnnxProviderOptions } from "./providers/onnx/provider.js";
|
|
4
5
|
import { type OpenAIProviderOptions } from "./providers/openai/provider.js";
|
|
5
6
|
import { type SystemModelProviderOptions } from "./providers/system-model/provider.js";
|
|
@@ -8,9 +9,11 @@ import { type SystemModelProviderOptions } from "./providers/system-model/provid
|
|
|
8
9
|
*/
|
|
9
10
|
export interface CreateIndeRunWebOptions {
|
|
10
11
|
/**
|
|
11
|
-
* OpenAI Responses provider configuration registered by the factory.
|
|
12
|
+
* OpenAI Responses provider configuration registered by the factory. One target is
|
|
13
|
+
* registered per entry in `models`, with id `<id>:<model>` (`id` defaults to
|
|
14
|
+
* `"openai"`), so model profiles can list each model separately.
|
|
12
15
|
*/
|
|
13
|
-
openAI?:
|
|
16
|
+
openAI?: OpenAIFactoryOptions;
|
|
14
17
|
/**
|
|
15
18
|
* Web ONNX Runtime provider configuration registered by the factory, for developer-supplied
|
|
16
19
|
* local models. Registering it is what makes `constraints.privacy = "local_required"` routable.
|
|
@@ -27,14 +30,29 @@ export interface CreateIndeRunWebOptions {
|
|
|
27
30
|
*/
|
|
28
31
|
hostServices?: CreateBrowserHostServicesOptions;
|
|
29
32
|
/**
|
|
30
|
-
* Explicit opt-in for calling
|
|
33
|
+
* Explicit opt-in for calling a public Responses endpoint directly from this Web SDK factory —
|
|
34
|
+
* OpenAI's or OpenRouter's — with browser-held credentials.
|
|
31
35
|
*
|
|
32
36
|
* Browser apps should leave this unset and use a proxy endpoint with `openAI.auth = "none"` instead.
|
|
33
37
|
* Direct calls can expose credentials to client-side code and are appropriate only for controlled
|
|
34
38
|
* non-production environments.
|
|
35
39
|
*/
|
|
36
40
|
allowDirectOpenAIEndpoint?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Named model profiles requests can route within. Each maps a profile name to an
|
|
43
|
+
* ordered list of target ids, for example `{ smart: ["openai:gpt-5.2"] }`.
|
|
44
|
+
*/
|
|
45
|
+
modelProfiles?: ModelProfiles;
|
|
37
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* OpenAI options for {@link createIndeRunWeb}: the provider options with `models` in
|
|
49
|
+
* place of the single `model`. The first model is not special; every model becomes
|
|
50
|
+
* its own target.
|
|
51
|
+
*/
|
|
52
|
+
export type OpenAIFactoryOptions = Omit<OpenAIProviderOptions, "model"> & {
|
|
53
|
+
/** OpenAI model ids to register, one target each. */
|
|
54
|
+
models: readonly string[];
|
|
55
|
+
};
|
|
38
56
|
/**
|
|
39
57
|
* Creates a Web SDK instance with the configured providers registered.
|
|
40
58
|
*
|
package/dist/web-sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-sdk.d.ts","sourceRoot":"","sources":["../src/web-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,sCAAsC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC
|
|
1
|
+
{"version":3,"file":"web-sdk.d.ts","sourceRoot":"","sources":["../src/web-sdk.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gCAAgC,EACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAA0B,KAAK,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAChG,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,sCAAsC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;OAGG;IACH,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,0BAA0B,CAAC;IACzC;;OAEG;IACH,YAAY,CAAC,EAAE,gCAAgC,CAAC;IAChD;;;;;;;OAOG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;OAGG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,qBAAqB,EAAE,OAAO,CAAC,GAAG;IACxE,qDAAqD;IACrD,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CA+B1E"}
|
package/dist/web-sdk.js
CHANGED
|
@@ -24,7 +24,13 @@ export function createIndeRunWeb(options) {
|
|
|
24
24
|
const registry = new ProviderRegistry();
|
|
25
25
|
if (options.openAI) {
|
|
26
26
|
assertSafeOpenAIEndpoint(options.openAI, options.allowDirectOpenAIEndpoint);
|
|
27
|
-
|
|
27
|
+
const { models, id = "openai", ...shared } = options.openAI;
|
|
28
|
+
if (models.length === 0) {
|
|
29
|
+
throw new Error("createIndeRunWeb requires openAI.models to list at least one model.");
|
|
30
|
+
}
|
|
31
|
+
for (const model of models) {
|
|
32
|
+
registry.register(new OpenAIResponsesProvider({ ...shared, id: `${id}:${model}`, model }));
|
|
33
|
+
}
|
|
28
34
|
}
|
|
29
35
|
if (options.onnx) {
|
|
30
36
|
registry.register(new OnnxRuntimeWebProvider(options.onnx));
|
|
@@ -32,17 +38,19 @@ export function createIndeRunWeb(options) {
|
|
|
32
38
|
if (options.systemModel) {
|
|
33
39
|
registry.register(new SystemModelWebProvider(options.systemModel));
|
|
34
40
|
}
|
|
35
|
-
return new IndeRun(registry, createBrowserHostServices(options.hostServices)
|
|
41
|
+
return new IndeRun(registry, createBrowserHostServices(options.hostServices), {
|
|
42
|
+
...(options.modelProfiles ? { modelProfiles: options.modelProfiles } : {})
|
|
43
|
+
});
|
|
36
44
|
}
|
|
37
45
|
function assertSafeOpenAIEndpoint(openAI, allowDirectOpenAIEndpoint) {
|
|
38
46
|
const endpointUrl = openAI.endpointUrl ?? DEFAULT_OPENAI_RESPONSES_ENDPOINT;
|
|
39
47
|
const auth = openAI.auth ?? "authContextRef";
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
48
|
+
const isDirectPublicEndpoint = isPublicResponsesEndpoint(endpointUrl);
|
|
49
|
+
if (isDirectPublicEndpoint && auth !== "none" && !allowDirectOpenAIEndpoint) {
|
|
42
50
|
throw new Error('createIndeRunWeb is proxy-first for browser safety. Configure openAI.endpointUrl to a server-side proxy with auth: "none", or set allowDirectOpenAIEndpoint: true for controlled direct OpenAI calls.');
|
|
43
51
|
}
|
|
44
52
|
}
|
|
45
|
-
function
|
|
53
|
+
function isPublicResponsesEndpoint(endpointUrl) {
|
|
46
54
|
let parsed;
|
|
47
55
|
try {
|
|
48
56
|
parsed = new URL(endpointUrl);
|
|
@@ -51,5 +59,14 @@ function isOpenAIResponsesEndpoint(endpointUrl) {
|
|
|
51
59
|
return false;
|
|
52
60
|
}
|
|
53
61
|
const pathname = parsed.pathname.replace(/\/+$/u, "");
|
|
54
|
-
|
|
62
|
+
const endpoint = `${parsed.hostname.toLowerCase()}${pathname}`;
|
|
63
|
+
return PUBLIC_RESPONSES_ENDPOINTS.includes(endpoint);
|
|
55
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Hosted Responses endpoints whose credentials are developer-owned API keys, which the
|
|
67
|
+
* factory keeps out of browser code unless `allowDirectOpenAIEndpoint` says otherwise.
|
|
68
|
+
*/
|
|
69
|
+
const PUBLIC_RESPONSES_ENDPOINTS = [
|
|
70
|
+
"api.openai.com/v1/responses",
|
|
71
|
+
"openrouter.ai/api/v1/responses"
|
|
72
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@independo/inderun-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-rc.2",
|
|
4
4
|
"description": "On-device AI with automatic cloud fallback for web apps. Runs ONNX Runtime or the Chrome Prompt API in the browser and falls back to an OpenAI-compatible endpoint, with streaming and cancellation behind one API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"types": "./dist/index.d.ts",
|
|
48
48
|
"default": "./dist/index.js"
|
|
49
49
|
},
|
|
50
|
+
"./provider": {
|
|
51
|
+
"types": "./dist/provider.d.ts",
|
|
52
|
+
"default": "./dist/provider.js"
|
|
53
|
+
},
|
|
50
54
|
"./openai": {
|
|
51
55
|
"types": "./dist/openai.d.ts",
|
|
52
56
|
"default": "./dist/openai.js"
|
|
@@ -68,8 +72,8 @@
|
|
|
68
72
|
"README.md"
|
|
69
73
|
],
|
|
70
74
|
"dependencies": {
|
|
71
|
-
"@independo/inderun-contracts": "^0.
|
|
72
|
-
"@independo/inderun-route-core-wasm": "^0.
|
|
75
|
+
"@independo/inderun-contracts": "^0.4.0-rc.2",
|
|
76
|
+
"@independo/inderun-route-core-wasm": "^0.4.0-rc.2"
|
|
73
77
|
},
|
|
74
78
|
"peerDependencies": {
|
|
75
79
|
"@huggingface/transformers": "^4.0.0"
|