realtime-avatar 0.12.0 → 0.13.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/{chunk-FQWEKJCX.js → chunk-NOHMMTFR.js} +1 -1
- package/dist/{chunk-ZFYNUHKB.js → chunk-UVNDDNU2.js} +1 -1
- package/dist/express.js +2 -2
- package/dist/hono.js +2 -2
- package/dist/index.js +1 -1
- package/dist/nextjs.js +2 -2
- package/dist/{proxy-client-8cQOLLE_.d.ts → proxy-client-BoUJWSkf.d.ts} +28 -5
- package/dist/react-native.d.ts +2 -2
- package/dist/react-native.js +158 -3
- package/dist/react.d.ts +2 -2
- package/dist/react.js +161 -4
- package/dist/server.js +1 -1
- package/dist/tanstack-start.js +2 -2
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-
|
|
1
|
+
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-UVNDDNU2.js';
|
|
2
2
|
|
|
3
3
|
// ../proxy/src/config.ts
|
|
4
4
|
var json = (body, status = 200) => new Response(JSON.stringify(body), {
|
|
@@ -236,7 +236,7 @@ function isTransient(cause) {
|
|
|
236
236
|
|
|
237
237
|
// ../http-client/src/client.ts
|
|
238
238
|
var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
|
|
239
|
-
var SDK_VERSION = "0.
|
|
239
|
+
var SDK_VERSION = "0.13.0";
|
|
240
240
|
var RealtimeAvatar = class {
|
|
241
241
|
#apiKey;
|
|
242
242
|
#baseUrl;
|
package/dist/express.js
CHANGED
package/dist/hono.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-UVNDDNU2.js';
|
package/dist/nextjs.js
CHANGED
|
@@ -403,6 +403,22 @@ interface Governor {
|
|
|
403
403
|
healthySinceMs: number | null;
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
+
declare const NETWORK_QUALITY_POLICY: {
|
|
407
|
+
readonly startupGraceMs: 3000;
|
|
408
|
+
readonly maxSampleGapMs: 1500;
|
|
409
|
+
readonly minimumPackets: 20;
|
|
410
|
+
readonly lossWindowSamples: 3;
|
|
411
|
+
readonly lossRatio: 0.05;
|
|
412
|
+
readonly jitterSeconds: 0.1;
|
|
413
|
+
readonly impairedFps: 12;
|
|
414
|
+
readonly downgradeMs: 2000;
|
|
415
|
+
readonly floorMs: 2000;
|
|
416
|
+
readonly notifyAfterDowngradeMs: 5000;
|
|
417
|
+
readonly recoverMs: 5000;
|
|
418
|
+
readonly clearNoticeMs: 10000;
|
|
419
|
+
};
|
|
420
|
+
type NetworkQualityStatus = "unknown" | "healthy" | "poor";
|
|
421
|
+
|
|
406
422
|
/** How the media is fit into the surface box. Mirrors CSS `object-fit`. */
|
|
407
423
|
type AvatarVideoFit = "contain" | "cover";
|
|
408
424
|
type AvatarVideoSurfaceProps = {
|
|
@@ -439,8 +455,13 @@ type AvatarVideoSurfaceProps = {
|
|
|
439
455
|
* Enable the subscriber-side fast-down/sticky-up quality governor. It starts on
|
|
440
456
|
* the lower simulcast rung, reacts immediately to an SFU pause or decoded-frame
|
|
441
457
|
* freeze, and only probes the full layer after a clean dwell. Default true.
|
|
458
|
+
* False releases the manual cap to HIGH on each subscription without running
|
|
459
|
+
* the governor. SFU bandwidth adaptation and live/idle fallback remain active.
|
|
442
460
|
*/
|
|
443
|
-
adaptiveQuality?: boolean;
|
|
461
|
+
adaptiveQuality?: boolean | "network-only";
|
|
462
|
+
/** Opt-in "network-only" keeps HIGH unless sustained receiver/network impairment is
|
|
463
|
+
* observed. Status is unknown until enough fresh data exists; poor after 5s. */
|
|
464
|
+
onNetworkStatusChange?: (status: NetworkQualityStatus) => void;
|
|
444
465
|
/**
|
|
445
466
|
* Opening bet for the governor when `adaptiveQuality` is on. `"high"` opens on the
|
|
446
467
|
* FULL simulcast layer under probation — sharp from frame 1, no LOW→HIGH layer-walk —
|
|
@@ -1539,9 +1560,11 @@ type FreezeReadingFn = () => {
|
|
|
1539
1560
|
inhibited: boolean;
|
|
1540
1561
|
};
|
|
1541
1562
|
interface UseAvatarQualityGovernorInput {
|
|
1542
|
-
/** Master switch (product policy
|
|
1543
|
-
*
|
|
1544
|
-
enabled: boolean;
|
|
1563
|
+
/** Master switch (product policy). Off releases the manual cap to HIGH on each
|
|
1564
|
+
* subscription, with no governor tick or congestion listeners. SFU adaptation stays on. */
|
|
1565
|
+
enabled: boolean | "network-only";
|
|
1566
|
+
/** Sustained, receiver-evidenced impairment. Never inferred from rendering jitter alone. */
|
|
1567
|
+
onNetworkStatusChange?: (status: NetworkQualityStatus) => void;
|
|
1545
1568
|
/** The player's rVFC freeze reading getter (see FreezeReadingFn). Optional: without
|
|
1546
1569
|
* it the governor still reacts to Paused + getStats freezes, just without the
|
|
1547
1570
|
* cross-browser rVFC signal. */
|
|
@@ -1624,4 +1647,4 @@ interface ProxyClientOptions {
|
|
|
1624
1647
|
}
|
|
1625
1648
|
declare function createProxyClient(options: ProxyClientOptions): AvatarSessionClient;
|
|
1626
1649
|
|
|
1627
|
-
export { RECONNECT_BACKOFF_MS as $, type AvatarVideoFit as A, type BehaviorSnapshot as B, type CallTranscript as C, DEFAULT_APPROACHING_END_LEAD_SECONDS as D, type EndReason as E, type FishTtsModel as F, type Governor as G, type GraceWindowClosedEvent as H, type GraceWindowOpenEvent as I, type GraceWindowState as J, type IdleWarningEvent as K, type LiveKitSessionGrant as L, type InboundRtpCursor as M, type InboundRtpReading as N, type KnownBehaviorState as O, type LLMProvider as P, type LLMSelection as Q, type LiveKitAvatarGrantState as R, type LiveKitAvatarGrantStatus as S, type LiveKitCapacityState as T, type LiveKitConnectionStatus as U, type LiveKitSessionRequest as V, type LiveKitSessionStartResult as W, MAX_RECONNECT_ATTEMPTS as X, MAX_SESSION_INSTRUCTIONS_CHARS as Y, type ProxyClientOptions as Z, type QualityCap as _, AdaptivePlayoutController as a, type RealtimeAvatarRequestOptions as a0, type RealtimeSessionApi as a1, type RealtimeSessionMedia as a2, type RealtimeSessionRoomSinks as a3, type ReconnectPolicy as a4, type ReconnectingEvent as a5, type RecoveryState as a6, type RetryStep as a7, type SessionBehavior as a8, type SessionClip as a9, useAvatarAdaptivePlayoutDelay as aA, useAvatarPlayoutDelay as aB, useAvatarQualityGovernor as aC, useCallTranscript as aD, useLiveKitAvatarGrant as aE, useLiveTrackProducing as aF, useMicLease as aG, useRealtimeSession as aH, useReleaseMicLeaseOnTrackEnded as aI, useSessionLifecycle as aJ, AvatarVideoSurface as aK, type AvatarVideoSurfaceProps as aL, type LivePlaybackKeeper as aM, MIC_LEASE_ENDED_TIMEOUT_MS as aN, type MicLease as aO, type PlayableVideoElement as
|
|
1650
|
+
export { RECONNECT_BACKOFF_MS as $, type AvatarVideoFit as A, type BehaviorSnapshot as B, type CallTranscript as C, DEFAULT_APPROACHING_END_LEAD_SECONDS as D, type EndReason as E, type FishTtsModel as F, type Governor as G, type GraceWindowClosedEvent as H, type GraceWindowOpenEvent as I, type GraceWindowState as J, type IdleWarningEvent as K, type LiveKitSessionGrant as L, type InboundRtpCursor as M, type InboundRtpReading as N, type KnownBehaviorState as O, type LLMProvider as P, type LLMSelection as Q, type LiveKitAvatarGrantState as R, type LiveKitAvatarGrantStatus as S, type LiveKitCapacityState as T, type LiveKitConnectionStatus as U, type LiveKitSessionRequest as V, type LiveKitSessionStartResult as W, MAX_RECONNECT_ATTEMPTS as X, MAX_SESSION_INSTRUCTIONS_CHARS as Y, type ProxyClientOptions as Z, type QualityCap as _, AdaptivePlayoutController as a, type RealtimeAvatarRequestOptions as a0, type RealtimeSessionApi as a1, type RealtimeSessionMedia as a2, type RealtimeSessionRoomSinks as a3, type ReconnectPolicy as a4, type ReconnectingEvent as a5, type RecoveryState as a6, type RetryStep as a7, type SessionBehavior as a8, type SessionClip as a9, useAvatarAdaptivePlayoutDelay as aA, useAvatarPlayoutDelay as aB, useAvatarQualityGovernor as aC, useCallTranscript as aD, useLiveKitAvatarGrant as aE, useLiveTrackProducing as aF, useMicLease as aG, useRealtimeSession as aH, useReleaseMicLeaseOnTrackEnded as aI, useSessionLifecycle as aJ, AvatarVideoSurface as aK, type AvatarVideoSurfaceProps as aL, type LivePlaybackKeeper as aM, MIC_LEASE_ENDED_TIMEOUT_MS as aN, type MicLease as aO, NETWORK_QUALITY_POLICY as aP, type NetworkQualityStatus as aQ, type PlayableVideoElement as aR, RealtimeAvatarCapacityError as aS, RealtimeAvatarLiveKitRoom as aT, type RealtimeAvatarLiveKitRoomProps as aU, shouldReplayPendingTurn as aV, type SessionClocks as aa, type SessionEndReason as ab, type SessionLifecycleApi as ac, type SessionLifecyclePhase as ad, type SessionLifecyclePhaseKind as ae, SessionLifecycleRoomBridge as af, type SessionLifecycleRoomBridgeProps as ag, type SurfaceLayers as ah, type TurnState as ai, type TurnTimeoutEvent as aj, type UseAvatarQualityGovernorInput as ak, type UseLiveKitAvatarGrantInput as al, type UseRealtimeSessionInput as am, type UseSessionLifecycleInput as an, type VoiceSpec as ao, type VoiceSpecInput as ap, capacityErrorFromBusy as aq, capacityStateFromGrant as ar, createProxyClient as as, isNativeLiveTrackSubscribed as at, knownBehaviorStates as au, mapTurnState as av, readInboundRtp as aw, sessionBehaviorSchema as ax, sessionClipSchema as ay, splitCallTranscript as az, type AdaptivePlayoutDecision as b, type AdaptivePlayoutOptions as c, type AdaptivePlayoutSample as d, type ApproachingEndEvent as e, type ApproachingEndReason as f, type AvatarSessionClient as g, type CallTranscriptSegment as h, type CartesiaTtsModel as i, type ClipResult as j, type ClosingTurnResult as k, type CreditsLowEvent as l, DEFAULT_CREDITS_LOW_LEAD_SECONDS as m, DEFAULT_GOVERNOR_CONFIG as n, DEFAULT_GRACE_CEILING_SECONDS as o, DEFAULT_GRACE_WINDOW_LEAD_SECONDS as p, DEFAULT_IDLE_SECONDS as q, DEFAULT_IDLE_WARN_LEAD_SECONDS as r, DEFAULT_TURN_TIMEOUT_SECONDS as s, type EndedEvent as t, type ExtendResult as u, type FreezeReadingFn as v, type GovernorAction as w, type GovernorConfig as x, type GovernorSignal as y, type GovernorState as z };
|
package/dist/react-native.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { LiveKitRoomProps } from '@livekit/react-native';
|
|
2
2
|
export { AudioSession, VideoTrack, VideoTrackProps, registerGlobals } from '@livekit/react-native';
|
|
3
3
|
import { ReactNode, ReactElement } from 'react';
|
|
4
|
-
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-
|
|
5
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, at as isNativeLiveTrackSubscribed, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aF as useLiveTrackProducing, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-
|
|
4
|
+
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-BoUJWSkf.js';
|
|
5
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, at as isNativeLiveTrackSubscribed, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aF as useLiveTrackProducing, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-BoUJWSkf.js';
|
|
6
6
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
7
|
export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
|
|
8
8
|
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
package/dist/react-native.js
CHANGED
|
@@ -3,7 +3,7 @@ export { AudioSession, VideoTrack, registerGlobals } from '@livekit/react-native
|
|
|
3
3
|
import { useEffect, createElement, useRef, useState, useCallback, useMemo } from 'react';
|
|
4
4
|
import { useTranscriptions, useLocalParticipant, useMaybeRoomContext, useVoiceAssistant, useConnectionState, isTrackReference, useRoomContext, useChat } from '@livekit/components-react';
|
|
5
5
|
export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
|
|
6
|
-
import { ConnectionQuality, RoomEvent, TrackEvent, ConnectionState, Track,
|
|
6
|
+
import { VideoQuality, ConnectionQuality, RoomEvent, TrackEvent, ConnectionState, Track, ConnectionError, ConnectionErrorReason, DisconnectReason, Room } from 'livekit-client';
|
|
7
7
|
export { DisconnectReason, Room, RoomEvent, Track } from 'livekit-client';
|
|
8
8
|
import { Platform, StyleSheet, Animated, Image, View, Text } from 'react-native';
|
|
9
9
|
import { z } from 'zod';
|
|
@@ -877,6 +877,144 @@ var resolveLowCapQuality = (declaredLayerQualities, lowUnhealthy = 0) => {
|
|
|
877
877
|
return lowUnhealthy >= LOW_CAP_STEP_AFTER_UNHEALTHY ? sorted[0] : sorted[sorted.length - 2];
|
|
878
878
|
};
|
|
879
879
|
|
|
880
|
+
// ../client/src/react/network-quality.ts
|
|
881
|
+
var NETWORK_QUALITY_POLICY = {
|
|
882
|
+
startupGraceMs: 3e3,
|
|
883
|
+
maxSampleGapMs: 1500,
|
|
884
|
+
minimumPackets: 20,
|
|
885
|
+
lossRatio: 0.05,
|
|
886
|
+
jitterSeconds: 0.1,
|
|
887
|
+
impairedFps: 12,
|
|
888
|
+
downgradeMs: 2e3,
|
|
889
|
+
floorMs: 2e3,
|
|
890
|
+
notifyAfterDowngradeMs: 5e3,
|
|
891
|
+
recoverMs: 5e3,
|
|
892
|
+
clearNoticeMs: 1e4
|
|
893
|
+
};
|
|
894
|
+
function readNetworkVideoSample(report) {
|
|
895
|
+
const samples = [];
|
|
896
|
+
report?.forEach((row) => {
|
|
897
|
+
if (row.type !== "inbound-rtp" || (row.kind ?? row.mediaType) !== "video") return;
|
|
898
|
+
const { timestamp, framesDecoded, packetsReceived, packetsLost, jitter } = row;
|
|
899
|
+
if (typeof row.id !== "string" || ![timestamp, framesDecoded, packetsReceived, packetsLost, jitter].every(
|
|
900
|
+
(n) => typeof n === "number" && Number.isFinite(n)
|
|
901
|
+
) || timestamp < 0 || framesDecoded < 0 || packetsReceived < 0 || jitter < 0) return;
|
|
902
|
+
samples.push({
|
|
903
|
+
id: `${row.id}:${row.ssrc ?? ""}`,
|
|
904
|
+
timestamp,
|
|
905
|
+
framesDecoded,
|
|
906
|
+
packetsReceived,
|
|
907
|
+
packetsLost,
|
|
908
|
+
jitter
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
return samples.length === 1 ? samples[0] : null;
|
|
912
|
+
}
|
|
913
|
+
function initNetworkQuality(nowMs) {
|
|
914
|
+
return {
|
|
915
|
+
cap: "high",
|
|
916
|
+
status: "unknown",
|
|
917
|
+
sample: null,
|
|
918
|
+
lastPollMs: null,
|
|
919
|
+
openedMs: nowMs,
|
|
920
|
+
badMs: 0,
|
|
921
|
+
postDowngradeBadMs: 0,
|
|
922
|
+
reducedBadMs: 0,
|
|
923
|
+
cleanMs: 0,
|
|
924
|
+
packetWindow: []
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function stepNetworkQuality(previous, sample, nowMs, options) {
|
|
928
|
+
const p = NETWORK_QUALITY_POLICY;
|
|
929
|
+
const s = { ...previous, sample, lastPollMs: nowMs };
|
|
930
|
+
const before = previous.sample;
|
|
931
|
+
const pollMs = previous.lastPollMs === null ? 0 : nowMs - previous.lastPollMs;
|
|
932
|
+
const elapsedMs = sample && before ? sample.timestamp - before.timestamp : 0;
|
|
933
|
+
const interrupted = options.inhibited || pollMs <= 0 || pollMs > p.maxSampleGapMs;
|
|
934
|
+
const valid = !interrupted && sample !== null && before !== null && sample.id === before.id && elapsedMs > 0 && elapsedMs <= p.maxSampleGapMs && sample.framesDecoded >= before.framesDecoded && sample.packetsReceived >= before.packetsReceived;
|
|
935
|
+
if (!valid || nowMs - s.openedMs < p.startupGraceMs) {
|
|
936
|
+
s.badMs = s.postDowngradeBadMs = s.reducedBadMs = s.cleanMs = 0;
|
|
937
|
+
s.packetWindow = [];
|
|
938
|
+
if (options.inhibited) s.sample = null;
|
|
939
|
+
return s;
|
|
940
|
+
}
|
|
941
|
+
const received = sample.packetsReceived - before.packetsReceived;
|
|
942
|
+
const lost = Math.max(0, sample.packetsLost - before.packetsLost);
|
|
943
|
+
s.packetWindow = [...previous.packetWindow, { received, lost }].slice(-3);
|
|
944
|
+
const totals = s.packetWindow.reduce((sum, interval) => ({
|
|
945
|
+
received: sum.received + interval.received,
|
|
946
|
+
lost: sum.lost + interval.lost
|
|
947
|
+
}), { received: 0, lost: 0 });
|
|
948
|
+
const packets = totals.received + totals.lost;
|
|
949
|
+
const fps = (sample.framesDecoded - before.framesDecoded) * 1e3 / elapsedMs;
|
|
950
|
+
const loss = packets >= p.minimumPackets ? totals.lost / packets : null;
|
|
951
|
+
const impaired = options.paused || fps < p.impairedFps;
|
|
952
|
+
const bad = options.paused || impaired && (loss !== null && loss >= p.lossRatio || received >= p.minimumPackets && sample.jitter >= p.jitterSeconds);
|
|
953
|
+
const clean = received > 0 && !options.paused && loss !== null && loss < p.lossRatio && sample.jitter < p.jitterSeconds && fps >= p.impairedFps;
|
|
954
|
+
const duration = Math.min(pollMs, elapsedMs);
|
|
955
|
+
s.badMs = bad ? previous.badMs + duration : 0;
|
|
956
|
+
s.postDowngradeBadMs = bad && previous.cap !== "high" ? previous.postDowngradeBadMs + duration : 0;
|
|
957
|
+
s.cleanMs = clean ? previous.cleanMs + duration : 0;
|
|
958
|
+
s.reducedBadMs = bad && fps === 0 && previous.cap !== "high" ? previous.reducedBadMs + duration : 0;
|
|
959
|
+
if (s.badMs >= p.downgradeMs && s.cap === "high") s.cap = "reduced";
|
|
960
|
+
else if (s.reducedBadMs >= p.floorMs && fps === 0 && s.cap === "reduced") s.cap = "floor";
|
|
961
|
+
if (s.postDowngradeBadMs >= p.notifyAfterDowngradeMs) s.status = "poor";
|
|
962
|
+
if (s.cleanMs >= p.recoverMs) s.cap = "high";
|
|
963
|
+
if (s.cleanMs >= p.clearNoticeMs) s.status = "healthy";
|
|
964
|
+
return s;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// ../client/src/react/network-quality-binding.ts
|
|
968
|
+
function bindNetworkQuality(publication, inhibited, notify, tickMs) {
|
|
969
|
+
let disposed = false;
|
|
970
|
+
let running = false;
|
|
971
|
+
let state = initNetworkQuality(performance.now());
|
|
972
|
+
const apply = () => {
|
|
973
|
+
const qualities = (publication.trackInfo?.layers ?? []).map((l) => l.quality);
|
|
974
|
+
const cap = state.cap === "high" ? VideoQuality.HIGH : resolveLowCapQuality(qualities, state.cap === "floor" ? 2 : 0);
|
|
975
|
+
try {
|
|
976
|
+
publication.setVideoQuality(cap);
|
|
977
|
+
} catch {
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
const emit = (status) => {
|
|
981
|
+
try {
|
|
982
|
+
notify(status);
|
|
983
|
+
} catch {
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
apply();
|
|
987
|
+
emit("unknown");
|
|
988
|
+
const tick = async () => {
|
|
989
|
+
if (disposed || running) return;
|
|
990
|
+
running = true;
|
|
991
|
+
try {
|
|
992
|
+
const track = publication.track;
|
|
993
|
+
const stats = await track?.getRTCStatsReport?.();
|
|
994
|
+
if (disposed || publication.track !== track) return;
|
|
995
|
+
const prior = state;
|
|
996
|
+
state = stepNetworkQuality(state, readNetworkVideoSample(stats), performance.now(), {
|
|
997
|
+
paused: track?.streamState === Track.StreamState.Paused,
|
|
998
|
+
inhibited: document.hidden || publication.isMuted || inhibited()
|
|
999
|
+
});
|
|
1000
|
+
if (state.cap !== prior.cap) apply();
|
|
1001
|
+
if (state.status !== prior.status) emit(state.status);
|
|
1002
|
+
} catch {
|
|
1003
|
+
if (!disposed) state = { ...state, sample: null, packetWindow: [], badMs: 0, postDowngradeBadMs: 0, cleanMs: 0, reducedBadMs: 0 };
|
|
1004
|
+
} finally {
|
|
1005
|
+
running = false;
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
const timer = setInterval(() => {
|
|
1009
|
+
void tick();
|
|
1010
|
+
}, tickMs);
|
|
1011
|
+
return () => {
|
|
1012
|
+
disposed = true;
|
|
1013
|
+
clearInterval(timer);
|
|
1014
|
+
emit("unknown");
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
880
1018
|
// ../client/src/react/use-quality-governor.ts
|
|
881
1019
|
var qualityToSignal = (q) => {
|
|
882
1020
|
switch (q) {
|
|
@@ -893,7 +1031,7 @@ var qualityToSignal = (q) => {
|
|
|
893
1031
|
}
|
|
894
1032
|
};
|
|
895
1033
|
function useAvatarQualityGovernor(input) {
|
|
896
|
-
const { enabled, freezeReading, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
1034
|
+
const { enabled, freezeReading, onNetworkStatusChange, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
897
1035
|
const room = useMaybeRoomContext();
|
|
898
1036
|
const { videoTrack } = useVoiceAssistant();
|
|
899
1037
|
const {
|
|
@@ -930,11 +1068,28 @@ function useAvatarQualityGovernor(input) {
|
|
|
930
1068
|
]);
|
|
931
1069
|
const freezeReadingRef = useRef(freezeReading);
|
|
932
1070
|
freezeReadingRef.current = freezeReading;
|
|
1071
|
+
const networkCallback = useRef(onNetworkStatusChange);
|
|
1072
|
+
networkCallback.current = onNetworkStatusChange;
|
|
933
1073
|
const targetPublication = videoTrack?.publication;
|
|
934
1074
|
const targetParticipant = videoTrack?.participant;
|
|
935
1075
|
const targetTrack = targetPublication?.track;
|
|
936
1076
|
useEffect(() => {
|
|
937
|
-
if (!
|
|
1077
|
+
if (!room || !targetPublication || !targetParticipant) return;
|
|
1078
|
+
if (enabled === "network-only") {
|
|
1079
|
+
return bindNetworkQuality(
|
|
1080
|
+
targetPublication,
|
|
1081
|
+
() => freezeReadingRef.current?.().inhibited ?? false,
|
|
1082
|
+
(status) => networkCallback.current?.(status),
|
|
1083
|
+
tickMs
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
if (!enabled) {
|
|
1087
|
+
try {
|
|
1088
|
+
targetPublication.setVideoQuality(VideoQuality.HIGH);
|
|
1089
|
+
} catch {
|
|
1090
|
+
}
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
938
1093
|
let pausedSinceTick = false;
|
|
939
1094
|
let lastFreezeStat = null;
|
|
940
1095
|
let jitterTrend = null;
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RegisteredTool, ToolRegistration } from './tools.js';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-
|
|
4
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, aK as AvatarVideoSurface, aL as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, aM as LivePlaybackKeeper, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, aN as MIC_LEASE_ENDED_TIMEOUT_MS, aO as MicLease, aP as PlayableVideoElement, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS,
|
|
3
|
+
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-BoUJWSkf.js';
|
|
4
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, aK as AvatarVideoSurface, aL as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, h as CallTranscriptSegment, i as CartesiaTtsModel, j as ClipResult, k as ClosingTurnResult, l as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, m as DEFAULT_CREDITS_LOW_LEAD_SECONDS, n as DEFAULT_GOVERNOR_CONFIG, o as DEFAULT_GRACE_CEILING_SECONDS, p as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, q as DEFAULT_IDLE_SECONDS, r as DEFAULT_IDLE_WARN_LEAD_SECONDS, s as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, t as EndedEvent, u as ExtendResult, F as FishTtsModel, v as FreezeReadingFn, G as Governor, w as GovernorAction, x as GovernorConfig, y as GovernorSignal, z as GovernorState, H as GraceWindowClosedEvent, I as GraceWindowOpenEvent, J as GraceWindowState, K as IdleWarningEvent, M as InboundRtpCursor, N as InboundRtpReading, O as KnownBehaviorState, P as LLMProvider, Q as LLMSelection, R as LiveKitAvatarGrantState, S as LiveKitAvatarGrantStatus, T as LiveKitCapacityState, U as LiveKitConnectionStatus, V as LiveKitSessionRequest, W as LiveKitSessionStartResult, aM as LivePlaybackKeeper, X as MAX_RECONNECT_ATTEMPTS, Y as MAX_SESSION_INSTRUCTIONS_CHARS, aN as MIC_LEASE_ENDED_TIMEOUT_MS, aO as MicLease, aP as NETWORK_QUALITY_POLICY, aQ as NetworkQualityStatus, aR as PlayableVideoElement, Z as ProxyClientOptions, _ as QualityCap, $ as RECONNECT_BACKOFF_MS, aS as RealtimeAvatarCapacityError, aT as RealtimeAvatarLiveKitRoom, aU as RealtimeAvatarLiveKitRoomProps, a0 as RealtimeAvatarRequestOptions, a1 as RealtimeSessionApi, a2 as RealtimeSessionMedia, a3 as RealtimeSessionRoomSinks, a4 as ReconnectPolicy, a5 as ReconnectingEvent, a6 as RecoveryState, a7 as RetryStep, a8 as SessionBehavior, a9 as SessionClip, aa as SessionClocks, ab as SessionEndReason, ac as SessionLifecycleApi, ad as SessionLifecyclePhase, ae as SessionLifecyclePhaseKind, af as SessionLifecycleRoomBridge, ag as SessionLifecycleRoomBridgeProps, ah as SurfaceLayers, ai as TurnState, aj as TurnTimeoutEvent, ak as UseAvatarQualityGovernorInput, al as UseLiveKitAvatarGrantInput, am as UseRealtimeSessionInput, an as UseSessionLifecycleInput, ao as VoiceSpec, ap as VoiceSpecInput, aq as capacityErrorFromBusy, ar as capacityStateFromGrant, as as createProxyClient, au as knownBehaviorStates, av as mapTurnState, aw as readInboundRtp, ax as sessionBehaviorSchema, ay as sessionClipSchema, aV as shouldReplayPendingTurn, az as splitCallTranscript, aA as useAvatarAdaptivePlayoutDelay, aB as useAvatarPlayoutDelay, aC as useAvatarQualityGovernor, aD as useCallTranscript, aE as useLiveKitAvatarGrant, aG as useMicLease, aH as useRealtimeSession, aI as useReleaseMicLeaseOnTrackEnded, aJ as useSessionLifecycle } from './proxy-client-BoUJWSkf.js';
|
|
5
5
|
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import '@livekit/components-react';
|
package/dist/react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useRef, useState, useEffect, useCallback, useMemo, createElement, Fragment } from 'react';
|
|
3
3
|
import { useRoomContext, useConnectionState, useTranscriptions, useLocalParticipant, RoomAudioRenderer, LiveKitRoom, useMaybeRoomContext, useVoiceAssistant, VideoTrack, useChat } from '@livekit/components-react';
|
|
4
|
-
import { ConnectionState, ConnectionQuality, RoomEvent, TrackEvent, Track,
|
|
4
|
+
import { ConnectionState, VideoQuality, ConnectionQuality, RoomEvent, TrackEvent, Track, ConnectionError, ConnectionErrorReason, DisconnectReason, Room } from 'livekit-client';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
|
|
7
7
|
// ../client/src/react/use-character-tools.ts
|
|
@@ -1078,6 +1078,145 @@ var resolveLowCapQuality = (declaredLayerQualities, lowUnhealthy = 0) => {
|
|
|
1078
1078
|
return lowUnhealthy >= LOW_CAP_STEP_AFTER_UNHEALTHY ? sorted[0] : sorted[sorted.length - 2];
|
|
1079
1079
|
};
|
|
1080
1080
|
|
|
1081
|
+
// ../client/src/react/network-quality.ts
|
|
1082
|
+
var NETWORK_QUALITY_POLICY = {
|
|
1083
|
+
startupGraceMs: 3e3,
|
|
1084
|
+
maxSampleGapMs: 1500,
|
|
1085
|
+
minimumPackets: 20,
|
|
1086
|
+
lossWindowSamples: 3,
|
|
1087
|
+
lossRatio: 0.05,
|
|
1088
|
+
jitterSeconds: 0.1,
|
|
1089
|
+
impairedFps: 12,
|
|
1090
|
+
downgradeMs: 2e3,
|
|
1091
|
+
floorMs: 2e3,
|
|
1092
|
+
notifyAfterDowngradeMs: 5e3,
|
|
1093
|
+
recoverMs: 5e3,
|
|
1094
|
+
clearNoticeMs: 1e4
|
|
1095
|
+
};
|
|
1096
|
+
function readNetworkVideoSample(report) {
|
|
1097
|
+
const samples = [];
|
|
1098
|
+
report?.forEach((row) => {
|
|
1099
|
+
if (row.type !== "inbound-rtp" || (row.kind ?? row.mediaType) !== "video") return;
|
|
1100
|
+
const { timestamp, framesDecoded, packetsReceived, packetsLost, jitter } = row;
|
|
1101
|
+
if (typeof row.id !== "string" || ![timestamp, framesDecoded, packetsReceived, packetsLost, jitter].every(
|
|
1102
|
+
(n) => typeof n === "number" && Number.isFinite(n)
|
|
1103
|
+
) || timestamp < 0 || framesDecoded < 0 || packetsReceived < 0 || jitter < 0) return;
|
|
1104
|
+
samples.push({
|
|
1105
|
+
id: `${row.id}:${row.ssrc ?? ""}`,
|
|
1106
|
+
timestamp,
|
|
1107
|
+
framesDecoded,
|
|
1108
|
+
packetsReceived,
|
|
1109
|
+
packetsLost,
|
|
1110
|
+
jitter
|
|
1111
|
+
});
|
|
1112
|
+
});
|
|
1113
|
+
return samples.length === 1 ? samples[0] : null;
|
|
1114
|
+
}
|
|
1115
|
+
function initNetworkQuality(nowMs) {
|
|
1116
|
+
return {
|
|
1117
|
+
cap: "high",
|
|
1118
|
+
status: "unknown",
|
|
1119
|
+
sample: null,
|
|
1120
|
+
lastPollMs: null,
|
|
1121
|
+
openedMs: nowMs,
|
|
1122
|
+
badMs: 0,
|
|
1123
|
+
postDowngradeBadMs: 0,
|
|
1124
|
+
reducedBadMs: 0,
|
|
1125
|
+
cleanMs: 0,
|
|
1126
|
+
packetWindow: []
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function stepNetworkQuality(previous, sample, nowMs, options) {
|
|
1130
|
+
const p = NETWORK_QUALITY_POLICY;
|
|
1131
|
+
const s = { ...previous, sample, lastPollMs: nowMs };
|
|
1132
|
+
const before = previous.sample;
|
|
1133
|
+
const pollMs = previous.lastPollMs === null ? 0 : nowMs - previous.lastPollMs;
|
|
1134
|
+
const elapsedMs = sample && before ? sample.timestamp - before.timestamp : 0;
|
|
1135
|
+
const interrupted = options.inhibited || pollMs <= 0 || pollMs > p.maxSampleGapMs;
|
|
1136
|
+
const valid = !interrupted && sample !== null && before !== null && sample.id === before.id && elapsedMs > 0 && elapsedMs <= p.maxSampleGapMs && sample.framesDecoded >= before.framesDecoded && sample.packetsReceived >= before.packetsReceived;
|
|
1137
|
+
if (!valid || nowMs - s.openedMs < p.startupGraceMs) {
|
|
1138
|
+
s.badMs = s.postDowngradeBadMs = s.reducedBadMs = s.cleanMs = 0;
|
|
1139
|
+
s.packetWindow = [];
|
|
1140
|
+
if (options.inhibited) s.sample = null;
|
|
1141
|
+
return s;
|
|
1142
|
+
}
|
|
1143
|
+
const received = sample.packetsReceived - before.packetsReceived;
|
|
1144
|
+
const lost = Math.max(0, sample.packetsLost - before.packetsLost);
|
|
1145
|
+
s.packetWindow = [...previous.packetWindow, { received, lost }].slice(-p.lossWindowSamples);
|
|
1146
|
+
const totals = s.packetWindow.reduce((sum, interval) => ({
|
|
1147
|
+
received: sum.received + interval.received,
|
|
1148
|
+
lost: sum.lost + interval.lost
|
|
1149
|
+
}), { received: 0, lost: 0 });
|
|
1150
|
+
const packets = totals.received + totals.lost;
|
|
1151
|
+
const fps = (sample.framesDecoded - before.framesDecoded) * 1e3 / elapsedMs;
|
|
1152
|
+
const loss = packets >= p.minimumPackets ? totals.lost / packets : null;
|
|
1153
|
+
const impaired = options.paused || fps < p.impairedFps;
|
|
1154
|
+
const bad = options.paused || impaired && (loss !== null && loss >= p.lossRatio || received >= p.minimumPackets && sample.jitter >= p.jitterSeconds);
|
|
1155
|
+
const clean = received > 0 && !options.paused && loss !== null && loss < p.lossRatio && sample.jitter < p.jitterSeconds && fps >= p.impairedFps;
|
|
1156
|
+
const duration = Math.min(pollMs, elapsedMs);
|
|
1157
|
+
s.badMs = bad ? previous.badMs + duration : 0;
|
|
1158
|
+
s.postDowngradeBadMs = bad && previous.cap !== "high" ? previous.postDowngradeBadMs + duration : 0;
|
|
1159
|
+
s.cleanMs = clean ? previous.cleanMs + duration : 0;
|
|
1160
|
+
s.reducedBadMs = bad && fps === 0 && previous.cap !== "high" ? previous.reducedBadMs + duration : 0;
|
|
1161
|
+
if (s.badMs >= p.downgradeMs && s.cap === "high") s.cap = "reduced";
|
|
1162
|
+
else if (s.reducedBadMs >= p.floorMs && fps === 0 && s.cap === "reduced") s.cap = "floor";
|
|
1163
|
+
if (s.postDowngradeBadMs >= p.notifyAfterDowngradeMs) s.status = "poor";
|
|
1164
|
+
if (s.cleanMs >= p.recoverMs) s.cap = "high";
|
|
1165
|
+
if (s.cleanMs >= p.clearNoticeMs) s.status = "healthy";
|
|
1166
|
+
return s;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
// ../client/src/react/network-quality-binding.ts
|
|
1170
|
+
function bindNetworkQuality(publication, inhibited, notify, tickMs) {
|
|
1171
|
+
let disposed = false;
|
|
1172
|
+
let running = false;
|
|
1173
|
+
let state = initNetworkQuality(performance.now());
|
|
1174
|
+
const apply = () => {
|
|
1175
|
+
const qualities = (publication.trackInfo?.layers ?? []).map((l) => l.quality);
|
|
1176
|
+
const cap = state.cap === "high" ? VideoQuality.HIGH : resolveLowCapQuality(qualities, state.cap === "floor" ? 2 : 0);
|
|
1177
|
+
try {
|
|
1178
|
+
publication.setVideoQuality(cap);
|
|
1179
|
+
} catch {
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
const emit = (status) => {
|
|
1183
|
+
try {
|
|
1184
|
+
notify(status);
|
|
1185
|
+
} catch {
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
apply();
|
|
1189
|
+
emit("unknown");
|
|
1190
|
+
const tick = async () => {
|
|
1191
|
+
if (disposed || running) return;
|
|
1192
|
+
running = true;
|
|
1193
|
+
try {
|
|
1194
|
+
const track = publication.track;
|
|
1195
|
+
const stats = await track?.getRTCStatsReport?.();
|
|
1196
|
+
if (disposed || publication.track !== track) return;
|
|
1197
|
+
const prior = state;
|
|
1198
|
+
state = stepNetworkQuality(state, readNetworkVideoSample(stats), performance.now(), {
|
|
1199
|
+
paused: track?.streamState === Track.StreamState.Paused,
|
|
1200
|
+
inhibited: document.hidden || publication.isMuted || inhibited()
|
|
1201
|
+
});
|
|
1202
|
+
if (state.cap !== prior.cap) apply();
|
|
1203
|
+
if (state.status !== prior.status) emit(state.status);
|
|
1204
|
+
} catch {
|
|
1205
|
+
if (!disposed) state = { ...state, sample: null, packetWindow: [], badMs: 0, postDowngradeBadMs: 0, cleanMs: 0, reducedBadMs: 0 };
|
|
1206
|
+
} finally {
|
|
1207
|
+
running = false;
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
const timer = setInterval(() => {
|
|
1211
|
+
void tick();
|
|
1212
|
+
}, tickMs);
|
|
1213
|
+
return () => {
|
|
1214
|
+
disposed = true;
|
|
1215
|
+
clearInterval(timer);
|
|
1216
|
+
emit("unknown");
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1081
1220
|
// ../client/src/react/use-quality-governor.ts
|
|
1082
1221
|
var qualityToSignal = (q) => {
|
|
1083
1222
|
switch (q) {
|
|
@@ -1094,7 +1233,7 @@ var qualityToSignal = (q) => {
|
|
|
1094
1233
|
}
|
|
1095
1234
|
};
|
|
1096
1235
|
function useAvatarQualityGovernor(input) {
|
|
1097
|
-
const { enabled, freezeReading, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
1236
|
+
const { enabled, freezeReading, onNetworkStatusChange, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
1098
1237
|
const room = useMaybeRoomContext();
|
|
1099
1238
|
const { videoTrack } = useVoiceAssistant();
|
|
1100
1239
|
const {
|
|
@@ -1131,11 +1270,28 @@ function useAvatarQualityGovernor(input) {
|
|
|
1131
1270
|
]);
|
|
1132
1271
|
const freezeReadingRef = useRef(freezeReading);
|
|
1133
1272
|
freezeReadingRef.current = freezeReading;
|
|
1273
|
+
const networkCallback = useRef(onNetworkStatusChange);
|
|
1274
|
+
networkCallback.current = onNetworkStatusChange;
|
|
1134
1275
|
const targetPublication = videoTrack?.publication;
|
|
1135
1276
|
const targetParticipant = videoTrack?.participant;
|
|
1136
1277
|
const targetTrack = targetPublication?.track;
|
|
1137
1278
|
useEffect(() => {
|
|
1138
|
-
if (!
|
|
1279
|
+
if (!room || !targetPublication || !targetParticipant) return;
|
|
1280
|
+
if (enabled === "network-only") {
|
|
1281
|
+
return bindNetworkQuality(
|
|
1282
|
+
targetPublication,
|
|
1283
|
+
() => freezeReadingRef.current?.().inhibited ?? false,
|
|
1284
|
+
(status) => networkCallback.current?.(status),
|
|
1285
|
+
tickMs
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
if (!enabled) {
|
|
1289
|
+
try {
|
|
1290
|
+
targetPublication.setVideoQuality(VideoQuality.HIGH);
|
|
1291
|
+
} catch {
|
|
1292
|
+
}
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1139
1295
|
let pausedSinceTick = false;
|
|
1140
1296
|
let lastFreezeStat = null;
|
|
1141
1297
|
let jitterTrend = null;
|
|
@@ -1367,6 +1523,7 @@ function AvatarVideoSurface(props) {
|
|
|
1367
1523
|
// Keep the governor alive across presentation-intent changes; resetting it on
|
|
1368
1524
|
// every turn would re-apply LOW and a normal short turn could never earn HIGH.
|
|
1369
1525
|
enabled: adaptiveQuality,
|
|
1526
|
+
onNetworkStatusChange: props.onNetworkStatusChange,
|
|
1370
1527
|
freezeReading: frameFlow.freezeReading,
|
|
1371
1528
|
config: governorConfig
|
|
1372
1529
|
});
|
|
@@ -3263,4 +3420,4 @@ function createProxyClient(options) {
|
|
|
3263
3420
|
};
|
|
3264
3421
|
}
|
|
3265
3422
|
|
|
3266
|
-
export { AdaptivePlayoutController, AvatarCall, AvatarVideoSurface, DEFAULT_APPROACHING_END_LEAD_SECONDS, DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, DEFAULT_AVATAR_UNSTABLE_STALL_MS, DEFAULT_CREDITS_LOW_LEAD_SECONDS, DEFAULT_GOVERNOR_CONFIG, DEFAULT_GRACE_CEILING_SECONDS, DEFAULT_GRACE_WINDOW_LEAD_SECONDS, DEFAULT_IDLE_SECONDS, DEFAULT_IDLE_WARN_LEAD_SECONDS, DEFAULT_TURN_TIMEOUT_SECONDS, MAX_RECONNECT_ATTEMPTS, MAX_SESSION_INSTRUCTIONS_CHARS, MIC_LEASE_ENDED_TIMEOUT_MS, RECONNECT_BACKOFF_MS, RealtimeAvatarCapacityError, RealtimeAvatarLiveKitRoom, SessionLifecycleRoomBridge, capacityErrorFromBusy, capacityStateFromGrant, createProxyClient, knownBehaviorStates, mapTurnState, readInboundRtp, sessionBehaviorSchema, sessionClipSchema, shouldReplayPendingTurn, splitCallTranscript, useAvatarAdaptivePlayoutDelay, useAvatarCall, useAvatarPlayoutDelay, useAvatarQualityGovernor, useCallTranscript, useCharacterTools, useLiveKitAvatarGrant, useMicLease, useRealtimeSession, useReleaseMicLeaseOnTrackEnded, useSessionLifecycle };
|
|
3423
|
+
export { AdaptivePlayoutController, AvatarCall, AvatarVideoSurface, DEFAULT_APPROACHING_END_LEAD_SECONDS, DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, DEFAULT_AVATAR_UNSTABLE_STALL_MS, DEFAULT_CREDITS_LOW_LEAD_SECONDS, DEFAULT_GOVERNOR_CONFIG, DEFAULT_GRACE_CEILING_SECONDS, DEFAULT_GRACE_WINDOW_LEAD_SECONDS, DEFAULT_IDLE_SECONDS, DEFAULT_IDLE_WARN_LEAD_SECONDS, DEFAULT_TURN_TIMEOUT_SECONDS, MAX_RECONNECT_ATTEMPTS, MAX_SESSION_INSTRUCTIONS_CHARS, MIC_LEASE_ENDED_TIMEOUT_MS, NETWORK_QUALITY_POLICY, RECONNECT_BACKOFF_MS, RealtimeAvatarCapacityError, RealtimeAvatarLiveKitRoom, SessionLifecycleRoomBridge, capacityErrorFromBusy, capacityStateFromGrant, createProxyClient, knownBehaviorStates, mapTurnState, readInboundRtp, sessionBehaviorSchema, sessionClipSchema, shouldReplayPendingTurn, splitCallTranscript, useAvatarAdaptivePlayoutDelay, useAvatarCall, useAvatarPlayoutDelay, useAvatarQualityGovernor, useCallTranscript, useCharacterTools, useLiveKitAvatarGrant, useMicLease, useRealtimeSession, useReleaseMicLeaseOnTrackEnded, useSessionLifecycle };
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, verifyTranscript } from './chunk-UVNDDNU2.js';
|
package/dist/tanstack-start.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtime-avatar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Realtime Avatar SDK. `realtime-avatar` and the route adapters hold your API key and are server-only; `realtime-avatar/react`, `/browser` and `/tools` never can — importing a server entry into a browser build throws at bundle time.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "The Influence Company",
|