@absolutejs/voice 0.0.22-beta.670 → 0.0.22-beta.671
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/core/modelAdapters.d.ts +5 -0
- package/dist/index.js +27 -10
- package/dist/testing/index.js +23 -8
- package/package.json +1 -1
|
@@ -13,6 +13,8 @@ export type OpenAIVoiceAssistantModelOptions = {
|
|
|
13
13
|
model?: string;
|
|
14
14
|
onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
|
|
15
15
|
temperature?: number;
|
|
16
|
+
/** Maximum silence between SSE body chunks. Default 10 seconds. */
|
|
17
|
+
streamInactivityMs?: number;
|
|
16
18
|
/**
|
|
17
19
|
* Hard cap on a single /responses stream. Aborts via AbortController if
|
|
18
20
|
* the stream doesn't complete in time. Default 60s — generous for typical
|
|
@@ -62,6 +64,9 @@ export type VoiceProviderRouterEvent<TProvider extends string = string> = {
|
|
|
62
64
|
suppressedUntil?: number;
|
|
63
65
|
status: "error" | "fallback" | "success";
|
|
64
66
|
timedOut?: boolean;
|
|
67
|
+
timeoutKind?: "latency-budget" | "stream-inactivity";
|
|
68
|
+
timeoutMs?: number;
|
|
69
|
+
totalElapsedMs?: number;
|
|
65
70
|
};
|
|
66
71
|
export type VoiceProviderRouterFallbackMode = "never" | "provider-error" | "rate-limit";
|
|
67
72
|
export type VoiceProviderRouterStrategy = "balanced" | "ordered" | "prefer-cheapest" | "prefer-fastest" | "prefer-selected" | "quality-first";
|
package/dist/index.js
CHANGED
|
@@ -41730,13 +41730,14 @@ var buildVoiceIOProviderRouterTraceEvent = (options) => ({
|
|
|
41730
41730
|
metadata: options.metadata,
|
|
41731
41731
|
payload: {
|
|
41732
41732
|
...options.event,
|
|
41733
|
+
recoverable: options.event.status === "error" && options.event.fallbackProvider !== undefined,
|
|
41733
41734
|
...options.payload ?? {},
|
|
41734
41735
|
providerStatus: options.event.status
|
|
41735
41736
|
},
|
|
41736
41737
|
scenarioId: options.scenarioId,
|
|
41737
41738
|
sessionId: options.sessionId,
|
|
41738
41739
|
turnId: options.turnId,
|
|
41739
|
-
type: options.type ?? "session.error"
|
|
41740
|
+
type: options.type ?? (options.event.status === "error" ? "session.error" : "provider.decision")
|
|
41740
41741
|
});
|
|
41741
41742
|
var buildVoiceProviderRouterTraceEvent = (options) => ({
|
|
41742
41743
|
at: options.at ?? options.event.at,
|
|
@@ -41744,13 +41745,14 @@ var buildVoiceProviderRouterTraceEvent = (options) => ({
|
|
|
41744
41745
|
metadata: options.metadata,
|
|
41745
41746
|
payload: {
|
|
41746
41747
|
...options.event,
|
|
41748
|
+
recoverable: options.event.status === "error" && options.event.fallbackProvider !== undefined,
|
|
41747
41749
|
...options.payload ?? {},
|
|
41748
41750
|
providerStatus: options.event.status
|
|
41749
41751
|
},
|
|
41750
41752
|
scenarioId: options.scenarioId,
|
|
41751
41753
|
sessionId: options.sessionId,
|
|
41752
41754
|
turnId: options.turnId,
|
|
41753
|
-
type: options.type ?? "session.error"
|
|
41755
|
+
type: options.type ?? (options.event.status === "error" ? "session.error" : "provider.decision")
|
|
41754
41756
|
});
|
|
41755
41757
|
// src/core/workflowContract.ts
|
|
41756
41758
|
var getObject2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
@@ -46363,6 +46365,15 @@ class VoiceProviderTimeoutError extends Error {
|
|
|
46363
46365
|
this.timeoutMs = timeoutMs;
|
|
46364
46366
|
}
|
|
46365
46367
|
}
|
|
46368
|
+
|
|
46369
|
+
class VoiceProviderStreamInactivityError extends Error {
|
|
46370
|
+
inactivityMs;
|
|
46371
|
+
constructor(inactivityMs) {
|
|
46372
|
+
super(`SSE read inactivity timeout (${inactivityMs}ms with no chunk)`);
|
|
46373
|
+
this.name = "VoiceProviderStreamInactivityError";
|
|
46374
|
+
this.inactivityMs = inactivityMs;
|
|
46375
|
+
}
|
|
46376
|
+
}
|
|
46366
46377
|
var getMessageToolCalls = (message) => {
|
|
46367
46378
|
const toolCalls = message.metadata?.toolCalls;
|
|
46368
46379
|
return Array.isArray(toolCalls) ? toolCalls.filter((toolCall) => toolCall && typeof toolCall === "object" && typeof toolCall.name === "string") : [];
|
|
@@ -46609,7 +46620,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46609
46620
|
}
|
|
46610
46621
|
}
|
|
46611
46622
|
};
|
|
46612
|
-
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt) => {
|
|
46623
|
+
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt, routingStartedAt) => {
|
|
46613
46624
|
let output = await runProvider(provider, model, input);
|
|
46614
46625
|
let emptyAttempt = 0;
|
|
46615
46626
|
while (emptyAttempt < emptyCompletionRetries && isEmptyModelOutput(output)) {
|
|
@@ -46621,7 +46632,8 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46621
46632
|
emptyCompletion: true,
|
|
46622
46633
|
provider,
|
|
46623
46634
|
selectedProvider,
|
|
46624
|
-
status: "success"
|
|
46635
|
+
status: "success",
|
|
46636
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
46625
46637
|
}, input);
|
|
46626
46638
|
output = await runProvider(provider, model, input);
|
|
46627
46639
|
}
|
|
@@ -46633,6 +46645,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46633
46645
|
if (!selectedProvider || order.length === 0) {
|
|
46634
46646
|
throw new Error("Voice provider router has no available providers.");
|
|
46635
46647
|
}
|
|
46648
|
+
const routingStartedAt = Date.now();
|
|
46636
46649
|
let lastError;
|
|
46637
46650
|
for (const [index, provider] of order.entries()) {
|
|
46638
46651
|
const model = options.providers[provider];
|
|
@@ -46641,7 +46654,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46641
46654
|
}
|
|
46642
46655
|
const startedAt = Date.now();
|
|
46643
46656
|
try {
|
|
46644
|
-
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt);
|
|
46657
|
+
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt, routingStartedAt);
|
|
46645
46658
|
const providerHealth = recordProviderSuccess(provider);
|
|
46646
46659
|
await emit({
|
|
46647
46660
|
at: Date.now(),
|
|
@@ -46653,14 +46666,15 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46653
46666
|
providerHealth,
|
|
46654
46667
|
recovered: provider !== selectedProvider,
|
|
46655
46668
|
selectedProvider,
|
|
46656
|
-
status: provider === selectedProvider ? "success" : "fallback"
|
|
46669
|
+
status: provider === selectedProvider ? "success" : "fallback",
|
|
46670
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
46657
46671
|
}, input);
|
|
46658
46672
|
return output;
|
|
46659
46673
|
} catch (error) {
|
|
46660
46674
|
lastError = error;
|
|
46661
46675
|
const hasNextProvider = index < order.length - 1;
|
|
46662
46676
|
const isProviderError = options.isProviderError?.(error, provider) ?? true;
|
|
46663
|
-
const timedOut = options.isTimeoutError?.(error, provider) ?? error instanceof VoiceProviderTimeoutError;
|
|
46677
|
+
const timedOut = options.isTimeoutError?.(error, provider) ?? (error instanceof VoiceProviderTimeoutError || error instanceof VoiceProviderStreamInactivityError);
|
|
46664
46678
|
const rateLimited = options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error);
|
|
46665
46679
|
const shouldFallback = fallbackMode === "provider-error" ? isProviderError : fallbackMode === "rate-limit" ? isProviderError && rateLimited : false;
|
|
46666
46680
|
const providerHealth = recordProviderError(provider, isProviderError, rateLimited);
|
|
@@ -46679,7 +46693,10 @@ var createVoiceProviderRouter = (options) => {
|
|
|
46679
46693
|
suppressionRemainingMs: getSuppressionRemainingMs(provider),
|
|
46680
46694
|
suppressedUntil: providerHealth?.suppressedUntil,
|
|
46681
46695
|
status: "error",
|
|
46682
|
-
timedOut
|
|
46696
|
+
timedOut,
|
|
46697
|
+
timeoutKind: error instanceof VoiceProviderStreamInactivityError ? "stream-inactivity" : error instanceof VoiceProviderTimeoutError ? "latency-budget" : undefined,
|
|
46698
|
+
timeoutMs: error instanceof VoiceProviderStreamInactivityError ? error.inactivityMs : error instanceof VoiceProviderTimeoutError ? error.timeoutMs : undefined,
|
|
46699
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
46683
46700
|
}, input);
|
|
46684
46701
|
if (!hasNextProvider || !shouldFallback) {
|
|
46685
46702
|
throw error;
|
|
@@ -46882,7 +46899,7 @@ var readServerSentEvents = async (response, onEvent, abortOptions) => {
|
|
|
46882
46899
|
return new Promise((resolve2, reject) => {
|
|
46883
46900
|
inactivityTimer = setTimeout(() => {
|
|
46884
46901
|
reader.cancel().catch(() => {});
|
|
46885
|
-
reject(new
|
|
46902
|
+
reject(new VoiceProviderStreamInactivityError(inactivityMs));
|
|
46886
46903
|
}, inactivityMs);
|
|
46887
46904
|
if (signal) {
|
|
46888
46905
|
onAbort = () => {
|
|
@@ -47071,7 +47088,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
47071
47088
|
outputItems
|
|
47072
47089
|
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
47073
47090
|
signal: ac.signal,
|
|
47074
|
-
inactivityMs: 1e4
|
|
47091
|
+
inactivityMs: options.streamInactivityMs ?? 1e4
|
|
47075
47092
|
}));
|
|
47076
47093
|
stamp("openai.stream-done", {
|
|
47077
47094
|
textChars: assistantText?.length ?? 0,
|
package/dist/testing/index.js
CHANGED
|
@@ -5248,6 +5248,15 @@ class VoiceProviderTimeoutError extends Error {
|
|
|
5248
5248
|
this.timeoutMs = timeoutMs;
|
|
5249
5249
|
}
|
|
5250
5250
|
}
|
|
5251
|
+
|
|
5252
|
+
class VoiceProviderStreamInactivityError extends Error {
|
|
5253
|
+
inactivityMs;
|
|
5254
|
+
constructor(inactivityMs) {
|
|
5255
|
+
super(`SSE read inactivity timeout (${inactivityMs}ms with no chunk)`);
|
|
5256
|
+
this.name = "VoiceProviderStreamInactivityError";
|
|
5257
|
+
this.inactivityMs = inactivityMs;
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5251
5260
|
var getMessageToolCalls = (message) => {
|
|
5252
5261
|
const toolCalls = message.metadata?.toolCalls;
|
|
5253
5262
|
return Array.isArray(toolCalls) ? toolCalls.filter((toolCall) => toolCall && typeof toolCall === "object" && typeof toolCall.name === "string") : [];
|
|
@@ -5494,7 +5503,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5494
5503
|
}
|
|
5495
5504
|
}
|
|
5496
5505
|
};
|
|
5497
|
-
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt) => {
|
|
5506
|
+
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt, routingStartedAt) => {
|
|
5498
5507
|
let output = await runProvider(provider, model, input);
|
|
5499
5508
|
let emptyAttempt = 0;
|
|
5500
5509
|
while (emptyAttempt < emptyCompletionRetries && isEmptyModelOutput(output)) {
|
|
@@ -5506,7 +5515,8 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5506
5515
|
emptyCompletion: true,
|
|
5507
5516
|
provider,
|
|
5508
5517
|
selectedProvider,
|
|
5509
|
-
status: "success"
|
|
5518
|
+
status: "success",
|
|
5519
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
5510
5520
|
}, input);
|
|
5511
5521
|
output = await runProvider(provider, model, input);
|
|
5512
5522
|
}
|
|
@@ -5518,6 +5528,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5518
5528
|
if (!selectedProvider || order.length === 0) {
|
|
5519
5529
|
throw new Error("Voice provider router has no available providers.");
|
|
5520
5530
|
}
|
|
5531
|
+
const routingStartedAt = Date.now();
|
|
5521
5532
|
let lastError;
|
|
5522
5533
|
for (const [index, provider] of order.entries()) {
|
|
5523
5534
|
const model = options.providers[provider];
|
|
@@ -5526,7 +5537,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5526
5537
|
}
|
|
5527
5538
|
const startedAt = Date.now();
|
|
5528
5539
|
try {
|
|
5529
|
-
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt);
|
|
5540
|
+
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt, routingStartedAt);
|
|
5530
5541
|
const providerHealth = recordProviderSuccess(provider);
|
|
5531
5542
|
await emit({
|
|
5532
5543
|
at: Date.now(),
|
|
@@ -5538,14 +5549,15 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5538
5549
|
providerHealth,
|
|
5539
5550
|
recovered: provider !== selectedProvider,
|
|
5540
5551
|
selectedProvider,
|
|
5541
|
-
status: provider === selectedProvider ? "success" : "fallback"
|
|
5552
|
+
status: provider === selectedProvider ? "success" : "fallback",
|
|
5553
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
5542
5554
|
}, input);
|
|
5543
5555
|
return output;
|
|
5544
5556
|
} catch (error) {
|
|
5545
5557
|
lastError = error;
|
|
5546
5558
|
const hasNextProvider = index < order.length - 1;
|
|
5547
5559
|
const isProviderError = options.isProviderError?.(error, provider) ?? true;
|
|
5548
|
-
const timedOut = options.isTimeoutError?.(error, provider) ?? error instanceof VoiceProviderTimeoutError;
|
|
5560
|
+
const timedOut = options.isTimeoutError?.(error, provider) ?? (error instanceof VoiceProviderTimeoutError || error instanceof VoiceProviderStreamInactivityError);
|
|
5549
5561
|
const rateLimited = options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error);
|
|
5550
5562
|
const shouldFallback = fallbackMode === "provider-error" ? isProviderError : fallbackMode === "rate-limit" ? isProviderError && rateLimited : false;
|
|
5551
5563
|
const providerHealth = recordProviderError(provider, isProviderError, rateLimited);
|
|
@@ -5564,7 +5576,10 @@ var createVoiceProviderRouter = (options) => {
|
|
|
5564
5576
|
suppressionRemainingMs: getSuppressionRemainingMs(provider),
|
|
5565
5577
|
suppressedUntil: providerHealth?.suppressedUntil,
|
|
5566
5578
|
status: "error",
|
|
5567
|
-
timedOut
|
|
5579
|
+
timedOut,
|
|
5580
|
+
timeoutKind: error instanceof VoiceProviderStreamInactivityError ? "stream-inactivity" : error instanceof VoiceProviderTimeoutError ? "latency-budget" : undefined,
|
|
5581
|
+
timeoutMs: error instanceof VoiceProviderStreamInactivityError ? error.inactivityMs : error instanceof VoiceProviderTimeoutError ? error.timeoutMs : undefined,
|
|
5582
|
+
totalElapsedMs: Date.now() - routingStartedAt
|
|
5568
5583
|
}, input);
|
|
5569
5584
|
if (!hasNextProvider || !shouldFallback) {
|
|
5570
5585
|
throw error;
|
|
@@ -5767,7 +5782,7 @@ var readServerSentEvents = async (response, onEvent, abortOptions) => {
|
|
|
5767
5782
|
return new Promise((resolve2, reject) => {
|
|
5768
5783
|
inactivityTimer = setTimeout(() => {
|
|
5769
5784
|
reader.cancel().catch(() => {});
|
|
5770
|
-
reject(new
|
|
5785
|
+
reject(new VoiceProviderStreamInactivityError(inactivityMs));
|
|
5771
5786
|
}, inactivityMs);
|
|
5772
5787
|
if (signal) {
|
|
5773
5788
|
onAbort = () => {
|
|
@@ -5956,7 +5971,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
5956
5971
|
outputItems
|
|
5957
5972
|
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
5958
5973
|
signal: ac.signal,
|
|
5959
|
-
inactivityMs: 1e4
|
|
5974
|
+
inactivityMs: options.streamInactivityMs ?? 1e4
|
|
5960
5975
|
}));
|
|
5961
5976
|
stamp("openai.stream-done", {
|
|
5962
5977
|
textChars: assistantText?.length ?? 0,
|