@urun-sh/openai 0.5.6 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ResponsesClient-BE3-hx3m.d.ts → ResponsesClient-CSSrOYD8.d.ts} +5 -1
- package/dist/{ResponsesClient-DjZWjlFH.d.cts → ResponsesClient-_OZERUjH.d.cts} +9 -1
- package/dist/{chunk-7M6LY6DR.js → chunk-3JWYIKHM.js} +1 -1
- package/dist/chunk-45FJ5GTP.js +7 -0
- package/dist/{chunk-BSHT6RZZ.js → chunk-6M5JK4YC.js} +1 -1
- package/dist/{chunk-5HKWNK3O.js → chunk-K23AZPI4.js} +1 -1
- package/dist/chunk-QTVTCMJU.js +1 -0
- package/dist/chunk-TDRZZGUK.js +1 -0
- package/dist/chunk-TPH77ZOW.js +58 -0
- package/dist/chunk-WCMX5VOF.js +4 -0
- package/dist/chunk-YXK42SKC.js +1 -0
- package/dist/gemini-live.cjs +2 -2
- package/dist/gemini-live.d.cts +64 -7
- package/dist/gemini-live.d.ts +27 -7
- package/dist/gemini-live.js +1 -1
- package/dist/hosted/bin.cjs +37 -35
- package/dist/hosted/bin.js +1 -1
- package/dist/hosted/index.cjs +33 -31
- package/dist/hosted/index.d.cts +70 -4
- package/dist/hosted/index.d.ts +21 -4
- package/dist/hosted/index.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/pi-extension/index.cjs +7 -7
- package/dist/pi-extension/index.d.cts +2 -2
- package/dist/pi-extension/index.d.ts +2 -2
- package/dist/pi-extension/index.js +1 -1
- package/dist/pi-extension/standalone.cjs +53 -53
- package/dist/proxy/cli.cjs +47 -45
- package/dist/proxy/cli.js +15 -14
- package/dist/proxy/index.cjs +28 -27
- package/dist/proxy/index.d.cts +54 -16
- package/dist/proxy/index.d.ts +18 -11
- package/dist/proxy/index.js +1 -1
- package/dist/{responses-turn-tNUHq5b0.d.cts → responses-turn-BfdBbey8.d.cts} +610 -23
- package/dist/{responses-turn-Dr37N3kH.d.ts → responses-turn-CdhIre_a.d.ts} +173 -7
- package/dist/{translator-Bh0Bp_Ie.d.cts → translator-CO8W_hmJ.d.cts} +1 -1
- package/dist/{translator-BCoRFaTs.d.ts → translator-Ddd65sXR.d.ts} +1 -1
- package/dist/{types-lsVTbNcH.d.cts → types-CHPtJx6d.d.cts} +13 -0
- package/dist/{types-lsVTbNcH.d.ts → types-CHPtJx6d.d.ts} +4 -0
- package/dist/{video-out-CCksIcj8.d.cts → video-out-BsuLqlID.d.cts} +1 -1
- package/dist/{video-out-IGQ4YQ-c.d.ts → video-out-Cawtm7SF.d.ts} +1 -1
- package/package.json +4 -2
- package/dist/chunk-2T2YYBVX.js +0 -1
- package/dist/chunk-3WAJD62J.js +0 -4
- package/dist/chunk-FP4RSAIE.js +0 -1
- package/dist/chunk-HR5H6S7L.js +0 -1
- package/dist/chunk-NY23USZF.js +0 -57
- package/dist/chunk-VLRMJRLS.js +0 -6
- /package/dist/{chunk-5CCJUNH7.js → chunk-YSFSRI3D.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as CatalogRow } from './models-DUdx_Y6X.cjs';
|
|
2
2
|
import { IncomingMessage } from 'node:http';
|
|
3
|
-
import { a as AudioBridge, h as VideoFrameLane, j as VideoOutLane } from './video-out-
|
|
3
|
+
import { a as AudioBridge, h as VideoFrameLane, j as VideoOutLane } from './video-out-BsuLqlID.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* WHAT a proxy serves — the `identity` block on `GET /stats`. Reuse of a
|
|
@@ -183,7 +183,9 @@ interface InferenceRecord {
|
|
|
183
183
|
* per-request resolver over the catalog edge function's `shared` block:
|
|
184
184
|
*
|
|
185
185
|
* { shared_org_id, endpoints: [{ model_id, variant, gpu_spec, app_slug,
|
|
186
|
-
* function, warm
|
|
186
|
+
* function, warm, ready, ready_runtimes,
|
|
187
|
+
* ready_since }],
|
|
188
|
+
* regions: [...], compliance: { zdr, hipaa } }
|
|
187
189
|
*
|
|
188
190
|
* Every function here is PURE and synchronous over the parsed block: the
|
|
189
191
|
* catalog fetch (U5's edge-function surface) is a separate seam and is NOT
|
|
@@ -217,11 +219,50 @@ interface SharedEndpointRow {
|
|
|
217
219
|
* row. Exactly one per model_id — zero or two is a loud catalog defect.
|
|
218
220
|
*/
|
|
219
221
|
compat_default?: boolean | null;
|
|
222
|
+
/**
|
|
223
|
+
* The catalog row's output-free opt-in (owner decision 2026-09-21, the
|
|
224
|
+
* `reflex-latest:bf16` decision lane): a ZERO per-token OUTPUT rate is a
|
|
225
|
+
* deliberate published price for this endpoint — the model is a
|
|
226
|
+
* non-generative decision/action surface whose tokens are billed on
|
|
227
|
+
* input alone, so the OpenRouter document emits `cost_usd: '0'`
|
|
228
|
+
* completion pricing instead of refusing it. Absent (pre-flag edge
|
|
229
|
+
* function) and null both mean NOT opted in — the zero-output refusal
|
|
230
|
+
* stands.
|
|
231
|
+
*/
|
|
232
|
+
output_free?: boolean | null;
|
|
233
|
+
/**
|
|
234
|
+
* Runtime-plane readiness (up-debounce, down-immediate upstream). ABSENT
|
|
235
|
+
* on blocks from an edge function that predates the readiness seam — the
|
|
236
|
+
* OpenRouter document builder treats that as NOT ready, loudly (see
|
|
237
|
+
* openrouter-doc.ts); `null` is likewise unknown, not ready.
|
|
238
|
+
*/
|
|
239
|
+
ready?: boolean | null;
|
|
240
|
+
/** How many ready runtimes stand behind `ready` (null when unknown). */
|
|
241
|
+
ready_runtimes?: number | null;
|
|
242
|
+
/** ISO 8601 instant the endpoint became (and stayed) ready; null when not. */
|
|
243
|
+
ready_since?: string | null;
|
|
220
244
|
}
|
|
221
245
|
/** The catalog edge function's `shared` block. */
|
|
222
246
|
interface SharedCatalogBlock {
|
|
223
247
|
shared_org_id: string;
|
|
224
248
|
endpoints: SharedEndpointRow[];
|
|
249
|
+
/**
|
|
250
|
+
* Deployment-topology region codes (AWS-style, e.g. 'us-west-2') — the
|
|
251
|
+
* physical surface the shared endpoints serve from. Absent/empty means the
|
|
252
|
+
* topology is undeclared.
|
|
253
|
+
*/
|
|
254
|
+
regions?: string[] | null;
|
|
255
|
+
/**
|
|
256
|
+
* Operator-declared data-handling posture (zero data retention, HIPAA).
|
|
257
|
+
* Present ONLY when an operator has declared BOTH flags — and nothing
|
|
258
|
+
* else: the wire contract is exactly {zdr, hipaa}, and the parser treats
|
|
259
|
+
* a further key as a catalog defect. ABSENT means UNDECLARED — never a
|
|
260
|
+
* default, because these are claims about where customer data goes.
|
|
261
|
+
*/
|
|
262
|
+
compliance?: {
|
|
263
|
+
zdr: boolean;
|
|
264
|
+
hipaa: boolean;
|
|
265
|
+
} | null;
|
|
225
266
|
}
|
|
226
267
|
|
|
227
268
|
/**
|
|
@@ -371,32 +412,125 @@ interface ModelPriceRow {
|
|
|
371
412
|
* source is model-prices.ts — the SAME per-token join the OpenAI listing
|
|
372
413
|
* uses ({@link pricingFor}); the per-million rate is shifted to per-token
|
|
373
414
|
* on its decimal string, never by float division. An entry with no
|
|
374
|
-
* per-token row — today
|
|
415
|
+
* per-token row — today MOST of them, because `model_prices` prices GPU
|
|
375
416
|
* minutes (`per_minute` et al.), which is not evidence about tokens —
|
|
376
417
|
* carries NO pricing array at all: the schema's rule is "a modality with
|
|
377
418
|
* no pricing array is simply unpriced". Never a zero, never a GPU-minute
|
|
378
419
|
* rate relabelled as a token rate (ENG-317 owns the human pricing
|
|
379
|
-
* decision that would make such rows exist)
|
|
420
|
+
* decision that would make such rows exist) — with ONE owner-ratified
|
|
421
|
+
* exception: `reflex-latest:bf16` is a non-generative decision lane whose
|
|
422
|
+
* catalog row declares `output_free` (owner decision 2026-09-21), so its
|
|
423
|
+
* ZERO completion rate is the published price (`cost_usd: '0'`), billed
|
|
424
|
+
* on input alone. The zero reaches this builder only through
|
|
425
|
+
* model-prices.ts's allowlist (routing resolves prices against the
|
|
426
|
+
* shared block's `output_free` set); the builder itself just renders
|
|
427
|
+
* whatever a resolved price says.
|
|
380
428
|
*
|
|
381
429
|
* The full closed-value-domain schema ships as OpenAPI 3.1 at
|
|
382
430
|
* openrouter.ai/docs/assets/provider-monitor-schema-v2.openapi.json.
|
|
431
|
+
*
|
|
432
|
+
* THE OPERATIONAL FIELDS (schema 2.4, the "launch control" surface):
|
|
433
|
+
* - `is_ready` is the shared endpoint's SELLABLE readiness:
|
|
434
|
+
* `runtime ready AND priced` — `endpoint.ready === true` AND a per-token
|
|
435
|
+
* price row for this exact (model_id, variant). Priced means the price
|
|
436
|
+
* book RESOLVES a pair for the SKU: an output-free row's `{input: >0,
|
|
437
|
+
* output: 0}` is a resolved pair (model-prices.ts's allowlist decides,
|
|
438
|
+
* from the shared block's `output_free` flag), so an input-priced,
|
|
439
|
+
* output-free, runtime-ready endpoint IS ready. An unpriced SKU is
|
|
440
|
+
* never sellable: OpenRouter's `is_ready: false` keeps an endpoint
|
|
441
|
+
* hidden, and we must never expose a model we cannot bill, so a ready
|
|
442
|
+
* runtime without a price is emitted NOT ready. Runtime readiness is
|
|
443
|
+
* THE ONE DELIBERATELY CONSERVATIVE DEFAULT
|
|
444
|
+
* in this module: a block whose endpoints carry NO `ready` field at all
|
|
445
|
+
* predates the readiness seam (the catalog edge function is not yet
|
|
446
|
+
* redeployed), and UNKNOWN READINESS IS NOT READINESS — every such
|
|
447
|
+
* endpoint is declared `is_ready: false` AND one loud warning per
|
|
448
|
+
* document build names the missing seam, so the default is visible,
|
|
449
|
+
* never silent. A `null` ready is likewise unknown, not ready. Endpoints
|
|
450
|
+
* of the SAME identity follow the existing dedup rule (first occurrence
|
|
451
|
+
* in the deterministic shared-block order wins).
|
|
452
|
+
* - `is_free` is `false` for every declared model: we bill. An unpriced row
|
|
453
|
+
* is UNPRICED, not free — `is_free: true` would mint a `:free` SKU whose
|
|
454
|
+
* every price OpenRouter ignores. The same holds for an output-free row:
|
|
455
|
+
* its input side bills, so it is not a `:free` SKU either — the zero
|
|
456
|
+
* lives in the completion `cost_usd`, nowhere else.
|
|
457
|
+
* - `deprecation_date` is OMITTED: the schema makes it optional and no
|
|
458
|
+
* shared endpoint has a deprecation schedule to declare.
|
|
459
|
+
* - `datacenters` maps the block's deployment-topology region codes
|
|
460
|
+
* (AWS-style) to the schema's `{ country_code, region }` elements through
|
|
461
|
+
* an EXPLICIT region→country table (a country cannot be derived from a
|
|
462
|
+
* prefix — sa-east-1 is BR). Unmappable codes are omitted and WARNED,
|
|
463
|
+
* never guessed; absent/empty topology omits the field.
|
|
464
|
+
* - `compliance` is emitted ONLY when the block's `compliance` is present:
|
|
465
|
+
* zdr/hipaa are claims about where customer data goes, DECLARED by an
|
|
466
|
+
* operator — never inferred, never defaulted, and never half-emitted.
|
|
467
|
+
* - `tokenizer` names the model family the catalog row's `hf_repo`
|
|
468
|
+
* carries (e.g. `Inferact/Qwen3.8-27B-NVFP4` → `Qwen`) through an
|
|
469
|
+
* explicit, BOUNDED table over the repo's model-NAME tokens — whole
|
|
470
|
+
* tokens only, version-aware for the families whose generations differ
|
|
471
|
+
* (llama-2 is not llama-3); a repo that names no known family OMITS the
|
|
472
|
+
* field.
|
|
383
473
|
*/
|
|
384
474
|
|
|
385
475
|
type OpenRouterProviderDoc = {
|
|
386
476
|
data: OpenRouterProviderModel[];
|
|
387
477
|
};
|
|
478
|
+
/** One schema-2.4 `datacenters` element: ISO 3166-1 alpha-2 country + region. */
|
|
479
|
+
interface OpenRouterDatacenter {
|
|
480
|
+
country_code: string;
|
|
481
|
+
region?: string;
|
|
482
|
+
}
|
|
483
|
+
/** The operator-declared data-handling posture (schema-2.4 `compliance`). */
|
|
484
|
+
interface OpenRouterCompliance {
|
|
485
|
+
zdr: boolean;
|
|
486
|
+
hipaa: boolean;
|
|
487
|
+
}
|
|
388
488
|
interface OpenRouterProviderModel {
|
|
389
489
|
schema_version: '2.4';
|
|
390
|
-
/**
|
|
490
|
+
/**
|
|
491
|
+
* The EXACT id OpenRouter sends back as `model`: the verbatim
|
|
492
|
+
* `<model_id>:<variant>` external identity (see MODEL-IDENTITY.md) — never
|
|
493
|
+
* the deployment slug.
|
|
494
|
+
*/
|
|
391
495
|
id: string;
|
|
392
496
|
name: string;
|
|
393
497
|
created: number;
|
|
394
498
|
/** Valid enum: int4|int8|fp4|mxfp4|nvfp4|fp6|fp8|mxfp8|fp16|bf16|fp32|null. */
|
|
395
499
|
quantization: string | null;
|
|
500
|
+
/** Tokenizer family name (e.g. 'Qwen'); omitted when the repo names none. */
|
|
501
|
+
tokenizer?: string;
|
|
396
502
|
description: string;
|
|
397
503
|
hugging_face_id: string;
|
|
504
|
+
/** SELLABLE readiness — `endpoint.ready === true` AND a per-token price. */
|
|
505
|
+
is_ready: boolean;
|
|
506
|
+
/** `false` always: we bill; an unpriced row is unpriced, not free. */
|
|
507
|
+
is_free: boolean;
|
|
398
508
|
input_modalities: Array<Record<string, unknown>>;
|
|
399
509
|
output_modalities: Array<Record<string, unknown>>;
|
|
510
|
+
/** Physical serving topology; omitted when the block declares none. */
|
|
511
|
+
datacenters?: OpenRouterDatacenter[];
|
|
512
|
+
/** Operator-declared posture; omitted entirely when undeclared. */
|
|
513
|
+
compliance?: OpenRouterCompliance;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* The resolution of ONE tts model's voice set — ONE answer BOTH consumers
|
|
517
|
+
* read, so the lane can never drift from the listing:
|
|
518
|
+
* - `published` is the set the model's /v1/models entry carries ([] =
|
|
519
|
+
* publish none).
|
|
520
|
+
* - `defect` is non-null ONLY when the catalog PRESENTED
|
|
521
|
+
* `engine_args.voices` that could not be vouched (empty, malformed, or
|
|
522
|
+
* not an identical non-empty string array on every GPU placement). The
|
|
523
|
+
* publish side keeps its long-standing posture through a defect (the
|
|
524
|
+
* card list for the qwen3 class, nothing otherwise — the listing is a
|
|
525
|
+
* best-effort enrichment), but the speech lane must never silently
|
|
526
|
+
* validate against the card's nine over a catalog that SPOKE — it fails
|
|
527
|
+
* loud (500) on `defect` instead.
|
|
528
|
+
*/
|
|
529
|
+
interface SpeechVoicesResolution {
|
|
530
|
+
/** The voices the /v1/models entry publishes for this model ([] = none). */
|
|
531
|
+
readonly published: readonly string[];
|
|
532
|
+
/** Why the catalog's own voices value could not be vouched, when it presented one. */
|
|
533
|
+
readonly defect: string | null;
|
|
400
534
|
}
|
|
401
535
|
|
|
402
536
|
/**
|
|
@@ -419,11 +553,12 @@ interface OpenRouterProviderModel {
|
|
|
419
553
|
*
|
|
420
554
|
* RESOLUTION ORDER (one canonical path, documented end to end):
|
|
421
555
|
* 1. model absent / "urun" / an alias of the startup app → DEFAULT app.
|
|
422
|
-
* 2. exact slug match on a deployed
|
|
556
|
+
* 2. exact slug match on a deployed app that declares serves="openai" →
|
|
557
|
+
* that app.
|
|
423
558
|
* 3. catalog-id form matching exactly one deployed app → that app
|
|
424
559
|
* (two or more candidates → loud ambiguity error naming them).
|
|
425
|
-
* 4. the name maps to an org app that
|
|
426
|
-
*
|
|
560
|
+
* 4. the name maps to an org app that has NO function declaring the
|
|
561
|
+
* serve protocol (`serves="openai"`) → loud 404
|
|
427
562
|
* naming `urun serve <model>` and the available models.
|
|
428
563
|
* 5. the name matches a catalog row but no deployed app → loud 404
|
|
429
564
|
* naming `urun serve <id>` (deployed-only v1).
|
|
@@ -449,9 +584,13 @@ interface OpenRouterProviderModel {
|
|
|
449
584
|
/** One org app row from `GET {orgApi}/apps` (urun-cli `ApiClient.list_apps`). */
|
|
450
585
|
interface DeployedApp {
|
|
451
586
|
app_slug: string;
|
|
587
|
+
/** The serve function the BACKHAUL dials on the app (URUN_FUNCTION); no
|
|
588
|
+
* longer a routing membership test — routing reads `serves`. */
|
|
452
589
|
function_name?: string | null;
|
|
590
|
+
/** Declared serve protocol (`"openai"`), or null/absent = undeclared = the
|
|
591
|
+
* proxy must not route this app. THE routing membership test. */
|
|
592
|
+
serves?: string | null;
|
|
453
593
|
deployment_status?: string | null;
|
|
454
|
-
[k: string]: unknown;
|
|
455
594
|
}
|
|
456
595
|
/**
|
|
457
596
|
* A model that does not resolve to a deployed app the proxy may serve.
|
|
@@ -502,6 +641,15 @@ interface RouterModelEntry {
|
|
|
502
641
|
pricing?: TokenPricing;
|
|
503
642
|
/** Idle-floor replica count for a shared-lane model (U6 reconciler). */
|
|
504
643
|
warm?: number;
|
|
644
|
+
/**
|
|
645
|
+
* The voices a `tts`-task model synthesizes under (ENG-331 — OpenAI's
|
|
646
|
+
* speech API names a voice per request, so a caller needs the list).
|
|
647
|
+
* Catalog-sourced from `engine_args.voices` when the row carries it;
|
|
648
|
+
* otherwise the qwen3 CustomVoice timbres for that one model class (see
|
|
649
|
+
* openrouter-doc.ts `QWEN3_CUSTOMVOICE_VOICES`). Absent on every non-tts
|
|
650
|
+
* entry, and on tts entries whose voices the catalog does not vouch.
|
|
651
|
+
*/
|
|
652
|
+
voices?: string[];
|
|
505
653
|
}
|
|
506
654
|
interface RouterModelList {
|
|
507
655
|
object: 'list';
|
|
@@ -523,7 +671,8 @@ interface RouterImageModelList {
|
|
|
523
671
|
* lane (the catalog `shared` block, the cross-org session dial) — the exact
|
|
524
672
|
* serve function on the shared org's app and the flag that switches the
|
|
525
673
|
* control plane's shared-admission carve-out on. A plain caller-org app
|
|
526
|
-
* carries only `appSlug`; the router's
|
|
674
|
+
* carries only `appSlug`; the router's `fnName` (the function the backhaul
|
|
675
|
+
* dials) applies there.
|
|
527
676
|
*/
|
|
528
677
|
interface RouteTarget {
|
|
529
678
|
appSlug: string;
|
|
@@ -540,6 +689,16 @@ interface RouteTarget {
|
|
|
540
689
|
modelId?: string;
|
|
541
690
|
/** Shared lanes only: the catalog variant the request pinned. */
|
|
542
691
|
variant?: string;
|
|
692
|
+
/**
|
|
693
|
+
* Shared lanes only: the pod org (`shared.shared_org_id` of the block that
|
|
694
|
+
* resolved the lane). THE STATELESS DECISION TOKEN'S PROVIDER CLAIM — the
|
|
695
|
+
* decision intake's provider-aware org gate accepts tenant OR provider
|
|
696
|
+
* matching the pod org, and on a shared pod the caller-org tenant differs
|
|
697
|
+
* from the shared-org pod. Sourced from the SAME block that resolved the
|
|
698
|
+
* lane, never a second env spelling, so key and pod-org identity cannot
|
|
699
|
+
* drift.
|
|
700
|
+
*/
|
|
701
|
+
sharedOrgId?: string;
|
|
543
702
|
}
|
|
544
703
|
interface ModelRouterOptions<S> {
|
|
545
704
|
/**
|
|
@@ -550,10 +709,25 @@ interface ModelRouterOptions<S> {
|
|
|
550
709
|
* "NO-DEFAULT MODE").
|
|
551
710
|
*/
|
|
552
711
|
defaultApp: string | null;
|
|
553
|
-
/**
|
|
712
|
+
/**
|
|
713
|
+
* The serve function the BACKHAUL dials on the resolved app (URUN_FUNCTION)
|
|
714
|
+
* — shared lanes may pin their own per-row function instead (RouteTarget
|
|
715
|
+
* fnName). NOT a routing membership test any more: routing reads the app's
|
|
716
|
+
* declared `serves` protocol (see {@link ModelRouter.servable}).
|
|
717
|
+
*/
|
|
554
718
|
fnName: string;
|
|
555
|
-
/**
|
|
556
|
-
|
|
719
|
+
/**
|
|
720
|
+
* Open a backhaul session for a resolved dial target (called at most once
|
|
721
|
+
* per pool key). The SECOND argument is the POOL KEY the entry will
|
|
722
|
+
* occupy — the same string `sessionFor`/`speechSessionFor` store it under
|
|
723
|
+
* — so an implementer can wire per-slot machinery (the backhaul's
|
|
724
|
+
* gone-watch eviction and the speech lane's subject scoping) against the
|
|
725
|
+
* slot the entry ACTUALLY holds: a lane-scoped open (`speech:<slug>`)
|
|
726
|
+
* that wired its eviction on the unscoped key would leave a dead entry
|
|
727
|
+
* stranded in the pool forever. Implementers that ignore it behave
|
|
728
|
+
* exactly as before.
|
|
729
|
+
*/
|
|
730
|
+
openSession: (target: RouteTarget, poolKey: string) => S | Promise<S>;
|
|
557
731
|
/** Terminal release for one pool entry (Session.end() underneath). */
|
|
558
732
|
closeSession: (entry: S) => Promise<void>;
|
|
559
733
|
/**
|
|
@@ -609,6 +783,19 @@ interface ModelRouterOptions<S> {
|
|
|
609
783
|
* discipline as the apps/catalog oracles.
|
|
610
784
|
*/
|
|
611
785
|
shared?: (() => Promise<SharedCatalogBlock | null>) | null;
|
|
786
|
+
/**
|
|
787
|
+
* OWNER RULING (2026-09-21, shared-endpoints inference-proxy): the model
|
|
788
|
+
* listing is the platform's SHARED surface and nothing else. When true,
|
|
789
|
+
* {@link ModelRouter.modelList} advertises ONLY the catalog `shared`
|
|
790
|
+
* block's endpoints and never the caller-org's deployed apps — the same
|
|
791
|
+
* id space {@link ModelRouter.openRouterModels} publishes, so every
|
|
792
|
+
* listing format agrees. The hosted lane (hosted/tenants.ts) sets this;
|
|
793
|
+
* the local `urun compat` lane leaves it absent and behaves
|
|
794
|
+
* byte-for-byte as before (a caller's own apps stay listed there).
|
|
795
|
+
* An absent shared block (or none configured) is an EMPTY listing —
|
|
796
|
+
* "no shared models" is an answer, never a fallback to the org's apps.
|
|
797
|
+
*/
|
|
798
|
+
sharedOnlyListing?: boolean;
|
|
612
799
|
}
|
|
613
800
|
/**
|
|
614
801
|
* The image-capability modes the Images lane gates on (images.ts's
|
|
@@ -681,11 +868,24 @@ declare class ModelRouter<S> {
|
|
|
681
868
|
* complete — the same posture `parseSharedBlock` takes on a malformed
|
|
682
869
|
* routing block.
|
|
683
870
|
*
|
|
871
|
+
* `outputFree` is the shared block's EXACT `(model_id, variant)` opt-in
|
|
872
|
+
* set ({@link outputFreeKey} keys, built from `SharedEndpointRow.output_free`):
|
|
873
|
+
* the only rows whose zero OUTPUT rate the resolver may publish — see
|
|
874
|
+
* model-prices.ts for the one-flag-one-lane rule. Absent/empty set: every
|
|
875
|
+
* zero keeps the refusal.
|
|
876
|
+
*
|
|
684
877
|
* The resolve step sits OUTSIDE the catch deliberately: it is our own
|
|
685
878
|
* code, so a programming error in it must surface as a crash, not become
|
|
686
879
|
* an empty price list.
|
|
687
880
|
*/
|
|
688
881
|
private pricesForListing;
|
|
882
|
+
/**
|
|
883
|
+
* The EXACT `(model_id, variant)` pairs the shared block declares
|
|
884
|
+
* output-free ({@link outputFreeKey} keys) — the only allowlist
|
|
885
|
+
* resolveTokenPrices accepts a zero OUTPUT rate against. Only a
|
|
886
|
+
* `true` flag contributes: absent and null are the un-flagged default.
|
|
887
|
+
*/
|
|
888
|
+
private outputFreeKeys;
|
|
689
889
|
/**
|
|
690
890
|
* Where a degradation says so. Defaults to `console.warn` rather than to
|
|
691
891
|
* nothing: a caller may ROUTE the signal (the CLI sends it to stderr, as
|
|
@@ -694,7 +894,14 @@ declare class ModelRouter<S> {
|
|
|
694
894
|
* time bomb.
|
|
695
895
|
*/
|
|
696
896
|
private warn;
|
|
697
|
-
/**
|
|
897
|
+
/**
|
|
898
|
+
* Apps this proxy may serve: active AND declaring the OpenAI-compatible
|
|
899
|
+
* serve protocol (`@app.function(serves="openai")`). The OLD rule — a
|
|
900
|
+
* function literally named `serve` — is GONE: the name carried no
|
|
901
|
+
* capability meaning (an app with a function named `serve_runtime` could
|
|
902
|
+
* never be routed), and the declaration is what the deploy actually knows.
|
|
903
|
+
* `fnName` survives only as the function the BACKHAUL dials.
|
|
904
|
+
*/
|
|
698
905
|
private servable;
|
|
699
906
|
private availableIds;
|
|
700
907
|
/**
|
|
@@ -754,6 +961,15 @@ declare class ModelRouter<S> {
|
|
|
754
961
|
* Rides {@link resolveTarget}'s cached listings and OPENS NO SESSION.
|
|
755
962
|
*/
|
|
756
963
|
servingAppSlugFor(model: string | undefined): Promise<string>;
|
|
964
|
+
/**
|
|
965
|
+
* THE STATELESS DIRECT PATH's resolution seam: the dial target for a
|
|
966
|
+
* model — the pure resolution (app slug, fn, shared flag) with NO session
|
|
967
|
+
* opened. The stateless decision dial (stateless-dispatch.ts) uses it to
|
|
968
|
+
* discover ready replicas for the resolved (org, app, fn) instead of
|
|
969
|
+
* minting a session; every public no-dial accessor above rides this same
|
|
970
|
+
* private resolution.
|
|
971
|
+
*/
|
|
972
|
+
targetFor(model: string | undefined): Promise<RouteTarget>;
|
|
757
973
|
/**
|
|
758
974
|
* Resolve a request's `model` to its POOL KEY — the app slug for a
|
|
759
975
|
* caller-org app, `shared:<slug>` for a shared-lane dial (the two never
|
|
@@ -782,6 +998,28 @@ declare class ModelRouter<S> {
|
|
|
782
998
|
entry: S;
|
|
783
999
|
identity: TurnCatalogIdentity;
|
|
784
1000
|
}>;
|
|
1001
|
+
/**
|
|
1002
|
+
* The SPEECH lane's pooled session for a model — the SAME resolution and
|
|
1003
|
+
* acquisition as {@link sessionFor}, stored under a LANE-SCOPED pool key
|
|
1004
|
+
* (`speech:` + the ordinary key) so the session a speech collector
|
|
1005
|
+
* subscribes to is one NO interactive lane (text turns via
|
|
1006
|
+
* `rehomingCreateResponse`/`sessionFor`, Realtime and Gemini Live via
|
|
1007
|
+
* `openAudio`/`handleFor`) can ever resolve: the per-session AudioBridge
|
|
1008
|
+
* broadcasts every untagged output frame to every subscriber, so a shared
|
|
1009
|
+
* session is exactly the mixed-audio defect (greptile urun-ts#500). The
|
|
1010
|
+
* entry's OWN lifecycle (gone-watch eviction, wedge re-salt, billing
|
|
1011
|
+
* identity) is identical — only the slot differs, which is the whole point.
|
|
1012
|
+
*/
|
|
1013
|
+
speechSessionFor(model: string | undefined): Promise<{
|
|
1014
|
+
app: string;
|
|
1015
|
+
entry: S;
|
|
1016
|
+
identity: TurnCatalogIdentity;
|
|
1017
|
+
}>;
|
|
1018
|
+
/**
|
|
1019
|
+
* The ONE acquisition path both lanes ride — opened lazily under exactly
|
|
1020
|
+
* `key`, reused afterwards. A failed open never poisons the slot.
|
|
1021
|
+
*/
|
|
1022
|
+
private acquire;
|
|
785
1023
|
private keyOf;
|
|
786
1024
|
/**
|
|
787
1025
|
* SESSION-IDENTITY SEAM (a): the opaque stable handle for the pooled
|
|
@@ -794,6 +1032,17 @@ declare class ModelRouter<S> {
|
|
|
794
1032
|
app: string;
|
|
795
1033
|
handle: string;
|
|
796
1034
|
}>;
|
|
1035
|
+
/**
|
|
1036
|
+
* SESSION-IDENTITY SEAM (a), SPEECH lane: the handle for the SPEECH-scoped
|
|
1037
|
+
* pooled session ({@link speechSessionFor} — `speech:<key>` as the handle's
|
|
1038
|
+
* pool key, so {@link sessionForHandle} resolves exactly the speech slot).
|
|
1039
|
+
* The handle codec needs no change: it already carries whatever pool key
|
|
1040
|
+
* minted it, and an interactive {@link handleFor} can never produce one.
|
|
1041
|
+
*/
|
|
1042
|
+
speechHandleFor(model: string | undefined): Promise<{
|
|
1043
|
+
app: string;
|
|
1044
|
+
handle: string;
|
|
1045
|
+
}>;
|
|
797
1046
|
/**
|
|
798
1047
|
* SESSION-IDENTITY SEAM (b): the exact pooled session a handle names.
|
|
799
1048
|
* NEVER opens a fresh session — a handle whose session is gone (closed,
|
|
@@ -849,11 +1098,20 @@ declare class ModelRouter<S> {
|
|
|
849
1098
|
* {@link pricesForListing}'s posture (an unreadable surface prices
|
|
850
1099
|
* nothing and WARNS — discovery outlives the price oracle); a model with
|
|
851
1100
|
* no per-token row carries NO pricing array (the schema rule: "a
|
|
852
|
-
* modality with no pricing array is simply unpriced")
|
|
1101
|
+
* modality with no pricing array is simply unpriced"), and the block's
|
|
1102
|
+
* `output_free` endpoints are the ONLY rows whose zero OUTPUT rate
|
|
1103
|
+
* prices as `cost_usd: '0'` — the allowlist resolves prices IN the
|
|
1104
|
+
* pricing step below, so the document cannot emit a zero the catalog
|
|
1105
|
+
* did not opt into.
|
|
853
1106
|
*
|
|
854
1107
|
* A CONFIGURED catalog read that FAILS propagates — the provider
|
|
855
1108
|
* document cannot vouch a sellable model from nothing, and a half-empty
|
|
856
1109
|
* one must never be served as if complete.
|
|
1110
|
+
*
|
|
1111
|
+
* The builder's own loud announcements (the absent readiness seam that
|
|
1112
|
+
* conservatively declares every endpoint not ready, unmappable region
|
|
1113
|
+
* codes) go through this router's {@link warn} sink, so a lane that
|
|
1114
|
+
* routes degradations somewhere sees them too — never a default print.
|
|
857
1115
|
*/
|
|
858
1116
|
openRouterModels(): Promise<OpenRouterProviderDoc>;
|
|
859
1117
|
/**
|
|
@@ -907,6 +1165,40 @@ declare class ModelRouter<S> {
|
|
|
907
1165
|
* for ⇒ false — the realtime resolver renders that as the loud 404.
|
|
908
1166
|
*/
|
|
909
1167
|
supportsAudio(model: string | undefined): Promise<boolean>;
|
|
1168
|
+
/**
|
|
1169
|
+
* THE SPEECH LANE'S GATE (proxy/speech.ts, `POST /v1/audio/speech`): does
|
|
1170
|
+
* `model`'s deployed app carry catalog `task` **tts** — INVARIANTLY across
|
|
1171
|
+
* every GPU-placement row ({@link audioModalityForRows} answering exactly
|
|
1172
|
+
* `'tts'`)? The SAME shape as {@link supportsImages}/{@link supportsAudio}:
|
|
1173
|
+
* authorized resolution ({@link resolveTarget} — undeployed uRun models
|
|
1174
|
+
* throw {@link UnknownModelError}), then the SAME catalog join
|
|
1175
|
+
* ({@link catalogRowsForSlug}); a SHARED-lane hit joins on the lane's exact
|
|
1176
|
+
* catalog id + variant, exactly like {@link supportsAudio}.
|
|
1177
|
+
*
|
|
1178
|
+
* An `stt` verdict is deliberately NOT speech-capable: a transcription
|
|
1179
|
+
* model must never be asked to synthesize, and a chat model on this route
|
|
1180
|
+
* is the loud 404 the images lane renders for non-image models. A
|
|
1181
|
+
* CONFIGURED catalog oracle that FAILS propagates its rejection — the gate
|
|
1182
|
+
* never fabricates a `false` out of an infrastructure outage.
|
|
1183
|
+
*/
|
|
1184
|
+
supportsSpeech(model: string | undefined): Promise<boolean>;
|
|
1185
|
+
/**
|
|
1186
|
+
* THE SPEECH LANE'S VOICE-SET RESOLUTION (proxy/speech.ts): the voice set
|
|
1187
|
+
* `/v1/models` publishes for `model` — resolved by the SAME resolver the
|
|
1188
|
+
* listing's enrichment applies (openrouter-doc.ts `speechVoicesFor`, via
|
|
1189
|
+
* {@link speechVoicesForMatches}) over the SAME catalog join
|
|
1190
|
+
* {@link supportsSpeech} uses (exact `(model_id, variant)` for a
|
|
1191
|
+
* shared-lane hit, {@link catalogRowsForSlug} for a caller-org app), so
|
|
1192
|
+
* the lane enforces exactly ONE list: the one the listing publishes — the
|
|
1193
|
+
* catalog-vouched `engine_args.voices`, else the qwen3 CustomVoice card's
|
|
1194
|
+
* nine for that one model class. A catalog that PRESENTS a voices value
|
|
1195
|
+
* it cannot vouch answers WITH the defect set on the resolution — the
|
|
1196
|
+
* lane fails loud (500) rather than silently validating against the card
|
|
1197
|
+
* fallback. Undeployed uRun-namespace models throw
|
|
1198
|
+
* {@link UnknownModelError} (the lane's 404), and a CONFIGURED catalog
|
|
1199
|
+
* oracle that FAILS propagates — never a fabricated empty set.
|
|
1200
|
+
*/
|
|
1201
|
+
speechVoices(model: string | undefined): Promise<SpeechVoicesResolution>;
|
|
910
1202
|
/**
|
|
911
1203
|
* `GET /v1/images/models`: the IMAGE-CAPABLE slice of the model surface —
|
|
912
1204
|
* the caller-org deployed apps whose catalog rows vouch an image mode
|
|
@@ -925,6 +1217,117 @@ declare class ModelRouter<S> {
|
|
|
925
1217
|
closeAll(): Promise<void>;
|
|
926
1218
|
}
|
|
927
1219
|
|
|
1220
|
+
/** One ready replica row from the presence read. */
|
|
1221
|
+
interface RuntimePresenceRow {
|
|
1222
|
+
runtime_id: string;
|
|
1223
|
+
/** Base URL of the runtime's :8099 endpoint, e.g. `http://10.0.1.5:8099`. */
|
|
1224
|
+
address: string;
|
|
1225
|
+
ready: boolean;
|
|
1226
|
+
/** ISO 8601 instant of the runtime's last presence heartbeat. */
|
|
1227
|
+
last_seen: string;
|
|
1228
|
+
status?: string | null;
|
|
1229
|
+
}
|
|
1230
|
+
/** One resolved dial target — the router's pure resolution (no session).
|
|
1231
|
+
* The shared arm's fields ride through so the dial can mint the
|
|
1232
|
+
* provider-aware token and the handlers can repin the CATALOG billing pair:
|
|
1233
|
+
* `sharedOrgId` is the shared block's `shared_org_id` (the pod org). */
|
|
1234
|
+
interface DialTarget {
|
|
1235
|
+
appSlug: string;
|
|
1236
|
+
fnName?: string;
|
|
1237
|
+
sharedApp?: boolean;
|
|
1238
|
+
modelId?: string;
|
|
1239
|
+
variant?: string;
|
|
1240
|
+
sharedOrgId?: string;
|
|
1241
|
+
}
|
|
1242
|
+
interface StatelessDecisionDialOptions {
|
|
1243
|
+
/** Resolve a request's model to its dial target WITHOUT opening a session. */
|
|
1244
|
+
targetFor: (model: string | undefined) => Promise<DialTarget>;
|
|
1245
|
+
apiUrl: string;
|
|
1246
|
+
apiKey: string;
|
|
1247
|
+
/** The caller org's id — the minted token's `tenant` claim. */
|
|
1248
|
+
orgId: string;
|
|
1249
|
+
/** The serve function name the resolved app must expose (the backhaul's
|
|
1250
|
+
* URUN_FUNCTION default, 'serve'). */
|
|
1251
|
+
fnName: string;
|
|
1252
|
+
/** The shared secret render injects (URUN_SESSION_TOKEN_SECRET). */
|
|
1253
|
+
secret: string;
|
|
1254
|
+
/** THE SHARED LANE'S credential (URUN_SHARED_ORG_API_KEY): the shared
|
|
1255
|
+
* org's API key, so discovery and decisions can reach the SHARED org's
|
|
1256
|
+
* pods too. The provider org id comes from the resolved target's
|
|
1257
|
+
* `sharedOrgId` (the block's `shared_org_id`), never a second env spelling
|
|
1258
|
+
* of it — key and pod-org identity cannot drift apart. Absent ⇒ shared
|
|
1259
|
+
* models keep the session lane (the v1 boundary narrows to "no shared
|
|
1260
|
+
* credential mounted" instead of "shared never"). */
|
|
1261
|
+
shared?: {
|
|
1262
|
+
apiKey: string;
|
|
1263
|
+
};
|
|
1264
|
+
fetchImpl?: typeof fetch;
|
|
1265
|
+
nowMs?: () => number;
|
|
1266
|
+
/** Per-attempt POST budget override (test seam; production rides
|
|
1267
|
+
* DECISION_TIMEOUT_MS). */
|
|
1268
|
+
timeoutMs?: number;
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* The stateless decision dial. `decide` resolves the target, discovers ready
|
|
1272
|
+
* replicas, picks the least-outstanding one, mints the scoped token and POSTs
|
|
1273
|
+
* the flat body to the runtime's `/v1/decision` intake.
|
|
1274
|
+
*/
|
|
1275
|
+
declare class StatelessDecisionDial {
|
|
1276
|
+
private readonly opts;
|
|
1277
|
+
private readonly replicas;
|
|
1278
|
+
private readonly nowMs;
|
|
1279
|
+
/** Per-(presence key, app, fn) fresh listing, TTL-bounded to
|
|
1280
|
+
* PRESENCE_FRESH_S. A per-DECISION control-plane read would be the same
|
|
1281
|
+
* design error the session dial was — a network round-trip for state the
|
|
1282
|
+
* runtime plane re-proves every ~5s heartbeat. The cache serves the
|
|
1283
|
+
* caller from memory, refreshes in the BACKGROUND past half the TTL, and
|
|
1284
|
+
* evicts a replica (or the whole entry) the moment a dial to its cached
|
|
1285
|
+
* address fails. */
|
|
1286
|
+
private readonly presenceCache;
|
|
1287
|
+
constructor(opts: StatelessDecisionDialOptions);
|
|
1288
|
+
/** Enabled = the pod secret is configured. Absent ⇒ the session lane serves
|
|
1289
|
+
* (byte-for-byte behavior). */
|
|
1290
|
+
get enabled(): boolean;
|
|
1291
|
+
/**
|
|
1292
|
+
* Resolve a request's lane target WITHOUT dialing: `null` for a SHARED-lane
|
|
1293
|
+
* model when the dial holds NO shared credential — the caller keeps the
|
|
1294
|
+
* session lane. WITH the shared key, a shared target is dialable and rides
|
|
1295
|
+
* through with its full identity (modelId/variant for the billing repin,
|
|
1296
|
+
* `sharedOrgId` for the provider-aware token). The HTTP handlers call this
|
|
1297
|
+
* FIRST and route non-null targets into decide; decide re-resolves its own
|
|
1298
|
+
* target so the one it reports is the one it actually dialed.
|
|
1299
|
+
*/
|
|
1300
|
+
targetFor(model: string | undefined): Promise<DialTarget | null>;
|
|
1301
|
+
/**
|
|
1302
|
+
* One decision. Returns the intake's `(status, body)` plus the SYNTHESIZED
|
|
1303
|
+
* completed body the lane stamps its billing receipt from — the runtime's
|
|
1304
|
+
* chat body carries `usage` (OpenAI shape) and `urun_timing`; both are the
|
|
1305
|
+
* exact fields the ledger's receipt stamper reads, mapped once here so the
|
|
1306
|
+
* stateless lane prices identically to the session lane. The RESOLVED
|
|
1307
|
+
* target rides along: the handlers repin the billing identity from it
|
|
1308
|
+
* (the dial's own answer, handed out — never asked for again) before
|
|
1309
|
+
* stamping.
|
|
1310
|
+
*/
|
|
1311
|
+
decide(body: Record<string, unknown>, model: string | undefined): Promise<{
|
|
1312
|
+
status: number;
|
|
1313
|
+
body: unknown;
|
|
1314
|
+
completed: unknown;
|
|
1315
|
+
target: DialTarget & {
|
|
1316
|
+
fnName: string;
|
|
1317
|
+
};
|
|
1318
|
+
}>;
|
|
1319
|
+
/** Serve the caller from the cache; a MISS (or a TTL-expired entry) reads
|
|
1320
|
+
* synchronously — the cold path is the one decision that pays the hop —
|
|
1321
|
+
* and an entry past half the TTL fires a single-flight BACKGROUND refresh
|
|
1322
|
+
* that never blocks this caller. */
|
|
1323
|
+
private ensureRows;
|
|
1324
|
+
private readPresence;
|
|
1325
|
+
/** LEAST-OUTSTANDING: pick the ready replica with the fewest in-flight
|
|
1326
|
+
* decisions (ties → the first), skipping replicas in failure cooldown. */
|
|
1327
|
+
pick(rows: RuntimePresenceRow[]): string | null;
|
|
1328
|
+
private post;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
928
1331
|
/**
|
|
929
1332
|
* THE CANONICAL USAGE-QUERY LANE — `POST /v1/usage/requests`.
|
|
930
1333
|
*
|
|
@@ -1220,6 +1623,58 @@ interface LedgerWrite {
|
|
|
1220
1623
|
prefill_ms: number | null;
|
|
1221
1624
|
decode_ms: number | null;
|
|
1222
1625
|
total_ms: number | null;
|
|
1626
|
+
/**
|
|
1627
|
+
* PROXY-OBSERVED TIME TO FIRST STREAMED CONTENT BYTE, in whole
|
|
1628
|
+
* milliseconds — what the CALLER experienced, and therefore the number
|
|
1629
|
+
* Hugging Face's 5s provider-listing gate is about (ENG-337).
|
|
1630
|
+
*
|
|
1631
|
+
* THE RECORD IS THE ONE SOURCE. This is {@link InferenceRecord.ttft_ms}
|
|
1632
|
+
* carried through, measured at the one place TTFT was already measured
|
|
1633
|
+
* (`ProxyStats.track` stamps `turn.ttftMs` on the first text delta) — the
|
|
1634
|
+
* same number the billing log line and the `urun_inference_ttft_seconds`
|
|
1635
|
+
* histogram already publish. The writer adds no second measurement, so the
|
|
1636
|
+
* row cannot disagree with either sink.
|
|
1637
|
+
*
|
|
1638
|
+
* NULL FOR NON-STREAM, NULL FOR UNOBSERVED, NEVER ZERO FOR EITHER. The
|
|
1639
|
+
* record stamps `ttft_ms` only for a streamed turn: a caller who receives
|
|
1640
|
+
* one JSON body at the end was never shown a "first byte", and publishing
|
|
1641
|
+
* the internal timing in its place would put flattering numbers into
|
|
1642
|
+
* exactly the series the HF gate reads. A stream that produced no text
|
|
1643
|
+
* delta before it ended was never observed at all. Null is the only honest
|
|
1644
|
+
* spelling of both; a 0 here would assert a sub-millisecond first byte
|
|
1645
|
+
* nobody measured. (A MEASURED sub-millisecond first byte does round to 0 —
|
|
1646
|
+
* that is a measurement, not an unknown; which side of the `??` a value
|
|
1647
|
+
* arrived on is the whole difference.)
|
|
1648
|
+
*
|
|
1649
|
+
* NOT THE RUNTIME'S `urun_timing.ttft_ms`, which is `queue_ms +
|
|
1650
|
+
* prefill_ms` and still deliberately does not ride the row (see
|
|
1651
|
+
* `TurnResidency`): that is the engine's view of its own internals, this
|
|
1652
|
+
* is the wall-clock the caller saw — network, proxy, cold-wake and all.
|
|
1653
|
+
* Neither can derive the other, so neither is a second spelling of the
|
|
1654
|
+
* other.
|
|
1655
|
+
*/
|
|
1656
|
+
ttft_ms: number | null;
|
|
1657
|
+
/**
|
|
1658
|
+
* PROXY-OBSERVED REQUEST RECEIPT → LAST BYTE, in whole milliseconds: the
|
|
1659
|
+
* end-to-end latency the caller experienced for the whole answer
|
|
1660
|
+
* (ENG-337).
|
|
1661
|
+
*
|
|
1662
|
+
* `Math.round` of the record's `duration_ms` — THE SAME MEASUREMENT
|
|
1663
|
+
* {@link startedAtOf} derives the row's `started_at` from, so the row's
|
|
1664
|
+
* window columns cannot disagree by construction: `started_at + e2e_ms`
|
|
1665
|
+
* and `completed_at` describe one clock. Rounded HERE, at the one place
|
|
1666
|
+
* the row is built, because the ledger's column
|
|
1667
|
+
* (`urun_record_inference_request`'s `p_e2e_ms`) is an `integer` — never
|
|
1668
|
+
* rounded a second time somewhere a new spelling could drift from this
|
|
1669
|
+
* one.
|
|
1670
|
+
*
|
|
1671
|
+
* ALWAYS PRESENT on rows this writer produces, unlike {@link ttft_ms}: the
|
|
1672
|
+
* duration is measured for every completed request, streamed or not,
|
|
1673
|
+
* failed or not. The TYPE still admits null because the wire column is
|
|
1674
|
+
* nullable — the contract is stated rather than implied by what this
|
|
1675
|
+
* writer happens to emit today.
|
|
1676
|
+
*/
|
|
1677
|
+
e2e_ms: number | null;
|
|
1223
1678
|
}
|
|
1224
1679
|
/** What the control plane answers with — the row as it was actually written. */
|
|
1225
1680
|
interface LedgerRecorded {
|
|
@@ -1341,6 +1796,18 @@ interface LedgerFailure {
|
|
|
1341
1796
|
*/
|
|
1342
1797
|
declare const LEDGER_SKIP_REASONS: readonly ["no_response_head", "no_org", "model_unresolved", "no_model_named", "queue_full"];
|
|
1343
1798
|
type LedgerSkipReason = (typeof LEDGER_SKIP_REASONS)[number];
|
|
1799
|
+
/**
|
|
1800
|
+
* THE TWO PRICING STATES a landed row can be in — ENG-317's
|
|
1801
|
+
* NULL-is-not-zero distinction, as a metric label value.
|
|
1802
|
+
*
|
|
1803
|
+
* A CONST ARRAY rather than an inline union because the vocabulary is now
|
|
1804
|
+
* ENUMERATED, not merely constrained: {@link LEDGER_OUTCOMES} materialises
|
|
1805
|
+
* every series this counter can ever produce, and a pricing state that existed
|
|
1806
|
+
* only in a type could not be enumerated. Adding a third state here adds its
|
|
1807
|
+
* series everywhere without a second edit.
|
|
1808
|
+
*/
|
|
1809
|
+
declare const LEDGER_PRICING_STATES: readonly ["priced", "unpriced"];
|
|
1810
|
+
type LedgerPricingState = (typeof LEDGER_PRICING_STATES)[number];
|
|
1344
1811
|
/**
|
|
1345
1812
|
* The TERMINAL fate of one request's ledger row, as a bounded pair of metric
|
|
1346
1813
|
* labels. Every `/v1` request that reaches a lane produces exactly one of
|
|
@@ -1359,7 +1826,7 @@ type LedgerOutcome =
|
|
|
1359
1826
|
/** The row is in the ledger. `priced` / `unpriced` is ENG-317's NULL-is-not-zero distinction. */
|
|
1360
1827
|
{
|
|
1361
1828
|
outcome: 'written';
|
|
1362
|
-
reason:
|
|
1829
|
+
reason: LedgerPricingState;
|
|
1363
1830
|
}
|
|
1364
1831
|
/**
|
|
1365
1832
|
* The row was ALREADY in the ledger and it is OURS — an earlier attempt
|
|
@@ -1369,7 +1836,7 @@ type LedgerOutcome =
|
|
|
1369
1836
|
*/
|
|
1370
1837
|
| {
|
|
1371
1838
|
outcome: 'already_written';
|
|
1372
|
-
reason:
|
|
1839
|
+
reason: LedgerPricingState;
|
|
1373
1840
|
}
|
|
1374
1841
|
/** No row was attempted. */
|
|
1375
1842
|
| {
|
|
@@ -1463,6 +1930,16 @@ interface ResponsesCreateParams {
|
|
|
1463
1930
|
* Forwarded verbatim — the serve runtime is the one validator.
|
|
1464
1931
|
*/
|
|
1465
1932
|
response_format?: unknown;
|
|
1933
|
+
/**
|
|
1934
|
+
* The SPEECH lane's per-request TTS voice (ENG-331, additive like `tools`):
|
|
1935
|
+
* the speaker id a `tts`-task engine synthesizes under (qwen3 CustomVoice
|
|
1936
|
+
* timbre, e.g. "Ryan"). Forwarded on the serve envelope so the runtime's
|
|
1937
|
+
* speak bridge can apply it through the engine's own per-session override
|
|
1938
|
+
* seam (`SpeakSessionConfig.voice` → `set_voice`, urun-python
|
|
1939
|
+
* tts_engine.py). Absent -> key absent: the engine's catalog-configured
|
|
1940
|
+
* default voice applies, today's behavior.
|
|
1941
|
+
*/
|
|
1942
|
+
voice?: string;
|
|
1466
1943
|
}
|
|
1467
1944
|
/** How one pinned session ended (the native phase machinery's terminal step). */
|
|
1468
1945
|
interface SessionEndInfo {
|
|
@@ -1571,6 +2048,31 @@ interface ProxyClients {
|
|
|
1571
2048
|
* allow-all, and never a chat model binding an audio session.
|
|
1572
2049
|
*/
|
|
1573
2050
|
supportsAudio?(model: string | undefined): Promise<boolean>;
|
|
2051
|
+
/**
|
|
2052
|
+
* The speech lane's capability gate (proxy/speech.ts), backed by
|
|
2053
|
+
* ModelRouter.supportsSpeech — the caller-org catalog's `task` column,
|
|
2054
|
+
* which must vouch `tts` (NOT `stt`: a transcription model must never be
|
|
2055
|
+
* asked to synthesize) INVARIANTLY across placements. Optional at the seam
|
|
2056
|
+
* exactly like {@link supportsImages}: hand-built clients may omit it, in
|
|
2057
|
+
* which case the mounted speech lane answers the loud 501 — never a
|
|
2058
|
+
* silent allow-all, and never a chat model asked to speak.
|
|
2059
|
+
*/
|
|
2060
|
+
supportsSpeech?(model: string | undefined): Promise<boolean>;
|
|
2061
|
+
/**
|
|
2062
|
+
* THE SPEECH LANE'S VOICE-SET RESOLUTION (proxy/speech.ts): the voices
|
|
2063
|
+
* `/v1/models` publishes for `model` (ModelRouter.speechVoices — the SAME
|
|
2064
|
+
* openrouter-doc resolver the listing's enrichment applies, over the SAME
|
|
2065
|
+
* catalog join the tts gate uses), so the lane validates the requested
|
|
2066
|
+
* `voice` against exactly the set the listing publishes — ONE list
|
|
2067
|
+
* everywhere, never a second copy. The resolution also carries the catalog
|
|
2068
|
+
* defect (`engine_args.voices` present but empty/malformed/disagreeing
|
|
2069
|
+
* across placements), which the lane answers with the loud 500 — never a
|
|
2070
|
+
* silent fallback to the qwen3 card's nine. Optional at the seam exactly
|
|
2071
|
+
* like {@link supportsSpeech}: hand-built clients may omit it, in which
|
|
2072
|
+
* case the mounted speech lane answers the loud 501 — skipping the check
|
|
2073
|
+
* silently would downgrade the lane to unvalidated voices.
|
|
2074
|
+
*/
|
|
2075
|
+
speechVoices?(model: string | undefined): Promise<SpeechVoicesResolution>;
|
|
1574
2076
|
/**
|
|
1575
2077
|
* `GET /v1/images/models` — the image-capable slice of the model surface
|
|
1576
2078
|
* (ModelRouter.imageModelList: deployed task-image apps + the shared block
|
|
@@ -1643,8 +2145,29 @@ interface ProxyClients {
|
|
|
1643
2145
|
* session; repeat calls return the same lane. Optional at the seam because
|
|
1644
2146
|
* text-only embeddings exist — but a surface that RECEIVES audio while the
|
|
1645
2147
|
* embedder wired no `openAudio` must fail LOUD, never drop chunks.
|
|
1646
|
-
|
|
1647
|
-
|
|
2148
|
+
*
|
|
2149
|
+
* `signal` aborts when the CLIENT that asked for the lane went away while
|
|
2150
|
+
* the lane's admission was still pending (the realtime lane's raw socket
|
|
2151
|
+
* close) — the backhaul aborts its `whenLive` wait on it and hands the
|
|
2152
|
+
* queued admission ticket back through core `Session.cancel()`, so a
|
|
2153
|
+
* vanished client never holds the sense pod's one-session slot.
|
|
2154
|
+
*/
|
|
2155
|
+
openAudio?(model: string | undefined, signal?: AbortSignal): Promise<ProxyAudioLane>;
|
|
2156
|
+
/**
|
|
2157
|
+
* THE PINNED AUDIO OPEN (speech lane): the NATIVE audio lane of the EXACT
|
|
2158
|
+
* pooled session `handle` names (ModelRouter.sessionForHandle — a gone or
|
|
2159
|
+
* replaced session throws {@link SessionGoneError} LOUDLY, never a fresh
|
|
2160
|
+
* session opened behind the handle's back). The speech lane opens its
|
|
2161
|
+
* audio collector here and dials its response turn on the SAME handle
|
|
2162
|
+
* ({@link createResponseOn}), so BOTH legs are one session identity by
|
|
2163
|
+
* construction: a re-home can never move the turn to a second session
|
|
2164
|
+
* while the collector stays subscribed to the first (which missed the
|
|
2165
|
+
* replacement's audio and answered a voiceless 502, or worse, served a
|
|
2166
|
+
* concurrent collector another session's frames). Optional at the seam
|
|
2167
|
+
* exactly like {@link openAudio}; the speech lane answers its absence
|
|
2168
|
+
* with the loud 501 — never a voiceless downgrade.
|
|
2169
|
+
*/
|
|
2170
|
+
openAudioOn?(handle: string): Promise<PinnedAudioLane>;
|
|
1648
2171
|
/**
|
|
1649
2172
|
* Open (or reuse) the NATIVE video FRAME lane on the pooled session `model`
|
|
1650
2173
|
* routes to (transport/media.ts `enableSessionVideo`: discrete JPEG frames →
|
|
@@ -1675,6 +2198,38 @@ interface ProxyClients {
|
|
|
1675
2198
|
* serve-side session-affinity tag rides (urun-python#1556/#1582).
|
|
1676
2199
|
*/
|
|
1677
2200
|
sessionHandle(model: string | undefined): Promise<string>;
|
|
2201
|
+
/**
|
|
2202
|
+
* THE SPEECH LANE'S OWN HANDLE (greptile urun-ts#500): the opaque handle
|
|
2203
|
+
* for the SPEECH-SCOPED pooled session (`speech:<pool key>` — routing.ts
|
|
2204
|
+
* {@link ModelRouter.speechSessionFor}), a slot no interactive lane can
|
|
2205
|
+
* resolve. The speech lane resolves its collector AND its turn through
|
|
2206
|
+
* THIS handle ({@link openAudioOn} + {@link createResponseOn} unchanged),
|
|
2207
|
+
* because the per-session AudioBridge broadcasts every untagged output
|
|
2208
|
+
* frame to every subscriber: a speech collection on the INTERACTIVE
|
|
2209
|
+
* pooled session would stitch a concurrent Realtime/Gemini Live turn's
|
|
2210
|
+
* frames into the returned WAV. Optional at the seam exactly like
|
|
2211
|
+
* {@link openAudioOn}; the speech lane answers its absence with the loud
|
|
2212
|
+
* 501 — NEVER a silent fall-back to the shared interactive session
|
|
2213
|
+
* (that "fallback" is precisely the mixed-audio defect).
|
|
2214
|
+
*/
|
|
2215
|
+
speechSessionHandle?(model: string | undefined): Promise<string>;
|
|
2216
|
+
/**
|
|
2217
|
+
* EVICT the SPEECH-scoped pooled session a handle names (CodeRabbit
|
|
2218
|
+
* urun-ts#500, round 8) — through the pool's OWN identity-guarded
|
|
2219
|
+
* eviction path (ModelRouter.evict under the handle's `speech:<key>`
|
|
2220
|
+
* slot, never a bespoke close), so the NEXT speechSessionHandle acquire
|
|
2221
|
+
* opens a FRESH session. The lane calls this after a collector failure
|
|
2222
|
+
* that leaves the session's audio state unknown — above all a turn-cap
|
|
2223
|
+
* timeout, where the serialized lane is released while the pinned
|
|
2224
|
+
* session may still be emitting the timed-out turn's audio: a later
|
|
2225
|
+
* request on the SAME entry would collect those stale frames into its
|
|
2226
|
+
* own answer. Throws {@link SessionGoneError} when the handle names a
|
|
2227
|
+
* session the pool no longer holds (the eviction's goal is then already
|
|
2228
|
+
* true — the lane treats it as success). Optional at the seam exactly
|
|
2229
|
+
* like {@link speechSessionHandle}; the speech lane answers its absence
|
|
2230
|
+
* with the loud 501 — never a silent reuse of the possibly-stale entry.
|
|
2231
|
+
*/
|
|
2232
|
+
evictSpeechSession?(handle: string): Promise<void>;
|
|
1678
2233
|
/**
|
|
1679
2234
|
* createResponse PINNED to the exact session a handle names
|
|
1680
2235
|
* (ModelRouter.sessionForHandle). Throws SessionGoneError LOUDLY when that
|
|
@@ -1687,16 +2242,48 @@ interface ProxyClients {
|
|
|
1687
2242
|
* Subscribe to the pinned session's terminal end via core's NATIVE phase
|
|
1688
2243
|
* machinery (Session.onPhase → terminal 'expired'/'ended'/'error'). Fires
|
|
1689
2244
|
* `cb` once. Throws SessionGoneError if the handle's session is already
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
2245
|
+
* gone — which doubles as the loud reattach check at resume time. Returns
|
|
2246
|
+
* the unsubscribe.
|
|
2247
|
+
*/
|
|
1693
2248
|
onSessionEnd(handle: string, cb: (end: SessionEndInfo) => void): Promise<() => void>;
|
|
2249
|
+
/**
|
|
2250
|
+
* THE STATELESS DIRECT PATH (stateless-dispatch.ts): the decision dial to
|
|
2251
|
+
* the serving pods' `POST /v1/decision` intake, present only when the
|
|
2252
|
+
* deployment carries the runtime scoped-token secret. Absent ⇒ every
|
|
2253
|
+
* decision rides the session lane exactly as before (the staged rollout).
|
|
2254
|
+
* Per-org: the HOSTED registry builds one per tenant so the minted token's
|
|
2255
|
+
* tenant claim is always the CALLER's org.
|
|
2256
|
+
*/
|
|
2257
|
+
readonly stateless?: StatelessDecisionDial;
|
|
1694
2258
|
}
|
|
1695
2259
|
/**
|
|
1696
2260
|
* The audio lane handle `openAudio` returns — structurally the transport
|
|
1697
2261
|
* AudioBridge (media.ts): base64 PCM16 @24 kHz mono in both directions.
|
|
2262
|
+
* `sessionId` is the pooled uRun session the lane rides (core `Session.id`),
|
|
2263
|
+
* when the backhaul reports it — the identity the realtime lane's structured
|
|
2264
|
+
* lifecycle log carries so an orphaned session is traceable from the proxy
|
|
2265
|
+
* log alone. Null is the HONEST ABSENCE (the same line {@link
|
|
2266
|
+
* PinnedAudioLane.sessionId} and the re-homing dial's `sessionIdOf` take —
|
|
2267
|
+
* an id-less session reports no id, never a fabricated one).
|
|
2268
|
+
*/
|
|
2269
|
+
type ProxyAudioLane = Pick<AudioBridge, 'appendInputAudio' | 'onOutputAudio'> & {
|
|
2270
|
+
sessionId?: string | null;
|
|
2271
|
+
};
|
|
2272
|
+
/**
|
|
2273
|
+
* The PINNED audio lane handle `openAudioOn` returns ({@link ProxyClients.openAudioOn}):
|
|
2274
|
+
* the session's native audio lane PLUS the identity of the exact pooled session
|
|
2275
|
+
* it is bound to — the two facts the speech lane needs to keep its collector and
|
|
2276
|
+
* its response turn on ONE session identity.
|
|
1698
2277
|
*/
|
|
1699
|
-
|
|
2278
|
+
interface PinnedAudioLane extends ProxyAudioLane {
|
|
2279
|
+
/**
|
|
2280
|
+
* The uRun session id of the pooled session this lane is bound to — the
|
|
2281
|
+
* honest instance link for a request whose collector and turn ride one
|
|
2282
|
+
* handle, the same derivation the dial report's `sessionId` uses. Null
|
|
2283
|
+
* when the entry carries no native session id; never a fabricated one.
|
|
2284
|
+
*/
|
|
2285
|
+
readonly sessionId: string | null;
|
|
2286
|
+
}
|
|
1700
2287
|
/**
|
|
1701
2288
|
* The video frame-lane handle `openVideo` returns — structurally the
|
|
1702
2289
|
* transport VideoFrameLane (media.ts): one raw encoded JPEG frame per call,
|