@opengeni/codex 6.8.0 → 6.9.1-canary.41937124785001
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/index.js +143 -29
- package/dist/index.js.map +1 -1
- package/dist/realtime.d.ts +42 -1
- package/package.json +5 -4
- package/src/realtime.ts +202 -18
package/dist/index.js
CHANGED
|
@@ -3234,7 +3234,9 @@ function boundedErrorMessage(body) {
|
|
|
3234
3234
|
// src/realtime.ts
|
|
3235
3235
|
var MAX_REALTIME_SDP_BYTES = 1024 * 1024;
|
|
3236
3236
|
var MAX_REALTIME_PROVIDER_VALUE_LENGTH = 128;
|
|
3237
|
+
var MAX_REALTIME_ERROR_BODY_BYTES = 16 * 1024;
|
|
3237
3238
|
var REALTIME_PROVIDER_VALUE = /^[a-z0-9][a-z0-9._-]*$/i;
|
|
3239
|
+
var REALTIME_PROVIDER_ERROR_TOKEN = /^[A-Za-z0-9][A-Za-z0-9 _.:-]{0,63}$/;
|
|
3238
3240
|
var REALTIME_CALL_ID = /^(?:rtc_.+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
3239
3241
|
var CODEX_REALTIME_VOICES = [
|
|
3240
3242
|
"juniper",
|
|
@@ -3248,17 +3250,19 @@ var CODEX_REALTIME_VOICES = [
|
|
|
3248
3250
|
"cove"
|
|
3249
3251
|
];
|
|
3250
3252
|
var CodexRealtimeError = class extends Error {
|
|
3251
|
-
constructor(code, message, providerStatus = null) {
|
|
3253
|
+
constructor(code, message, providerStatus = null, diagnostics = { stage: "request" }) {
|
|
3252
3254
|
super(message);
|
|
3253
3255
|
this.code = code;
|
|
3254
3256
|
this.providerStatus = providerStatus;
|
|
3257
|
+
this.diagnostics = diagnostics;
|
|
3255
3258
|
this.name = "CodexRealtimeError";
|
|
3256
3259
|
}
|
|
3257
3260
|
};
|
|
3258
3261
|
var FALLBACK_REALTIME_PROVIDER_CONFIG = {
|
|
3259
3262
|
architecture: CODEX_REALTIME_PROVIDER_ARCHITECTURE_FALLBACK,
|
|
3260
3263
|
model: CODEX_REALTIME_PROVIDER_MODEL_FALLBACK,
|
|
3261
|
-
version: CODEX_REALTIME_VERSION
|
|
3264
|
+
version: CODEX_REALTIME_VERSION,
|
|
3265
|
+
source: "fallback"
|
|
3262
3266
|
};
|
|
3263
3267
|
async function fetchCodexRealtimeProviderConfig(auth, fetchImpl = fetch, options = {}) {
|
|
3264
3268
|
if (options.signal?.aborted) {
|
|
@@ -3294,7 +3298,8 @@ async function fetchCodexRealtimeProviderConfig(auth, fetchImpl = fetch, options
|
|
|
3294
3298
|
throw new CodexRealtimeError(
|
|
3295
3299
|
"authentication",
|
|
3296
3300
|
"Codex subscription rejected authentication",
|
|
3297
|
-
response.status
|
|
3301
|
+
response.status,
|
|
3302
|
+
{ stage: "config" }
|
|
3298
3303
|
);
|
|
3299
3304
|
}
|
|
3300
3305
|
if (!response.ok) {
|
|
@@ -3307,18 +3312,26 @@ async function fetchCodexRealtimeProviderConfig(auth, fetchImpl = fetch, options
|
|
|
3307
3312
|
const value = payload.dynamic_configs?.[CODEX_REALTIME_CONFIG_ID]?.value;
|
|
3308
3313
|
const version = value?.version;
|
|
3309
3314
|
if (version !== void 0 && version !== CODEX_REALTIME_VERSION) {
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3315
|
+
return {
|
|
3316
|
+
...FALLBACK_REALTIME_PROVIDER_CONFIG,
|
|
3317
|
+
...validProviderValue(version) ? { advertisedVersion: version } : {}
|
|
3318
|
+
};
|
|
3314
3319
|
}
|
|
3315
3320
|
const architecture = validProviderValue(value?.architecture) ? value.architecture : FALLBACK_REALTIME_PROVIDER_CONFIG.architecture;
|
|
3316
3321
|
const model = validProviderValue(value?.model) ? value.model : FALLBACK_REALTIME_PROVIDER_CONFIG.model;
|
|
3317
|
-
return {
|
|
3322
|
+
return {
|
|
3323
|
+
architecture,
|
|
3324
|
+
model,
|
|
3325
|
+
version: CODEX_REALTIME_VERSION,
|
|
3326
|
+
source: value ? "remote" : "fallback",
|
|
3327
|
+
...version === void 0 ? {} : { advertisedVersion: CODEX_REALTIME_VERSION }
|
|
3328
|
+
};
|
|
3318
3329
|
} catch (error) {
|
|
3319
3330
|
if (error instanceof CodexRealtimeError) throw error;
|
|
3320
3331
|
if (options.signal?.aborted) {
|
|
3321
|
-
throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled"
|
|
3332
|
+
throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled", null, {
|
|
3333
|
+
stage: "config"
|
|
3334
|
+
});
|
|
3322
3335
|
}
|
|
3323
3336
|
return FALLBACK_REALTIME_PROVIDER_CONFIG;
|
|
3324
3337
|
} finally {
|
|
@@ -3330,6 +3343,13 @@ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options =
|
|
|
3330
3343
|
validateRealtimeInput(input);
|
|
3331
3344
|
const timeoutMs = options.timeoutMs ?? CODEX_REALTIME_CALL_TIMEOUT_MS;
|
|
3332
3345
|
const providerConfig = options.providerConfig ?? FALLBACK_REALTIME_PROVIDER_CONFIG;
|
|
3346
|
+
const callDiagnostics = {
|
|
3347
|
+
stage: "call",
|
|
3348
|
+
configSource: providerConfig.source,
|
|
3349
|
+
advertisedVersion: validProviderValue(providerConfig.advertisedVersion) ? providerConfig.advertisedVersion : void 0,
|
|
3350
|
+
providerModel: validProviderValue(providerConfig.model) ? providerConfig.model : void 0,
|
|
3351
|
+
providerArchitecture: validProviderValue(providerConfig.architecture) ? providerConfig.architecture : void 0
|
|
3352
|
+
};
|
|
3333
3353
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
3334
3354
|
throw new CodexRealtimeError("invalid_request", "Codex realtime timeout must be positive");
|
|
3335
3355
|
}
|
|
@@ -3357,14 +3377,28 @@ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options =
|
|
|
3357
3377
|
});
|
|
3358
3378
|
const onAbort = () => {
|
|
3359
3379
|
controller.abort(options.signal?.reason);
|
|
3360
|
-
rejectCancellation?.(
|
|
3380
|
+
rejectCancellation?.(
|
|
3381
|
+
new CodexRealtimeError(
|
|
3382
|
+
"cancelled",
|
|
3383
|
+
"Codex realtime request cancelled",
|
|
3384
|
+
null,
|
|
3385
|
+
callDiagnostics
|
|
3386
|
+
)
|
|
3387
|
+
);
|
|
3361
3388
|
};
|
|
3362
3389
|
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
3363
3390
|
const deadline = new Promise((_resolve, reject) => {
|
|
3364
3391
|
timeout = setTimeout(() => {
|
|
3365
3392
|
timedOut = true;
|
|
3366
3393
|
controller.abort();
|
|
3367
|
-
reject(
|
|
3394
|
+
reject(
|
|
3395
|
+
new CodexRealtimeError(
|
|
3396
|
+
"timeout",
|
|
3397
|
+
"Codex realtime request timed out",
|
|
3398
|
+
null,
|
|
3399
|
+
callDiagnostics
|
|
3400
|
+
)
|
|
3401
|
+
);
|
|
3368
3402
|
}, timeoutMs);
|
|
3369
3403
|
});
|
|
3370
3404
|
const request = (async () => {
|
|
@@ -3406,8 +3440,8 @@ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options =
|
|
|
3406
3440
|
}
|
|
3407
3441
|
);
|
|
3408
3442
|
if (!response.ok) {
|
|
3409
|
-
|
|
3410
|
-
throw providerHttpError(response.status);
|
|
3443
|
+
const providerError = await readProviderErrorFields(response);
|
|
3444
|
+
throw providerHttpError(response.status, { ...callDiagnostics, ...providerError });
|
|
3411
3445
|
}
|
|
3412
3446
|
const location = response.headers.get("location");
|
|
3413
3447
|
if (!location || !validRealtimeLocation(location)) {
|
|
@@ -3415,15 +3449,17 @@ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options =
|
|
|
3415
3449
|
throw new CodexRealtimeError(
|
|
3416
3450
|
"invalid_response",
|
|
3417
3451
|
"Codex realtime response did not identify a compatible call",
|
|
3418
|
-
response.status
|
|
3452
|
+
response.status,
|
|
3453
|
+
callDiagnostics
|
|
3419
3454
|
);
|
|
3420
3455
|
}
|
|
3421
|
-
const sdp = await readBoundedSdp(response);
|
|
3456
|
+
const sdp = await readBoundedSdp(response, callDiagnostics);
|
|
3422
3457
|
if (!isAudioSdp(sdp)) {
|
|
3423
3458
|
throw new CodexRealtimeError(
|
|
3424
3459
|
"invalid_response",
|
|
3425
3460
|
"Codex realtime response was not an audio SDP answer",
|
|
3426
|
-
response.status
|
|
3461
|
+
response.status,
|
|
3462
|
+
callDiagnostics
|
|
3427
3463
|
);
|
|
3428
3464
|
}
|
|
3429
3465
|
return {
|
|
@@ -3437,12 +3473,27 @@ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options =
|
|
|
3437
3473
|
} catch (error) {
|
|
3438
3474
|
if (error instanceof CodexRealtimeError) throw error;
|
|
3439
3475
|
if (options.signal?.aborted) {
|
|
3440
|
-
throw new CodexRealtimeError(
|
|
3476
|
+
throw new CodexRealtimeError(
|
|
3477
|
+
"cancelled",
|
|
3478
|
+
"Codex realtime request cancelled",
|
|
3479
|
+
null,
|
|
3480
|
+
callDiagnostics
|
|
3481
|
+
);
|
|
3441
3482
|
}
|
|
3442
3483
|
if (timedOut || controller.signal.aborted) {
|
|
3443
|
-
throw new CodexRealtimeError(
|
|
3484
|
+
throw new CodexRealtimeError(
|
|
3485
|
+
"timeout",
|
|
3486
|
+
"Codex realtime request timed out",
|
|
3487
|
+
null,
|
|
3488
|
+
callDiagnostics
|
|
3489
|
+
);
|
|
3444
3490
|
}
|
|
3445
|
-
throw new CodexRealtimeError(
|
|
3491
|
+
throw new CodexRealtimeError(
|
|
3492
|
+
"network",
|
|
3493
|
+
"Codex realtime provider request failed",
|
|
3494
|
+
null,
|
|
3495
|
+
callDiagnostics
|
|
3496
|
+
);
|
|
3446
3497
|
} finally {
|
|
3447
3498
|
if (timeout) clearTimeout(timeout);
|
|
3448
3499
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -3501,32 +3552,92 @@ function validateRealtimeInput(input) {
|
|
|
3501
3552
|
throw new CodexRealtimeError("invalid_request", "Codex realtime history is too large");
|
|
3502
3553
|
}
|
|
3503
3554
|
}
|
|
3504
|
-
function
|
|
3555
|
+
async function readProviderErrorFields(response) {
|
|
3556
|
+
const declared = Number(response.headers.get("content-length"));
|
|
3557
|
+
if (!response.body || Number.isFinite(declared) && declared > MAX_REALTIME_ERROR_BODY_BYTES) {
|
|
3558
|
+
await response.body?.cancel().catch(() => void 0);
|
|
3559
|
+
return {};
|
|
3560
|
+
}
|
|
3561
|
+
let text = "";
|
|
3562
|
+
const reader = response.body.getReader();
|
|
3563
|
+
const decoder = new TextDecoder();
|
|
3564
|
+
let total = 0;
|
|
3565
|
+
try {
|
|
3566
|
+
while (true) {
|
|
3567
|
+
const next = await reader.read();
|
|
3568
|
+
if (next.done) break;
|
|
3569
|
+
total += next.value.byteLength;
|
|
3570
|
+
if (total > MAX_REALTIME_ERROR_BODY_BYTES) {
|
|
3571
|
+
await reader.cancel().catch(() => void 0);
|
|
3572
|
+
return {};
|
|
3573
|
+
}
|
|
3574
|
+
text += decoder.decode(next.value, { stream: true });
|
|
3575
|
+
}
|
|
3576
|
+
} catch {
|
|
3577
|
+
return {};
|
|
3578
|
+
} finally {
|
|
3579
|
+
reader.releaseLock();
|
|
3580
|
+
}
|
|
3581
|
+
let parsed;
|
|
3582
|
+
try {
|
|
3583
|
+
parsed = JSON.parse(text);
|
|
3584
|
+
} catch {
|
|
3585
|
+
return {};
|
|
3586
|
+
}
|
|
3587
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
3588
|
+
const record = parsed;
|
|
3589
|
+
const error = record.error && typeof record.error === "object" ? record.error : null;
|
|
3590
|
+
const code = providerErrorToken(error?.code) ?? providerErrorToken(record.detail);
|
|
3591
|
+
const type = providerErrorToken(error?.type);
|
|
3592
|
+
const param = providerErrorToken(error?.param);
|
|
3593
|
+
return {
|
|
3594
|
+
...code ? { providerErrorCode: code } : {},
|
|
3595
|
+
...type ? { providerErrorType: type } : {},
|
|
3596
|
+
...param ? { providerErrorParam: param } : {}
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
function providerErrorToken(value) {
|
|
3600
|
+
return typeof value === "string" && REALTIME_PROVIDER_ERROR_TOKEN.test(value) ? value : null;
|
|
3601
|
+
}
|
|
3602
|
+
function providerHttpError(status, diagnostics) {
|
|
3505
3603
|
if (status === 401) {
|
|
3506
3604
|
return new CodexRealtimeError(
|
|
3507
3605
|
"authentication",
|
|
3508
3606
|
"Codex subscription rejected authentication",
|
|
3509
|
-
status
|
|
3607
|
+
status,
|
|
3608
|
+
diagnostics
|
|
3510
3609
|
);
|
|
3511
3610
|
}
|
|
3512
3611
|
if (status === 403) {
|
|
3513
3612
|
return new CodexRealtimeError(
|
|
3514
3613
|
"entitlement",
|
|
3515
3614
|
"Codex subscription lacks realtime entitlement",
|
|
3516
|
-
status
|
|
3615
|
+
status,
|
|
3616
|
+
diagnostics
|
|
3517
3617
|
);
|
|
3518
3618
|
}
|
|
3519
3619
|
if (status === 404) {
|
|
3520
3620
|
return new CodexRealtimeError(
|
|
3521
3621
|
"incompatible",
|
|
3522
3622
|
"Codex subscription realtime is unavailable",
|
|
3523
|
-
status
|
|
3623
|
+
status,
|
|
3624
|
+
diagnostics
|
|
3524
3625
|
);
|
|
3525
3626
|
}
|
|
3526
3627
|
if (status === 429) {
|
|
3527
|
-
return new CodexRealtimeError(
|
|
3628
|
+
return new CodexRealtimeError(
|
|
3629
|
+
"rate_limited",
|
|
3630
|
+
"Codex realtime is rate limited",
|
|
3631
|
+
status,
|
|
3632
|
+
diagnostics
|
|
3633
|
+
);
|
|
3528
3634
|
}
|
|
3529
|
-
return new CodexRealtimeError(
|
|
3635
|
+
return new CodexRealtimeError(
|
|
3636
|
+
"provider",
|
|
3637
|
+
"Codex realtime provider request failed",
|
|
3638
|
+
status,
|
|
3639
|
+
diagnostics
|
|
3640
|
+
);
|
|
3530
3641
|
}
|
|
3531
3642
|
function validRealtimeLocation(location) {
|
|
3532
3643
|
const path = location.split("?", 1)[0] ?? "";
|
|
@@ -3536,14 +3647,15 @@ function validRealtimeLocation(location) {
|
|
|
3536
3647
|
function isAudioSdp(sdp) {
|
|
3537
3648
|
return /^v=0(?:\r?\n)/.test(sdp) && /(?:^|\r?\n)m=audio\s/m.test(sdp);
|
|
3538
3649
|
}
|
|
3539
|
-
async function readBoundedSdp(response) {
|
|
3650
|
+
async function readBoundedSdp(response, diagnostics) {
|
|
3540
3651
|
const declared = Number(response.headers.get("content-length"));
|
|
3541
3652
|
if (Number.isFinite(declared) && declared > MAX_REALTIME_SDP_BYTES) {
|
|
3542
3653
|
await response.body?.cancel().catch(() => void 0);
|
|
3543
3654
|
throw new CodexRealtimeError(
|
|
3544
3655
|
"invalid_response",
|
|
3545
3656
|
"Codex realtime SDP answer is too large",
|
|
3546
|
-
response.status
|
|
3657
|
+
response.status,
|
|
3658
|
+
diagnostics
|
|
3547
3659
|
);
|
|
3548
3660
|
}
|
|
3549
3661
|
if (!response.body) return "";
|
|
@@ -3560,7 +3672,8 @@ async function readBoundedSdp(response) {
|
|
|
3560
3672
|
throw new CodexRealtimeError(
|
|
3561
3673
|
"invalid_response",
|
|
3562
3674
|
"Codex realtime SDP answer is too large",
|
|
3563
|
-
response.status
|
|
3675
|
+
response.status,
|
|
3676
|
+
diagnostics
|
|
3564
3677
|
);
|
|
3565
3678
|
}
|
|
3566
3679
|
chunks.push(next.value);
|
|
@@ -3580,7 +3693,8 @@ async function readBoundedSdp(response) {
|
|
|
3580
3693
|
throw new CodexRealtimeError(
|
|
3581
3694
|
"invalid_response",
|
|
3582
3695
|
"Codex realtime SDP answer was not valid UTF-8",
|
|
3583
|
-
response.status
|
|
3696
|
+
response.status,
|
|
3697
|
+
diagnostics
|
|
3584
3698
|
);
|
|
3585
3699
|
}
|
|
3586
3700
|
}
|