@urun-sh/openai 0.5.6 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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-BSHT6RZZ.js → chunk-6M5JK4YC.js} +1 -1
- package/dist/chunk-76BZLCGT.js +1 -0
- package/dist/chunk-7V2JKPIG.js +1 -0
- package/dist/{chunk-5HKWNK3O.js → chunk-K23AZPI4.js} +1 -1
- package/dist/chunk-T2LNHDPS.js +7 -0
- package/dist/chunk-TMQJY56F.js +4 -0
- package/dist/chunk-WEBOBE7Z.js +58 -0
- package/dist/chunk-X3GLH2HQ.js +1 -0
- package/dist/gemini-live.cjs +2 -2
- package/dist/gemini-live.d.cts +75 -7
- package/dist/gemini-live.d.ts +30 -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 +48 -46
- package/dist/proxy/cli.js +15 -14
- package/dist/proxy/index.cjs +25 -24
- package/dist/proxy/index.d.cts +142 -25
- package/dist/proxy/index.d.ts +22 -11
- package/dist/proxy/index.js +1 -1
- package/dist/{responses-turn-Dr37N3kH.d.ts → responses-turn-EwxfRJJg.d.ts} +187 -7
- package/dist/{responses-turn-tNUHq5b0.d.cts → responses-turn-vMefRdgJ.d.cts} +707 -23
- 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,32 @@ 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[];
|
|
653
|
+
/**
|
|
654
|
+
* Declared lane capabilities, derived from the catalog row — never
|
|
655
|
+
* hand-set. `reasoning` is true/false exactly when the row's
|
|
656
|
+
* `engine_args.reasoning_parser` is configured/absent on EVERY GPU
|
|
657
|
+
* placement (the runtime's own per-row parser knob, urun-python
|
|
658
|
+
* engines_vllm_engine.py / engines_sglang_engine.py): true means the
|
|
659
|
+
* serve lane separates thinking text onto `reasoning_content` /
|
|
660
|
+
* reasoning items / `thinking` blocks and `content` carries only the
|
|
661
|
+
* answer; false means the model emits no separated reasoning. A variant
|
|
662
|
+
* whose placements disagree, a malformed value, or a slug with no catalog
|
|
663
|
+
* row omit the object entirely — unknown, never guessed
|
|
664
|
+
* (openrouter-doc.ts `placementsReasoning`, applied by both listing
|
|
665
|
+
* lanes).
|
|
666
|
+
*/
|
|
667
|
+
capabilities?: {
|
|
668
|
+
reasoning: boolean;
|
|
669
|
+
};
|
|
505
670
|
}
|
|
506
671
|
interface RouterModelList {
|
|
507
672
|
object: 'list';
|
|
@@ -523,7 +688,8 @@ interface RouterImageModelList {
|
|
|
523
688
|
* lane (the catalog `shared` block, the cross-org session dial) — the exact
|
|
524
689
|
* serve function on the shared org's app and the flag that switches the
|
|
525
690
|
* control plane's shared-admission carve-out on. A plain caller-org app
|
|
526
|
-
* carries only `appSlug`; the router's
|
|
691
|
+
* carries only `appSlug`; the router's `fnName` (the function the backhaul
|
|
692
|
+
* dials) applies there.
|
|
527
693
|
*/
|
|
528
694
|
interface RouteTarget {
|
|
529
695
|
appSlug: string;
|
|
@@ -540,6 +706,16 @@ interface RouteTarget {
|
|
|
540
706
|
modelId?: string;
|
|
541
707
|
/** Shared lanes only: the catalog variant the request pinned. */
|
|
542
708
|
variant?: string;
|
|
709
|
+
/**
|
|
710
|
+
* Shared lanes only: the pod org (`shared.shared_org_id` of the block that
|
|
711
|
+
* resolved the lane). THE STATELESS DECISION TOKEN'S PROVIDER CLAIM — the
|
|
712
|
+
* decision intake's provider-aware org gate accepts tenant OR provider
|
|
713
|
+
* matching the pod org, and on a shared pod the caller-org tenant differs
|
|
714
|
+
* from the shared-org pod. Sourced from the SAME block that resolved the
|
|
715
|
+
* lane, never a second env spelling, so key and pod-org identity cannot
|
|
716
|
+
* drift.
|
|
717
|
+
*/
|
|
718
|
+
sharedOrgId?: string;
|
|
543
719
|
}
|
|
544
720
|
interface ModelRouterOptions<S> {
|
|
545
721
|
/**
|
|
@@ -550,10 +726,25 @@ interface ModelRouterOptions<S> {
|
|
|
550
726
|
* "NO-DEFAULT MODE").
|
|
551
727
|
*/
|
|
552
728
|
defaultApp: string | null;
|
|
553
|
-
/**
|
|
729
|
+
/**
|
|
730
|
+
* The serve function the BACKHAUL dials on the resolved app (URUN_FUNCTION)
|
|
731
|
+
* — shared lanes may pin their own per-row function instead (RouteTarget
|
|
732
|
+
* fnName). NOT a routing membership test any more: routing reads the app's
|
|
733
|
+
* declared `serves` protocol (see {@link ModelRouter.servable}).
|
|
734
|
+
*/
|
|
554
735
|
fnName: string;
|
|
555
|
-
/**
|
|
556
|
-
|
|
736
|
+
/**
|
|
737
|
+
* Open a backhaul session for a resolved dial target (called at most once
|
|
738
|
+
* per pool key). The SECOND argument is the POOL KEY the entry will
|
|
739
|
+
* occupy — the same string `sessionFor`/`speechSessionFor` store it under
|
|
740
|
+
* — so an implementer can wire per-slot machinery (the backhaul's
|
|
741
|
+
* gone-watch eviction and the speech lane's subject scoping) against the
|
|
742
|
+
* slot the entry ACTUALLY holds: a lane-scoped open (`speech:<slug>`)
|
|
743
|
+
* that wired its eviction on the unscoped key would leave a dead entry
|
|
744
|
+
* stranded in the pool forever. Implementers that ignore it behave
|
|
745
|
+
* exactly as before.
|
|
746
|
+
*/
|
|
747
|
+
openSession: (target: RouteTarget, poolKey: string) => S | Promise<S>;
|
|
557
748
|
/** Terminal release for one pool entry (Session.end() underneath). */
|
|
558
749
|
closeSession: (entry: S) => Promise<void>;
|
|
559
750
|
/**
|
|
@@ -609,6 +800,19 @@ interface ModelRouterOptions<S> {
|
|
|
609
800
|
* discipline as the apps/catalog oracles.
|
|
610
801
|
*/
|
|
611
802
|
shared?: (() => Promise<SharedCatalogBlock | null>) | null;
|
|
803
|
+
/**
|
|
804
|
+
* OWNER RULING (2026-09-21, shared-endpoints inference-proxy): the model
|
|
805
|
+
* listing is the platform's SHARED surface and nothing else. When true,
|
|
806
|
+
* {@link ModelRouter.modelList} advertises ONLY the catalog `shared`
|
|
807
|
+
* block's endpoints and never the caller-org's deployed apps — the same
|
|
808
|
+
* id space {@link ModelRouter.openRouterModels} publishes, so every
|
|
809
|
+
* listing format agrees. The hosted lane (hosted/tenants.ts) sets this;
|
|
810
|
+
* the local `urun compat` lane leaves it absent and behaves
|
|
811
|
+
* byte-for-byte as before (a caller's own apps stay listed there).
|
|
812
|
+
* An absent shared block (or none configured) is an EMPTY listing —
|
|
813
|
+
* "no shared models" is an answer, never a fallback to the org's apps.
|
|
814
|
+
*/
|
|
815
|
+
sharedOnlyListing?: boolean;
|
|
612
816
|
}
|
|
613
817
|
/**
|
|
614
818
|
* The image-capability modes the Images lane gates on (images.ts's
|
|
@@ -681,11 +885,24 @@ declare class ModelRouter<S> {
|
|
|
681
885
|
* complete — the same posture `parseSharedBlock` takes on a malformed
|
|
682
886
|
* routing block.
|
|
683
887
|
*
|
|
888
|
+
* `outputFree` is the shared block's EXACT `(model_id, variant)` opt-in
|
|
889
|
+
* set ({@link outputFreeKey} keys, built from `SharedEndpointRow.output_free`):
|
|
890
|
+
* the only rows whose zero OUTPUT rate the resolver may publish — see
|
|
891
|
+
* model-prices.ts for the one-flag-one-lane rule. Absent/empty set: every
|
|
892
|
+
* zero keeps the refusal.
|
|
893
|
+
*
|
|
684
894
|
* The resolve step sits OUTSIDE the catch deliberately: it is our own
|
|
685
895
|
* code, so a programming error in it must surface as a crash, not become
|
|
686
896
|
* an empty price list.
|
|
687
897
|
*/
|
|
688
898
|
private pricesForListing;
|
|
899
|
+
/**
|
|
900
|
+
* The EXACT `(model_id, variant)` pairs the shared block declares
|
|
901
|
+
* output-free ({@link outputFreeKey} keys) — the only allowlist
|
|
902
|
+
* resolveTokenPrices accepts a zero OUTPUT rate against. Only a
|
|
903
|
+
* `true` flag contributes: absent and null are the un-flagged default.
|
|
904
|
+
*/
|
|
905
|
+
private outputFreeKeys;
|
|
689
906
|
/**
|
|
690
907
|
* Where a degradation says so. Defaults to `console.warn` rather than to
|
|
691
908
|
* nothing: a caller may ROUTE the signal (the CLI sends it to stderr, as
|
|
@@ -694,7 +911,14 @@ declare class ModelRouter<S> {
|
|
|
694
911
|
* time bomb.
|
|
695
912
|
*/
|
|
696
913
|
private warn;
|
|
697
|
-
/**
|
|
914
|
+
/**
|
|
915
|
+
* Apps this proxy may serve: active AND declaring the OpenAI-compatible
|
|
916
|
+
* serve protocol (`@app.function(serves="openai")`). The OLD rule — a
|
|
917
|
+
* function literally named `serve` — is GONE: the name carried no
|
|
918
|
+
* capability meaning (an app with a function named `serve_runtime` could
|
|
919
|
+
* never be routed), and the declaration is what the deploy actually knows.
|
|
920
|
+
* `fnName` survives only as the function the BACKHAUL dials.
|
|
921
|
+
*/
|
|
698
922
|
private servable;
|
|
699
923
|
private availableIds;
|
|
700
924
|
/**
|
|
@@ -754,6 +978,15 @@ declare class ModelRouter<S> {
|
|
|
754
978
|
* Rides {@link resolveTarget}'s cached listings and OPENS NO SESSION.
|
|
755
979
|
*/
|
|
756
980
|
servingAppSlugFor(model: string | undefined): Promise<string>;
|
|
981
|
+
/**
|
|
982
|
+
* THE STATELESS DIRECT PATH's resolution seam: the dial target for a
|
|
983
|
+
* model — the pure resolution (app slug, fn, shared flag) with NO session
|
|
984
|
+
* opened. The stateless decision dial (stateless-dispatch.ts) uses it to
|
|
985
|
+
* discover ready replicas for the resolved (org, app, fn) instead of
|
|
986
|
+
* minting a session; every public no-dial accessor above rides this same
|
|
987
|
+
* private resolution.
|
|
988
|
+
*/
|
|
989
|
+
targetFor(model: string | undefined): Promise<RouteTarget>;
|
|
757
990
|
/**
|
|
758
991
|
* Resolve a request's `model` to its POOL KEY — the app slug for a
|
|
759
992
|
* caller-org app, `shared:<slug>` for a shared-lane dial (the two never
|
|
@@ -782,6 +1015,28 @@ declare class ModelRouter<S> {
|
|
|
782
1015
|
entry: S;
|
|
783
1016
|
identity: TurnCatalogIdentity;
|
|
784
1017
|
}>;
|
|
1018
|
+
/**
|
|
1019
|
+
* The SPEECH lane's pooled session for a model — the SAME resolution and
|
|
1020
|
+
* acquisition as {@link sessionFor}, stored under a LANE-SCOPED pool key
|
|
1021
|
+
* (`speech:` + the ordinary key) so the session a speech collector
|
|
1022
|
+
* subscribes to is one NO interactive lane (text turns via
|
|
1023
|
+
* `rehomingCreateResponse`/`sessionFor`, Realtime and Gemini Live via
|
|
1024
|
+
* `openAudio`/`handleFor`) can ever resolve: the per-session AudioBridge
|
|
1025
|
+
* broadcasts every untagged output frame to every subscriber, so a shared
|
|
1026
|
+
* session is exactly the mixed-audio defect (greptile urun-ts#500). The
|
|
1027
|
+
* entry's OWN lifecycle (gone-watch eviction, wedge re-salt, billing
|
|
1028
|
+
* identity) is identical — only the slot differs, which is the whole point.
|
|
1029
|
+
*/
|
|
1030
|
+
speechSessionFor(model: string | undefined): Promise<{
|
|
1031
|
+
app: string;
|
|
1032
|
+
entry: S;
|
|
1033
|
+
identity: TurnCatalogIdentity;
|
|
1034
|
+
}>;
|
|
1035
|
+
/**
|
|
1036
|
+
* The ONE acquisition path both lanes ride — opened lazily under exactly
|
|
1037
|
+
* `key`, reused afterwards. A failed open never poisons the slot.
|
|
1038
|
+
*/
|
|
1039
|
+
private acquire;
|
|
785
1040
|
private keyOf;
|
|
786
1041
|
/**
|
|
787
1042
|
* SESSION-IDENTITY SEAM (a): the opaque stable handle for the pooled
|
|
@@ -794,6 +1049,17 @@ declare class ModelRouter<S> {
|
|
|
794
1049
|
app: string;
|
|
795
1050
|
handle: string;
|
|
796
1051
|
}>;
|
|
1052
|
+
/**
|
|
1053
|
+
* SESSION-IDENTITY SEAM (a), SPEECH lane: the handle for the SPEECH-scoped
|
|
1054
|
+
* pooled session ({@link speechSessionFor} — `speech:<key>` as the handle's
|
|
1055
|
+
* pool key, so {@link sessionForHandle} resolves exactly the speech slot).
|
|
1056
|
+
* The handle codec needs no change: it already carries whatever pool key
|
|
1057
|
+
* minted it, and an interactive {@link handleFor} can never produce one.
|
|
1058
|
+
*/
|
|
1059
|
+
speechHandleFor(model: string | undefined): Promise<{
|
|
1060
|
+
app: string;
|
|
1061
|
+
handle: string;
|
|
1062
|
+
}>;
|
|
797
1063
|
/**
|
|
798
1064
|
* SESSION-IDENTITY SEAM (b): the exact pooled session a handle names.
|
|
799
1065
|
* NEVER opens a fresh session — a handle whose session is gone (closed,
|
|
@@ -849,11 +1115,20 @@ declare class ModelRouter<S> {
|
|
|
849
1115
|
* {@link pricesForListing}'s posture (an unreadable surface prices
|
|
850
1116
|
* nothing and WARNS — discovery outlives the price oracle); a model with
|
|
851
1117
|
* no per-token row carries NO pricing array (the schema rule: "a
|
|
852
|
-
* modality with no pricing array is simply unpriced")
|
|
1118
|
+
* modality with no pricing array is simply unpriced"), and the block's
|
|
1119
|
+
* `output_free` endpoints are the ONLY rows whose zero OUTPUT rate
|
|
1120
|
+
* prices as `cost_usd: '0'` — the allowlist resolves prices IN the
|
|
1121
|
+
* pricing step below, so the document cannot emit a zero the catalog
|
|
1122
|
+
* did not opt into.
|
|
853
1123
|
*
|
|
854
1124
|
* A CONFIGURED catalog read that FAILS propagates — the provider
|
|
855
1125
|
* document cannot vouch a sellable model from nothing, and a half-empty
|
|
856
1126
|
* one must never be served as if complete.
|
|
1127
|
+
*
|
|
1128
|
+
* The builder's own loud announcements (the absent readiness seam that
|
|
1129
|
+
* conservatively declares every endpoint not ready, unmappable region
|
|
1130
|
+
* codes) go through this router's {@link warn} sink, so a lane that
|
|
1131
|
+
* routes degradations somewhere sees them too — never a default print.
|
|
857
1132
|
*/
|
|
858
1133
|
openRouterModels(): Promise<OpenRouterProviderDoc>;
|
|
859
1134
|
/**
|
|
@@ -907,6 +1182,57 @@ declare class ModelRouter<S> {
|
|
|
907
1182
|
* for ⇒ false — the realtime resolver renders that as the loud 404.
|
|
908
1183
|
*/
|
|
909
1184
|
supportsAudio(model: string | undefined): Promise<boolean>;
|
|
1185
|
+
/**
|
|
1186
|
+
* THE SPEECH LANE'S GATE (proxy/speech.ts, `POST /v1/audio/speech`): does
|
|
1187
|
+
* `model`'s deployed app carry catalog `task` **tts** — INVARIANTLY across
|
|
1188
|
+
* every GPU-placement row ({@link audioModalityForRows} answering exactly
|
|
1189
|
+
* `'tts'`)? The SAME shape as {@link supportsImages}/{@link supportsAudio}:
|
|
1190
|
+
* authorized resolution ({@link resolveTarget} — undeployed uRun models
|
|
1191
|
+
* throw {@link UnknownModelError}), then the SAME catalog join
|
|
1192
|
+
* ({@link catalogRowsForSlug}); a SHARED-lane hit joins on the lane's exact
|
|
1193
|
+
* catalog id + variant, exactly like {@link supportsAudio}.
|
|
1194
|
+
*
|
|
1195
|
+
* An `stt` verdict is deliberately NOT speech-capable: a transcription
|
|
1196
|
+
* model must never be asked to synthesize, and a chat model on this route
|
|
1197
|
+
* is the loud 404 the images lane renders for non-image models. A
|
|
1198
|
+
* CONFIGURED catalog oracle that FAILS propagates its rejection — the gate
|
|
1199
|
+
* never fabricates a `false` out of an infrastructure outage.
|
|
1200
|
+
*/
|
|
1201
|
+
supportsSpeech(model: string | undefined): Promise<boolean>;
|
|
1202
|
+
/**
|
|
1203
|
+
* THE TRANSCRIPTION LANE'S GATE (proxy/transcriptions.ts,
|
|
1204
|
+
* `POST /v1/audio/transcriptions`): does `model`'s deployed app carry
|
|
1205
|
+
* catalog `task` **stt** — INVARIANTLY across every GPU-placement row
|
|
1206
|
+
* ({@link audioModalityForRows} answering exactly `'stt'`)? The SAME
|
|
1207
|
+
* shape as {@link supportsImages}/{@link supportsSpeech}, and deliberately
|
|
1208
|
+
* NOT {@link supportsAudio} (which is modality-AGNISTIC — `!== null`
|
|
1209
|
+
* accepts stt OR tts — because the realtime lane serves both): a `tts`
|
|
1210
|
+
* model must never be asked to transcribe and a chat model is the loud
|
|
1211
|
+
* 404 the images lane renders for non-image models. A SHARED-lane hit
|
|
1212
|
+
* joins on the lane's exact catalog id + variant (the SAME join
|
|
1213
|
+
* {@link imageModelList} uses for the shared block), exactly like
|
|
1214
|
+
* {@link supportsSpeech}. A CONFIGURED catalog oracle that FAILS
|
|
1215
|
+
* propagates its rejection — the gate never fabricates a `false` out of
|
|
1216
|
+
* an infrastructure outage.
|
|
1217
|
+
*/
|
|
1218
|
+
supportsTranscription(model: string | undefined): Promise<boolean>;
|
|
1219
|
+
/**
|
|
1220
|
+
* THE SPEECH LANE'S VOICE-SET RESOLUTION (proxy/speech.ts): the voice set
|
|
1221
|
+
* `/v1/models` publishes for `model` — resolved by the SAME resolver the
|
|
1222
|
+
* listing's enrichment applies (openrouter-doc.ts `speechVoicesFor`, via
|
|
1223
|
+
* {@link speechVoicesForMatches}) over the SAME catalog join
|
|
1224
|
+
* {@link supportsSpeech} uses (exact `(model_id, variant)` for a
|
|
1225
|
+
* shared-lane hit, {@link catalogRowsForSlug} for a caller-org app), so
|
|
1226
|
+
* the lane enforces exactly ONE list: the one the listing publishes — the
|
|
1227
|
+
* catalog-vouched `engine_args.voices`, else the qwen3 CustomVoice card's
|
|
1228
|
+
* nine for that one model class. A catalog that PRESENTS a voices value
|
|
1229
|
+
* it cannot vouch answers WITH the defect set on the resolution — the
|
|
1230
|
+
* lane fails loud (500) rather than silently validating against the card
|
|
1231
|
+
* fallback. Undeployed uRun-namespace models throw
|
|
1232
|
+
* {@link UnknownModelError} (the lane's 404), and a CONFIGURED catalog
|
|
1233
|
+
* oracle that FAILS propagates — never a fabricated empty set.
|
|
1234
|
+
*/
|
|
1235
|
+
speechVoices(model: string | undefined): Promise<SpeechVoicesResolution>;
|
|
910
1236
|
/**
|
|
911
1237
|
* `GET /v1/images/models`: the IMAGE-CAPABLE slice of the model surface —
|
|
912
1238
|
* the caller-org deployed apps whose catalog rows vouch an image mode
|
|
@@ -921,10 +1247,136 @@ declare class ModelRouter<S> {
|
|
|
921
1247
|
* bare-entries degradation here.
|
|
922
1248
|
*/
|
|
923
1249
|
imageModelList(): Promise<RouterImageModelList>;
|
|
1250
|
+
/**
|
|
1251
|
+
* `GET /v1/audio/models`: the TRANSCRIPTION-capable (task `stt`) slice of
|
|
1252
|
+
* the model surface — the audio twin of {@link imageModelList}: the
|
|
1253
|
+
* caller-org deployed apps whose catalog rows vouch `stt`
|
|
1254
|
+
* ({@link audioModalityForRows} answering exactly `'stt'`, the SAME
|
|
1255
|
+
* consensus {@link supportsTranscription} applies), plus the shared block's
|
|
1256
|
+
* endpoints joined against each endpoint's OWN catalog row (exact model_id +
|
|
1257
|
+
* variant; a definite non-stt task is simply skipped, never a loud defect —
|
|
1258
|
+
* the task column IS the vouch, there is no expects_image-style flag to
|
|
1259
|
+
* require). The catalog read propagates (this listing IS the capability
|
|
1260
|
+
* oracle's answer), and the PRICE SURFACE IS NOT READ AT ALL — an stt model
|
|
1261
|
+
* is not token-priced, so {@link UNPRICED_SURFACE} states that as a fact
|
|
1262
|
+
* about the lane, exactly like the images listing.
|
|
1263
|
+
*/
|
|
1264
|
+
speechToTextModelList(): Promise<RouterModelList>;
|
|
924
1265
|
/** Close every pooled session (Session.end() underneath) — proxy shutdown. */
|
|
925
1266
|
closeAll(): Promise<void>;
|
|
926
1267
|
}
|
|
927
1268
|
|
|
1269
|
+
/** One ready replica row from the presence read. */
|
|
1270
|
+
interface RuntimePresenceRow {
|
|
1271
|
+
runtime_id: string;
|
|
1272
|
+
/** Base URL of the runtime's :8099 endpoint, e.g. `http://10.0.1.5:8099`. */
|
|
1273
|
+
address: string;
|
|
1274
|
+
ready: boolean;
|
|
1275
|
+
/** ISO 8601 instant of the runtime's last presence heartbeat. */
|
|
1276
|
+
last_seen: string;
|
|
1277
|
+
status?: string | null;
|
|
1278
|
+
}
|
|
1279
|
+
/** One resolved dial target — the router's pure resolution (no session).
|
|
1280
|
+
* The shared arm's fields ride through so the dial can mint the
|
|
1281
|
+
* provider-aware token and the handlers can repin the CATALOG billing pair:
|
|
1282
|
+
* `sharedOrgId` is the shared block's `shared_org_id` (the pod org). */
|
|
1283
|
+
interface DialTarget {
|
|
1284
|
+
appSlug: string;
|
|
1285
|
+
fnName?: string;
|
|
1286
|
+
sharedApp?: boolean;
|
|
1287
|
+
modelId?: string;
|
|
1288
|
+
variant?: string;
|
|
1289
|
+
sharedOrgId?: string;
|
|
1290
|
+
}
|
|
1291
|
+
interface StatelessDecisionDialOptions {
|
|
1292
|
+
/** Resolve a request's model to its dial target WITHOUT opening a session. */
|
|
1293
|
+
targetFor: (model: string | undefined) => Promise<DialTarget>;
|
|
1294
|
+
apiUrl: string;
|
|
1295
|
+
apiKey: string;
|
|
1296
|
+
/** The caller org's id — the minted token's `tenant` claim. */
|
|
1297
|
+
orgId: string;
|
|
1298
|
+
/** The serve function name the resolved app must expose (the backhaul's
|
|
1299
|
+
* URUN_FUNCTION default, 'serve'). */
|
|
1300
|
+
fnName: string;
|
|
1301
|
+
/** The shared secret render injects (URUN_SESSION_TOKEN_SECRET). */
|
|
1302
|
+
secret: string;
|
|
1303
|
+
/** THE SHARED LANE'S credential (URUN_SHARED_ORG_API_KEY): the shared
|
|
1304
|
+
* org's API key, so discovery and decisions can reach the SHARED org's
|
|
1305
|
+
* pods too. The provider org id comes from the resolved target's
|
|
1306
|
+
* `sharedOrgId` (the block's `shared_org_id`), never a second env spelling
|
|
1307
|
+
* of it — key and pod-org identity cannot drift apart. Absent ⇒ shared
|
|
1308
|
+
* models keep the session lane (the v1 boundary narrows to "no shared
|
|
1309
|
+
* credential mounted" instead of "shared never"). */
|
|
1310
|
+
shared?: {
|
|
1311
|
+
apiKey: string;
|
|
1312
|
+
};
|
|
1313
|
+
fetchImpl?: typeof fetch;
|
|
1314
|
+
nowMs?: () => number;
|
|
1315
|
+
/** Per-attempt POST budget override (test seam; production rides
|
|
1316
|
+
* DECISION_TIMEOUT_MS). */
|
|
1317
|
+
timeoutMs?: number;
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* The stateless decision dial. `decide` resolves the target, discovers ready
|
|
1321
|
+
* replicas, picks the least-outstanding one, mints the scoped token and POSTs
|
|
1322
|
+
* the flat body to the runtime's `/v1/decision` intake.
|
|
1323
|
+
*/
|
|
1324
|
+
declare class StatelessDecisionDial {
|
|
1325
|
+
private readonly opts;
|
|
1326
|
+
private readonly replicas;
|
|
1327
|
+
private readonly nowMs;
|
|
1328
|
+
/** Per-(presence key, app, fn) fresh listing, TTL-bounded to
|
|
1329
|
+
* PRESENCE_FRESH_S. A per-DECISION control-plane read would be the same
|
|
1330
|
+
* design error the session dial was — a network round-trip for state the
|
|
1331
|
+
* runtime plane re-proves every ~5s heartbeat. The cache serves the
|
|
1332
|
+
* caller from memory, refreshes in the BACKGROUND past half the TTL, and
|
|
1333
|
+
* evicts a replica (or the whole entry) the moment a dial to its cached
|
|
1334
|
+
* address fails. */
|
|
1335
|
+
private readonly presenceCache;
|
|
1336
|
+
constructor(opts: StatelessDecisionDialOptions);
|
|
1337
|
+
/** Enabled = the pod secret is configured. Absent ⇒ the session lane serves
|
|
1338
|
+
* (byte-for-byte behavior). */
|
|
1339
|
+
get enabled(): boolean;
|
|
1340
|
+
/**
|
|
1341
|
+
* Resolve a request's lane target WITHOUT dialing: `null` for a SHARED-lane
|
|
1342
|
+
* model when the dial holds NO shared credential — the caller keeps the
|
|
1343
|
+
* session lane. WITH the shared key, a shared target is dialable and rides
|
|
1344
|
+
* through with its full identity (modelId/variant for the billing repin,
|
|
1345
|
+
* `sharedOrgId` for the provider-aware token). The HTTP handlers call this
|
|
1346
|
+
* FIRST and route non-null targets into decide; decide re-resolves its own
|
|
1347
|
+
* target so the one it reports is the one it actually dialed.
|
|
1348
|
+
*/
|
|
1349
|
+
targetFor(model: string | undefined): Promise<DialTarget | null>;
|
|
1350
|
+
/**
|
|
1351
|
+
* One decision. Returns the intake's `(status, body)` plus the SYNTHESIZED
|
|
1352
|
+
* completed body the lane stamps its billing receipt from — the runtime's
|
|
1353
|
+
* chat body carries `usage` (OpenAI shape) and `urun_timing`; both are the
|
|
1354
|
+
* exact fields the ledger's receipt stamper reads, mapped once here so the
|
|
1355
|
+
* stateless lane prices identically to the session lane. The RESOLVED
|
|
1356
|
+
* target rides along: the handlers repin the billing identity from it
|
|
1357
|
+
* (the dial's own answer, handed out — never asked for again) before
|
|
1358
|
+
* stamping.
|
|
1359
|
+
*/
|
|
1360
|
+
decide(body: Record<string, unknown>, model: string | undefined): Promise<{
|
|
1361
|
+
status: number;
|
|
1362
|
+
body: unknown;
|
|
1363
|
+
completed: unknown;
|
|
1364
|
+
target: DialTarget & {
|
|
1365
|
+
fnName: string;
|
|
1366
|
+
};
|
|
1367
|
+
}>;
|
|
1368
|
+
/** Serve the caller from the cache; a MISS (or a TTL-expired entry) reads
|
|
1369
|
+
* synchronously — the cold path is the one decision that pays the hop —
|
|
1370
|
+
* and an entry past half the TTL fires a single-flight BACKGROUND refresh
|
|
1371
|
+
* that never blocks this caller. */
|
|
1372
|
+
private ensureRows;
|
|
1373
|
+
private readPresence;
|
|
1374
|
+
/** LEAST-OUTSTANDING: pick the ready replica with the fewest in-flight
|
|
1375
|
+
* decisions (ties → the first), skipping replicas in failure cooldown. */
|
|
1376
|
+
pick(rows: RuntimePresenceRow[]): string | null;
|
|
1377
|
+
private post;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
928
1380
|
/**
|
|
929
1381
|
* THE CANONICAL USAGE-QUERY LANE — `POST /v1/usage/requests`.
|
|
930
1382
|
*
|
|
@@ -1220,6 +1672,58 @@ interface LedgerWrite {
|
|
|
1220
1672
|
prefill_ms: number | null;
|
|
1221
1673
|
decode_ms: number | null;
|
|
1222
1674
|
total_ms: number | null;
|
|
1675
|
+
/**
|
|
1676
|
+
* PROXY-OBSERVED TIME TO FIRST STREAMED CONTENT BYTE, in whole
|
|
1677
|
+
* milliseconds — what the CALLER experienced, and therefore the number
|
|
1678
|
+
* Hugging Face's 5s provider-listing gate is about (ENG-337).
|
|
1679
|
+
*
|
|
1680
|
+
* THE RECORD IS THE ONE SOURCE. This is {@link InferenceRecord.ttft_ms}
|
|
1681
|
+
* carried through, measured at the one place TTFT was already measured
|
|
1682
|
+
* (`ProxyStats.track` stamps `turn.ttftMs` on the first text delta) — the
|
|
1683
|
+
* same number the billing log line and the `urun_inference_ttft_seconds`
|
|
1684
|
+
* histogram already publish. The writer adds no second measurement, so the
|
|
1685
|
+
* row cannot disagree with either sink.
|
|
1686
|
+
*
|
|
1687
|
+
* NULL FOR NON-STREAM, NULL FOR UNOBSERVED, NEVER ZERO FOR EITHER. The
|
|
1688
|
+
* record stamps `ttft_ms` only for a streamed turn: a caller who receives
|
|
1689
|
+
* one JSON body at the end was never shown a "first byte", and publishing
|
|
1690
|
+
* the internal timing in its place would put flattering numbers into
|
|
1691
|
+
* exactly the series the HF gate reads. A stream that produced no text
|
|
1692
|
+
* delta before it ended was never observed at all. Null is the only honest
|
|
1693
|
+
* spelling of both; a 0 here would assert a sub-millisecond first byte
|
|
1694
|
+
* nobody measured. (A MEASURED sub-millisecond first byte does round to 0 —
|
|
1695
|
+
* that is a measurement, not an unknown; which side of the `??` a value
|
|
1696
|
+
* arrived on is the whole difference.)
|
|
1697
|
+
*
|
|
1698
|
+
* NOT THE RUNTIME'S `urun_timing.ttft_ms`, which is `queue_ms +
|
|
1699
|
+
* prefill_ms` and still deliberately does not ride the row (see
|
|
1700
|
+
* `TurnResidency`): that is the engine's view of its own internals, this
|
|
1701
|
+
* is the wall-clock the caller saw — network, proxy, cold-wake and all.
|
|
1702
|
+
* Neither can derive the other, so neither is a second spelling of the
|
|
1703
|
+
* other.
|
|
1704
|
+
*/
|
|
1705
|
+
ttft_ms: number | null;
|
|
1706
|
+
/**
|
|
1707
|
+
* PROXY-OBSERVED REQUEST RECEIPT → LAST BYTE, in whole milliseconds: the
|
|
1708
|
+
* end-to-end latency the caller experienced for the whole answer
|
|
1709
|
+
* (ENG-337).
|
|
1710
|
+
*
|
|
1711
|
+
* `Math.round` of the record's `duration_ms` — THE SAME MEASUREMENT
|
|
1712
|
+
* {@link startedAtOf} derives the row's `started_at` from, so the row's
|
|
1713
|
+
* window columns cannot disagree by construction: `started_at + e2e_ms`
|
|
1714
|
+
* and `completed_at` describe one clock. Rounded HERE, at the one place
|
|
1715
|
+
* the row is built, because the ledger's column
|
|
1716
|
+
* (`urun_record_inference_request`'s `p_e2e_ms`) is an `integer` — never
|
|
1717
|
+
* rounded a second time somewhere a new spelling could drift from this
|
|
1718
|
+
* one.
|
|
1719
|
+
*
|
|
1720
|
+
* ALWAYS PRESENT on rows this writer produces, unlike {@link ttft_ms}: the
|
|
1721
|
+
* duration is measured for every completed request, streamed or not,
|
|
1722
|
+
* failed or not. The TYPE still admits null because the wire column is
|
|
1723
|
+
* nullable — the contract is stated rather than implied by what this
|
|
1724
|
+
* writer happens to emit today.
|
|
1725
|
+
*/
|
|
1726
|
+
e2e_ms: number | null;
|
|
1223
1727
|
}
|
|
1224
1728
|
/** What the control plane answers with — the row as it was actually written. */
|
|
1225
1729
|
interface LedgerRecorded {
|
|
@@ -1341,6 +1845,18 @@ interface LedgerFailure {
|
|
|
1341
1845
|
*/
|
|
1342
1846
|
declare const LEDGER_SKIP_REASONS: readonly ["no_response_head", "no_org", "model_unresolved", "no_model_named", "queue_full"];
|
|
1343
1847
|
type LedgerSkipReason = (typeof LEDGER_SKIP_REASONS)[number];
|
|
1848
|
+
/**
|
|
1849
|
+
* THE TWO PRICING STATES a landed row can be in — ENG-317's
|
|
1850
|
+
* NULL-is-not-zero distinction, as a metric label value.
|
|
1851
|
+
*
|
|
1852
|
+
* A CONST ARRAY rather than an inline union because the vocabulary is now
|
|
1853
|
+
* ENUMERATED, not merely constrained: {@link LEDGER_OUTCOMES} materialises
|
|
1854
|
+
* every series this counter can ever produce, and a pricing state that existed
|
|
1855
|
+
* only in a type could not be enumerated. Adding a third state here adds its
|
|
1856
|
+
* series everywhere without a second edit.
|
|
1857
|
+
*/
|
|
1858
|
+
declare const LEDGER_PRICING_STATES: readonly ["priced", "unpriced"];
|
|
1859
|
+
type LedgerPricingState = (typeof LEDGER_PRICING_STATES)[number];
|
|
1344
1860
|
/**
|
|
1345
1861
|
* The TERMINAL fate of one request's ledger row, as a bounded pair of metric
|
|
1346
1862
|
* labels. Every `/v1` request that reaches a lane produces exactly one of
|
|
@@ -1359,7 +1875,7 @@ type LedgerOutcome =
|
|
|
1359
1875
|
/** The row is in the ledger. `priced` / `unpriced` is ENG-317's NULL-is-not-zero distinction. */
|
|
1360
1876
|
{
|
|
1361
1877
|
outcome: 'written';
|
|
1362
|
-
reason:
|
|
1878
|
+
reason: LedgerPricingState;
|
|
1363
1879
|
}
|
|
1364
1880
|
/**
|
|
1365
1881
|
* The row was ALREADY in the ledger and it is OURS — an earlier attempt
|
|
@@ -1369,7 +1885,7 @@ type LedgerOutcome =
|
|
|
1369
1885
|
*/
|
|
1370
1886
|
| {
|
|
1371
1887
|
outcome: 'already_written';
|
|
1372
|
-
reason:
|
|
1888
|
+
reason: LedgerPricingState;
|
|
1373
1889
|
}
|
|
1374
1890
|
/** No row was attempted. */
|
|
1375
1891
|
| {
|
|
@@ -1463,6 +1979,27 @@ interface ResponsesCreateParams {
|
|
|
1463
1979
|
* Forwarded verbatim — the serve runtime is the one validator.
|
|
1464
1980
|
*/
|
|
1465
1981
|
response_format?: unknown;
|
|
1982
|
+
/**
|
|
1983
|
+
* The SPEECH lane's per-request TTS voice (ENG-331, additive like `tools`):
|
|
1984
|
+
* the speaker id a `tts`-task engine synthesizes under (qwen3 CustomVoice
|
|
1985
|
+
* timbre, e.g. "Ryan"). Forwarded on the serve envelope so the runtime's
|
|
1986
|
+
* speak bridge can apply it through the engine's own per-session override
|
|
1987
|
+
* seam (`SpeakSessionConfig.voice` → `set_voice`, urun-python
|
|
1988
|
+
* tts_engine.py). Absent -> key absent: the engine's catalog-configured
|
|
1989
|
+
* default voice applies, today's behavior.
|
|
1990
|
+
*/
|
|
1991
|
+
voice?: string;
|
|
1992
|
+
/**
|
|
1993
|
+
* The transcription lane's per-request LANGUAGE hint (ENG-332, additive
|
|
1994
|
+
* like `voice`): the ISO code a `stt`-task engine transcribes under
|
|
1995
|
+
* (LiveKit's `openai.STT` always sends one, so refusing the field would
|
|
1996
|
+
* break the stock consumer). Forwarded VERBATIM on the serve envelope so
|
|
1997
|
+
* the runtime validates it against the transcribe engine's declared
|
|
1998
|
+
* `served_languages` — the proxy never validates languages itself (it
|
|
1999
|
+
* cannot know the engine's set) and never silently drops the field.
|
|
2000
|
+
* Absent -> key absent, never an empty string.
|
|
2001
|
+
*/
|
|
2002
|
+
language?: string;
|
|
1466
2003
|
}
|
|
1467
2004
|
/** How one pinned session ended (the native phase machinery's terminal step). */
|
|
1468
2005
|
interface SessionEndInfo {
|
|
@@ -1571,6 +2108,42 @@ interface ProxyClients {
|
|
|
1571
2108
|
* allow-all, and never a chat model binding an audio session.
|
|
1572
2109
|
*/
|
|
1573
2110
|
supportsAudio?(model: string | undefined): Promise<boolean>;
|
|
2111
|
+
/**
|
|
2112
|
+
* The speech lane's capability gate (proxy/speech.ts), backed by
|
|
2113
|
+
* ModelRouter.supportsSpeech — the caller-org catalog's `task` column,
|
|
2114
|
+
* which must vouch `tts` (NOT `stt`: a transcription model must never be
|
|
2115
|
+
* asked to synthesize) INVARIANTLY across placements. Optional at the seam
|
|
2116
|
+
* exactly like {@link supportsImages}: hand-built clients may omit it, in
|
|
2117
|
+
* which case the mounted speech lane answers the loud 501 — never a
|
|
2118
|
+
* silent allow-all, and never a chat model asked to speak.
|
|
2119
|
+
*/
|
|
2120
|
+
supportsSpeech?(model: string | undefined): Promise<boolean>;
|
|
2121
|
+
/**
|
|
2122
|
+
* The transcription lane's capability gate (proxy/transcriptions.ts), backed
|
|
2123
|
+
* by ModelRouter.supportsTranscription — the caller-org catalog's `task`
|
|
2124
|
+
* column, which must vouch `stt` (NOT `tts`: a synthesis model must never
|
|
2125
|
+
* be asked to transcribe; NOT chat) INVARIANTLY across placements. Optional
|
|
2126
|
+
* at the seam exactly like {@link supportsSpeech}: hand-built clients may
|
|
2127
|
+
* omit it, in which case the mounted transcription lane answers the loud
|
|
2128
|
+
* 501 — never a silent allow-all, and never a chat model asked to
|
|
2129
|
+
* transcribe.
|
|
2130
|
+
*/
|
|
2131
|
+
supportsTranscription?(model: string | undefined): Promise<boolean>;
|
|
2132
|
+
/**
|
|
2133
|
+
* THE SPEECH LANE'S VOICE-SET RESOLUTION (proxy/speech.ts): the voices
|
|
2134
|
+
* `/v1/models` publishes for `model` (ModelRouter.speechVoices — the SAME
|
|
2135
|
+
* openrouter-doc resolver the listing's enrichment applies, over the SAME
|
|
2136
|
+
* catalog join the tts gate uses), so the lane validates the requested
|
|
2137
|
+
* `voice` against exactly the set the listing publishes — ONE list
|
|
2138
|
+
* everywhere, never a second copy. The resolution also carries the catalog
|
|
2139
|
+
* defect (`engine_args.voices` present but empty/malformed/disagreeing
|
|
2140
|
+
* across placements), which the lane answers with the loud 500 — never a
|
|
2141
|
+
* silent fallback to the qwen3 card's nine. Optional at the seam exactly
|
|
2142
|
+
* like {@link supportsSpeech}: hand-built clients may omit it, in which
|
|
2143
|
+
* case the mounted speech lane answers the loud 501 — skipping the check
|
|
2144
|
+
* silently would downgrade the lane to unvalidated voices.
|
|
2145
|
+
*/
|
|
2146
|
+
speechVoices?(model: string | undefined): Promise<SpeechVoicesResolution>;
|
|
1574
2147
|
/**
|
|
1575
2148
|
* `GET /v1/images/models` — the image-capable slice of the model surface
|
|
1576
2149
|
* (ModelRouter.imageModelList: deployed task-image apps + the shared block
|
|
@@ -1578,6 +2151,14 @@ interface ProxyClients {
|
|
|
1578
2151
|
* its absence is the loud 501, never a silently empty list.
|
|
1579
2152
|
*/
|
|
1580
2153
|
listImageModels?(): Promise<unknown>;
|
|
2154
|
+
/**
|
|
2155
|
+
* `GET /v1/audio/models` — the transcription-capable (task `stt`) slice of
|
|
2156
|
+
* the model surface (ModelRouter.speechToTextModelList: deployed stt apps +
|
|
2157
|
+
* the shared block joined against the catalog), the audio twin of
|
|
2158
|
+
* {@link listImageModels}. Optional at the seam for the same reason; its
|
|
2159
|
+
* absence is the loud 501, never a silently empty list.
|
|
2160
|
+
*/
|
|
2161
|
+
listSpeechToTextModels?(): Promise<unknown>;
|
|
1581
2162
|
/**
|
|
1582
2163
|
* THE PRE-DIAL DEFAULT FOR THE LEDGER'S BILLING IDENTITY
|
|
1583
2164
|
* (ModelRouter.catalogIdentityFor): the CATALOG `(model_id, variant)` a
|
|
@@ -1643,8 +2224,47 @@ interface ProxyClients {
|
|
|
1643
2224
|
* session; repeat calls return the same lane. Optional at the seam because
|
|
1644
2225
|
* text-only embeddings exist — but a surface that RECEIVES audio while the
|
|
1645
2226
|
* embedder wired no `openAudio` must fail LOUD, never drop chunks.
|
|
1646
|
-
|
|
1647
|
-
|
|
2227
|
+
*
|
|
2228
|
+
* `signal` aborts when the CLIENT that asked for the lane went away while
|
|
2229
|
+
* the lane's admission was still pending (the realtime lane's raw socket
|
|
2230
|
+
* close) — the backhaul aborts its `whenLive` wait on it and hands the
|
|
2231
|
+
* queued admission ticket back through core `Session.cancel()`, so a
|
|
2232
|
+
* vanished client never holds the sense pod's one-session slot. The
|
|
2233
|
+
* client-gone wait is REFERENCE-COUNTED per pooled entry: several callers
|
|
2234
|
+
* can wait on ONE queued admission, and the ticket goes back only when
|
|
2235
|
+
* the LAST waiter aborts — one client's disconnect rejects only its own
|
|
2236
|
+
* open, never another caller's admission (round-1 review, Greptile).
|
|
2237
|
+
*/
|
|
2238
|
+
openAudio?(model: string | undefined, signal?: AbortSignal): Promise<ProxyAudioLane>;
|
|
2239
|
+
/**
|
|
2240
|
+
* THE PINNED AUDIO OPEN (speech + realtime lanes): the NATIVE audio lane of
|
|
2241
|
+
* the EXACT pooled session `handle` names (ModelRouter.sessionForHandle — a
|
|
2242
|
+
* gone or replaced session throws {@link SessionGoneError} LOUDLY, never a
|
|
2243
|
+
* fresh session opened behind the handle's back). The speech lane opens its
|
|
2244
|
+
* audio collector here and dials its response turn on the SAME handle
|
|
2245
|
+
* ({@link createResponseOn}), so BOTH legs are one session identity by
|
|
2246
|
+
* construction: a re-home can never move the turn to a second session
|
|
2247
|
+
* while the collector stays subscribed to the first (which missed the
|
|
2248
|
+
* replacement's audio and answered a voiceless 502, or worse, served a
|
|
2249
|
+
* concurrent collector another session's frames). The realtime binding
|
|
2250
|
+
* (openai-realtime/binding.ts, ENG-446) rides the SAME pin: one handle per
|
|
2251
|
+
* connection for the bridge AND every turn, so a re-home can never detach
|
|
2252
|
+
* the turn from the bridge the audio rides. Optional at the seam exactly
|
|
2253
|
+
* like {@link openAudio}; both lanes answer its absence with the loud 501 —
|
|
2254
|
+
* never a voiceless downgrade.
|
|
2255
|
+
*
|
|
2256
|
+
* `signal` carries {@link openAudio}'s client-gone semantics onto the
|
|
2257
|
+
* pinned open (ENG-446): the realtime lane threads the upgrade's
|
|
2258
|
+
* client-socket abort down here — abort while the pinned session's
|
|
2259
|
+
* admission is still queued abandons the queued session (core
|
|
2260
|
+
* `Session.cancel()`), and the open rejects. A vanished client never holds
|
|
2261
|
+
* the sense pod's one-session slot through a queue wait it is no longer
|
|
2262
|
+
* party to. An already-cached lane is returned untouched — and the
|
|
2263
|
+
* client-gone wait rides the SAME per-entry refcount as {@link openAudio}:
|
|
2264
|
+
* one of several waiters aborting rejects only that caller's open, the
|
|
2265
|
+
* queued admission going back only at the LAST waiter's abort.
|
|
2266
|
+
*/
|
|
2267
|
+
openAudioOn?(handle: string, signal?: AbortSignal): Promise<PinnedAudioLane>;
|
|
1648
2268
|
/**
|
|
1649
2269
|
* Open (or reuse) the NATIVE video FRAME lane on the pooled session `model`
|
|
1650
2270
|
* routes to (transport/media.ts `enableSessionVideo`: discrete JPEG frames →
|
|
@@ -1675,6 +2295,38 @@ interface ProxyClients {
|
|
|
1675
2295
|
* serve-side session-affinity tag rides (urun-python#1556/#1582).
|
|
1676
2296
|
*/
|
|
1677
2297
|
sessionHandle(model: string | undefined): Promise<string>;
|
|
2298
|
+
/**
|
|
2299
|
+
* THE SPEECH LANE'S OWN HANDLE (greptile urun-ts#500): the opaque handle
|
|
2300
|
+
* for the SPEECH-SCOPED pooled session (`speech:<pool key>` — routing.ts
|
|
2301
|
+
* {@link ModelRouter.speechSessionFor}), a slot no interactive lane can
|
|
2302
|
+
* resolve. The speech lane resolves its collector AND its turn through
|
|
2303
|
+
* THIS handle ({@link openAudioOn} + {@link createResponseOn} unchanged),
|
|
2304
|
+
* because the per-session AudioBridge broadcasts every untagged output
|
|
2305
|
+
* frame to every subscriber: a speech collection on the INTERACTIVE
|
|
2306
|
+
* pooled session would stitch a concurrent Realtime/Gemini Live turn's
|
|
2307
|
+
* frames into the returned WAV. Optional at the seam exactly like
|
|
2308
|
+
* {@link openAudioOn}; the speech lane answers its absence with the loud
|
|
2309
|
+
* 501 — NEVER a silent fall-back to the shared interactive session
|
|
2310
|
+
* (that "fallback" is precisely the mixed-audio defect).
|
|
2311
|
+
*/
|
|
2312
|
+
speechSessionHandle?(model: string | undefined): Promise<string>;
|
|
2313
|
+
/**
|
|
2314
|
+
* EVICT the SPEECH-scoped pooled session a handle names (CodeRabbit
|
|
2315
|
+
* urun-ts#500, round 8) — through the pool's OWN identity-guarded
|
|
2316
|
+
* eviction path (ModelRouter.evict under the handle's `speech:<key>`
|
|
2317
|
+
* slot, never a bespoke close), so the NEXT speechSessionHandle acquire
|
|
2318
|
+
* opens a FRESH session. The lane calls this after a collector failure
|
|
2319
|
+
* that leaves the session's audio state unknown — above all a turn-cap
|
|
2320
|
+
* timeout, where the serialized lane is released while the pinned
|
|
2321
|
+
* session may still be emitting the timed-out turn's audio: a later
|
|
2322
|
+
* request on the SAME entry would collect those stale frames into its
|
|
2323
|
+
* own answer. Throws {@link SessionGoneError} when the handle names a
|
|
2324
|
+
* session the pool no longer holds (the eviction's goal is then already
|
|
2325
|
+
* true — the lane treats it as success). Optional at the seam exactly
|
|
2326
|
+
* like {@link speechSessionHandle}; the speech lane answers its absence
|
|
2327
|
+
* with the loud 501 — never a silent reuse of the possibly-stale entry.
|
|
2328
|
+
*/
|
|
2329
|
+
evictSpeechSession?(handle: string): Promise<void>;
|
|
1678
2330
|
/**
|
|
1679
2331
|
* createResponse PINNED to the exact session a handle names
|
|
1680
2332
|
* (ModelRouter.sessionForHandle). Throws SessionGoneError LOUDLY when that
|
|
@@ -1687,16 +2339,48 @@ interface ProxyClients {
|
|
|
1687
2339
|
* Subscribe to the pinned session's terminal end via core's NATIVE phase
|
|
1688
2340
|
* machinery (Session.onPhase → terminal 'expired'/'ended'/'error'). Fires
|
|
1689
2341
|
* `cb` once. Throws SessionGoneError if the handle's session is already
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
2342
|
+
* gone — which doubles as the loud reattach check at resume time. Returns
|
|
2343
|
+
* the unsubscribe.
|
|
2344
|
+
*/
|
|
1693
2345
|
onSessionEnd(handle: string, cb: (end: SessionEndInfo) => void): Promise<() => void>;
|
|
2346
|
+
/**
|
|
2347
|
+
* THE STATELESS DIRECT PATH (stateless-dispatch.ts): the decision dial to
|
|
2348
|
+
* the serving pods' `POST /v1/decision` intake, present only when the
|
|
2349
|
+
* deployment carries the runtime scoped-token secret. Absent ⇒ every
|
|
2350
|
+
* decision rides the session lane exactly as before (the staged rollout).
|
|
2351
|
+
* Per-org: the HOSTED registry builds one per tenant so the minted token's
|
|
2352
|
+
* tenant claim is always the CALLER's org.
|
|
2353
|
+
*/
|
|
2354
|
+
readonly stateless?: StatelessDecisionDial;
|
|
1694
2355
|
}
|
|
1695
2356
|
/**
|
|
1696
2357
|
* The audio lane handle `openAudio` returns — structurally the transport
|
|
1697
2358
|
* AudioBridge (media.ts): base64 PCM16 @24 kHz mono in both directions.
|
|
2359
|
+
* `sessionId` is the pooled uRun session the lane rides (core `Session.id`),
|
|
2360
|
+
* when the backhaul reports it — the identity the realtime lane's structured
|
|
2361
|
+
* lifecycle log carries so an orphaned session is traceable from the proxy
|
|
2362
|
+
* log alone. Null is the HONEST ABSENCE (the same line {@link
|
|
2363
|
+
* PinnedAudioLane.sessionId} and the re-homing dial's `sessionIdOf` take —
|
|
2364
|
+
* an id-less session reports no id, never a fabricated one).
|
|
2365
|
+
*/
|
|
2366
|
+
type ProxyAudioLane = Pick<AudioBridge, 'appendInputAudio' | 'onOutputAudio'> & {
|
|
2367
|
+
sessionId?: string | null;
|
|
2368
|
+
};
|
|
2369
|
+
/**
|
|
2370
|
+
* The PINNED audio lane handle `openAudioOn` returns ({@link ProxyClients.openAudioOn}):
|
|
2371
|
+
* the session's native audio lane PLUS the identity of the exact pooled session
|
|
2372
|
+
* it is bound to — the two facts the speech lane needs to keep its collector and
|
|
2373
|
+
* its response turn on ONE session identity.
|
|
1698
2374
|
*/
|
|
1699
|
-
|
|
2375
|
+
interface PinnedAudioLane extends ProxyAudioLane {
|
|
2376
|
+
/**
|
|
2377
|
+
* The uRun session id of the pooled session this lane is bound to — the
|
|
2378
|
+
* honest instance link for a request whose collector and turn ride one
|
|
2379
|
+
* handle, the same derivation the dial report's `sessionId` uses. Null
|
|
2380
|
+
* when the entry carries no native session id; never a fabricated one.
|
|
2381
|
+
*/
|
|
2382
|
+
readonly sessionId: string | null;
|
|
2383
|
+
}
|
|
1700
2384
|
/**
|
|
1701
2385
|
* The video frame-lane handle `openVideo` returns — structurally the
|
|
1702
2386
|
* transport VideoFrameLane (media.ts): one raw encoded JPEG frame per call,
|