realtime-avatar 0.13.0 → 0.15.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/README.md +62 -1
- package/dist/{chunk-NOHMMTFR.js → chunk-LZMJJX7C.js} +1 -1
- package/dist/{chunk-UVNDDNU2.js → chunk-PKKTFUA7.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-BoUJWSkf.d.ts → proxy-client-BGPikYWc.d.ts} +25 -10
- package/dist/react-native.d.ts +2 -2
- package/dist/react-native.js +93 -27
- package/dist/react.d.ts +4 -4
- package/dist/react.js +98 -28
- package/dist/server.js +1 -1
- package/dist/tanstack-start.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A live character your users can talk to — voice, or voice and video. She liste
|
|
|
4
4
|
speaks, so you can interrupt her mid-sentence and she stops, the way a person stops.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
npm install realtime-avatar
|
|
7
|
+
npm install --save-exact realtime-avatar@0.15.0
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
```ts
|
|
@@ -147,6 +147,67 @@ call. Policy — `instructions`, `maxSeconds`, `voice`, `video` — is decided i
|
|
|
147
147
|
your server. A route that spreads the request body into `startCall` hands your caller your
|
|
148
148
|
system prompt and your bill.
|
|
149
149
|
|
|
150
|
+
### Optional connection details
|
|
151
|
+
|
|
152
|
+
`AvatarCall` provides call status, actions and end reasons for your default UI. To show
|
|
153
|
+
connection diagnostics, opt in with `onConnectionDetailsChange`. It supplies data for
|
|
154
|
+
your own UI; enabling it adds no built-in network notice or controls.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useState } from "react";
|
|
158
|
+
import { AvatarCall, type AvatarCallProps, type AvatarConnectionDetails } from "realtime-avatar/react";
|
|
159
|
+
|
|
160
|
+
function CallWithDetails(props: Pick<AvatarCallProps, "client" | "avatarId">) {
|
|
161
|
+
const [details, setDetails] = useState<AvatarConnectionDetails | null>(null);
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<>
|
|
165
|
+
<AvatarCall {...props} onConnectionDetailsChange={setDetails} />
|
|
166
|
+
{details && <p>Connection: {details.connectionState}. Local quality: {details.localQuality}.</p>}
|
|
167
|
+
</>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Once bound to a call, the callback receives an initial snapshot, then changed facts only. It receives `null`
|
|
173
|
+
when its room or session binding is retired, before a replacement binding's snapshot.
|
|
174
|
+
Clearing the session grant keeps details reset until a new grant arrives.
|
|
175
|
+
Keep this state scoped to one call and clear it on `null`, as the example does. This is
|
|
176
|
+
a current snapshot, not a lossless event history. Replacing the handler does not restart
|
|
177
|
+
the call, and errors in your handler do not interrupt it. Omitting the callback adds no
|
|
178
|
+
reporting listeners; the callback adds no timers, stats polling or uploads.
|
|
179
|
+
|
|
180
|
+
Each field preserves LiveKit's native type and meaning:
|
|
181
|
+
|
|
182
|
+
| Field | Source |
|
|
183
|
+
| --- | --- |
|
|
184
|
+
| `connectionState` | The bound room's `state`. |
|
|
185
|
+
| `localQuality` | The local participant's `connectionQuality`. |
|
|
186
|
+
| `audio.publisherQuality` | The selected audio publisher's `connectionQuality`. |
|
|
187
|
+
| `video.publisherQuality` | The selected video publisher's `connectionQuality`. |
|
|
188
|
+
| `audio.streamState`, `video.streamState` | The corresponding remote track's `streamState`, or `null` before the track exists. |
|
|
189
|
+
|
|
190
|
+
Audio and video publishers are selected independently and can differ. Neither field
|
|
191
|
+
substitutes the control agent or an arbitrary remote participant. `audio` or `video` is
|
|
192
|
+
`null` when no corresponding track reference is selected; LiveKit's `Unknown` quality
|
|
193
|
+
means a publisher exists but its quality is unknown. An active or subscribed track does
|
|
194
|
+
not prove that video has rendered or audio is audible. Continue using call status for
|
|
195
|
+
the call lifecycle.
|
|
196
|
+
|
|
197
|
+
Native and custom integrations pass the same optional callback to their existing
|
|
198
|
+
`SessionLifecycleRoomBridge` inside `RealtimeAvatarLiveKitRoom`. Both
|
|
199
|
+
`realtime-avatar/react` and `realtime-avatar/react-native` export `AvatarConnectionDetails`.
|
|
200
|
+
For deeper receiver measurements, use LiveKit's public `RemoteVideoTrack.getReceiverStats()`
|
|
201
|
+
and `RemoteAudioTrack.getReceiverStats()` methods and their native return types.
|
|
202
|
+
|
|
203
|
+
Retain the app's session-to-room association (`room_name`, timestamps and participant
|
|
204
|
+
identity), adding the server-observed room SID when exact room-lifetime lookup needs it.
|
|
205
|
+
LiveKit owns participant connection history; a participant SID identifies one incarnation.
|
|
206
|
+
Use these references to look up details in LiveKit's own diagnostics. The existing quality governor
|
|
207
|
+
and opt-in adaptive playout are product policies over LiveKit facts; setting
|
|
208
|
+
`adaptiveQuality={false}` releases the manual quality ceiling while LiveKit's bandwidth
|
|
209
|
+
adaptation continues. Adaptive playout reads only public LiveKit track reports.
|
|
210
|
+
|
|
150
211
|
### Importing a server entry into a browser build throws
|
|
151
212
|
|
|
152
213
|
Not a lint rule and not a naming convention — the six server subpaths carry `browser` and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-
|
|
1
|
+
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-PKKTFUA7.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.15.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-PKKTFUA7.js';
|
package/dist/nextjs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TrackReferenceOrPlaceholder, useTranscriptions, useConnectionState, LiveKitRoomProps } from '@livekit/components-react';
|
|
2
2
|
import { CSSProperties, ReactNode, ReactElement } from 'react';
|
|
3
|
-
import { DisconnectReason, SendTextOptions } from 'livekit-client';
|
|
3
|
+
import { ConnectionQuality, Room, Participant, Track, DisconnectReason, SendTextOptions } from 'livekit-client';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -312,7 +312,7 @@ type GovernorState = "opening" | "opening_high" | "cap_low_sticky" | "cap_low_el
|
|
|
312
312
|
/**
|
|
313
313
|
* One normalized input the hook feeds each tick. The hook translates ALL of
|
|
314
314
|
* {TrackStreamStateChanged, ConnectionQualityChanged, getStats, rVFC freeze} into
|
|
315
|
-
* this shape
|
|
315
|
+
* this shape. LiveKit owns the connection-quality vocabulary.
|
|
316
316
|
*/
|
|
317
317
|
interface GovernorSignal {
|
|
318
318
|
/** SFU congestion controller paused the track since the last tick — Tier-0, the
|
|
@@ -323,9 +323,8 @@ interface GovernorSignal {
|
|
|
323
323
|
freezeMsInWindow: number;
|
|
324
324
|
/** jitterBufferDelay is trending up — the earliest LEADING pre-freeze sign. */
|
|
325
325
|
jitterRising: boolean;
|
|
326
|
-
/** LiveKit
|
|
327
|
-
|
|
328
|
-
connectionQuality: "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
326
|
+
/** LiveKit connection quality blocks up-probes; it is never a sole downgrade trigger. */
|
|
327
|
+
connectionQuality: `${ConnectionQuality}`;
|
|
329
328
|
/** The tab is hidden / track muted / freezes correlate with local CPU not network —
|
|
330
329
|
* the false-positive fence (our own Dia-freeze lesson). When true the machine is
|
|
331
330
|
* frozen: no signal is trusted, no transition fires. */
|
|
@@ -987,7 +986,7 @@ declare function readInboundRtp(reports: Iterable<Record<string, unknown>>, prev
|
|
|
987
986
|
* (default false ⇒ a complete no-op, the incumbent flat cushion untouched).
|
|
988
987
|
*
|
|
989
988
|
* When enabled it runs the {@link AdaptivePlayoutController} closed loop: a 1Hz
|
|
990
|
-
* `
|
|
989
|
+
* LiveKit `getRTCStatsReport()` poll over BOTH avatar tracks, the worst
|
|
991
990
|
* jitter/loss of the pair feeding one shared target so audio and video stay on
|
|
992
991
|
* the SAME depth (lip-sync law — WebRTC pairs the streams to the larger of the
|
|
993
992
|
* two hints). The loop opens at the ceiling (the flat cushion already applied by
|
|
@@ -995,7 +994,7 @@ declare function readInboundRtp(reports: Iterable<Record<string, unknown>>, prev
|
|
|
995
994
|
* snaps back up within a couple of ticks when jitter or loss appears.
|
|
996
995
|
*
|
|
997
996
|
* Degrades to the incumbent behavior wherever the substrate is missing: a track
|
|
998
|
-
* without
|
|
997
|
+
* without an available LiveKit stats report, a report with no `inbound-rtp` yet,
|
|
999
998
|
* or a browser without the playout-delay hint all leave the flat cushion exactly
|
|
1000
999
|
* as `useAvatarPlayoutDelay` set it.
|
|
1001
1000
|
*
|
|
@@ -1292,8 +1291,24 @@ type RealtimeSessionRoomSinks = Partial<{
|
|
|
1292
1291
|
audio: "flowing" | "silent";
|
|
1293
1292
|
}) => void;
|
|
1294
1293
|
}>;
|
|
1294
|
+
type PublisherConnectionDetails = Readonly<{
|
|
1295
|
+
publisherQuality: Participant["connectionQuality"];
|
|
1296
|
+
streamState: Track["streamState"] | null;
|
|
1297
|
+
}>;
|
|
1298
|
+
/** Current native connection facts. Track activity does not prove rendered or audible media. */
|
|
1299
|
+
type AvatarConnectionDetails = Readonly<{
|
|
1300
|
+
connectionState: Room["state"];
|
|
1301
|
+
localQuality: Participant["connectionQuality"];
|
|
1302
|
+
audio: PublisherConnectionDetails | null;
|
|
1303
|
+
video: PublisherConnectionDetails | null;
|
|
1304
|
+
}>;
|
|
1295
1305
|
type SessionLifecycleRoomBridgeProps = {
|
|
1296
|
-
|
|
1306
|
+
/**
|
|
1307
|
+
* Opt in to an initial snapshot and changed facts. Null clears a retired binding.
|
|
1308
|
+
* Callback failures never affect the call; no stats polling or uploads are added.
|
|
1309
|
+
*/
|
|
1310
|
+
onConnectionDetailsChange?: (details: AvatarConnectionDetails | null) => void;
|
|
1311
|
+
lifecycle: Pick<SessionLifecycleApi, "onConnectionStateChange" | "setAgentPresent" | "registerLeaveRoom" | "markActivity"> & Partial<Pick<SessionLifecycleApi, "grant">> & RealtimeSessionRoomSinks;
|
|
1297
1312
|
};
|
|
1298
1313
|
/**
|
|
1299
1314
|
* Wire the in-room LiveKit facts into a {@link useSessionLifecycle} instance:
|
|
@@ -1312,7 +1327,7 @@ type SessionLifecycleRoomBridgeProps = {
|
|
|
1312
1327
|
*
|
|
1313
1328
|
* Renders nothing. Mount it once inside RealtimeAvatarLiveKitRoom.
|
|
1314
1329
|
*/
|
|
1315
|
-
declare function SessionLifecycleRoomBridge({ lifecycle }: SessionLifecycleRoomBridgeProps): null;
|
|
1330
|
+
declare function SessionLifecycleRoomBridge({ lifecycle, onConnectionDetailsChange, }: SessionLifecycleRoomBridgeProps): null;
|
|
1316
1331
|
|
|
1317
1332
|
/**
|
|
1318
1333
|
* The terminal, LABELED end reason surfaced to the app via `onEnded`. This is the
|
|
@@ -1647,4 +1662,4 @@ interface ProxyClientOptions {
|
|
|
1647
1662
|
}
|
|
1648
1663
|
declare function createProxyClient(options: ProxyClientOptions): AvatarSessionClient;
|
|
1649
1664
|
|
|
1650
|
-
export {
|
|
1665
|
+
export { type QualityCap 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 GovernorState as H, type GraceWindowClosedEvent as I, type GraceWindowOpenEvent as J, type GraceWindowState as K, type LiveKitSessionGrant as L, type IdleWarningEvent as M, type InboundRtpCursor as N, type InboundRtpReading as O, type KnownBehaviorState as P, type LLMProvider as Q, type LLMSelection as R, type LiveKitAvatarGrantState as S, type LiveKitAvatarGrantStatus as T, type LiveKitCapacityState as U, type LiveKitConnectionStatus as V, type LiveKitSessionRequest as W, type LiveKitSessionStartResult as X, MAX_RECONNECT_ATTEMPTS as Y, MAX_SESSION_INSTRUCTIONS_CHARS as Z, type ProxyClientOptions as _, AdaptivePlayoutController as a, RECONNECT_BACKOFF_MS as a0, type RealtimeAvatarRequestOptions as a1, type RealtimeSessionApi as a2, type RealtimeSessionMedia as a3, type RealtimeSessionRoomSinks as a4, type ReconnectPolicy as a5, type ReconnectingEvent as a6, type RecoveryState as a7, type RetryStep as a8, type SessionBehavior as a9, splitCallTranscript as aA, useAvatarAdaptivePlayoutDelay as aB, useAvatarPlayoutDelay as aC, useAvatarQualityGovernor as aD, useCallTranscript as aE, useLiveKitAvatarGrant as aF, useLiveTrackProducing as aG, useMicLease as aH, useRealtimeSession as aI, useReleaseMicLeaseOnTrackEnded as aJ, useSessionLifecycle as aK, AvatarVideoSurface as aL, type AvatarVideoSurfaceProps as aM, type LivePlaybackKeeper as aN, MIC_LEASE_ENDED_TIMEOUT_MS as aO, type MicLease as aP, NETWORK_QUALITY_POLICY as aQ, type NetworkQualityStatus as aR, type PlayableVideoElement as aS, RealtimeAvatarCapacityError as aT, RealtimeAvatarLiveKitRoom as aU, type RealtimeAvatarLiveKitRoomProps as aV, shouldReplayPendingTurn as aW, type SessionClip as aa, type SessionClocks as ab, type SessionEndReason as ac, type SessionLifecycleApi as ad, type SessionLifecyclePhase as ae, type SessionLifecyclePhaseKind as af, SessionLifecycleRoomBridge as ag, type SessionLifecycleRoomBridgeProps as ah, type SurfaceLayers as ai, type TurnState as aj, type TurnTimeoutEvent as ak, type UseAvatarQualityGovernorInput as al, type UseLiveKitAvatarGrantInput as am, type UseRealtimeSessionInput as an, type UseSessionLifecycleInput as ao, type VoiceSpec as ap, type VoiceSpecInput as aq, capacityErrorFromBusy as ar, capacityStateFromGrant as as, createProxyClient as at, isNativeLiveTrackSubscribed as au, knownBehaviorStates as av, mapTurnState as aw, readInboundRtp as ax, sessionBehaviorSchema as ay, sessionClipSchema as az, type AdaptivePlayoutDecision as b, type AdaptivePlayoutOptions as c, type AdaptivePlayoutSample as d, type ApproachingEndEvent as e, type ApproachingEndReason as f, type AvatarConnectionDetails as g, type AvatarSessionClient as h, type CallTranscriptSegment as i, type CartesiaTtsModel as j, type ClipResult as k, type ClosingTurnResult as l, type CreditsLowEvent as m, DEFAULT_CREDITS_LOW_LEAD_SECONDS as n, DEFAULT_GOVERNOR_CONFIG as o, DEFAULT_GRACE_CEILING_SECONDS as p, DEFAULT_GRACE_WINDOW_LEAD_SECONDS as q, DEFAULT_IDLE_SECONDS as r, DEFAULT_IDLE_WARN_LEAD_SECONDS as s, DEFAULT_TURN_TIMEOUT_SECONDS as t, type EndedEvent as u, type ExtendResult as v, type FreezeReadingFn as w, type GovernorAction as x, type GovernorConfig as y, type GovernorSignal 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,
|
|
4
|
+
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-BGPikYWc.js';
|
|
5
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, h as AvatarSessionClient, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ah as SessionLifecycleRoomBridgeProps, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, au as isNativeLiveTrackSubscribed, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aG as useLiveTrackProducing, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-BGPikYWc.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 { VideoQuality, ConnectionQuality, RoomEvent, TrackEvent, ConnectionState, Track, ConnectionError, ConnectionErrorReason, DisconnectReason, Room } from 'livekit-client';
|
|
6
|
+
import { isRemoteTrack, 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';
|
|
@@ -679,10 +679,8 @@ function useAvatarAdaptivePlayoutDelay(videoTrack, audioTrack, enabled = false,
|
|
|
679
679
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
680
680
|
useEffect(() => {
|
|
681
681
|
if (!enabled || !videoMediaTrack && !audioMediaTrack) return;
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
);
|
|
685
|
-
if (receivers.length === 0) return;
|
|
682
|
+
const tracks = [videoMediaTrack, audioMediaTrack].filter(isRemoteTrack);
|
|
683
|
+
if (tracks.length === 0) return;
|
|
686
684
|
const controller = new AdaptivePlayoutController(options);
|
|
687
685
|
const cursors = /* @__PURE__ */ new Map();
|
|
688
686
|
let closed = false;
|
|
@@ -692,9 +690,9 @@ function useAvatarAdaptivePlayoutDelay(videoTrack, audioTrack, enabled = false,
|
|
|
692
690
|
inFlight = true;
|
|
693
691
|
try {
|
|
694
692
|
let worst;
|
|
695
|
-
for (let i = 0; i <
|
|
696
|
-
const stats = await
|
|
697
|
-
if (closed ||
|
|
693
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
694
|
+
const stats = await tracks[i].getRTCStatsReport();
|
|
695
|
+
if (closed || !stats) continue;
|
|
698
696
|
const reading = readInboundRtp(stats.values(), cursors.get(i));
|
|
699
697
|
if (!reading) continue;
|
|
700
698
|
cursors.set(i, reading.cursor);
|
|
@@ -1016,20 +1014,6 @@ function bindNetworkQuality(publication, inhibited, notify, tickMs) {
|
|
|
1016
1014
|
}
|
|
1017
1015
|
|
|
1018
1016
|
// ../client/src/react/use-quality-governor.ts
|
|
1019
|
-
var qualityToSignal = (q) => {
|
|
1020
|
-
switch (q) {
|
|
1021
|
-
case ConnectionQuality.Excellent:
|
|
1022
|
-
return "excellent";
|
|
1023
|
-
case ConnectionQuality.Good:
|
|
1024
|
-
return "good";
|
|
1025
|
-
case ConnectionQuality.Poor:
|
|
1026
|
-
return "poor";
|
|
1027
|
-
case ConnectionQuality.Lost:
|
|
1028
|
-
return "lost";
|
|
1029
|
-
default:
|
|
1030
|
-
return "unknown";
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
1017
|
function useAvatarQualityGovernor(input) {
|
|
1034
1018
|
const { enabled, freezeReading, onNetworkStatusChange, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
1035
1019
|
const room = useMaybeRoomContext();
|
|
@@ -1093,16 +1077,14 @@ function useAvatarQualityGovernor(input) {
|
|
|
1093
1077
|
let pausedSinceTick = false;
|
|
1094
1078
|
let lastFreezeStat = null;
|
|
1095
1079
|
let jitterTrend = null;
|
|
1096
|
-
let connQuality =
|
|
1097
|
-
targetParticipant?.connectionQuality ?? ConnectionQuality.Unknown
|
|
1098
|
-
);
|
|
1080
|
+
let connQuality = targetParticipant?.connectionQuality ?? ConnectionQuality.Unknown;
|
|
1099
1081
|
const onStreamState = (pub, streamState) => {
|
|
1100
1082
|
if (pub !== targetPublication) return;
|
|
1101
1083
|
if (streamState === Track.StreamState.Paused) pausedSinceTick = true;
|
|
1102
1084
|
};
|
|
1103
1085
|
const onQuality = (q, participant) => {
|
|
1104
1086
|
if (participant.sid !== targetParticipant.sid) return;
|
|
1105
|
-
connQuality =
|
|
1087
|
+
connQuality = q;
|
|
1106
1088
|
};
|
|
1107
1089
|
try {
|
|
1108
1090
|
room.on(RoomEvent.TrackStreamStateChanged, onStreamState);
|
|
@@ -2193,7 +2175,10 @@ function useSessionLifecycle(input) {
|
|
|
2193
2175
|
]
|
|
2194
2176
|
);
|
|
2195
2177
|
}
|
|
2196
|
-
function SessionLifecycleRoomBridge({
|
|
2178
|
+
function SessionLifecycleRoomBridge({
|
|
2179
|
+
lifecycle,
|
|
2180
|
+
onConnectionDetailsChange
|
|
2181
|
+
}) {
|
|
2197
2182
|
const {
|
|
2198
2183
|
onConnectionStateChange,
|
|
2199
2184
|
setAgentPresent,
|
|
@@ -2216,6 +2201,87 @@ function SessionLifecycleRoomBridge({ lifecycle }) {
|
|
|
2216
2201
|
const audioLive = Boolean(assistant.audioTrack);
|
|
2217
2202
|
const transcriptionCount = transcriptions.length;
|
|
2218
2203
|
const prevTranscriptionCountRef = useRef(0);
|
|
2204
|
+
const audio = assistant.audioTrack;
|
|
2205
|
+
const video = assistant.videoTrack;
|
|
2206
|
+
const detailsEnabled = onConnectionDetailsChange !== void 0 && lifecycle.grant !== null;
|
|
2207
|
+
const detailsSessionId = lifecycle.grant?.session_id;
|
|
2208
|
+
const updateDetailsRef = useRef(null);
|
|
2209
|
+
useEffect(() => {
|
|
2210
|
+
if (!detailsEnabled || !onConnectionDetailsChange) return;
|
|
2211
|
+
let callback = onConnectionDetailsChange;
|
|
2212
|
+
let audioSource = audio;
|
|
2213
|
+
let videoSource = video;
|
|
2214
|
+
let closed = false;
|
|
2215
|
+
let pending = false;
|
|
2216
|
+
let previous;
|
|
2217
|
+
const deliver = (details) => {
|
|
2218
|
+
try {
|
|
2219
|
+
void Promise.resolve(callback(details)).catch(() => {
|
|
2220
|
+
});
|
|
2221
|
+
} catch {
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
const publisher = (source) => source ? {
|
|
2225
|
+
publisherQuality: source.participant.connectionQuality,
|
|
2226
|
+
streamState: source.publication.track?.streamState ?? null
|
|
2227
|
+
} : null;
|
|
2228
|
+
const schedule = () => {
|
|
2229
|
+
if (pending) return;
|
|
2230
|
+
pending = true;
|
|
2231
|
+
queueMicrotask(() => {
|
|
2232
|
+
pending = false;
|
|
2233
|
+
if (closed) return;
|
|
2234
|
+
const next = {
|
|
2235
|
+
connectionState: room.state,
|
|
2236
|
+
localQuality: room.localParticipant.connectionQuality,
|
|
2237
|
+
audio: publisher(audioSource),
|
|
2238
|
+
video: publisher(videoSource)
|
|
2239
|
+
};
|
|
2240
|
+
const last = previous;
|
|
2241
|
+
if (last?.connectionState === next.connectionState && last.localQuality === next.localQuality && ["audio", "video"].every((kind) => last[kind]?.publisherQuality === next[kind]?.publisherQuality && last[kind]?.streamState === next[kind]?.streamState)) return;
|
|
2242
|
+
previous = next;
|
|
2243
|
+
deliver(next);
|
|
2244
|
+
});
|
|
2245
|
+
};
|
|
2246
|
+
const onQuality = (_quality, participant) => {
|
|
2247
|
+
if (participant === room.localParticipant || participant === audioSource?.participant || participant === videoSource?.participant) schedule();
|
|
2248
|
+
};
|
|
2249
|
+
const onPublication = (publication) => {
|
|
2250
|
+
if (publication === audioSource?.publication || publication === videoSource?.publication) schedule();
|
|
2251
|
+
};
|
|
2252
|
+
const onTrack = (_track, publication) => onPublication(publication);
|
|
2253
|
+
room.on(RoomEvent.ConnectionStateChanged, schedule);
|
|
2254
|
+
room.on(RoomEvent.ConnectionQualityChanged, onQuality);
|
|
2255
|
+
room.on(RoomEvent.TrackStreamStateChanged, onPublication);
|
|
2256
|
+
room.on(RoomEvent.TrackSubscribed, onTrack);
|
|
2257
|
+
room.on(RoomEvent.TrackUnsubscribed, onTrack);
|
|
2258
|
+
updateDetailsRef.current = (nextCallback, nextAudio, nextVideo) => {
|
|
2259
|
+
callback = nextCallback;
|
|
2260
|
+
audioSource = nextAudio;
|
|
2261
|
+
videoSource = nextVideo;
|
|
2262
|
+
schedule();
|
|
2263
|
+
};
|
|
2264
|
+
schedule();
|
|
2265
|
+
return () => {
|
|
2266
|
+
closed = true;
|
|
2267
|
+
updateDetailsRef.current = null;
|
|
2268
|
+
room.off(RoomEvent.ConnectionStateChanged, schedule);
|
|
2269
|
+
room.off(RoomEvent.ConnectionQualityChanged, onQuality);
|
|
2270
|
+
room.off(RoomEvent.TrackStreamStateChanged, onPublication);
|
|
2271
|
+
room.off(RoomEvent.TrackSubscribed, onTrack);
|
|
2272
|
+
room.off(RoomEvent.TrackUnsubscribed, onTrack);
|
|
2273
|
+
deliver(null);
|
|
2274
|
+
};
|
|
2275
|
+
}, [room, detailsSessionId, detailsEnabled]);
|
|
2276
|
+
useEffect(() => {
|
|
2277
|
+
if (onConnectionDetailsChange) updateDetailsRef.current?.(onConnectionDetailsChange, audio, video);
|
|
2278
|
+
}, [
|
|
2279
|
+
onConnectionDetailsChange,
|
|
2280
|
+
detailsEnabled ? audio?.participant : void 0,
|
|
2281
|
+
detailsEnabled ? audio?.publication : void 0,
|
|
2282
|
+
detailsEnabled ? video?.participant : void 0,
|
|
2283
|
+
detailsEnabled ? video?.publication : void 0
|
|
2284
|
+
]);
|
|
2219
2285
|
useEffect(() => {
|
|
2220
2286
|
onConnectionStateChange(connectionState);
|
|
2221
2287
|
}, [connectionState, onConnectionStateChange]);
|
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 {
|
|
4
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason,
|
|
3
|
+
import { ah as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-BGPikYWc.js';
|
|
4
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aL as AvatarVideoSurface, aM as AvatarVideoSurfaceProps, B as BehaviorSnapshot, C as CallTranscript, i as CallTranscriptSegment, j as CartesiaTtsModel, k as ClipResult, l as ClosingTurnResult, m as CreditsLowEvent, D as DEFAULT_APPROACHING_END_LEAD_SECONDS, n as DEFAULT_CREDITS_LOW_LEAD_SECONDS, o as DEFAULT_GOVERNOR_CONFIG, p as DEFAULT_GRACE_CEILING_SECONDS, q as DEFAULT_GRACE_WINDOW_LEAD_SECONDS, r as DEFAULT_IDLE_SECONDS, s as DEFAULT_IDLE_WARN_LEAD_SECONDS, t as DEFAULT_TURN_TIMEOUT_SECONDS, E as EndReason, u as EndedEvent, v as ExtendResult, F as FishTtsModel, w as FreezeReadingFn, G as Governor, x as GovernorAction, y as GovernorConfig, z as GovernorSignal, H as GovernorState, I as GraceWindowClosedEvent, J as GraceWindowOpenEvent, K as GraceWindowState, M as IdleWarningEvent, N as InboundRtpCursor, O as InboundRtpReading, P as KnownBehaviorState, Q as LLMProvider, R as LLMSelection, S as LiveKitAvatarGrantState, T as LiveKitAvatarGrantStatus, U as LiveKitCapacityState, V as LiveKitConnectionStatus, W as LiveKitSessionRequest, X as LiveKitSessionStartResult, aN as LivePlaybackKeeper, Y as MAX_RECONNECT_ATTEMPTS, Z as MAX_SESSION_INSTRUCTIONS_CHARS, aO as MIC_LEASE_ENDED_TIMEOUT_MS, aP as MicLease, aQ as NETWORK_QUALITY_POLICY, aR as NetworkQualityStatus, aS as PlayableVideoElement, _ as ProxyClientOptions, $ as QualityCap, a0 as RECONNECT_BACKOFF_MS, aT as RealtimeAvatarCapacityError, aU as RealtimeAvatarLiveKitRoom, aV as RealtimeAvatarLiveKitRoomProps, a1 as RealtimeAvatarRequestOptions, a2 as RealtimeSessionApi, a3 as RealtimeSessionMedia, a4 as RealtimeSessionRoomSinks, a5 as ReconnectPolicy, a6 as ReconnectingEvent, a7 as RecoveryState, a8 as RetryStep, a9 as SessionBehavior, aa as SessionClip, ab as SessionClocks, ac as SessionEndReason, ad as SessionLifecycleApi, ae as SessionLifecyclePhase, af as SessionLifecyclePhaseKind, ag as SessionLifecycleRoomBridge, ai as SurfaceLayers, aj as TurnState, ak as TurnTimeoutEvent, al as UseAvatarQualityGovernorInput, am as UseLiveKitAvatarGrantInput, an as UseRealtimeSessionInput, ao as UseSessionLifecycleInput, ap as VoiceSpec, aq as VoiceSpecInput, ar as capacityErrorFromBusy, as as capacityStateFromGrant, at as createProxyClient, av as knownBehaviorStates, aw as mapTurnState, ax as readInboundRtp, ay as sessionBehaviorSchema, az as sessionClipSchema, aW as shouldReplayPendingTurn, aA as splitCallTranscript, aB as useAvatarAdaptivePlayoutDelay, aC as useAvatarPlayoutDelay, aD as useAvatarQualityGovernor, aE as useCallTranscript, aF as useLiveKitAvatarGrant, aH as useMicLease, aI as useRealtimeSession, aJ as useReleaseMicLeaseOnTrackEnded, aK as useSessionLifecycle } from './proxy-client-BGPikYWc.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';
|
|
@@ -82,7 +82,7 @@ type AvatarCallHandle = {
|
|
|
82
82
|
/** End now. */
|
|
83
83
|
end: () => void;
|
|
84
84
|
};
|
|
85
|
-
type AvatarCallProps = {
|
|
85
|
+
type AvatarCallProps = Pick<SessionLifecycleRoomBridgeProps, "onConnectionDetailsChange"> & {
|
|
86
86
|
client: AvatarSessionClient;
|
|
87
87
|
/** Which character. */
|
|
88
88
|
avatarId: string;
|
|
@@ -129,4 +129,4 @@ declare function useAvatarCall(props: AvatarCallProps): {
|
|
|
129
129
|
/** One component: a live character on screen. */
|
|
130
130
|
declare function AvatarCall(props: AvatarCallProps): ReactNode;
|
|
131
131
|
|
|
132
|
-
export { AvatarCall, type AvatarCallEndReason, type AvatarCallHandle, type AvatarCallProps, type AvatarCallStatus, AvatarSessionClient, AvatarVideoFit, DEFAULT_AVATAR_UNSTABLE_STALL_MS, useAvatarCall, useCharacterTools };
|
|
132
|
+
export { AvatarCall, type AvatarCallEndReason, type AvatarCallHandle, type AvatarCallProps, type AvatarCallStatus, AvatarSessionClient, AvatarVideoFit, DEFAULT_AVATAR_UNSTABLE_STALL_MS, SessionLifecycleRoomBridgeProps, useAvatarCall, useCharacterTools };
|
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, VideoQuality, ConnectionQuality, RoomEvent, TrackEvent, Track, ConnectionError, ConnectionErrorReason, DisconnectReason, Room } from 'livekit-client';
|
|
4
|
+
import { ConnectionState, isRemoteTrack, 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
|
|
@@ -880,10 +880,8 @@ function useAvatarAdaptivePlayoutDelay(videoTrack, audioTrack, enabled = false,
|
|
|
880
880
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
881
881
|
useEffect(() => {
|
|
882
882
|
if (!enabled || !videoMediaTrack && !audioMediaTrack) return;
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
);
|
|
886
|
-
if (receivers.length === 0) return;
|
|
883
|
+
const tracks = [videoMediaTrack, audioMediaTrack].filter(isRemoteTrack);
|
|
884
|
+
if (tracks.length === 0) return;
|
|
887
885
|
const controller = new AdaptivePlayoutController(options);
|
|
888
886
|
const cursors = /* @__PURE__ */ new Map();
|
|
889
887
|
let closed = false;
|
|
@@ -893,9 +891,9 @@ function useAvatarAdaptivePlayoutDelay(videoTrack, audioTrack, enabled = false,
|
|
|
893
891
|
inFlight = true;
|
|
894
892
|
try {
|
|
895
893
|
let worst;
|
|
896
|
-
for (let i = 0; i <
|
|
897
|
-
const stats = await
|
|
898
|
-
if (closed ||
|
|
894
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
895
|
+
const stats = await tracks[i].getRTCStatsReport();
|
|
896
|
+
if (closed || !stats) continue;
|
|
899
897
|
const reading = readInboundRtp(stats.values(), cursors.get(i));
|
|
900
898
|
if (!reading) continue;
|
|
901
899
|
cursors.set(i, reading.cursor);
|
|
@@ -1218,20 +1216,6 @@ function bindNetworkQuality(publication, inhibited, notify, tickMs) {
|
|
|
1218
1216
|
}
|
|
1219
1217
|
|
|
1220
1218
|
// ../client/src/react/use-quality-governor.ts
|
|
1221
|
-
var qualityToSignal = (q) => {
|
|
1222
|
-
switch (q) {
|
|
1223
|
-
case ConnectionQuality.Excellent:
|
|
1224
|
-
return "excellent";
|
|
1225
|
-
case ConnectionQuality.Good:
|
|
1226
|
-
return "good";
|
|
1227
|
-
case ConnectionQuality.Poor:
|
|
1228
|
-
return "poor";
|
|
1229
|
-
case ConnectionQuality.Lost:
|
|
1230
|
-
return "lost";
|
|
1231
|
-
default:
|
|
1232
|
-
return "unknown";
|
|
1233
|
-
}
|
|
1234
|
-
};
|
|
1235
1219
|
function useAvatarQualityGovernor(input) {
|
|
1236
1220
|
const { enabled, freezeReading, onNetworkStatusChange, config: policy = DEFAULT_GOVERNOR_CONFIG, tickMs = 1e3 } = input;
|
|
1237
1221
|
const room = useMaybeRoomContext();
|
|
@@ -1295,16 +1279,14 @@ function useAvatarQualityGovernor(input) {
|
|
|
1295
1279
|
let pausedSinceTick = false;
|
|
1296
1280
|
let lastFreezeStat = null;
|
|
1297
1281
|
let jitterTrend = null;
|
|
1298
|
-
let connQuality =
|
|
1299
|
-
targetParticipant?.connectionQuality ?? ConnectionQuality.Unknown
|
|
1300
|
-
);
|
|
1282
|
+
let connQuality = targetParticipant?.connectionQuality ?? ConnectionQuality.Unknown;
|
|
1301
1283
|
const onStreamState = (pub, streamState) => {
|
|
1302
1284
|
if (pub !== targetPublication) return;
|
|
1303
1285
|
if (streamState === Track.StreamState.Paused) pausedSinceTick = true;
|
|
1304
1286
|
};
|
|
1305
1287
|
const onQuality = (q, participant) => {
|
|
1306
1288
|
if (participant.sid !== targetParticipant.sid) return;
|
|
1307
|
-
connQuality =
|
|
1289
|
+
connQuality = q;
|
|
1308
1290
|
};
|
|
1309
1291
|
try {
|
|
1310
1292
|
room.on(RoomEvent.TrackStreamStateChanged, onStreamState);
|
|
@@ -2732,7 +2714,10 @@ function useSessionLifecycle(input) {
|
|
|
2732
2714
|
]
|
|
2733
2715
|
);
|
|
2734
2716
|
}
|
|
2735
|
-
function SessionLifecycleRoomBridge({
|
|
2717
|
+
function SessionLifecycleRoomBridge({
|
|
2718
|
+
lifecycle,
|
|
2719
|
+
onConnectionDetailsChange
|
|
2720
|
+
}) {
|
|
2736
2721
|
const {
|
|
2737
2722
|
onConnectionStateChange,
|
|
2738
2723
|
setAgentPresent,
|
|
@@ -2755,6 +2740,87 @@ function SessionLifecycleRoomBridge({ lifecycle }) {
|
|
|
2755
2740
|
const audioLive = Boolean(assistant.audioTrack);
|
|
2756
2741
|
const transcriptionCount = transcriptions.length;
|
|
2757
2742
|
const prevTranscriptionCountRef = useRef(0);
|
|
2743
|
+
const audio = assistant.audioTrack;
|
|
2744
|
+
const video = assistant.videoTrack;
|
|
2745
|
+
const detailsEnabled = onConnectionDetailsChange !== void 0 && lifecycle.grant !== null;
|
|
2746
|
+
const detailsSessionId = lifecycle.grant?.session_id;
|
|
2747
|
+
const updateDetailsRef = useRef(null);
|
|
2748
|
+
useEffect(() => {
|
|
2749
|
+
if (!detailsEnabled || !onConnectionDetailsChange) return;
|
|
2750
|
+
let callback = onConnectionDetailsChange;
|
|
2751
|
+
let audioSource = audio;
|
|
2752
|
+
let videoSource = video;
|
|
2753
|
+
let closed = false;
|
|
2754
|
+
let pending = false;
|
|
2755
|
+
let previous;
|
|
2756
|
+
const deliver = (details) => {
|
|
2757
|
+
try {
|
|
2758
|
+
void Promise.resolve(callback(details)).catch(() => {
|
|
2759
|
+
});
|
|
2760
|
+
} catch {
|
|
2761
|
+
}
|
|
2762
|
+
};
|
|
2763
|
+
const publisher = (source) => source ? {
|
|
2764
|
+
publisherQuality: source.participant.connectionQuality,
|
|
2765
|
+
streamState: source.publication.track?.streamState ?? null
|
|
2766
|
+
} : null;
|
|
2767
|
+
const schedule = () => {
|
|
2768
|
+
if (pending) return;
|
|
2769
|
+
pending = true;
|
|
2770
|
+
queueMicrotask(() => {
|
|
2771
|
+
pending = false;
|
|
2772
|
+
if (closed) return;
|
|
2773
|
+
const next = {
|
|
2774
|
+
connectionState: room.state,
|
|
2775
|
+
localQuality: room.localParticipant.connectionQuality,
|
|
2776
|
+
audio: publisher(audioSource),
|
|
2777
|
+
video: publisher(videoSource)
|
|
2778
|
+
};
|
|
2779
|
+
const last = previous;
|
|
2780
|
+
if (last?.connectionState === next.connectionState && last.localQuality === next.localQuality && ["audio", "video"].every((kind) => last[kind]?.publisherQuality === next[kind]?.publisherQuality && last[kind]?.streamState === next[kind]?.streamState)) return;
|
|
2781
|
+
previous = next;
|
|
2782
|
+
deliver(next);
|
|
2783
|
+
});
|
|
2784
|
+
};
|
|
2785
|
+
const onQuality = (_quality, participant) => {
|
|
2786
|
+
if (participant === room.localParticipant || participant === audioSource?.participant || participant === videoSource?.participant) schedule();
|
|
2787
|
+
};
|
|
2788
|
+
const onPublication = (publication) => {
|
|
2789
|
+
if (publication === audioSource?.publication || publication === videoSource?.publication) schedule();
|
|
2790
|
+
};
|
|
2791
|
+
const onTrack = (_track, publication) => onPublication(publication);
|
|
2792
|
+
room.on(RoomEvent.ConnectionStateChanged, schedule);
|
|
2793
|
+
room.on(RoomEvent.ConnectionQualityChanged, onQuality);
|
|
2794
|
+
room.on(RoomEvent.TrackStreamStateChanged, onPublication);
|
|
2795
|
+
room.on(RoomEvent.TrackSubscribed, onTrack);
|
|
2796
|
+
room.on(RoomEvent.TrackUnsubscribed, onTrack);
|
|
2797
|
+
updateDetailsRef.current = (nextCallback, nextAudio, nextVideo) => {
|
|
2798
|
+
callback = nextCallback;
|
|
2799
|
+
audioSource = nextAudio;
|
|
2800
|
+
videoSource = nextVideo;
|
|
2801
|
+
schedule();
|
|
2802
|
+
};
|
|
2803
|
+
schedule();
|
|
2804
|
+
return () => {
|
|
2805
|
+
closed = true;
|
|
2806
|
+
updateDetailsRef.current = null;
|
|
2807
|
+
room.off(RoomEvent.ConnectionStateChanged, schedule);
|
|
2808
|
+
room.off(RoomEvent.ConnectionQualityChanged, onQuality);
|
|
2809
|
+
room.off(RoomEvent.TrackStreamStateChanged, onPublication);
|
|
2810
|
+
room.off(RoomEvent.TrackSubscribed, onTrack);
|
|
2811
|
+
room.off(RoomEvent.TrackUnsubscribed, onTrack);
|
|
2812
|
+
deliver(null);
|
|
2813
|
+
};
|
|
2814
|
+
}, [room, detailsSessionId, detailsEnabled]);
|
|
2815
|
+
useEffect(() => {
|
|
2816
|
+
if (onConnectionDetailsChange) updateDetailsRef.current?.(onConnectionDetailsChange, audio, video);
|
|
2817
|
+
}, [
|
|
2818
|
+
onConnectionDetailsChange,
|
|
2819
|
+
detailsEnabled ? audio?.participant : void 0,
|
|
2820
|
+
detailsEnabled ? audio?.publication : void 0,
|
|
2821
|
+
detailsEnabled ? video?.participant : void 0,
|
|
2822
|
+
detailsEnabled ? video?.publication : void 0
|
|
2823
|
+
]);
|
|
2758
2824
|
useEffect(() => {
|
|
2759
2825
|
onConnectionStateChange(connectionState);
|
|
2760
2826
|
}, [connectionState, onConnectionStateChange]);
|
|
@@ -3324,7 +3390,11 @@ function useAvatarCall(props) {
|
|
|
3324
3390
|
onDisconnected: session.onDisconnected,
|
|
3325
3391
|
onError: session.onConnectionError
|
|
3326
3392
|
},
|
|
3327
|
-
createElement(SessionLifecycleRoomBridge, {
|
|
3393
|
+
createElement(SessionLifecycleRoomBridge, {
|
|
3394
|
+
key: "bridge",
|
|
3395
|
+
lifecycle: session,
|
|
3396
|
+
onConnectionDetailsChange: props.onConnectionDetailsChange
|
|
3397
|
+
}),
|
|
3328
3398
|
createElement(
|
|
3329
3399
|
AvatarVideoSurface,
|
|
3330
3400
|
{
|
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-PKKTFUA7.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.15.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",
|