realtime-avatar 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-DNHHQ5GZ.js +143 -0
- package/dist/{chunk-EODL5S3A.js → chunk-GMYGHDY2.js} +26 -8
- package/dist/express.d.ts +1 -1
- package/dist/express.js +5 -3
- package/dist/hono.d.ts +1 -1
- package/dist/hono.js +2 -2
- package/dist/index.d.ts +12 -1
- package/dist/index.js +1 -1
- package/dist/nextjs.d.ts +1 -1
- package/dist/nextjs.js +2 -2
- package/dist/{proxy-client-DlFXaXtn.d.ts → proxy-client-O-Mu9JKt.d.ts} +5 -1
- package/dist/react-native.d.ts +2 -2
- package/dist/react-native.js +44 -14
- package/dist/react.d.ts +2 -2
- package/dist/react.js +44 -14
- package/dist/server.js +1 -1
- package/dist/tanstack-start.d.ts +1 -1
- package/dist/tanstack-start.js +2 -2
- package/dist/{types-CpqMHna3.d.ts → types-C7B7jOx6.d.ts} +7 -0
- package/package.json +1 -1
- package/dist/chunk-4PWWEERK.js +0 -87
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { RealtimeAvatar, ROUTE_TIMEOUT_MS, isQueued, RealtimeAvatarHttpError, RealtimeAvatarError } from './chunk-GMYGHDY2.js';
|
|
2
|
+
|
|
3
|
+
// ../http-client/src/generated/error-semantics.ts
|
|
4
|
+
var ERROR_SEMANTICS = {
|
|
5
|
+
"bad_request": { statuses: [400], message: "Check the request and try again.", retryable: false },
|
|
6
|
+
"unauthorized": { statuses: [401], message: "Authentication is required to use Realtime Avatar.", retryable: false },
|
|
7
|
+
"payment_required": { statuses: [402], message: "Add credits to continue using realtime avatars.", retryable: false },
|
|
8
|
+
"forbidden": { statuses: [403], message: "This API key does not have access to that action.", retryable: false },
|
|
9
|
+
"not_found": { statuses: [404], message: "We could not find that avatar or resource.", retryable: false },
|
|
10
|
+
"method_not_allowed": { statuses: [405], message: "This request method is not supported.", retryable: false },
|
|
11
|
+
"not_acceptable": { statuses: [406], message: "The requested response format is not supported.", retryable: false },
|
|
12
|
+
"request_aborted": { statuses: [408], message: "The session request was cancelled.", retryable: false },
|
|
13
|
+
"conflict": { statuses: [409], message: "That request is already in progress. Try again in a moment.", retryable: true },
|
|
14
|
+
"length_required": { statuses: [411], message: "A request content length is required.", retryable: false },
|
|
15
|
+
"payload_too_large": { statuses: [413], message: "The request is too large.", retryable: false },
|
|
16
|
+
"unprocessable_entity": { statuses: [422], message: "Check the request and try again.", retryable: false },
|
|
17
|
+
"rate_limited": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
|
|
18
|
+
"internal_error": { statuses: [500], message: "The session service encountered an error. Try again.", retryable: true },
|
|
19
|
+
"unavailable": { statuses: [503], message: "Realtime Avatar is temporarily unavailable. Try again in a moment.", retryable: true },
|
|
20
|
+
"insufficient_scope": { statuses: [403], message: "This API key is missing a required permission.", retryable: false },
|
|
21
|
+
"loop_pending": { statuses: [409], message: "An avatar loop update is still in progress. Try again shortly.", retryable: true },
|
|
22
|
+
"rate_limit_exceeded": { statuses: [429], message: "Too many realtime requests. Please slow down and retry.", retryable: true },
|
|
23
|
+
"upstream_failed": { statuses: [500, 502, 503], message: "The session service could not complete the request. Try again.", retryable: true },
|
|
24
|
+
"upstream_unreachable": { statuses: [502, 503], message: "The session service could not be reached. Try again.", retryable: true },
|
|
25
|
+
"invalid_upstream_response": { statuses: [502], message: "The session service returned an invalid response. Try again.", retryable: true },
|
|
26
|
+
"upstream_timeout": { statuses: [504], message: "The session service took too long to respond. Try again.", retryable: true },
|
|
27
|
+
"capacity_unavailable": { statuses: [503], message: "Session availability could not be checked. Try again.", retryable: true },
|
|
28
|
+
"session_dispatch_failed": { statuses: [503], message: "The avatar session could not be started. Try again.", retryable: true },
|
|
29
|
+
"admission_unavailable": { statuses: [503], message: "Session admission is temporarily unavailable. Try again.", retryable: true },
|
|
30
|
+
"service_warming": { statuses: [503], message: "Realtime Avatar is warming up. Try again in a moment.", retryable: true },
|
|
31
|
+
"avatar_not_ready": { statuses: [409], message: "This avatar is still being built \u2014 it is usually ready within a minute. Try again then.", retryable: true },
|
|
32
|
+
"clip_library_not_ready": { statuses: [409], message: "This avatar's motion clips are still rendering. Try again in a minute.", retryable: true },
|
|
33
|
+
"recording_unsupported": { statuses: [501], message: "Session recording is not enabled for this deployment.", retryable: false },
|
|
34
|
+
"recording_requires_new_room": { statuses: [422], message: "Recording requires a new session room. Start a fresh session to record it.", retryable: false },
|
|
35
|
+
"recording_unavailable": { statuses: [503], message: "Session recording is temporarily unavailable. Try again in a moment.", retryable: true }
|
|
36
|
+
};
|
|
37
|
+
var MESSAGE_SHAPE_PATTERNS = { "machine": { "source": "[_=<>{}\\[\\]|\\\\$`]|\\w+\\.\\w+\\.\\w|\\S:\\d|\\w\\(\\)|^\\s*(?:at|File)\\b|\\bTraceback \\(most recent call last\\)", "flags": "mu" }, "locator": { "source": "https?://|/[\\w.-]+/|[\\w.+-]+@[\\w.-]+\\.[a-z]{2,}|\\bbearer\\s|[\\r\\n]", "flags": "i" } };
|
|
38
|
+
Object.values(MESSAGE_SHAPE_PATTERNS).map(
|
|
39
|
+
(pattern) => new RegExp(pattern.source, pattern.flags)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// ../proxy/src/config.ts
|
|
43
|
+
var MISCONFIGURED = "internal_error";
|
|
44
|
+
var TIMED_OUT = "upstream_timeout";
|
|
45
|
+
var json = (body, status = 200) => new Response(JSON.stringify(body), {
|
|
46
|
+
status,
|
|
47
|
+
headers: { "content-type": "application/json", "cache-control": "no-store" }
|
|
48
|
+
});
|
|
49
|
+
function relayFailure(operation, error) {
|
|
50
|
+
const misconfigured = error.status === 401 || error.status === 403;
|
|
51
|
+
const status = misconfigured ? ERROR_SEMANTICS[MISCONFIGURED].statuses[0] : error.status;
|
|
52
|
+
const verdict = error.retryable === void 0 ? {} : { retryable: error.retryable };
|
|
53
|
+
const body = misconfigured ? { code: MISCONFIGURED, status, retryable: false } : error.status === 429 ? throttled(error) : { code: error.isBilling ? error.code ?? "insufficient_credits" : error.code, status, ...verdict };
|
|
54
|
+
console.error(
|
|
55
|
+
`realtime-avatar: platform answered ${operation} with ${error.status} (code ${error.code ?? "none"}, requestId ${error.requestId ?? "none"}); relayed as ${status}` + (misconfigured ? " \u2014 the platform refused this route's API key" : "")
|
|
56
|
+
);
|
|
57
|
+
const response = json({ ...body, ...error.requestId ? { requestId: error.requestId } : {} }, status);
|
|
58
|
+
if (error.requestId) response.headers.set("X-Request-ID", error.requestId);
|
|
59
|
+
return response;
|
|
60
|
+
}
|
|
61
|
+
function timedOut(operation) {
|
|
62
|
+
const { retryable, statuses: [status] } = ERROR_SEMANTICS[TIMED_OUT];
|
|
63
|
+
console.error(`realtime-avatar: platform did not answer ${operation} inside the route's budget; answered ${status}`);
|
|
64
|
+
return json({ code: TIMED_OUT, status, retryable }, status);
|
|
65
|
+
}
|
|
66
|
+
function throttled(error) {
|
|
67
|
+
const concurrency = error.code === "concurrency_limit_reached";
|
|
68
|
+
const counts = error.concurrency;
|
|
69
|
+
const message = concurrency ? counts?.maxConcurrentSessions !== void 0 && counts.activeSessions !== void 0 && counts.connectingSessions !== void 0 && counts.pendingSessions !== void 0 ? `Session limit reached (${counts.maxConcurrentSessions} allowed): ${counts.activeSessions} active, ${counts.connectingSessions} connecting, ${counts.pendingSessions} starting. End a session or wait for pending starts to clear, then retry.` : "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry." : "Too many requests. Wait before retrying.";
|
|
70
|
+
return {
|
|
71
|
+
error: message,
|
|
72
|
+
code: concurrency ? "concurrency_limit_reached" : "rate_limited",
|
|
73
|
+
status: 429,
|
|
74
|
+
retryable: error.retryable ?? true,
|
|
75
|
+
...concurrency ? counts : {}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function operationFor(pathname, method) {
|
|
79
|
+
const tail = pathname.replace(/\/+$/, "").split("/").pop() ?? "";
|
|
80
|
+
if (method === "POST" && (tail === "connect" || tail === "call")) return "connect";
|
|
81
|
+
if (method === "POST" && (tail === "end" || tail === "release")) return "end";
|
|
82
|
+
if (method === "GET" && tail === "avatars") return "avatars";
|
|
83
|
+
if (method === "GET" && tail === "credits") return "credits";
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function createProxyHandler(config) {
|
|
87
|
+
const minted = /* @__PURE__ */ new Map();
|
|
88
|
+
const MINTED_TTL_MS = 30 * 6e4;
|
|
89
|
+
return async (request) => {
|
|
90
|
+
const url = new URL(request.url);
|
|
91
|
+
const operation = operationFor(url.pathname, request.method);
|
|
92
|
+
if (!operation) return json({ error: "not found" }, 404);
|
|
93
|
+
const refusal = await config.authorize?.({ request, operation });
|
|
94
|
+
if (refusal instanceof Response) return refusal;
|
|
95
|
+
const apiKey = typeof config.apiKey === "function" ? await config.apiKey() : config.apiKey;
|
|
96
|
+
const rta = new RealtimeAvatar({ apiKey, baseUrl: config.baseUrl, totalTimeoutMs: config.timeoutMs ?? ROUTE_TIMEOUT_MS });
|
|
97
|
+
try {
|
|
98
|
+
if (operation === "avatars") return json({ data: await rta.listAvatars() });
|
|
99
|
+
if (operation === "credits") return json(await rta.creditBalance());
|
|
100
|
+
if (operation === "end") {
|
|
101
|
+
const ended = await request.json().catch(() => ({}));
|
|
102
|
+
const sessionId = ended.session_id ?? ended.queue_ticket_id;
|
|
103
|
+
if (!sessionId) return json({ error: "session_id or queue_ticket_id is required" }, 422);
|
|
104
|
+
const owns = config.ownsSession ? await config.ownsSession({ request, sessionId }) : minted.delete(sessionId);
|
|
105
|
+
if (!owns) return new Response(null, { status: 204 });
|
|
106
|
+
const reason = ended.reason === "page_hide" || ended.reason === "unmount" ? ended.reason : "manual";
|
|
107
|
+
await rta.endCall(sessionId, { reason });
|
|
108
|
+
return new Response(null, { status: 204 });
|
|
109
|
+
}
|
|
110
|
+
const body = await request.json().catch(() => ({}));
|
|
111
|
+
if (!body.avatarId) return json({ error: "avatarId is required" }, 422);
|
|
112
|
+
const mode = body.mode === "voice" ? "voice" : "avatar";
|
|
113
|
+
const decided = await config.session?.({ request, avatarId: body.avatarId, mode });
|
|
114
|
+
if (decided instanceof Response) return decided;
|
|
115
|
+
const call = await rta.startCall({ avatarId: body.avatarId, mode, ...decided ?? {} });
|
|
116
|
+
if (isQueued(call)) {
|
|
117
|
+
if (call.queueTicketId) minted.set(call.queueTicketId, Date.now());
|
|
118
|
+
return json(
|
|
119
|
+
{
|
|
120
|
+
queued: true,
|
|
121
|
+
position: call.position,
|
|
122
|
+
size: call.size,
|
|
123
|
+
retryAfterMs: call.retryAfterMs,
|
|
124
|
+
queue_ticket_id: call.queueTicketId
|
|
125
|
+
},
|
|
126
|
+
429
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const now = Date.now();
|
|
130
|
+
for (const [id, at] of minted) if (now - at > MINTED_TTL_MS) minted.delete(id);
|
|
131
|
+
minted.set(call.sessionId, now);
|
|
132
|
+
return json(call.raw);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (error instanceof RealtimeAvatarHttpError) return relayFailure(operation, error);
|
|
135
|
+
if (error instanceof RealtimeAvatarError && error.cause instanceof Error && error.cause.name === "TimeoutError") {
|
|
136
|
+
return timedOut(operation);
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export { createProxyHandler };
|
|
@@ -8,11 +8,16 @@ var RealtimeAvatarError = class extends Error {
|
|
|
8
8
|
this.name = "RealtimeAvatarError";
|
|
9
9
|
}
|
|
10
10
|
};
|
|
11
|
+
function isRequestId(value) {
|
|
12
|
+
return typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
|
|
13
|
+
}
|
|
11
14
|
var RealtimeAvatarHttpError = class extends RealtimeAvatarError {
|
|
12
15
|
status;
|
|
13
16
|
code;
|
|
14
17
|
body;
|
|
15
18
|
requestId;
|
|
19
|
+
/** The platform's own verdict on retrying as-is, when its body gave one. */
|
|
20
|
+
retryable;
|
|
16
21
|
concurrency;
|
|
17
22
|
constructor(status, code, body, requestId) {
|
|
18
23
|
const bounded = body.slice(0, 400);
|
|
@@ -27,8 +32,8 @@ var RealtimeAvatarHttpError = class extends RealtimeAvatarError {
|
|
|
27
32
|
if (value && typeof value === "object" && !Array.isArray(value)) parsed = value;
|
|
28
33
|
} catch {
|
|
29
34
|
}
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
this.requestId = isRequestId(parsed.requestId) ? parsed.requestId : isRequestId(requestId) ? requestId : void 0;
|
|
36
|
+
if (typeof parsed.retryable === "boolean") this.retryable = parsed.retryable;
|
|
32
37
|
if (status === 429 && code === "concurrency_limit_reached") {
|
|
33
38
|
this.concurrency = {};
|
|
34
39
|
for (const key of ["maxConcurrentSessions", "liveSessions", "activeSessions", "connectingSessions", "pendingSessions"]) {
|
|
@@ -293,6 +298,12 @@ function backoffMs(attempt, retryAfter) {
|
|
|
293
298
|
return Math.random() * Math.min(500 * 2 ** attempt, 8e3);
|
|
294
299
|
}
|
|
295
300
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
301
|
+
var PROXY_CLIENT_TIMEOUT_MS = 6e4;
|
|
302
|
+
var ROUTE_TIMEOUT_MS = PROXY_CLIENT_TIMEOUT_MS - 1e4;
|
|
303
|
+
function retryFits(attemptStartedAt, waitMs, deadline) {
|
|
304
|
+
const now = Date.now();
|
|
305
|
+
return now + waitMs + (now - attemptStartedAt) <= deadline;
|
|
306
|
+
}
|
|
296
307
|
function isTransient(cause) {
|
|
297
308
|
const name = cause?.name;
|
|
298
309
|
return name === "TimeoutError" || name === "TypeError" || name === "FetchError";
|
|
@@ -654,12 +665,13 @@ z.enum([
|
|
|
654
665
|
|
|
655
666
|
// ../http-client/src/client.ts
|
|
656
667
|
var DEFAULT_BASE_URL = "https://realtimeavatar.ai/api/v1";
|
|
657
|
-
var SDK_VERSION = "0.
|
|
668
|
+
var SDK_VERSION = "0.25.0";
|
|
658
669
|
var RealtimeAvatar = class {
|
|
659
670
|
#apiKey;
|
|
660
671
|
#baseUrl;
|
|
661
672
|
#fetch;
|
|
662
673
|
#timeoutMs;
|
|
674
|
+
#totalTimeoutMs;
|
|
663
675
|
#maxRetries;
|
|
664
676
|
#userAgent;
|
|
665
677
|
constructor(options) {
|
|
@@ -673,6 +685,7 @@ var RealtimeAvatar = class {
|
|
|
673
685
|
this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
674
686
|
this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
675
687
|
this.#timeoutMs = options.timeoutMs ?? 6e4;
|
|
688
|
+
this.#totalTimeoutMs = options.totalTimeoutMs;
|
|
676
689
|
this.#maxRetries = Math.max(0, options.maxRetries ?? 2);
|
|
677
690
|
this.#userAgent = [`realtime-avatar-sdk/${SDK_VERSION}`, runtimeTag(), options.userAgent].filter(Boolean).join(" ");
|
|
678
691
|
}
|
|
@@ -1099,24 +1112,29 @@ var RealtimeAvatar = class {
|
|
|
1099
1112
|
body = JSON.stringify(init.json);
|
|
1100
1113
|
}
|
|
1101
1114
|
if (MUTATING.has(method)) headers["idempotency-key"] = newIdempotencyKey();
|
|
1115
|
+
const deadline = this.#totalTimeoutMs === void 0 ? Infinity : Date.now() + this.#totalTimeoutMs;
|
|
1102
1116
|
let lastError;
|
|
1103
1117
|
for (let attempt = 0; ; attempt++) {
|
|
1118
|
+
const startedAt = Date.now();
|
|
1104
1119
|
try {
|
|
1105
|
-
const signal = AbortSignal.timeout(this.#timeoutMs);
|
|
1120
|
+
const signal = AbortSignal.timeout(Math.max(0, Math.min(this.#timeoutMs, deadline - startedAt)));
|
|
1106
1121
|
const response = await this.#fetch(`${this.#baseUrl}${path}`, { method, headers, body, signal });
|
|
1107
1122
|
if (attempt >= this.#maxRetries || !RETRYABLE_STATUS.has(response.status)) return response;
|
|
1123
|
+
const wait = backoffMs(attempt, response.headers.get("retry-after"));
|
|
1124
|
+
if (!retryFits(startedAt, wait, deadline)) return response;
|
|
1108
1125
|
await response.body?.cancel().catch(() => {
|
|
1109
1126
|
});
|
|
1110
|
-
await sleep(
|
|
1127
|
+
await sleep(wait);
|
|
1111
1128
|
} catch (cause) {
|
|
1112
1129
|
lastError = cause;
|
|
1113
|
-
|
|
1130
|
+
const wait = backoffMs(attempt, null);
|
|
1131
|
+
if (attempt >= this.#maxRetries || !isTransient(cause) || isStream(body) || !retryFits(startedAt, wait, deadline)) {
|
|
1114
1132
|
throw new RealtimeAvatarError(
|
|
1115
1133
|
`${method} ${path} failed after ${attempt + 1} attempt(s): ${cause.message}`,
|
|
1116
1134
|
{ cause: lastError }
|
|
1117
1135
|
);
|
|
1118
1136
|
}
|
|
1119
|
-
await sleep(
|
|
1137
|
+
await sleep(wait);
|
|
1120
1138
|
}
|
|
1121
1139
|
}
|
|
1122
1140
|
}
|
|
@@ -1263,4 +1281,4 @@ function isQueued(result) {
|
|
|
1263
1281
|
return "queued" in result;
|
|
1264
1282
|
}
|
|
1265
1283
|
|
|
1266
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript };
|
|
1284
|
+
export { ROUTE_TIMEOUT_MS, RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript };
|
package/dist/express.d.ts
CHANGED
package/dist/express.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createProxyHandler } from './chunk-
|
|
2
|
-
import './chunk-
|
|
1
|
+
import { createProxyHandler } from './chunk-DNHHQ5GZ.js';
|
|
2
|
+
import './chunk-GMYGHDY2.js';
|
|
3
3
|
import './chunk-MCIXU6XP.js';
|
|
4
4
|
|
|
5
5
|
// ../proxy/src/express.ts
|
|
@@ -16,7 +16,9 @@ function realtimeAvatarExpress(config) {
|
|
|
16
16
|
body: req.method === "GET" ? void 0 : JSON.stringify(req.body ?? {})
|
|
17
17
|
});
|
|
18
18
|
const response = await handler(request);
|
|
19
|
-
res.status(response.status).set("content-type", "application/json")
|
|
19
|
+
res.status(response.status).set("content-type", "application/json");
|
|
20
|
+
response.headers.forEach((value, field) => res.set(field, value));
|
|
21
|
+
res.send(await response.text());
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
24
|
|
package/dist/hono.d.ts
CHANGED
package/dist/hono.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -8,8 +8,17 @@ interface RealtimeAvatarOptions {
|
|
|
8
8
|
apiKey: string;
|
|
9
9
|
baseUrl?: string;
|
|
10
10
|
fetch?: typeof fetch;
|
|
11
|
-
/** Per-
|
|
11
|
+
/** Per-attempt timeout. Default 60s; starting a call does real work upstream. */
|
|
12
12
|
timeoutMs?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The whole request: every attempt and the backoff between them. Default unbounded, so the
|
|
15
|
+
* worst case is `timeoutMs` per attempt plus up to 20s of `Retry-After` between them.
|
|
16
|
+
*
|
|
17
|
+
* Set it when something else is waiting on this request with a deadline of its own (a
|
|
18
|
+
* browser, a serverless max duration). An attempt is cut off at the budget, and a retry is
|
|
19
|
+
* started only if it can take as long as the attempt before it and still finish inside it.
|
|
20
|
+
*/
|
|
21
|
+
totalTimeoutMs?: number;
|
|
13
22
|
/**
|
|
14
23
|
* Extra attempts after a transient failure. Default 2, so 3 attempts in total.
|
|
15
24
|
*
|
|
@@ -319,6 +328,8 @@ declare class RealtimeAvatarHttpError extends RealtimeAvatarError {
|
|
|
319
328
|
readonly code: string | undefined;
|
|
320
329
|
readonly body: string;
|
|
321
330
|
readonly requestId?: string;
|
|
331
|
+
/** The platform's own verdict on retrying as-is, when its body gave one. */
|
|
332
|
+
readonly retryable?: boolean;
|
|
322
333
|
readonly concurrency?: Partial<Record<"maxConcurrentSessions" | "liveSessions" | "activeSessions" | "connectingSessions" | "pendingSessions", number>>;
|
|
323
334
|
constructor(status: number, code: string | undefined, body: string, requestId?: string);
|
|
324
335
|
/** Out of credits, or over this key's spend limit. Surface a paywall, not an error. */
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-GMYGHDY2.js';
|
|
2
2
|
import './chunk-MCIXU6XP.js';
|
package/dist/nextjs.d.ts
CHANGED
package/dist/nextjs.js
CHANGED
|
@@ -1858,7 +1858,11 @@ interface ProxyClientOptions {
|
|
|
1858
1858
|
/** Swap the transport — a test double, or a fetch that carries your session cookie. */
|
|
1859
1859
|
fetch?: typeof globalThis.fetch;
|
|
1860
1860
|
/**
|
|
1861
|
-
* Per-request deadline, default 60s, `0` to disable.
|
|
1861
|
+
* Per-request deadline, default 60s, `0` to disable. Keep it above your route's `timeoutMs`
|
|
1862
|
+
* (default 50s), so the route's answer arrives before the page stops listening. Running out throws a
|
|
1863
|
+
* `RealtimeAvatarApiError` with `code: "upstream_timeout"` (status 504, `response: null`).
|
|
1864
|
+
* Do not re-send a timed-out `connect` automatically: your route may still be minting it, and
|
|
1865
|
+
* this client never retries a mint — the server client inside your route owns that retry.
|
|
1862
1866
|
*
|
|
1863
1867
|
* Not optional in spirit: a proxy that accepts the connection and then never answers leaves
|
|
1864
1868
|
* a promise that never settles, which presents as a page stuck on "connecting" with no error
|
package/dist/react-native.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LiveKitRoomProps } from '@livekit/react-native';
|
|
2
2
|
export { AudioSession, VideoTrack, VideoTrackProps, registerGlobals } from '@livekit/react-native';
|
|
3
|
-
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-
|
|
4
|
-
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, 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, u as DeclaredInputSource, E as EndReason, v as EndedEvent, w as ExtendResult, F as FishTtsModel, x as FreezeReadingFn, G as Governor, y as GovernorAction, z as GovernorConfig, H as GovernorSignal, I as GovernorState, J as GraceWindowClosedEvent, K as GraceWindowOpenEvent, M as GraceWindowState, N as IdleWarningEvent, O as InboundRtpCursor, P as InboundRtpReading, Q as KnownBehaviorState, R as LLMProvider, S as LLMSelection, T as LiveKitAvatarGrantState, U as LiveKitAvatarGrantStatus, V as LiveKitCapacityState, W as LiveKitConnectionStatus, X as LiveKitSessionRequest, Y as LiveKitSessionStartResult, Z as MAX_RECONNECT_ATTEMPTS, _ as MAX_SESSION_INSTRUCTIONS_CHARS, $ as ProxyClientOptions, a0 as QualityCap, a1 as RECONNECT_BACKOFF_MS, a2 as RealtimeAvatarRequestOptions, a3 as RealtimeSessionApi, a4 as RealtimeSessionMedia, a5 as RealtimeSessionRoomSinks, a6 as ReconnectPolicy, a7 as ReconnectingEvent, a8 as RecoveryState, a9 as RetryStep, aa as SendTurnOptions, ab as SessionBehavior, ac as SessionClip, ad as SessionClocks, ae as SessionEndReason, af as SessionLifecycleApi, ag as SessionLifecyclePhase, ah as SessionLifecyclePhaseKind, ai as SessionLifecycleRoomBridge, aj as SessionLifecycleRoomBridgeProps, ak as SurfaceLayers, al as TranscriptSender, am as TranscriptSenderOptions, an as TurnState, ao as TurnTimeoutEvent, ap as UseAvatarQualityGovernorInput, aq as UseLiveKitAvatarGrantInput, ar as UseRealtimeSessionInput, as as UseSessionLifecycleInput, at as VoiceSpec, au as VoiceSpecInput, av as capacityErrorFromBusy, aw as capacityStateFromGrant, ax as createProxyClient, ay as isNativeLiveTrackSubscribed, az as knownBehaviorStates, aA as mapTurnState, aB as readInboundRtp, aC as sessionBehaviorSchema, aD as sessionClipSchema, aE as splitCallTranscript, aF as useAvatarAdaptivePlayoutDelay, aG as useAvatarCamera, aH as useAvatarPlayoutDelay, aI as useAvatarQualityGovernor, aJ as useCallTranscript, aK as useLiveKitAvatarGrant, aL as useLiveTrackProducing, aM as useMicLease, aN as useRealtimeSession, aO as useReleaseMicLeaseOnTrackEnded, aP as useSessionLifecycle } from './proxy-client-
|
|
3
|
+
import { L as LiveKitSessionGrant, A as AvatarVideoFit } from './proxy-client-O-Mu9JKt.js';
|
|
4
|
+
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, u as DeclaredInputSource, E as EndReason, v as EndedEvent, w as ExtendResult, F as FishTtsModel, x as FreezeReadingFn, G as Governor, y as GovernorAction, z as GovernorConfig, H as GovernorSignal, I as GovernorState, J as GraceWindowClosedEvent, K as GraceWindowOpenEvent, M as GraceWindowState, N as IdleWarningEvent, O as InboundRtpCursor, P as InboundRtpReading, Q as KnownBehaviorState, R as LLMProvider, S as LLMSelection, T as LiveKitAvatarGrantState, U as LiveKitAvatarGrantStatus, V as LiveKitCapacityState, W as LiveKitConnectionStatus, X as LiveKitSessionRequest, Y as LiveKitSessionStartResult, Z as MAX_RECONNECT_ATTEMPTS, _ as MAX_SESSION_INSTRUCTIONS_CHARS, $ as ProxyClientOptions, a0 as QualityCap, a1 as RECONNECT_BACKOFF_MS, a2 as RealtimeAvatarRequestOptions, a3 as RealtimeSessionApi, a4 as RealtimeSessionMedia, a5 as RealtimeSessionRoomSinks, a6 as ReconnectPolicy, a7 as ReconnectingEvent, a8 as RecoveryState, a9 as RetryStep, aa as SendTurnOptions, ab as SessionBehavior, ac as SessionClip, ad as SessionClocks, ae as SessionEndReason, af as SessionLifecycleApi, ag as SessionLifecyclePhase, ah as SessionLifecyclePhaseKind, ai as SessionLifecycleRoomBridge, aj as SessionLifecycleRoomBridgeProps, ak as SurfaceLayers, al as TranscriptSender, am as TranscriptSenderOptions, an as TurnState, ao as TurnTimeoutEvent, ap as UseAvatarQualityGovernorInput, aq as UseLiveKitAvatarGrantInput, ar as UseRealtimeSessionInput, as as UseSessionLifecycleInput, at as VoiceSpec, au as VoiceSpecInput, av as capacityErrorFromBusy, aw as capacityStateFromGrant, ax as createProxyClient, ay as isNativeLiveTrackSubscribed, az as knownBehaviorStates, aA as mapTurnState, aB as readInboundRtp, aC as sessionBehaviorSchema, aD as sessionClipSchema, aE as splitCallTranscript, aF as useAvatarAdaptivePlayoutDelay, aG as useAvatarCamera, aH as useAvatarPlayoutDelay, aI as useAvatarQualityGovernor, aJ as useCallTranscript, aK as useLiveKitAvatarGrant, aL as useLiveTrackProducing, aM as useMicLease, aN as useRealtimeSession, aO as useReleaseMicLeaseOnTrackEnded, aP as useSessionLifecycle } from './proxy-client-O-Mu9JKt.js';
|
|
5
5
|
import { ReactNode, ReactElement } from 'react';
|
|
6
6
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
7
7
|
export { useChat, useConnectionState, useLocalParticipant, useRoomContext, useTrackToggle, useTranscriptions, useVoiceAssistant } from '@livekit/components-react';
|
package/dist/react-native.js
CHANGED
|
@@ -414,7 +414,13 @@ function normalizeError(input, overrides) {
|
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
416
|
|
|
417
|
+
// ../http-client/src/errors.ts
|
|
418
|
+
function isRequestId(value) {
|
|
419
|
+
return typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
|
|
420
|
+
}
|
|
421
|
+
|
|
417
422
|
// ../client/src/errors.ts
|
|
423
|
+
var TIMEOUT_CODE = "upstream_timeout";
|
|
418
424
|
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
419
425
|
constructor(message, status, body, response, metadata = {}) {
|
|
420
426
|
super(message);
|
|
@@ -426,6 +432,7 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
426
432
|
this.retryable = metadata.retryable;
|
|
427
433
|
this.billingUrl = metadata.billingUrl;
|
|
428
434
|
this.rawMessage = metadata.rawMessage ?? null;
|
|
435
|
+
this.requestId = metadata.requestId;
|
|
429
436
|
}
|
|
430
437
|
status;
|
|
431
438
|
body;
|
|
@@ -435,6 +442,8 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
435
442
|
billingUrl;
|
|
436
443
|
/** Debug detail returned by the API. Do not render directly in user-facing UI. */
|
|
437
444
|
rawMessage;
|
|
445
|
+
/** The platform's correlation ID for this attempt. Quote it to support. */
|
|
446
|
+
requestId;
|
|
438
447
|
get isBillingRequired() {
|
|
439
448
|
return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
|
|
440
449
|
}
|
|
@@ -443,14 +452,31 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
443
452
|
const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
|
|
444
453
|
const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
|
|
445
454
|
const metadata = extractErrorMetadata(body, response.status, rawMessage);
|
|
455
|
+
const bodyRequestId = readRecord(body)?.requestId;
|
|
456
|
+
const headerRequestId = response.headers.get("x-request-id");
|
|
446
457
|
return new _RealtimeAvatarApiError(
|
|
447
458
|
metadata.message,
|
|
448
459
|
response.status,
|
|
449
460
|
body,
|
|
450
461
|
response,
|
|
451
|
-
|
|
462
|
+
{
|
|
463
|
+
...metadata,
|
|
464
|
+
requestId: isRequestId(bodyRequestId) ? bodyRequestId : isRequestId(headerRequestId) ? headerRequestId : void 0
|
|
465
|
+
}
|
|
452
466
|
);
|
|
453
467
|
}
|
|
468
|
+
/**
|
|
469
|
+
* A request that got no answer before its deadline. The status is the one the vocabulary pairs
|
|
470
|
+
* with the code, so `normalizeRealtimeAvatarError` reads it back as the same failure.
|
|
471
|
+
*/
|
|
472
|
+
static timeout(timeoutMs) {
|
|
473
|
+
const { message, retryable, statuses: [status] } = ERROR_SEMANTICS[TIMEOUT_CODE];
|
|
474
|
+
return new _RealtimeAvatarApiError(message, status, null, null, {
|
|
475
|
+
code: TIMEOUT_CODE,
|
|
476
|
+
retryable,
|
|
477
|
+
rawMessage: `No response within ${timeoutMs}ms.`
|
|
478
|
+
});
|
|
479
|
+
}
|
|
454
480
|
};
|
|
455
481
|
var RealtimeAvatarCapacityError = class extends Error {
|
|
456
482
|
constructor(message, busy) {
|
|
@@ -3270,26 +3296,30 @@ function useRealtimeSession(input) {
|
|
|
3270
3296
|
);
|
|
3271
3297
|
}
|
|
3272
3298
|
|
|
3299
|
+
// ../http-client/src/retry.ts
|
|
3300
|
+
var PROXY_CLIENT_TIMEOUT_MS = 6e4;
|
|
3301
|
+
|
|
3273
3302
|
// ../client/src/proxy-client.ts
|
|
3274
|
-
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
3275
3303
|
var normalize = (url) => url.replace(/\/+$/, "");
|
|
3276
3304
|
function createProxyClient(options) {
|
|
3277
3305
|
const base = normalize(options.proxyUrl);
|
|
3278
3306
|
const doFetch = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
3279
|
-
const timeoutMs = options.timeoutMs ??
|
|
3280
|
-
const deadline = (caller) => {
|
|
3281
|
-
if (!timeoutMs) return caller;
|
|
3282
|
-
const timer = AbortSignal.timeout(timeoutMs);
|
|
3283
|
-
return caller ? AbortSignal.any([caller, timer]) : timer;
|
|
3284
|
-
};
|
|
3307
|
+
const timeoutMs = options.timeoutMs ?? PROXY_CLIENT_TIMEOUT_MS;
|
|
3285
3308
|
const post = async (path, body, request) => {
|
|
3286
3309
|
if (!doFetch) throw new Error("realtime-avatar: no fetch available \u2014 pass one via `fetch`.");
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3310
|
+
const caller = request?.signal;
|
|
3311
|
+
const timer = timeoutMs ? AbortSignal.timeout(timeoutMs) : void 0;
|
|
3312
|
+
try {
|
|
3313
|
+
return await doFetch(`${base}${path}`, {
|
|
3314
|
+
method: "POST",
|
|
3315
|
+
headers: { "content-type": "application/json", ...request?.headers ?? {} },
|
|
3316
|
+
body: JSON.stringify(body),
|
|
3317
|
+
signal: timer && caller ? AbortSignal.any([caller, timer]) : timer ?? caller
|
|
3318
|
+
});
|
|
3319
|
+
} catch (cause) {
|
|
3320
|
+
if (timer?.aborted && !caller?.aborted) throw RealtimeAvatarApiError.timeout(timeoutMs);
|
|
3321
|
+
throw cause;
|
|
3322
|
+
}
|
|
3293
3323
|
};
|
|
3294
3324
|
const beacon = (path, body) => {
|
|
3295
3325
|
const send = globalThis.navigator?.sendBeacon?.bind(globalThis.navigator);
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RegisteredTool, ToolRegistration } from './tools.js';
|
|
2
|
-
import { aj as SessionLifecycleRoomBridgeProps, h 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, g as AvatarConnectionDetails, aQ as AvatarVideoSurface, aR 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, u as DeclaredInputSource, E as EndReason, v as EndedEvent, w as ExtendResult, F as FishTtsModel, x as FreezeReadingFn, G as Governor, y as GovernorAction, z as GovernorConfig, H as GovernorSignal, I as GovernorState, J as GraceWindowClosedEvent, K as GraceWindowOpenEvent, M as GraceWindowState, N as IdleWarningEvent, O as InboundRtpCursor, P as InboundRtpReading, Q as KnownBehaviorState, R as LLMProvider, S as LLMSelection, T as LiveKitAvatarGrantState, U as LiveKitAvatarGrantStatus, V as LiveKitCapacityState, W as LiveKitConnectionStatus, X as LiveKitSessionRequest, Y as LiveKitSessionStartResult, aS as LivePlaybackKeeper, Z as MAX_RECONNECT_ATTEMPTS, _ as MAX_SESSION_INSTRUCTIONS_CHARS, aT as MIC_LEASE_ENDED_TIMEOUT_MS, aU as MicLease, aV as NETWORK_QUALITY_POLICY, aW as NetworkQualityStatus, aX as PlayableVideoElement, $ as ProxyClientOptions, a0 as QualityCap, a1 as RECONNECT_BACKOFF_MS, aY as RealtimeAvatarCapacityError, aZ as RealtimeAvatarLiveKitRoom, a_ as RealtimeAvatarLiveKitRoomProps, a2 as RealtimeAvatarRequestOptions, a3 as RealtimeSessionApi, a4 as RealtimeSessionMedia, a5 as RealtimeSessionRoomSinks, a6 as ReconnectPolicy, a7 as ReconnectingEvent, a8 as RecoveryState, a9 as RetryStep, aa as SendTurnOptions, ab as SessionBehavior, ac as SessionClip, ad as SessionClocks, ae as SessionEndReason, af as SessionLifecycleApi, ag as SessionLifecyclePhase, ah as SessionLifecyclePhaseKind, ai as SessionLifecycleRoomBridge, ak as SurfaceLayers, al as TranscriptSender, am as TranscriptSenderOptions, an as TurnState, ao as TurnTimeoutEvent, ap as UseAvatarQualityGovernorInput, aq as UseLiveKitAvatarGrantInput, ar as UseRealtimeSessionInput, as as UseSessionLifecycleInput, at as VoiceSpec, au as VoiceSpecInput, av as capacityErrorFromBusy, aw as capacityStateFromGrant, ax as createProxyClient, az as knownBehaviorStates, aA as mapTurnState, aB as readInboundRtp, aC as sessionBehaviorSchema, aD as sessionClipSchema, a$ as shouldReplayPendingTurn, aE as splitCallTranscript, aF as useAvatarAdaptivePlayoutDelay, aG as useAvatarCamera, aH as useAvatarPlayoutDelay, aI as useAvatarQualityGovernor, aJ as useCallTranscript, aK as useLiveKitAvatarGrant, aM as useMicLease, aN as useRealtimeSession, aO as useReleaseMicLeaseOnTrackEnded, aP as useSessionLifecycle } from './proxy-client-
|
|
2
|
+
import { aj as SessionLifecycleRoomBridgeProps, h as AvatarSessionClient, A as AvatarVideoFit } from './proxy-client-O-Mu9JKt.js';
|
|
3
|
+
export { a as AdaptivePlayoutController, b as AdaptivePlayoutDecision, c as AdaptivePlayoutOptions, d as AdaptivePlayoutSample, e as ApproachingEndEvent, f as ApproachingEndReason, g as AvatarConnectionDetails, aQ as AvatarVideoSurface, aR 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, u as DeclaredInputSource, E as EndReason, v as EndedEvent, w as ExtendResult, F as FishTtsModel, x as FreezeReadingFn, G as Governor, y as GovernorAction, z as GovernorConfig, H as GovernorSignal, I as GovernorState, J as GraceWindowClosedEvent, K as GraceWindowOpenEvent, M as GraceWindowState, N as IdleWarningEvent, O as InboundRtpCursor, P as InboundRtpReading, Q as KnownBehaviorState, R as LLMProvider, S as LLMSelection, T as LiveKitAvatarGrantState, U as LiveKitAvatarGrantStatus, V as LiveKitCapacityState, W as LiveKitConnectionStatus, X as LiveKitSessionRequest, Y as LiveKitSessionStartResult, aS as LivePlaybackKeeper, Z as MAX_RECONNECT_ATTEMPTS, _ as MAX_SESSION_INSTRUCTIONS_CHARS, aT as MIC_LEASE_ENDED_TIMEOUT_MS, aU as MicLease, aV as NETWORK_QUALITY_POLICY, aW as NetworkQualityStatus, aX as PlayableVideoElement, $ as ProxyClientOptions, a0 as QualityCap, a1 as RECONNECT_BACKOFF_MS, aY as RealtimeAvatarCapacityError, aZ as RealtimeAvatarLiveKitRoom, a_ as RealtimeAvatarLiveKitRoomProps, a2 as RealtimeAvatarRequestOptions, a3 as RealtimeSessionApi, a4 as RealtimeSessionMedia, a5 as RealtimeSessionRoomSinks, a6 as ReconnectPolicy, a7 as ReconnectingEvent, a8 as RecoveryState, a9 as RetryStep, aa as SendTurnOptions, ab as SessionBehavior, ac as SessionClip, ad as SessionClocks, ae as SessionEndReason, af as SessionLifecycleApi, ag as SessionLifecyclePhase, ah as SessionLifecyclePhaseKind, ai as SessionLifecycleRoomBridge, ak as SurfaceLayers, al as TranscriptSender, am as TranscriptSenderOptions, an as TurnState, ao as TurnTimeoutEvent, ap as UseAvatarQualityGovernorInput, aq as UseLiveKitAvatarGrantInput, ar as UseRealtimeSessionInput, as as UseSessionLifecycleInput, at as VoiceSpec, au as VoiceSpecInput, av as capacityErrorFromBusy, aw as capacityStateFromGrant, ax as createProxyClient, az as knownBehaviorStates, aA as mapTurnState, aB as readInboundRtp, aC as sessionBehaviorSchema, aD as sessionClipSchema, a$ as shouldReplayPendingTurn, aE as splitCallTranscript, aF as useAvatarAdaptivePlayoutDelay, aG as useAvatarCamera, aH as useAvatarPlayoutDelay, aI as useAvatarQualityGovernor, aJ as useCallTranscript, aK as useLiveKitAvatarGrant, aM as useMicLease, aN as useRealtimeSession, aO as useReleaseMicLeaseOnTrackEnded, aP as useSessionLifecycle } from './proxy-client-O-Mu9JKt.js';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
6
|
import { R as RecordingArtifact } from './recording-DjswTx5B.js';
|
package/dist/react.js
CHANGED
|
@@ -568,7 +568,13 @@ function normalizeError(input, overrides) {
|
|
|
568
568
|
};
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
+
// ../http-client/src/errors.ts
|
|
572
|
+
function isRequestId(value) {
|
|
573
|
+
return typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value);
|
|
574
|
+
}
|
|
575
|
+
|
|
571
576
|
// ../client/src/errors.ts
|
|
577
|
+
var TIMEOUT_CODE = "upstream_timeout";
|
|
572
578
|
var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
573
579
|
constructor(message, status, body, response, metadata = {}) {
|
|
574
580
|
super(message);
|
|
@@ -580,6 +586,7 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
580
586
|
this.retryable = metadata.retryable;
|
|
581
587
|
this.billingUrl = metadata.billingUrl;
|
|
582
588
|
this.rawMessage = metadata.rawMessage ?? null;
|
|
589
|
+
this.requestId = metadata.requestId;
|
|
583
590
|
}
|
|
584
591
|
status;
|
|
585
592
|
body;
|
|
@@ -589,6 +596,8 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
589
596
|
billingUrl;
|
|
590
597
|
/** Debug detail returned by the API. Do not render directly in user-facing UI. */
|
|
591
598
|
rawMessage;
|
|
599
|
+
/** The platform's correlation ID for this attempt. Quote it to support. */
|
|
600
|
+
requestId;
|
|
592
601
|
get isBillingRequired() {
|
|
593
602
|
return this.status === 402 || this.code === "insufficient_credits" || this.code === "spend_limit_exceeded";
|
|
594
603
|
}
|
|
@@ -597,14 +606,31 @@ var RealtimeAvatarApiError = class _RealtimeAvatarApiError extends Error {
|
|
|
597
606
|
const body = contentType.includes("application/json") ? await response.clone().json().catch(() => null) : await response.clone().text().catch(() => "");
|
|
598
607
|
const rawMessage = (extractErrorMessage(body) ?? response.statusText) || null;
|
|
599
608
|
const metadata = extractErrorMetadata(body, response.status, rawMessage);
|
|
609
|
+
const bodyRequestId = readRecord(body)?.requestId;
|
|
610
|
+
const headerRequestId = response.headers.get("x-request-id");
|
|
600
611
|
return new _RealtimeAvatarApiError(
|
|
601
612
|
metadata.message,
|
|
602
613
|
response.status,
|
|
603
614
|
body,
|
|
604
615
|
response,
|
|
605
|
-
|
|
616
|
+
{
|
|
617
|
+
...metadata,
|
|
618
|
+
requestId: isRequestId(bodyRequestId) ? bodyRequestId : isRequestId(headerRequestId) ? headerRequestId : void 0
|
|
619
|
+
}
|
|
606
620
|
);
|
|
607
621
|
}
|
|
622
|
+
/**
|
|
623
|
+
* A request that got no answer before its deadline. The status is the one the vocabulary pairs
|
|
624
|
+
* with the code, so `normalizeRealtimeAvatarError` reads it back as the same failure.
|
|
625
|
+
*/
|
|
626
|
+
static timeout(timeoutMs) {
|
|
627
|
+
const { message, retryable, statuses: [status] } = ERROR_SEMANTICS[TIMEOUT_CODE];
|
|
628
|
+
return new _RealtimeAvatarApiError(message, status, null, null, {
|
|
629
|
+
code: TIMEOUT_CODE,
|
|
630
|
+
retryable,
|
|
631
|
+
rawMessage: `No response within ${timeoutMs}ms.`
|
|
632
|
+
});
|
|
633
|
+
}
|
|
608
634
|
};
|
|
609
635
|
var RealtimeAvatarCapacityError = class extends Error {
|
|
610
636
|
constructor(message, busy) {
|
|
@@ -3896,26 +3922,30 @@ function AvatarCall(props) {
|
|
|
3896
3922
|
return view;
|
|
3897
3923
|
}
|
|
3898
3924
|
|
|
3925
|
+
// ../http-client/src/retry.ts
|
|
3926
|
+
var PROXY_CLIENT_TIMEOUT_MS = 6e4;
|
|
3927
|
+
|
|
3899
3928
|
// ../client/src/proxy-client.ts
|
|
3900
|
-
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
3901
3929
|
var normalize = (url) => url.replace(/\/+$/, "");
|
|
3902
3930
|
function createProxyClient(options) {
|
|
3903
3931
|
const base = normalize(options.proxyUrl);
|
|
3904
3932
|
const doFetch = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
3905
|
-
const timeoutMs = options.timeoutMs ??
|
|
3906
|
-
const deadline = (caller) => {
|
|
3907
|
-
if (!timeoutMs) return caller;
|
|
3908
|
-
const timer = AbortSignal.timeout(timeoutMs);
|
|
3909
|
-
return caller ? AbortSignal.any([caller, timer]) : timer;
|
|
3910
|
-
};
|
|
3933
|
+
const timeoutMs = options.timeoutMs ?? PROXY_CLIENT_TIMEOUT_MS;
|
|
3911
3934
|
const post = async (path, body, request) => {
|
|
3912
3935
|
if (!doFetch) throw new Error("realtime-avatar: no fetch available \u2014 pass one via `fetch`.");
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3936
|
+
const caller = request?.signal;
|
|
3937
|
+
const timer = timeoutMs ? AbortSignal.timeout(timeoutMs) : void 0;
|
|
3938
|
+
try {
|
|
3939
|
+
return await doFetch(`${base}${path}`, {
|
|
3940
|
+
method: "POST",
|
|
3941
|
+
headers: { "content-type": "application/json", ...request?.headers ?? {} },
|
|
3942
|
+
body: JSON.stringify(body),
|
|
3943
|
+
signal: timer && caller ? AbortSignal.any([caller, timer]) : timer ?? caller
|
|
3944
|
+
});
|
|
3945
|
+
} catch (cause) {
|
|
3946
|
+
if (timer?.aborted && !caller?.aborted) throw RealtimeAvatarApiError.timeout(timeoutMs);
|
|
3947
|
+
throw cause;
|
|
3948
|
+
}
|
|
3919
3949
|
};
|
|
3920
3950
|
const beacon = (path, body) => {
|
|
3921
3951
|
const send = globalThis.navigator?.sendBeacon?.bind(globalThis.navigator);
|
package/dist/server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-
|
|
1
|
+
export { RealtimeAvatar, RealtimeAvatarError, RealtimeAvatarHttpError, clipLibraryDeclarationSchema, isQueued, listRecordingsResponseSchema, recordingAccessResponseSchema, recordingArtifactSchema, recordingModeSchema, verifyTranscript } from './chunk-GMYGHDY2.js';
|
|
2
2
|
import './chunk-MCIXU6XP.js';
|
package/dist/tanstack-start.d.ts
CHANGED
package/dist/tanstack-start.js
CHANGED
|
@@ -16,6 +16,13 @@ interface ProxyConfig {
|
|
|
16
16
|
/** Server-only. `tic_live_…` / `tic_test_…`. */
|
|
17
17
|
apiKey: string | (() => string | Promise<string>);
|
|
18
18
|
baseUrl?: string;
|
|
19
|
+
/**
|
|
20
|
+
* How long this route spends on one platform request, every retry and backoff included.
|
|
21
|
+
* Default 50s, below `createProxyClient`'s default 60s wait so the page hears the answer. Raise
|
|
22
|
+
* the browser's `timeoutMs` if you raise this, and keep it under your host's max function
|
|
23
|
+
* duration. Running out answers `504` `upstream_timeout`.
|
|
24
|
+
*/
|
|
25
|
+
timeoutMs?: number;
|
|
19
26
|
/**
|
|
20
27
|
* Who may do this. Return a `Response` to refuse, or nothing to allow.
|
|
21
28
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtime-avatar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.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",
|
package/dist/chunk-4PWWEERK.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { RealtimeAvatar, isQueued, RealtimeAvatarHttpError } from './chunk-EODL5S3A.js';
|
|
2
|
-
|
|
3
|
-
// ../proxy/src/config.ts
|
|
4
|
-
var json = (body, status = 200) => new Response(JSON.stringify(body), {
|
|
5
|
-
status,
|
|
6
|
-
headers: { "content-type": "application/json", "cache-control": "no-store" }
|
|
7
|
-
});
|
|
8
|
-
function operationFor(pathname, method) {
|
|
9
|
-
const tail = pathname.replace(/\/+$/, "").split("/").pop() ?? "";
|
|
10
|
-
if (method === "POST" && (tail === "connect" || tail === "call")) return "connect";
|
|
11
|
-
if (method === "POST" && (tail === "end" || tail === "release")) return "end";
|
|
12
|
-
if (method === "GET" && tail === "avatars") return "avatars";
|
|
13
|
-
if (method === "GET" && tail === "credits") return "credits";
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
function createProxyHandler(config) {
|
|
17
|
-
const minted = /* @__PURE__ */ new Map();
|
|
18
|
-
const MINTED_TTL_MS = 30 * 6e4;
|
|
19
|
-
return async (request) => {
|
|
20
|
-
const url = new URL(request.url);
|
|
21
|
-
const operation = operationFor(url.pathname, request.method);
|
|
22
|
-
if (!operation) return json({ error: "not found" }, 404);
|
|
23
|
-
const refusal = await config.authorize?.({ request, operation });
|
|
24
|
-
if (refusal instanceof Response) return refusal;
|
|
25
|
-
const apiKey = typeof config.apiKey === "function" ? await config.apiKey() : config.apiKey;
|
|
26
|
-
const rta = new RealtimeAvatar({ apiKey, baseUrl: config.baseUrl });
|
|
27
|
-
try {
|
|
28
|
-
if (operation === "avatars") return json({ data: await rta.listAvatars() });
|
|
29
|
-
if (operation === "credits") return json(await rta.creditBalance());
|
|
30
|
-
if (operation === "end") {
|
|
31
|
-
const ended = await request.json().catch(() => ({}));
|
|
32
|
-
const sessionId = ended.session_id ?? ended.queue_ticket_id;
|
|
33
|
-
if (!sessionId) return json({ error: "session_id or queue_ticket_id is required" }, 422);
|
|
34
|
-
const owns = config.ownsSession ? await config.ownsSession({ request, sessionId }) : minted.delete(sessionId);
|
|
35
|
-
if (!owns) return new Response(null, { status: 204 });
|
|
36
|
-
const reason = ended.reason === "page_hide" || ended.reason === "unmount" ? ended.reason : "manual";
|
|
37
|
-
await rta.endCall(sessionId, { reason });
|
|
38
|
-
return new Response(null, { status: 204 });
|
|
39
|
-
}
|
|
40
|
-
const body = await request.json().catch(() => ({}));
|
|
41
|
-
if (!body.avatarId) return json({ error: "avatarId is required" }, 422);
|
|
42
|
-
const mode = body.mode === "voice" ? "voice" : "avatar";
|
|
43
|
-
const decided = await config.session?.({ request, avatarId: body.avatarId, mode });
|
|
44
|
-
if (decided instanceof Response) return decided;
|
|
45
|
-
const call = await rta.startCall({ avatarId: body.avatarId, mode, ...decided ?? {} });
|
|
46
|
-
if (isQueued(call)) {
|
|
47
|
-
if (call.queueTicketId) minted.set(call.queueTicketId, Date.now());
|
|
48
|
-
return json(
|
|
49
|
-
{
|
|
50
|
-
queued: true,
|
|
51
|
-
position: call.position,
|
|
52
|
-
size: call.size,
|
|
53
|
-
retryAfterMs: call.retryAfterMs,
|
|
54
|
-
queue_ticket_id: call.queueTicketId
|
|
55
|
-
},
|
|
56
|
-
429
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
const now = Date.now();
|
|
60
|
-
for (const [id, at] of minted) if (now - at > MINTED_TTL_MS) minted.delete(id);
|
|
61
|
-
minted.set(call.sessionId, now);
|
|
62
|
-
return json(call.raw);
|
|
63
|
-
} catch (error) {
|
|
64
|
-
if (error instanceof RealtimeAvatarHttpError && error.status === 429) {
|
|
65
|
-
const concurrency = error.code === "concurrency_limit_reached";
|
|
66
|
-
const counts = error.concurrency;
|
|
67
|
-
const message = concurrency ? counts?.maxConcurrentSessions !== void 0 && counts.activeSessions !== void 0 && counts.connectingSessions !== void 0 && counts.pendingSessions !== void 0 ? `Session limit reached (${counts.maxConcurrentSessions} allowed): ${counts.activeSessions} active, ${counts.connectingSessions} connecting, ${counts.pendingSessions} starting. End a session or wait for pending starts to clear, then retry.` : "The concurrent session limit is reached. Active and starting sessions count. End a session or wait for pending starts to clear, then retry." : "Too many requests. Wait before retrying.";
|
|
68
|
-
const response = json({
|
|
69
|
-
error: message,
|
|
70
|
-
code: concurrency ? "concurrency_limit_reached" : "rate_limited",
|
|
71
|
-
status: 429,
|
|
72
|
-
retryable: true,
|
|
73
|
-
...concurrency ? counts : {},
|
|
74
|
-
...error.requestId ? { requestId: error.requestId } : {}
|
|
75
|
-
}, 429);
|
|
76
|
-
if (error.requestId) response.headers.set("X-Request-ID", error.requestId);
|
|
77
|
-
return response;
|
|
78
|
-
}
|
|
79
|
-
if (error instanceof RealtimeAvatarHttpError && error.isBilling) {
|
|
80
|
-
return json({ code: error.code ?? "insufficient_credits" }, 402);
|
|
81
|
-
}
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export { createProxyHandler };
|