realtime-avatar 0.7.2 → 0.8.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 +11 -1
- package/dist/browser.d.ts +42 -1
- package/dist/browser.js +76 -4
- package/dist/{chunk-ARILC2U3.js → chunk-JNRRH2G7.js} +1 -1
- package/dist/{chunk-MQQKAUOB.js → chunk-TOXLWFQY.js} +1 -1
- package/dist/express.d.ts +2 -2
- package/dist/express.js +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/nextjs.d.ts +2 -2
- package/dist/nextjs.js +2 -2
- package/dist/playout-delay-DWOc8iCl.d.ts +65 -0
- package/dist/{proxy-client-BzblQMvg.d.ts → proxy-client-DDqYRz-_.d.ts} +10 -26
- package/dist/react-native.d.ts +3 -2
- package/dist/react-native.js +157 -12
- package/dist/react.d.ts +3 -2
- package/dist/react.js +178 -13
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/tanstack-start.d.ts +2 -2
- package/dist/tanstack-start.js +2 -2
- package/dist/{types-ZfY6oxQY.d.ts → types-D_aIKv4P.d.ts} +16 -4
- package/dist/{types-NKCoqM8E.d.ts → types-QoMHpR1h.d.ts} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -140,7 +140,7 @@ Browser — these never can:
|
|
|
140
140
|
| --- | --- |
|
|
141
141
|
| `realtime-avatar/react` | `AvatarCall`, `useAvatarCall`, `useRealtimeSession`, `useSessionLifecycle` |
|
|
142
142
|
| `realtime-avatar/react-native` | The same surface for Expo / React Native |
|
|
143
|
-
| `realtime-avatar/browser` | `enableMicrophone`, `attachRemoteAudio` — no React |
|
|
143
|
+
| `realtime-avatar/browser` | `enableMicrophone`, `attachRemoteAudio`, `prepareAvatarRoom` — no React |
|
|
144
144
|
| `realtime-avatar/tools` | `attachAvatarTools` — the browser tool plane |
|
|
145
145
|
|
|
146
146
|
Every adapter takes the same two hooks: `authorize` gates the request, `session` decides the
|
|
@@ -169,6 +169,16 @@ start" is one sentence covering six causes with different fixes — and one of t
|
|
|
169
169
|
system denial, cannot be fixed from the address bar and needs the browser restarted.
|
|
170
170
|
`attachRemoteAudio` attaches into the DOM *before* `connect`, which is what stops a track
|
|
171
171
|
arriving mid-connect from being lost on a fast connection.
|
|
172
|
+
`prepareAvatarRoom(room)` — one call after `new Room()` — gives every track the room subscribes
|
|
173
|
+
the same 0.5s receiver cushion the React surface applies on its own: the avatar crosses the public
|
|
174
|
+
internet, a shallow buffer freezes on every lost packet, and a deeper one recovers it before
|
|
175
|
+
playout (measured at ~5% loss: ~11fps with multi-second freezes → a steady 25fps). It covers audio
|
|
176
|
+
and video alike so the pair stays lip-locked, and `attachRemoteAudio` calls it for you, so a page
|
|
177
|
+
that follows the pattern above never has to know the knob exists. `applyPlayoutDelay(track)` is the
|
|
178
|
+
per-track primitive underneath. Until these lived here the cushion was only reachable through
|
|
179
|
+
`/react`, which is why every plain-DOM page — this repo's demos included — froze that way; and in
|
|
180
|
+
React it was applied only by `AvatarVideoSurface`, so `RealtimeAvatarLiveKitRoom` now applies it
|
|
181
|
+
room-wide to whatever you render inside it (`playoutDelaySeconds={false}` opts out).
|
|
172
182
|
|
|
173
183
|
`attachAvatarTools` runs your functions in the page. Nothing is executed on the platform, and
|
|
174
184
|
a tool has **2.5 seconds** to answer before the call gives up on it and tells her it failed.
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, P as PlayoutDelayTarget, a as applyAvatarPlayoutDelay, b as applyPlayoutDelay } from './playout-delay-DWOc8iCl.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Turning the microphone on, and saying what went wrong when it does not.
|
|
3
5
|
*
|
|
@@ -118,6 +120,14 @@ interface AudioCapableRoom {
|
|
|
118
120
|
off(event: string, listener: (...args: never[]) => void): unknown;
|
|
119
121
|
}
|
|
120
122
|
interface AttachRemoteAudioOptions {
|
|
123
|
+
/**
|
|
124
|
+
* Receiver playout cushion applied to EVERY remote track the room subscribes (video
|
|
125
|
+
* included, so the pair stays lip-locked) via `prepareAvatarRoom`. Default 0.5s — the
|
|
126
|
+
* measured freeze-free depth. `false` leaves the room untouched, for a page that owns
|
|
127
|
+
* its own buffering. Not undone by `detach()`: a shallower buffer mid-call only brings
|
|
128
|
+
* the freezes back.
|
|
129
|
+
*/
|
|
130
|
+
playoutDelaySeconds?: number | false;
|
|
121
131
|
/**
|
|
122
132
|
* Where to park the audio elements. Defaults to `document.body`.
|
|
123
133
|
*
|
|
@@ -162,6 +172,37 @@ interface RemoteAudioAttachment {
|
|
|
162
172
|
*/
|
|
163
173
|
declare function attachRemoteAudio(room: AudioCapableRoom, options?: AttachRemoteAudioOptions): RemoteAudioAttachment;
|
|
164
174
|
|
|
175
|
+
/** The sliver of a livekit-client `Room` this needs. */
|
|
176
|
+
interface PreparableRoom {
|
|
177
|
+
on(event: string, listener: (...args: never[]) => void): unknown;
|
|
178
|
+
off(event: string, listener: (...args: never[]) => void): unknown;
|
|
179
|
+
/** Read once, so tracks subscribed BEFORE this call are covered too. Optional on purpose. */
|
|
180
|
+
remoteParticipants?: Iterable<[unknown, PreparableParticipant]> | {
|
|
181
|
+
values(): Iterable<PreparableParticipant>;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** The sliver of a livekit-client `RemoteParticipant` this needs. */
|
|
185
|
+
interface PreparableParticipant {
|
|
186
|
+
trackPublications?: Iterable<[unknown, {
|
|
187
|
+
track?: unknown;
|
|
188
|
+
}]> | {
|
|
189
|
+
values(): Iterable<{
|
|
190
|
+
track?: unknown;
|
|
191
|
+
}>;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
interface PrepareAvatarRoomOptions {
|
|
195
|
+
/** Receiver cushion in seconds for every remote track. Default 0.5 — see `./playout-delay.ts`. */
|
|
196
|
+
playoutDelaySeconds?: number;
|
|
197
|
+
}
|
|
198
|
+
interface PreparedAvatarRoom {
|
|
199
|
+
/** Stop applying the cushion to tracks subscribed from now on. Idempotent. */
|
|
200
|
+
detach(): void;
|
|
201
|
+
/** The delay currently applied to new tracks. */
|
|
202
|
+
readonly playoutDelaySeconds: number;
|
|
203
|
+
}
|
|
204
|
+
declare function prepareAvatarRoom(room: PreparableRoom, options?: PrepareAvatarRoomOptions): PreparedAvatarRoom;
|
|
205
|
+
|
|
165
206
|
/**
|
|
166
207
|
* Ping-pong (boomerang) playback for idle/ambient clips.
|
|
167
208
|
*
|
|
@@ -200,4 +241,4 @@ type BoomerangPlayback = {
|
|
|
200
241
|
*/
|
|
201
242
|
declare function attachBoomerangPlayback(video: HTMLVideoElement, options?: BoomerangPlaybackOptions): BoomerangPlayback;
|
|
202
243
|
|
|
203
|
-
export { type AttachRemoteAudioOptions, type AttachableTrack, type AudioCapableRoom, type BoomerangPlayback, type BoomerangPlaybackOptions, type EnableMicrophoneOptions, type MicrophoneCapableRoom, type MicrophoneFailureReason, type MicrophoneResult, type RemoteAudioAttachment, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone };
|
|
244
|
+
export { type AttachRemoteAudioOptions, type AttachableTrack, type AudioCapableRoom, type BoomerangPlayback, type BoomerangPlaybackOptions, type EnableMicrophoneOptions, type MicrophoneCapableRoom, type MicrophoneFailureReason, type MicrophoneResult, type PreparableParticipant, type PreparableRoom, type PrepareAvatarRoomOptions, type PreparedAvatarRoom, type RemoteAudioAttachment, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone, prepareAvatarRoom };
|
package/dist/browser.js
CHANGED
|
@@ -75,14 +75,86 @@ async function enableMicrophone(room, options = {}) {
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// ../browser/src/playout-delay.ts
|
|
79
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
80
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
81
|
+
const target = playoutDelayTarget(track);
|
|
82
|
+
if (!target) return false;
|
|
83
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
87
|
+
const delay = Math.max(0, delaySeconds);
|
|
88
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
89
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
90
|
+
}
|
|
91
|
+
function playoutDelayTarget(track) {
|
|
92
|
+
const candidate = track;
|
|
93
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ../browser/src/prepare-room.ts
|
|
97
|
+
var TRACK_SUBSCRIBED = "trackSubscribed";
|
|
98
|
+
var prepared = /* @__PURE__ */ new WeakMap();
|
|
99
|
+
function prepareAvatarRoom(room, options = {}) {
|
|
100
|
+
const requested = options.playoutDelaySeconds;
|
|
101
|
+
const existing = prepared.get(room);
|
|
102
|
+
if (existing) {
|
|
103
|
+
if (requested !== void 0 && requested !== existing.delay) {
|
|
104
|
+
existing.delay = requested;
|
|
105
|
+
applyToExisting(room, requested);
|
|
106
|
+
}
|
|
107
|
+
return existing.handle;
|
|
108
|
+
}
|
|
109
|
+
const state = {
|
|
110
|
+
delay: requested ?? DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS,
|
|
111
|
+
listener: ((track) => {
|
|
112
|
+
const current = prepared.get(room);
|
|
113
|
+
applyPlayoutDelay(track, current ? current.delay : DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS);
|
|
114
|
+
}),
|
|
115
|
+
handle: {
|
|
116
|
+
get playoutDelaySeconds() {
|
|
117
|
+
return prepared.get(room)?.delay ?? state.delay;
|
|
118
|
+
},
|
|
119
|
+
detach() {
|
|
120
|
+
const live = prepared.get(room);
|
|
121
|
+
if (!live) return;
|
|
122
|
+
room.off(TRACK_SUBSCRIBED, live.listener);
|
|
123
|
+
prepared.delete(room);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
prepared.set(room, state);
|
|
128
|
+
room.on(TRACK_SUBSCRIBED, state.listener);
|
|
129
|
+
applyToExisting(room, state.delay);
|
|
130
|
+
return state.handle;
|
|
131
|
+
}
|
|
132
|
+
function applyToExisting(room, delay) {
|
|
133
|
+
for (const participant of values(room.remoteParticipants)) {
|
|
134
|
+
for (const publication of values(participant?.trackPublications)) {
|
|
135
|
+
if (publication?.track) applyPlayoutDelay(publication.track, delay);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function values(source) {
|
|
140
|
+
if (!source) return [];
|
|
141
|
+
if (Symbol.iterator in source) {
|
|
142
|
+
return Array.from(source, ([, value]) => value);
|
|
143
|
+
}
|
|
144
|
+
return source.values();
|
|
145
|
+
}
|
|
146
|
+
|
|
78
147
|
// ../browser/src/remote-audio.ts
|
|
79
148
|
var AUDIO_PLAYBACK_CHANGED = "audioPlaybackChanged";
|
|
80
|
-
var
|
|
149
|
+
var TRACK_SUBSCRIBED2 = "trackSubscribed";
|
|
81
150
|
var AUDIO_KIND = "audio";
|
|
82
151
|
function attachRemoteAudio(room, options = {}) {
|
|
83
152
|
const container = options.container ?? document.body;
|
|
84
153
|
const elements = /* @__PURE__ */ new Set();
|
|
85
154
|
let detached = false;
|
|
155
|
+
if (options.playoutDelaySeconds !== false) {
|
|
156
|
+
prepareAvatarRoom(room, { playoutDelaySeconds: options.playoutDelaySeconds });
|
|
157
|
+
}
|
|
86
158
|
const publishPlaybackState = () => {
|
|
87
159
|
if (detached) return;
|
|
88
160
|
options.onPlaybackBlocked?.(room.canPlaybackAudio ? null : () => room.startAudio());
|
|
@@ -95,14 +167,14 @@ function attachRemoteAudio(room, options = {}) {
|
|
|
95
167
|
elements.add(element);
|
|
96
168
|
publishPlaybackState();
|
|
97
169
|
};
|
|
98
|
-
room.on(
|
|
170
|
+
room.on(TRACK_SUBSCRIBED2, onTrackSubscribed);
|
|
99
171
|
room.on(AUDIO_PLAYBACK_CHANGED, publishPlaybackState);
|
|
100
172
|
publishPlaybackState();
|
|
101
173
|
return {
|
|
102
174
|
detach() {
|
|
103
175
|
if (detached) return;
|
|
104
176
|
detached = true;
|
|
105
|
-
room.off(
|
|
177
|
+
room.off(TRACK_SUBSCRIBED2, onTrackSubscribed);
|
|
106
178
|
room.off(AUDIO_PLAYBACK_CHANGED, publishPlaybackState);
|
|
107
179
|
for (const element of elements) element.remove();
|
|
108
180
|
elements.clear();
|
|
@@ -169,4 +241,4 @@ function now() {
|
|
|
169
241
|
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
170
242
|
}
|
|
171
243
|
|
|
172
|
-
export { attachBoomerangPlayback, attachRemoteAudio, enableMicrophone };
|
|
244
|
+
export { DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS, applyAvatarPlayoutDelay, applyPlayoutDelay, attachBoomerangPlayback, attachRemoteAudio, enableMicrophone, prepareAvatarRoom };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-
|
|
1
|
+
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-JNRRH2G7.js';
|
|
2
2
|
|
|
3
3
|
// ../proxy/src/config.ts
|
|
4
4
|
var json = (body, status = 200) => new Response(JSON.stringify(body), {
|
package/dist/express.d.ts
CHANGED
package/dist/express.js
CHANGED
package/dist/hono.d.ts
CHANGED
package/dist/hono.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as ClipSyncResult, h as AssetKind, i as Asset, j as ListSessionsOptions, U as UsageSessionPage, k as UsageSession, l as CreditBalance, T as TranscriptPayload } from './types-
|
|
2
|
-
export { m as AvatarClip, n as CallConnection, o as CallQueued, p as ClipLibraryPlan, q as ClipSource, r as ContextMessage, s as EndCallReason, V as VideoPolicy, t as VideoState, u as isQueued } from './types-
|
|
1
|
+
import { a as CallPolicy, C as CallMode, S as StartCallResult, E as EndCallOptions, A as Avatar, b as AvatarUpdate, c as AvatarSourceSwap, d as ClipDeclaration, e as ClipLibraryUpdate, L as LoopRedirect, f as ClipLibrary, g as ClipSyncResult, h as AssetKind, i as Asset, j as ListSessionsOptions, U as UsageSessionPage, k as UsageSession, l as CreditBalance, T as TranscriptPayload } from './types-D_aIKv4P.js';
|
|
2
|
+
export { m as AvatarClip, n as CallConnection, o as CallQueued, p as ClipLibraryPlan, q as ClipSource, r as ContextMessage, s as EndCallReason, V as VideoPolicy, t as VideoState, u as isQueued } from './types-D_aIKv4P.js';
|
|
3
3
|
|
|
4
4
|
interface RealtimeAvatarOptions {
|
|
5
5
|
/** `tic_live_…` or `tic_test_…`. Server-side only — never ship this to a browser. */
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-JNRRH2G7.js';
|
package/dist/nextjs.d.ts
CHANGED
package/dist/nextjs.js
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receiver-side playout delay (SECONDS) for the avatar media tracks.
|
|
3
|
+
*
|
|
4
|
+
* The avatar is published from a GPU datacenter and reaches viewers over the
|
|
5
|
+
* public internet, where a few percent of packet loss is normal. A shallow
|
|
6
|
+
* receiver buffer stalls (freezes) on every loss while it waits for a
|
|
7
|
+
* retransmit; a deeper buffer recovers the lost packets BEFORE playout, so the
|
|
8
|
+
* viewer sees smooth 25fps instead of freezing. Measured at ~5% loss this took
|
|
9
|
+
* the stream from ~11fps with multi-second freezes to a steady 25fps with zero
|
|
10
|
+
* freezes.
|
|
11
|
+
*
|
|
12
|
+
* 0.5s is applied via the native `RemoteTrack.setPlayoutDelay`, which sets the
|
|
13
|
+
* receiver's `playoutDelayHint` — the same Chromium jitter-buffer knob the old
|
|
14
|
+
* hand-rolled `jitterBufferTarget` reached into (the spec renamed
|
|
15
|
+
* playoutDelayHint→jitterBufferTarget; both influence the same buffer depth),
|
|
16
|
+
* but typed and SDK-owned so there is no cast or feature-probe. The hint is NOT
|
|
17
|
+
* free at the open: the receiver holds the FIRST video frame toward the target
|
|
18
|
+
* (~250-450ms of measured TTFF), a deliberate cost paid once for the freeze-free
|
|
19
|
+
* steady state above. The open-small-then-ramp variant that tried to dodge that
|
|
20
|
+
* cost was shipped, failed twice (the rVFC ramp trigger is throttled on the
|
|
21
|
+
* opacity:0-mounted element, stranding the shallow buffer), and was deliberately
|
|
22
|
+
* removed in 2262e4c7b — do not re-propose it. No-op on browsers that don't
|
|
23
|
+
* support the hint (the SDK warns and moves on).
|
|
24
|
+
*
|
|
25
|
+
* This lives in the vanilla browser package, not the React one, because the
|
|
26
|
+
* React surface applies it for you and a plain-DOM page has to be given it.
|
|
27
|
+
* Until this file existed the helper was only reachable through
|
|
28
|
+
* `realtime-avatar/react`, so every plain-DOM adopter — every one of this repo's
|
|
29
|
+
* own demos included — ran with the browser's default shallow buffer and froze on
|
|
30
|
+
* every lost packet. Nobody should have to know this knob exists: `prepareAvatarRoom`
|
|
31
|
+
* (and `attachRemoteAudio`, which calls it) applies it to every track the room
|
|
32
|
+
* subscribes, and `RealtimeAvatarLiveKitRoom` does the same for React. This file is
|
|
33
|
+
* the one place the number and its reasoning live.
|
|
34
|
+
* Structural like its siblings: no `livekit-client` import, no version pin.
|
|
35
|
+
*/
|
|
36
|
+
declare const DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
37
|
+
/**
|
|
38
|
+
* A track that can take a native playout-delay hint — livekit-client's subscribed
|
|
39
|
+
* `RemoteTrack`. Duck-typed on the method rather than `instanceof`, so a
|
|
40
|
+
* placeholder or local track is skipped and the helper is testable with a fake.
|
|
41
|
+
*/
|
|
42
|
+
interface PlayoutDelayTarget {
|
|
43
|
+
setPlayoutDelay(delayInSeconds: number): void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Apply the receiver playout delay to ONE subscribed track.
|
|
47
|
+
*
|
|
48
|
+
* Call it for every track `RoomEvent.TrackSubscribed` hands you, audio and video
|
|
49
|
+
* alike, before or after `attach()` — WebRTC syncs the pair to the LARGER hint,
|
|
50
|
+
* so applying it to one stream and not the other lets the lips lag the voice.
|
|
51
|
+
* Returns `true` when the hint was applied and `false` when the object cannot take
|
|
52
|
+
* one (a local track, a placeholder, a not-yet-subscribed publication), so a
|
|
53
|
+
* caller can log the miss without a try/catch. The delay is floored at 0 so a
|
|
54
|
+
* negative input can never throw.
|
|
55
|
+
*/
|
|
56
|
+
declare function applyPlayoutDelay(track: unknown, delaySeconds?: number): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Apply the SAME native playout delay to the avatar's audio + video tracks
|
|
59
|
+
* (kept equal so the a/v sync stays lip-locked). Pure and exported so the clamp
|
|
60
|
+
* and the both-tracks-equal invariant are unit-testable without a DOM; the React
|
|
61
|
+
* hook `useAvatarPlayoutDelay` is a thin effect wrapper around this.
|
|
62
|
+
*/
|
|
63
|
+
declare function applyAvatarPlayoutDelay(videoTrack: unknown, audioTrack: unknown, delaySeconds?: number): void;
|
|
64
|
+
|
|
65
|
+
export { DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS as D, type PlayoutDelayTarget as P, applyAvatarPlayoutDelay as a, applyPlayoutDelay as b };
|
|
@@ -740,31 +740,7 @@ declare function useCallTranscript(): CallTranscript;
|
|
|
740
740
|
* (needs `useLocalParticipant`), e.g. under {@link RealtimeAvatarLiveKitRoom}.
|
|
741
741
|
*/
|
|
742
742
|
declare function useReleaseMicLeaseOnTrackEnded(token: symbol): void;
|
|
743
|
-
|
|
744
|
-
* Receiver-side playout delay (SECONDS) for the avatar media tracks.
|
|
745
|
-
*
|
|
746
|
-
* The avatar is published from a GPU datacenter and reaches viewers over the
|
|
747
|
-
* public internet, where a few percent of packet loss is normal. A shallow
|
|
748
|
-
* receiver buffer stalls (freezes) on every loss while it waits for a
|
|
749
|
-
* retransmit; a deeper buffer recovers the lost packets BEFORE playout, so the
|
|
750
|
-
* viewer sees smooth 25fps instead of freezing. Measured at ~5% loss this took
|
|
751
|
-
* the stream from ~11fps with multi-second freezes to a steady 25fps with zero
|
|
752
|
-
* freezes.
|
|
753
|
-
*
|
|
754
|
-
* 0.5s is applied via the native {@link RemoteTrack.setPlayoutDelay}, which sets
|
|
755
|
-
* the receiver's `playoutDelayHint` — the same Chromium jitter-buffer knob the
|
|
756
|
-
* old hand-rolled `jitterBufferTarget` reached into (the spec renamed
|
|
757
|
-
* playoutDelayHint→jitterBufferTarget; both influence the same buffer depth),
|
|
758
|
-
* but typed and SDK-owned so there is no cast or feature-probe. The hint is NOT
|
|
759
|
-
* free at the open: the receiver holds the FIRST video frame toward the target
|
|
760
|
-
* (~250-450ms of measured TTFF), a deliberate cost paid once for the freeze-free
|
|
761
|
-
* steady state above. The open-small-then-ramp variant that tried to dodge that
|
|
762
|
-
* cost was shipped, failed twice (the rVFC ramp trigger is throttled on the
|
|
763
|
-
* opacity:0-mounted element, stranding the shallow buffer), and was deliberately
|
|
764
|
-
* removed in 2262e4c7b — do not re-propose it. No-op on browsers that don't
|
|
765
|
-
* support the hint (the SDK warns and moves on).
|
|
766
|
-
*/
|
|
767
|
-
declare const DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
743
|
+
|
|
768
744
|
/**
|
|
769
745
|
* Sets the avatar AUDIO and VIDEO tracks' native playout delay so the stream has
|
|
770
746
|
* a real de-jitter / loss-recovery cushion for smooth, freeze-free playback.
|
|
@@ -861,6 +837,14 @@ type RealtimeAvatarLiveKitRoomProps = Omit<LiveKitRoomProps, "serverUrl" | "toke
|
|
|
861
837
|
/** Passed directly to LiveKitRoom. */
|
|
862
838
|
options?: LiveKitRoomProps["options"];
|
|
863
839
|
renderRoomAudio?: boolean;
|
|
840
|
+
/**
|
|
841
|
+
* Receiver playout cushion applied to EVERY remote track this room subscribes — so
|
|
842
|
+
* whatever you render inside (a bare `<VideoTrack>`, your own `<video>`, or
|
|
843
|
+
* {@link AvatarVideoSurface}) gets the freeze-free buffer, not only the SDK's own
|
|
844
|
+
* surface. Default {@link DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS}. `false` opts out
|
|
845
|
+
* for a consumer that owns its own buffering.
|
|
846
|
+
*/
|
|
847
|
+
playoutDelaySeconds?: number | false;
|
|
864
848
|
children?: ReactNode;
|
|
865
849
|
};
|
|
866
850
|
/** Thin typed bridge from a Realtime Avatar grant into LiveKitRoom. */
|
|
@@ -1633,4 +1617,4 @@ interface ProxyClientOptions {
|
|
|
1633
1617
|
}
|
|
1634
1618
|
declare function createProxyClient(options: ProxyClientOptions): AvatarSessionClient;
|
|
1635
1619
|
|
|
1636
|
-
export {
|
|
1620
|
+
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 aP, RealtimeAvatarCapacityError as aQ, RealtimeAvatarLiveKitRoom as aR, type RealtimeAvatarLiveKitRoomProps as aS, shouldReplayPendingTurn as aT, 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,10 +1,11 @@
|
|
|
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
|
|
4
|
+
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-DDqYRz-_.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-DDqYRz-_.js';
|
|
6
6
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
7
|
export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
|
|
8
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
|
8
9
|
export { DisconnectReason, RemoteAudioTrack, RemoteTrack, RemoteVideoTrack, Room, RoomEvent, SendTextOptions, Track } from 'livekit-client';
|
|
9
10
|
import 'zod';
|
|
10
11
|
|
package/dist/react-native.js
CHANGED
|
@@ -130,7 +130,62 @@ function timeoutSetter(fn, ms) {
|
|
|
130
130
|
return () => clearTimeout(id);
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
// ../browser/src/playout-delay.ts
|
|
134
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
135
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
136
|
+
const target = playoutDelayTarget(track);
|
|
137
|
+
if (!target) return false;
|
|
138
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
142
|
+
const delay = Math.max(0, delaySeconds);
|
|
143
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
144
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
145
|
+
}
|
|
146
|
+
function playoutDelayTarget(track) {
|
|
147
|
+
const candidate = track;
|
|
148
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
133
151
|
// ../client/src/errors.ts
|
|
152
|
+
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
153
|
+
constructor(message, status, body, response, metadata = {}) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.status = status;
|
|
156
|
+
this.body = body;
|
|
157
|
+
this.response = response;
|
|
158
|
+
this.name = "RealtimeAvatarApiError";
|
|
159
|
+
this.code = metadata.code;
|
|
160
|
+
this.retryable = metadata.retryable;
|
|
161
|
+
this.billingUrl = metadata.billingUrl;
|
|
162
|
+
this.rawMessage = metadata.rawMessage ?? null;
|
|
163
|
+
}
|
|
164
|
+
status;
|
|
165
|
+
body;
|
|
166
|
+
response;
|
|
167
|
+
code;
|
|
168
|
+
retryable;
|
|
169
|
+
billingUrl;
|
|
170
|
+
/** Debug detail returned by the API. Do not render directly in user-facing UI. */
|
|
171
|
+
rawMessage;
|
|
172
|
+
get isBillingRequired() {
|
|
173
|
+
return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
|
|
174
|
+
}
|
|
175
|
+
static async fromResponse(response) {
|
|
176
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
177
|
+
const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
|
|
178
|
+
const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
|
|
179
|
+
const metadata = extractErrorMetadata(body, response.status, rawMessage);
|
|
180
|
+
return new _RealtimeAvatarApiError(
|
|
181
|
+
metadata.message,
|
|
182
|
+
response.status,
|
|
183
|
+
body,
|
|
184
|
+
response,
|
|
185
|
+
metadata
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
134
189
|
var RealtimeAvatarCapacityError = class extends Error {
|
|
135
190
|
constructor(message, busy) {
|
|
136
191
|
super(message);
|
|
@@ -151,6 +206,107 @@ var RealtimeAvatarCapacityError = class extends Error {
|
|
|
151
206
|
return this.busy.recommended_retry_ms;
|
|
152
207
|
}
|
|
153
208
|
};
|
|
209
|
+
function extractErrorMessage(body) {
|
|
210
|
+
if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
|
|
211
|
+
const record = body;
|
|
212
|
+
const detail = readMessageValue(record.detail);
|
|
213
|
+
if (detail) return detail;
|
|
214
|
+
const error = readMessageValue(record.error);
|
|
215
|
+
if (error) return error;
|
|
216
|
+
const message = readMessageValue(record.message);
|
|
217
|
+
if (message) return message;
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
function normalizeRealtimeAvatarError(input) {
|
|
221
|
+
const status = input.status ?? 0;
|
|
222
|
+
const rawCode = input.code?.trim() || null;
|
|
223
|
+
const rawMessage = input.message?.trim() || "";
|
|
224
|
+
const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
|
|
225
|
+
if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
|
|
226
|
+
return {
|
|
227
|
+
code: rawCode ?? "billing_required",
|
|
228
|
+
message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
|
|
229
|
+
retryable: false
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (status === 401) {
|
|
233
|
+
return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
|
|
234
|
+
}
|
|
235
|
+
if (status === 403) {
|
|
236
|
+
return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
|
|
237
|
+
}
|
|
238
|
+
if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
|
|
239
|
+
return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
|
|
240
|
+
}
|
|
241
|
+
if (status === 409) {
|
|
242
|
+
return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
|
|
243
|
+
}
|
|
244
|
+
if (status === 429) {
|
|
245
|
+
return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
|
|
246
|
+
}
|
|
247
|
+
if (isWarmingMessage(blob)) {
|
|
248
|
+
return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
|
|
249
|
+
}
|
|
250
|
+
if (status === 400 || status === 422) {
|
|
251
|
+
return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
|
|
252
|
+
}
|
|
253
|
+
if (status >= 500) {
|
|
254
|
+
return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
|
|
255
|
+
}
|
|
256
|
+
return {
|
|
257
|
+
code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
|
|
258
|
+
message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
|
|
259
|
+
retryable: status === 0 || status >= 500
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function extractErrorMetadata(body, status, rawMessage) {
|
|
263
|
+
if (!body || typeof body !== "object") {
|
|
264
|
+
const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
|
|
265
|
+
return { ...normalized2, rawMessage };
|
|
266
|
+
}
|
|
267
|
+
const record = body;
|
|
268
|
+
const code = typeof record.code === "string" ? record.code : null;
|
|
269
|
+
const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
|
|
270
|
+
return {
|
|
271
|
+
code: normalized.code,
|
|
272
|
+
retryable: typeof record.retryable === "boolean" ? record.retryable : normalized.retryable,
|
|
273
|
+
...typeof record.billingUrl === "string" ? { billingUrl: record.billingUrl } : {},
|
|
274
|
+
rawMessage,
|
|
275
|
+
message: normalized.message
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
279
|
+
"billing_required",
|
|
280
|
+
"invalid_request",
|
|
281
|
+
"unauthorized",
|
|
282
|
+
"forbidden",
|
|
283
|
+
"not_found",
|
|
284
|
+
"conflict",
|
|
285
|
+
"rate_limited",
|
|
286
|
+
"service_unavailable",
|
|
287
|
+
"request_failed",
|
|
288
|
+
"network_error"
|
|
289
|
+
]);
|
|
290
|
+
function isPublicErrorCode(code) {
|
|
291
|
+
return Boolean(code && PUBLIC_ERROR_CODES.has(code));
|
|
292
|
+
}
|
|
293
|
+
function readMessageValue(value) {
|
|
294
|
+
if (typeof value === "string") return value.trim() || null;
|
|
295
|
+
if (!value || typeof value !== "object") return null;
|
|
296
|
+
const record = value;
|
|
297
|
+
return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
|
|
298
|
+
}
|
|
299
|
+
function userSafeMessage(message) {
|
|
300
|
+
if (!message || hasInternalDetail(message)) return null;
|
|
301
|
+
return message.length > 180 ? `${message.slice(0, 177)}...` : message;
|
|
302
|
+
}
|
|
303
|
+
function isWarmingMessage(blob) {
|
|
304
|
+
return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
|
|
305
|
+
}
|
|
306
|
+
var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
|
|
307
|
+
function hasInternalDetail(message) {
|
|
308
|
+
return MACHINE_SHAPED.test(message);
|
|
309
|
+
}
|
|
154
310
|
function splitCallTranscript(all, localIdentity) {
|
|
155
311
|
if (!localIdentity) return { user: [], agent: all };
|
|
156
312
|
const user = [];
|
|
@@ -189,7 +345,6 @@ function useReleaseMicLeaseOnTrackEnded(token) {
|
|
|
189
345
|
};
|
|
190
346
|
}, [localParticipant, token, localParticipant?.audioTrackPublications.size]);
|
|
191
347
|
}
|
|
192
|
-
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
193
348
|
function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
194
349
|
const videoMediaTrack = videoTrack?.publication?.track;
|
|
195
350
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
@@ -197,15 +352,6 @@ function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AV
|
|
|
197
352
|
applyAvatarPlayoutDelay(videoMediaTrack, audioMediaTrack, delaySeconds);
|
|
198
353
|
}, [videoMediaTrack, audioMediaTrack, delaySeconds]);
|
|
199
354
|
}
|
|
200
|
-
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
201
|
-
const delay = Math.max(0, delaySeconds);
|
|
202
|
-
playoutDelayTarget(videoTrack)?.setPlayoutDelay(delay);
|
|
203
|
-
playoutDelayTarget(audioTrack)?.setPlayoutDelay(delay);
|
|
204
|
-
}
|
|
205
|
-
function playoutDelayTarget(track) {
|
|
206
|
-
const candidate = track;
|
|
207
|
-
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
208
|
-
}
|
|
209
355
|
function resolveReleaseTarget(heldSessionId, queueTicketId) {
|
|
210
356
|
if (heldSessionId) return { kind: "session", sessionId: heldSessionId };
|
|
211
357
|
if (queueTicketId) return { kind: "ticket", queueTicketId };
|
|
@@ -2390,8 +2536,7 @@ function createProxyClient(options) {
|
|
|
2390
2536
|
return { status: "busy", busy };
|
|
2391
2537
|
}
|
|
2392
2538
|
if (!response.ok) {
|
|
2393
|
-
|
|
2394
|
-
throw new Error(`realtime-avatar: proxy refused the call (${response.status}) ${detail}`.trim());
|
|
2539
|
+
throw await RealtimeAvatarApiError.fromResponse(response);
|
|
2395
2540
|
}
|
|
2396
2541
|
return { status: "ready", grant: await response.json() };
|
|
2397
2542
|
},
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-
|
|
3
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason,
|
|
2
|
+
import { g as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-DDqYRz-_.js';
|
|
3
|
+
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, aQ as RealtimeAvatarCapacityError, aR as RealtimeAvatarLiveKitRoom, aS 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, aT 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-DDqYRz-_.js';
|
|
4
|
+
export { D as DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS } from './playout-delay-DWOc8iCl.js';
|
|
4
5
|
import '@livekit/components-react';
|
|
5
6
|
import 'livekit-client';
|
|
6
7
|
import 'zod';
|
package/dist/react.js
CHANGED
|
@@ -89,7 +89,62 @@ function timeoutSetter(fn, ms) {
|
|
|
89
89
|
return () => clearTimeout(id);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// ../browser/src/playout-delay.ts
|
|
93
|
+
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
94
|
+
function applyPlayoutDelay(track, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
95
|
+
const target = playoutDelayTarget(track);
|
|
96
|
+
if (!target) return false;
|
|
97
|
+
target.setPlayoutDelay(Math.max(0, delaySeconds));
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
101
|
+
const delay = Math.max(0, delaySeconds);
|
|
102
|
+
applyPlayoutDelay(videoTrack, delay);
|
|
103
|
+
applyPlayoutDelay(audioTrack, delay);
|
|
104
|
+
}
|
|
105
|
+
function playoutDelayTarget(track) {
|
|
106
|
+
const candidate = track;
|
|
107
|
+
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
92
110
|
// ../client/src/errors.ts
|
|
111
|
+
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
112
|
+
constructor(message, status, body, response, metadata = {}) {
|
|
113
|
+
super(message);
|
|
114
|
+
this.status = status;
|
|
115
|
+
this.body = body;
|
|
116
|
+
this.response = response;
|
|
117
|
+
this.name = "RealtimeAvatarApiError";
|
|
118
|
+
this.code = metadata.code;
|
|
119
|
+
this.retryable = metadata.retryable;
|
|
120
|
+
this.billingUrl = metadata.billingUrl;
|
|
121
|
+
this.rawMessage = metadata.rawMessage ?? null;
|
|
122
|
+
}
|
|
123
|
+
status;
|
|
124
|
+
body;
|
|
125
|
+
response;
|
|
126
|
+
code;
|
|
127
|
+
retryable;
|
|
128
|
+
billingUrl;
|
|
129
|
+
/** Debug detail returned by the API. Do not render directly in user-facing UI. */
|
|
130
|
+
rawMessage;
|
|
131
|
+
get isBillingRequired() {
|
|
132
|
+
return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
|
|
133
|
+
}
|
|
134
|
+
static async fromResponse(response) {
|
|
135
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
136
|
+
const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
|
|
137
|
+
const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
|
|
138
|
+
const metadata = extractErrorMetadata(body, response.status, rawMessage);
|
|
139
|
+
return new _RealtimeAvatarApiError(
|
|
140
|
+
metadata.message,
|
|
141
|
+
response.status,
|
|
142
|
+
body,
|
|
143
|
+
response,
|
|
144
|
+
metadata
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
93
148
|
var RealtimeAvatarCapacityError = class extends Error {
|
|
94
149
|
constructor(message, busy) {
|
|
95
150
|
super(message);
|
|
@@ -110,6 +165,107 @@ var RealtimeAvatarCapacityError = class extends Error {
|
|
|
110
165
|
return this.busy.recommended_retry_ms;
|
|
111
166
|
}
|
|
112
167
|
};
|
|
168
|
+
function extractErrorMessage(body) {
|
|
169
|
+
if (!body || typeof body !== "object") return typeof body === "string" && body ? body : null;
|
|
170
|
+
const record = body;
|
|
171
|
+
const detail = readMessageValue(record.detail);
|
|
172
|
+
if (detail) return detail;
|
|
173
|
+
const error = readMessageValue(record.error);
|
|
174
|
+
if (error) return error;
|
|
175
|
+
const message = readMessageValue(record.message);
|
|
176
|
+
if (message) return message;
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
function normalizeRealtimeAvatarError(input) {
|
|
180
|
+
const status = input.status ?? 0;
|
|
181
|
+
const rawCode = input.code?.trim() || null;
|
|
182
|
+
const rawMessage = input.message?.trim() || "";
|
|
183
|
+
const blob = `${rawCode ?? ""} ${rawMessage}`.toLowerCase();
|
|
184
|
+
if (rawCode === "insufficient_credits" || rawCode === "spend_limit_exceeded" || status === 402) {
|
|
185
|
+
return {
|
|
186
|
+
code: rawCode ?? "billing_required",
|
|
187
|
+
message: rawMessage && !hasInternalDetail(rawMessage) ? rawMessage : "Add credits to continue using realtime avatars.",
|
|
188
|
+
retryable: false
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (status === 401) {
|
|
192
|
+
return { code: "unauthorized", message: "Authentication is required to use Realtime Avatar.", retryable: false };
|
|
193
|
+
}
|
|
194
|
+
if (status === 403) {
|
|
195
|
+
return { code: "forbidden", message: "This API key does not have access to that action.", retryable: false };
|
|
196
|
+
}
|
|
197
|
+
if (status === 404 || blob.includes("unknown avatar_id") || blob.includes("avatar not found")) {
|
|
198
|
+
return { code: "not_found", message: "We could not find that avatar or resource.", retryable: false };
|
|
199
|
+
}
|
|
200
|
+
if (status === 409) {
|
|
201
|
+
return { code: "conflict", message: "That request is already in progress. Try again in a moment.", retryable: true };
|
|
202
|
+
}
|
|
203
|
+
if (status === 429) {
|
|
204
|
+
return { code: "rate_limited", message: "Too many realtime requests. Please slow down and retry.", retryable: true };
|
|
205
|
+
}
|
|
206
|
+
if (isWarmingMessage(blob)) {
|
|
207
|
+
return { code: "service_unavailable", message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true };
|
|
208
|
+
}
|
|
209
|
+
if (status === 400 || status === 422) {
|
|
210
|
+
return { code: "invalid_request", message: userSafeMessage(rawMessage) ?? "Check the request and try again.", retryable: false };
|
|
211
|
+
}
|
|
212
|
+
if (status >= 500) {
|
|
213
|
+
return { code: "service_unavailable", message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true };
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
code: isPublicErrorCode(rawCode) ? rawCode : "request_failed",
|
|
217
|
+
message: userSafeMessage(rawMessage) ?? "Realtime Avatar request failed. Try again.",
|
|
218
|
+
retryable: status === 0 || status >= 500
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function extractErrorMetadata(body, status, rawMessage) {
|
|
222
|
+
if (!body || typeof body !== "object") {
|
|
223
|
+
const normalized2 = normalizeRealtimeAvatarError({ status, message: rawMessage });
|
|
224
|
+
return { ...normalized2, rawMessage };
|
|
225
|
+
}
|
|
226
|
+
const record = body;
|
|
227
|
+
const code = typeof record.code === "string" ? record.code : null;
|
|
228
|
+
const normalized = normalizeRealtimeAvatarError({ status, code, message: rawMessage });
|
|
229
|
+
return {
|
|
230
|
+
code: normalized.code,
|
|
231
|
+
retryable: typeof record.retryable === "boolean" ? record.retryable : normalized.retryable,
|
|
232
|
+
...typeof record.billingUrl === "string" ? { billingUrl: record.billingUrl } : {},
|
|
233
|
+
rawMessage,
|
|
234
|
+
message: normalized.message
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
238
|
+
"billing_required",
|
|
239
|
+
"invalid_request",
|
|
240
|
+
"unauthorized",
|
|
241
|
+
"forbidden",
|
|
242
|
+
"not_found",
|
|
243
|
+
"conflict",
|
|
244
|
+
"rate_limited",
|
|
245
|
+
"service_unavailable",
|
|
246
|
+
"request_failed",
|
|
247
|
+
"network_error"
|
|
248
|
+
]);
|
|
249
|
+
function isPublicErrorCode(code) {
|
|
250
|
+
return Boolean(code && PUBLIC_ERROR_CODES.has(code));
|
|
251
|
+
}
|
|
252
|
+
function readMessageValue(value) {
|
|
253
|
+
if (typeof value === "string") return value.trim() || null;
|
|
254
|
+
if (!value || typeof value !== "object") return null;
|
|
255
|
+
const record = value;
|
|
256
|
+
return readMessageValue(record.message) ?? readMessageValue(record.error) ?? readMessageValue(record.detail);
|
|
257
|
+
}
|
|
258
|
+
function userSafeMessage(message) {
|
|
259
|
+
if (!message || hasInternalDetail(message)) return null;
|
|
260
|
+
return message.length > 180 ? `${message.slice(0, 177)}...` : message;
|
|
261
|
+
}
|
|
262
|
+
function isWarmingMessage(blob) {
|
|
263
|
+
return blob.includes("warming") || blob.includes("not ready") || blob.includes("starting up");
|
|
264
|
+
}
|
|
265
|
+
var MACHINE_SHAPED = /[_=<>{}\[\]|\\$`]|\w+\.\w+\.\w|\S:\d|\w\(\)|^\s*(?:at|File)\b/mu;
|
|
266
|
+
function hasInternalDetail(message) {
|
|
267
|
+
return MACHINE_SHAPED.test(message);
|
|
268
|
+
}
|
|
113
269
|
function splitCallTranscript(all, localIdentity) {
|
|
114
270
|
if (!localIdentity) return { user: [], agent: all };
|
|
115
271
|
const user = [];
|
|
@@ -148,7 +304,6 @@ function useReleaseMicLeaseOnTrackEnded(token) {
|
|
|
148
304
|
};
|
|
149
305
|
}, [localParticipant, token, localParticipant?.audioTrackPublications.size]);
|
|
150
306
|
}
|
|
151
|
-
var DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS = 0.5;
|
|
152
307
|
function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
153
308
|
const videoMediaTrack = videoTrack?.publication?.track;
|
|
154
309
|
const audioMediaTrack = audioTrack?.publication?.track;
|
|
@@ -156,15 +311,6 @@ function useAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AV
|
|
|
156
311
|
applyAvatarPlayoutDelay(videoMediaTrack, audioMediaTrack, delaySeconds);
|
|
157
312
|
}, [videoMediaTrack, audioMediaTrack, delaySeconds]);
|
|
158
313
|
}
|
|
159
|
-
function applyAvatarPlayoutDelay(videoTrack, audioTrack, delaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS) {
|
|
160
|
-
const delay = Math.max(0, delaySeconds);
|
|
161
|
-
playoutDelayTarget(videoTrack)?.setPlayoutDelay(delay);
|
|
162
|
-
playoutDelayTarget(audioTrack)?.setPlayoutDelay(delay);
|
|
163
|
-
}
|
|
164
|
-
function playoutDelayTarget(track) {
|
|
165
|
-
const candidate = track;
|
|
166
|
-
return typeof candidate?.setPlayoutDelay === "function" ? candidate : void 0;
|
|
167
|
-
}
|
|
168
314
|
function resolveReleaseTarget(heldSessionId, queueTicketId) {
|
|
169
315
|
if (heldSessionId) return { kind: "session", sessionId: heldSessionId };
|
|
170
316
|
if (queueTicketId) return { kind: "ticket", queueTicketId };
|
|
@@ -402,6 +548,24 @@ function sameCapacityState(a, b) {
|
|
|
402
548
|
return true;
|
|
403
549
|
}
|
|
404
550
|
}
|
|
551
|
+
function AvatarPlayoutCushion({ seconds }) {
|
|
552
|
+
const room = useRoomContext();
|
|
553
|
+
useEffect(() => {
|
|
554
|
+
const onSubscribed = (track) => {
|
|
555
|
+
applyPlayoutDelay(track, seconds);
|
|
556
|
+
};
|
|
557
|
+
for (const participant of room.remoteParticipants.values()) {
|
|
558
|
+
for (const publication of participant.trackPublications.values()) {
|
|
559
|
+
applyPlayoutDelay(publication.track, seconds);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
room.on(RoomEvent.TrackSubscribed, onSubscribed);
|
|
563
|
+
return () => {
|
|
564
|
+
room.off(RoomEvent.TrackSubscribed, onSubscribed);
|
|
565
|
+
};
|
|
566
|
+
}, [room, seconds]);
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
405
569
|
function RealtimeAvatarLiveKitRoom(props) {
|
|
406
570
|
const {
|
|
407
571
|
grant,
|
|
@@ -410,11 +574,13 @@ function RealtimeAvatarLiveKitRoom(props) {
|
|
|
410
574
|
video = false,
|
|
411
575
|
options,
|
|
412
576
|
renderRoomAudio = true,
|
|
577
|
+
playoutDelaySeconds = DEFAULT_AVATAR_PLAYOUT_DELAY_SECONDS,
|
|
413
578
|
children,
|
|
414
579
|
...roomProps
|
|
415
580
|
} = props;
|
|
416
581
|
const shouldConnect = Boolean(grant && connect);
|
|
417
582
|
const roomAudio = renderRoomAudio ? createElement(RoomAudioRenderer, { key: "room-audio" }) : null;
|
|
583
|
+
const cushion = playoutDelaySeconds === false ? null : createElement(AvatarPlayoutCushion, { key: "playout-cushion", seconds: playoutDelaySeconds });
|
|
418
584
|
const roomOptions = { adaptiveStream: false, dynacast: true, ...options };
|
|
419
585
|
return createElement(
|
|
420
586
|
LiveKitRoom,
|
|
@@ -427,7 +593,7 @@ function RealtimeAvatarLiveKitRoom(props) {
|
|
|
427
593
|
video,
|
|
428
594
|
options: roomOptions
|
|
429
595
|
},
|
|
430
|
-
createElement(Fragment, null, children, roomAudio)
|
|
596
|
+
createElement(Fragment, null, cushion, children, roomAudio)
|
|
431
597
|
);
|
|
432
598
|
}
|
|
433
599
|
var capacityErrorFromBusy = (busy) => {
|
|
@@ -2695,8 +2861,7 @@ function createProxyClient(options) {
|
|
|
2695
2861
|
return { status: "busy", busy };
|
|
2696
2862
|
}
|
|
2697
2863
|
if (!response.ok) {
|
|
2698
|
-
|
|
2699
|
-
throw new Error(`realtime-avatar: proxy refused the call (${response.status}) ${detail}`.trim());
|
|
2864
|
+
throw await RealtimeAvatarApiError.fromResponse(response);
|
|
2700
2865
|
}
|
|
2701
2866
|
return { status: "ready", grant: await response.json() };
|
|
2702
2867
|
},
|
package/dist/server.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, RealtimeAvatarOptions, StartCallOptions, verifyTranscript } from './index.js';
|
|
2
|
-
export { i as Asset, h as AssetKind, A as Avatar, m as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, n as CallConnection, C as CallMode, a as CallPolicy, o as CallQueued, d as ClipDeclaration, f as ClipLibrary, p as ClipLibraryPlan, e as ClipLibraryUpdate, q as ClipSource, g as ClipSyncResult, r as ContextMessage, l as CreditBalance, E as EndCallOptions, s as EndCallReason, j as ListSessionsOptions, L as LoopRedirect, S as StartCallResult, T as TranscriptPayload, k as UsageSession, U as UsageSessionPage, V as VideoPolicy, t as VideoState, u as isQueued } from './types-
|
|
2
|
+
export { i as Asset, h as AssetKind, A as Avatar, m as AvatarClip, c as AvatarSourceSwap, b as AvatarUpdate, n as CallConnection, C as CallMode, a as CallPolicy, o as CallQueued, d as ClipDeclaration, f as ClipLibrary, p as ClipLibraryPlan, e as ClipLibraryUpdate, q as ClipSource, g as ClipSyncResult, r as ContextMessage, l as CreditBalance, E as EndCallOptions, s as EndCallReason, j as ListSessionsOptions, L as LoopRedirect, S as StartCallResult, T as TranscriptPayload, k as UsageSession, U as UsageSessionPage, V as VideoPolicy, t as VideoState, u as isQueued } from './types-D_aIKv4P.js';
|
package/dist/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, isQueued, verifyTranscript } from './chunk-JNRRH2G7.js';
|
package/dist/tanstack-start.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as ProxyConfig } from './types-
|
|
2
|
-
import './types-
|
|
1
|
+
import { P as ProxyConfig } from './types-QoMHpR1h.js';
|
|
2
|
+
import './types-D_aIKv4P.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* TanStack Start. Mount at `routes/api/realtime-avatar/$.ts` — the trailing `$` is Start's
|
package/dist/tanstack-start.js
CHANGED
|
@@ -585,12 +585,24 @@ interface components {
|
|
|
585
585
|
Error: {
|
|
586
586
|
/** @description Human-readable message. Not a stable contract — switch on `code` or `status`. */
|
|
587
587
|
error: string;
|
|
588
|
-
/** @description Repeats the HTTP status in the body.
|
|
589
|
-
status
|
|
590
|
-
/** @description Machine-readable discriminator
|
|
591
|
-
code
|
|
588
|
+
/** @description Repeats the HTTP status in the body. Always present. */
|
|
589
|
+
status: number;
|
|
590
|
+
/** @description Machine-readable discriminator. ALWAYS present: a failure that names its own — billing, clip-library and loop refusals, `insufficient_scope` — carries that, and everything else carries the status's default (`unauthorized`, `not_found`, `rate_limited`, …). Switch on this rather than on `error`, whose wording is not a contract. The set is open and grows with the API; the codes a given endpoint can return are documented with that endpoint at https://realtimeavatar.ai/docs/api-reference. */
|
|
591
|
+
code: string;
|
|
592
|
+
/**
|
|
593
|
+
* Format: uri
|
|
594
|
+
* @description Where to read how to resolve a failure of this class — the authentication guide for 401/403, the API reference otherwise. Always present.
|
|
595
|
+
*/
|
|
596
|
+
documentation: string;
|
|
592
597
|
/** @description Set when the failure is worth retrying as-is. */
|
|
593
598
|
retryable?: boolean;
|
|
599
|
+
/**
|
|
600
|
+
* @description Present on `403 insufficient_scope` — the one scope this operation needed and the key did not carry. Repeated in the `WWW-Authenticate` challenge as `scope=`.
|
|
601
|
+
* @enum {string}
|
|
602
|
+
*/
|
|
603
|
+
requiredScope?: "*" | "api_keys:write" | "credits:read" | "avatars:read" | "avatars:write" | "realtime:write" | "usage:read" | "usage:write";
|
|
604
|
+
/** @description Present alongside `requiredScope` — the scopes the presented key does carry. */
|
|
605
|
+
grantedScopes?: ("*" | "api_keys:write" | "credits:read" | "avatars:read" | "avatars:write" | "realtime:write" | "usage:read" | "usage:write")[];
|
|
594
606
|
/** @description Present on 402 — where to send the user to top up. */
|
|
595
607
|
billingUrl?: string;
|
|
596
608
|
} & {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CallMode, a as CallPolicy } from './types-
|
|
1
|
+
import { C as CallMode, a as CallPolicy } from './types-D_aIKv4P.js';
|
|
2
2
|
|
|
3
3
|
/** The three things a caller can ask the proxy to do. Gate on these, not on URLs. */
|
|
4
4
|
type ProxyOperation = "connect" | "end" | "avatars" | "credits";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtime-avatar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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",
|