@absolutejs/voice 0.0.22-beta.616 → 0.0.22-beta.618
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.
|
@@ -46,6 +46,10 @@ export type VoiceProviderRouterEvent<TProvider extends string = string> = {
|
|
|
46
46
|
at: number;
|
|
47
47
|
attempt: number;
|
|
48
48
|
elapsedMs: number;
|
|
49
|
+
/** Set when this event marks a provider returning an empty completion (no text
|
|
50
|
+
* and no tool call) that triggered a same-provider retry — so consumers can
|
|
51
|
+
* track how often the model glitches empty, and whether the retry recovered. */
|
|
52
|
+
emptyCompletion?: boolean;
|
|
49
53
|
error?: string;
|
|
50
54
|
fallbackProvider?: TProvider;
|
|
51
55
|
latencyBudgetMs?: number;
|
|
@@ -139,6 +143,14 @@ export type VoiceProviderRouterOptions<TContext = unknown, TSession extends Voic
|
|
|
139
143
|
allowProviders?: readonly TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
|
|
140
144
|
fallback?: TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
|
|
141
145
|
fallbackMode?: VoiceProviderRouterFallbackMode;
|
|
146
|
+
/** Retry the SAME provider this many times when it returns a non-error but
|
|
147
|
+
* EMPTY completion (no assistant text AND no tool call) before accepting it.
|
|
148
|
+
* An empty completion is a transient model glitch (observed on OpenAI
|
|
149
|
+
* /responses returning a 200 with zero output items mid-conversation), NOT a
|
|
150
|
+
* provider error — so it bypasses fallbackMode and never suppresses provider
|
|
151
|
+
* health. Without this a one-off empty surfaces to the app as a dead turn
|
|
152
|
+
* (the caller hears a re-engage / "go on, I'm listening" loop). Default 1. */
|
|
153
|
+
emptyCompletionRetries?: number;
|
|
142
154
|
isProviderError?: (error: unknown, provider: TProvider) => boolean;
|
|
143
155
|
isRateLimitError?: (error: unknown, provider: TProvider) => boolean;
|
|
144
156
|
isTimeoutError?: (error: unknown, provider: TProvider) => boolean;
|
package/dist/index.js
CHANGED
|
@@ -39579,6 +39579,7 @@ var voice = (config) => {
|
|
|
39579
39579
|
const monitorBindings = new Map;
|
|
39580
39580
|
const runtime = {
|
|
39581
39581
|
activeSessions: new Map,
|
|
39582
|
+
pendingSessions: new Map,
|
|
39582
39583
|
logger: resolveLogger(config.logger),
|
|
39583
39584
|
profileSwitchGuardAutoSwitchCounts: new Map,
|
|
39584
39585
|
profileSwitchGuardedSessions: new Set,
|
|
@@ -39738,6 +39739,31 @@ var voice = (config) => {
|
|
|
39738
39739
|
turnDetection: sessionOptions.turnDetection
|
|
39739
39740
|
});
|
|
39740
39741
|
};
|
|
39742
|
+
const createAndConnectSession = async (ws, sessionId, scenarioId) => {
|
|
39743
|
+
const session = await createManagedSession(ws, sessionId, scenarioId);
|
|
39744
|
+
const typedSession = session;
|
|
39745
|
+
runtime.activeSessions.set(sessionId, typedSession);
|
|
39746
|
+
registerMonitorSession(sessionId, typedSession);
|
|
39747
|
+
await session.connect(buildSocketAdapter(ws, sessionId));
|
|
39748
|
+
return typedSession;
|
|
39749
|
+
};
|
|
39750
|
+
const ensureManagedSession = async (ws, sessionId, scenarioId) => {
|
|
39751
|
+
const active = runtime.activeSessions.get(sessionId);
|
|
39752
|
+
if (active) {
|
|
39753
|
+
return active;
|
|
39754
|
+
}
|
|
39755
|
+
const inFlight = runtime.pendingSessions.get(sessionId);
|
|
39756
|
+
if (inFlight) {
|
|
39757
|
+
return inFlight;
|
|
39758
|
+
}
|
|
39759
|
+
const creation = createAndConnectSession(ws, sessionId, scenarioId);
|
|
39760
|
+
runtime.pendingSessions.set(sessionId, creation);
|
|
39761
|
+
try {
|
|
39762
|
+
return await creation;
|
|
39763
|
+
} finally {
|
|
39764
|
+
runtime.pendingSessions.delete(sessionId);
|
|
39765
|
+
}
|
|
39766
|
+
};
|
|
39741
39767
|
const mountSurface = (app, value, factory) => {
|
|
39742
39768
|
if (value === undefined || value === false) {
|
|
39743
39769
|
return app;
|
|
@@ -39950,13 +39976,7 @@ var voice = (config) => {
|
|
|
39950
39976
|
if (!audio) {
|
|
39951
39977
|
return;
|
|
39952
39978
|
}
|
|
39953
|
-
const session = current ?? await
|
|
39954
|
-
if (!current) {
|
|
39955
|
-
const typedSession = session;
|
|
39956
|
-
runtime.activeSessions.set(sessionState.sessionId, typedSession);
|
|
39957
|
-
registerMonitorSession(sessionState.sessionId, typedSession);
|
|
39958
|
-
await session.connect(buildSocketAdapter(ws, sessionState.sessionId));
|
|
39959
|
-
}
|
|
39979
|
+
const session = current ?? await ensureManagedSession(ws, sessionState.sessionId, sessionState.scenarioId ?? undefined);
|
|
39960
39980
|
await session.receiveAudio(audio);
|
|
39961
39981
|
},
|
|
39962
39982
|
open: async (ws) => {
|
|
@@ -39967,11 +39987,7 @@ var voice = (config) => {
|
|
|
39967
39987
|
runtime.activeSessions.delete(sessionState.sessionId);
|
|
39968
39988
|
deregisterMonitorSession(sessionState.sessionId, "superseded");
|
|
39969
39989
|
}
|
|
39970
|
-
|
|
39971
|
-
const typedSession = session;
|
|
39972
|
-
runtime.activeSessions.set(sessionState.sessionId, typedSession);
|
|
39973
|
-
registerMonitorSession(sessionState.sessionId, typedSession);
|
|
39974
|
-
await session.connect(buildSocketAdapter(ws, sessionState.sessionId));
|
|
39990
|
+
await ensureManagedSession(ws, sessionState.sessionId, sessionState.scenarioId ?? undefined);
|
|
39975
39991
|
}
|
|
39976
39992
|
}).use(htmxRoutes()).use(surfaceRoutes());
|
|
39977
39993
|
};
|
|
@@ -45454,6 +45470,7 @@ var createJSONVoiceAssistantModel = (options) => ({
|
|
|
45454
45470
|
return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
|
|
45455
45471
|
}
|
|
45456
45472
|
});
|
|
45473
|
+
var isEmptyModelOutput = (output) => !output.assistantText?.trim() && !output.toolCalls?.length;
|
|
45457
45474
|
var createVoiceProviderRouter = (options) => {
|
|
45458
45475
|
const providerIds = Object.keys(options.providers);
|
|
45459
45476
|
const firstProvider = providerIds[0];
|
|
@@ -45461,6 +45478,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
45461
45478
|
const policy = resolveVoiceProviderRoutingPolicy(options.policy) ?? resolveVoiceProviderRoutingPolicy(orchestrationSurface?.policy);
|
|
45462
45479
|
const strategy = policy?.strategy ?? "prefer-selected";
|
|
45463
45480
|
const fallbackMode = policy?.fallbackMode ?? options.fallbackMode ?? orchestrationSurface?.fallbackMode ?? "provider-error";
|
|
45481
|
+
const emptyCompletionRetries = Math.max(0, options.emptyCompletionRetries ?? 1);
|
|
45464
45482
|
const providerProfiles = {
|
|
45465
45483
|
...orchestrationSurface?.providerProfiles ?? {},
|
|
45466
45484
|
...options.providerProfiles ?? {}
|
|
@@ -45635,6 +45653,24 @@ var createVoiceProviderRouter = (options) => {
|
|
|
45635
45653
|
}
|
|
45636
45654
|
}
|
|
45637
45655
|
};
|
|
45656
|
+
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt) => {
|
|
45657
|
+
let output = await runProvider(provider, model, input);
|
|
45658
|
+
let emptyAttempt = 0;
|
|
45659
|
+
while (emptyAttempt < emptyCompletionRetries && isEmptyModelOutput(output)) {
|
|
45660
|
+
emptyAttempt += 1;
|
|
45661
|
+
await emit({
|
|
45662
|
+
at: Date.now(),
|
|
45663
|
+
attempt: index + 1,
|
|
45664
|
+
elapsedMs: Date.now() - startedAt,
|
|
45665
|
+
emptyCompletion: true,
|
|
45666
|
+
provider,
|
|
45667
|
+
selectedProvider,
|
|
45668
|
+
status: "success"
|
|
45669
|
+
}, input);
|
|
45670
|
+
output = await runProvider(provider, model, input);
|
|
45671
|
+
}
|
|
45672
|
+
return output;
|
|
45673
|
+
};
|
|
45638
45674
|
return {
|
|
45639
45675
|
generate: async (input) => {
|
|
45640
45676
|
const { order, selectedProvider } = await resolveOrder(input);
|
|
@@ -45649,7 +45685,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
45649
45685
|
}
|
|
45650
45686
|
const startedAt = Date.now();
|
|
45651
45687
|
try {
|
|
45652
|
-
const output = await
|
|
45688
|
+
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt);
|
|
45653
45689
|
const providerHealth = recordProviderSuccess(provider);
|
|
45654
45690
|
await emit({
|
|
45655
45691
|
at: Date.now(),
|
|
@@ -45934,10 +45970,21 @@ var finalizeToolCalls = (calls) => [...calls.values()].filter((call) => call.nam
|
|
|
45934
45970
|
var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) => {
|
|
45935
45971
|
let assistantText = "";
|
|
45936
45972
|
let usage;
|
|
45973
|
+
let status;
|
|
45974
|
+
let incompleteReason;
|
|
45975
|
+
let refusal = "";
|
|
45976
|
+
let failureMessage;
|
|
45977
|
+
let outputItems = 0;
|
|
45937
45978
|
const calls = new Map;
|
|
45938
45979
|
await readServerSentEvents(response, (event) => {
|
|
45939
45980
|
const type = typeof event.type === "string" ? event.type : "";
|
|
45940
45981
|
const item = event.item;
|
|
45982
|
+
if (type === "response.output_item.added") {
|
|
45983
|
+
outputItems += 1;
|
|
45984
|
+
}
|
|
45985
|
+
if ((type === "response.refusal.delta" || type === "response.refusal.done") && typeof event.delta === "string") {
|
|
45986
|
+
refusal += event.delta;
|
|
45987
|
+
}
|
|
45941
45988
|
if (type === "response.output_text.delta" && typeof event.delta === "string") {
|
|
45942
45989
|
assistantText += event.delta;
|
|
45943
45990
|
onTextDelta?.(event.delta);
|
|
@@ -45955,14 +46002,34 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
45955
46002
|
const entry = calls.get(String(item.id ?? item.call_id ?? ""));
|
|
45956
46003
|
if (entry)
|
|
45957
46004
|
entry.args = item.arguments;
|
|
45958
|
-
} else if (type === "response.completed") {
|
|
46005
|
+
} else if (type === "response.completed" || type === "response.incomplete" || type === "response.failed") {
|
|
45959
46006
|
const completed = event.response;
|
|
45960
46007
|
if (completed?.usage && typeof completed.usage === "object") {
|
|
45961
46008
|
usage = completed.usage;
|
|
45962
46009
|
}
|
|
46010
|
+
if (typeof completed?.status === "string") {
|
|
46011
|
+
status = completed.status;
|
|
46012
|
+
}
|
|
46013
|
+
const incomplete = completed?.incomplete_details;
|
|
46014
|
+
if (typeof incomplete?.reason === "string") {
|
|
46015
|
+
incompleteReason = incomplete.reason;
|
|
46016
|
+
}
|
|
46017
|
+
const failure = completed?.error;
|
|
46018
|
+
if (typeof failure?.message === "string") {
|
|
46019
|
+
failureMessage = failure.message;
|
|
46020
|
+
}
|
|
45963
46021
|
}
|
|
45964
46022
|
}, abortOptions);
|
|
45965
|
-
return {
|
|
46023
|
+
return {
|
|
46024
|
+
assistantText,
|
|
46025
|
+
failureMessage,
|
|
46026
|
+
incompleteReason,
|
|
46027
|
+
outputItems,
|
|
46028
|
+
refusal: refusal || undefined,
|
|
46029
|
+
status,
|
|
46030
|
+
toolCalls: finalizeToolCalls(calls),
|
|
46031
|
+
usage
|
|
46032
|
+
};
|
|
45966
46033
|
};
|
|
45967
46034
|
var createOpenAIVoiceAssistantModel = (options) => {
|
|
45968
46035
|
const fetchImpl = hardenFetch(options.fetch);
|
|
@@ -46023,6 +46090,11 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
46023
46090
|
let assistantText;
|
|
46024
46091
|
let toolCalls;
|
|
46025
46092
|
let usage;
|
|
46093
|
+
let status;
|
|
46094
|
+
let incompleteReason;
|
|
46095
|
+
let refusal;
|
|
46096
|
+
let failureMessage;
|
|
46097
|
+
let outputItems = 0;
|
|
46026
46098
|
let firstDeltaSeen = false;
|
|
46027
46099
|
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
46028
46100
|
if (!firstDeltaSeen) {
|
|
@@ -46032,7 +46104,16 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
46032
46104
|
input.onTextDelta?.(delta);
|
|
46033
46105
|
} : undefined;
|
|
46034
46106
|
try {
|
|
46035
|
-
({
|
|
46107
|
+
({
|
|
46108
|
+
assistantText,
|
|
46109
|
+
toolCalls,
|
|
46110
|
+
usage,
|
|
46111
|
+
status,
|
|
46112
|
+
incompleteReason,
|
|
46113
|
+
refusal,
|
|
46114
|
+
failureMessage,
|
|
46115
|
+
outputItems
|
|
46116
|
+
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
46036
46117
|
signal: ac.signal,
|
|
46037
46118
|
inactivityMs: 1e4
|
|
46038
46119
|
}));
|
|
@@ -46040,6 +46121,15 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
46040
46121
|
textChars: assistantText?.length ?? 0,
|
|
46041
46122
|
toolCalls: toolCalls.length
|
|
46042
46123
|
});
|
|
46124
|
+
if (!assistantText?.trim() && !toolCalls.length) {
|
|
46125
|
+
stamp("openai.empty-completion", {
|
|
46126
|
+
failureMessage: failureMessage ?? "none",
|
|
46127
|
+
incompleteReason: incompleteReason ?? "none",
|
|
46128
|
+
outputItems,
|
|
46129
|
+
refusal: refusal ? refusal.slice(0, 200) : "none",
|
|
46130
|
+
status: status ?? "unknown"
|
|
46131
|
+
});
|
|
46132
|
+
}
|
|
46043
46133
|
} finally {
|
|
46044
46134
|
clearTimeout(timer);
|
|
46045
46135
|
}
|
package/dist/testing/index.js
CHANGED
|
@@ -4477,6 +4477,7 @@ var createJSONVoiceAssistantModel = (options) => ({
|
|
|
4477
4477
|
return options.mapOutput?.(output) ?? normalizeRouteOutput(output);
|
|
4478
4478
|
}
|
|
4479
4479
|
});
|
|
4480
|
+
var isEmptyModelOutput = (output) => !output.assistantText?.trim() && !output.toolCalls?.length;
|
|
4480
4481
|
var createVoiceProviderRouter = (options) => {
|
|
4481
4482
|
const providerIds = Object.keys(options.providers);
|
|
4482
4483
|
const firstProvider = providerIds[0];
|
|
@@ -4484,6 +4485,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
4484
4485
|
const policy = resolveVoiceProviderRoutingPolicy(options.policy) ?? resolveVoiceProviderRoutingPolicy(orchestrationSurface?.policy);
|
|
4485
4486
|
const strategy = policy?.strategy ?? "prefer-selected";
|
|
4486
4487
|
const fallbackMode = policy?.fallbackMode ?? options.fallbackMode ?? orchestrationSurface?.fallbackMode ?? "provider-error";
|
|
4488
|
+
const emptyCompletionRetries = Math.max(0, options.emptyCompletionRetries ?? 1);
|
|
4487
4489
|
const providerProfiles = {
|
|
4488
4490
|
...orchestrationSurface?.providerProfiles ?? {},
|
|
4489
4491
|
...options.providerProfiles ?? {}
|
|
@@ -4658,6 +4660,24 @@ var createVoiceProviderRouter = (options) => {
|
|
|
4658
4660
|
}
|
|
4659
4661
|
}
|
|
4660
4662
|
};
|
|
4663
|
+
const runProviderWithEmptyRetry = async (provider, model, input, index, selectedProvider, startedAt) => {
|
|
4664
|
+
let output = await runProvider(provider, model, input);
|
|
4665
|
+
let emptyAttempt = 0;
|
|
4666
|
+
while (emptyAttempt < emptyCompletionRetries && isEmptyModelOutput(output)) {
|
|
4667
|
+
emptyAttempt += 1;
|
|
4668
|
+
await emit({
|
|
4669
|
+
at: Date.now(),
|
|
4670
|
+
attempt: index + 1,
|
|
4671
|
+
elapsedMs: Date.now() - startedAt,
|
|
4672
|
+
emptyCompletion: true,
|
|
4673
|
+
provider,
|
|
4674
|
+
selectedProvider,
|
|
4675
|
+
status: "success"
|
|
4676
|
+
}, input);
|
|
4677
|
+
output = await runProvider(provider, model, input);
|
|
4678
|
+
}
|
|
4679
|
+
return output;
|
|
4680
|
+
};
|
|
4661
4681
|
return {
|
|
4662
4682
|
generate: async (input) => {
|
|
4663
4683
|
const { order, selectedProvider } = await resolveOrder(input);
|
|
@@ -4672,7 +4692,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
4672
4692
|
}
|
|
4673
4693
|
const startedAt = Date.now();
|
|
4674
4694
|
try {
|
|
4675
|
-
const output = await
|
|
4695
|
+
const output = await runProviderWithEmptyRetry(provider, model, input, index, selectedProvider, startedAt);
|
|
4676
4696
|
const providerHealth = recordProviderSuccess(provider);
|
|
4677
4697
|
await emit({
|
|
4678
4698
|
at: Date.now(),
|
|
@@ -4957,10 +4977,21 @@ var finalizeToolCalls = (calls) => [...calls.values()].filter((call) => call.nam
|
|
|
4957
4977
|
var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) => {
|
|
4958
4978
|
let assistantText = "";
|
|
4959
4979
|
let usage;
|
|
4980
|
+
let status;
|
|
4981
|
+
let incompleteReason;
|
|
4982
|
+
let refusal = "";
|
|
4983
|
+
let failureMessage;
|
|
4984
|
+
let outputItems = 0;
|
|
4960
4985
|
const calls = new Map;
|
|
4961
4986
|
await readServerSentEvents(response, (event) => {
|
|
4962
4987
|
const type = typeof event.type === "string" ? event.type : "";
|
|
4963
4988
|
const item = event.item;
|
|
4989
|
+
if (type === "response.output_item.added") {
|
|
4990
|
+
outputItems += 1;
|
|
4991
|
+
}
|
|
4992
|
+
if ((type === "response.refusal.delta" || type === "response.refusal.done") && typeof event.delta === "string") {
|
|
4993
|
+
refusal += event.delta;
|
|
4994
|
+
}
|
|
4964
4995
|
if (type === "response.output_text.delta" && typeof event.delta === "string") {
|
|
4965
4996
|
assistantText += event.delta;
|
|
4966
4997
|
onTextDelta?.(event.delta);
|
|
@@ -4978,14 +5009,34 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
4978
5009
|
const entry = calls.get(String(item.id ?? item.call_id ?? ""));
|
|
4979
5010
|
if (entry)
|
|
4980
5011
|
entry.args = item.arguments;
|
|
4981
|
-
} else if (type === "response.completed") {
|
|
5012
|
+
} else if (type === "response.completed" || type === "response.incomplete" || type === "response.failed") {
|
|
4982
5013
|
const completed = event.response;
|
|
4983
5014
|
if (completed?.usage && typeof completed.usage === "object") {
|
|
4984
5015
|
usage = completed.usage;
|
|
4985
5016
|
}
|
|
5017
|
+
if (typeof completed?.status === "string") {
|
|
5018
|
+
status = completed.status;
|
|
5019
|
+
}
|
|
5020
|
+
const incomplete = completed?.incomplete_details;
|
|
5021
|
+
if (typeof incomplete?.reason === "string") {
|
|
5022
|
+
incompleteReason = incomplete.reason;
|
|
5023
|
+
}
|
|
5024
|
+
const failure = completed?.error;
|
|
5025
|
+
if (typeof failure?.message === "string") {
|
|
5026
|
+
failureMessage = failure.message;
|
|
5027
|
+
}
|
|
4986
5028
|
}
|
|
4987
5029
|
}, abortOptions);
|
|
4988
|
-
return {
|
|
5030
|
+
return {
|
|
5031
|
+
assistantText,
|
|
5032
|
+
failureMessage,
|
|
5033
|
+
incompleteReason,
|
|
5034
|
+
outputItems,
|
|
5035
|
+
refusal: refusal || undefined,
|
|
5036
|
+
status,
|
|
5037
|
+
toolCalls: finalizeToolCalls(calls),
|
|
5038
|
+
usage
|
|
5039
|
+
};
|
|
4989
5040
|
};
|
|
4990
5041
|
var createOpenAIVoiceAssistantModel = (options) => {
|
|
4991
5042
|
const fetchImpl = hardenFetch(options.fetch);
|
|
@@ -5046,6 +5097,11 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
5046
5097
|
let assistantText;
|
|
5047
5098
|
let toolCalls;
|
|
5048
5099
|
let usage;
|
|
5100
|
+
let status;
|
|
5101
|
+
let incompleteReason;
|
|
5102
|
+
let refusal;
|
|
5103
|
+
let failureMessage;
|
|
5104
|
+
let outputItems = 0;
|
|
5049
5105
|
let firstDeltaSeen = false;
|
|
5050
5106
|
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
5051
5107
|
if (!firstDeltaSeen) {
|
|
@@ -5055,7 +5111,16 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
5055
5111
|
input.onTextDelta?.(delta);
|
|
5056
5112
|
} : undefined;
|
|
5057
5113
|
try {
|
|
5058
|
-
({
|
|
5114
|
+
({
|
|
5115
|
+
assistantText,
|
|
5116
|
+
toolCalls,
|
|
5117
|
+
usage,
|
|
5118
|
+
status,
|
|
5119
|
+
incompleteReason,
|
|
5120
|
+
refusal,
|
|
5121
|
+
failureMessage,
|
|
5122
|
+
outputItems
|
|
5123
|
+
} = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
5059
5124
|
signal: ac.signal,
|
|
5060
5125
|
inactivityMs: 1e4
|
|
5061
5126
|
}));
|
|
@@ -5063,6 +5128,15 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
5063
5128
|
textChars: assistantText?.length ?? 0,
|
|
5064
5129
|
toolCalls: toolCalls.length
|
|
5065
5130
|
});
|
|
5131
|
+
if (!assistantText?.trim() && !toolCalls.length) {
|
|
5132
|
+
stamp("openai.empty-completion", {
|
|
5133
|
+
failureMessage: failureMessage ?? "none",
|
|
5134
|
+
incompleteReason: incompleteReason ?? "none",
|
|
5135
|
+
outputItems,
|
|
5136
|
+
refusal: refusal ? refusal.slice(0, 200) : "none",
|
|
5137
|
+
status: status ?? "unknown"
|
|
5138
|
+
});
|
|
5139
|
+
}
|
|
5066
5140
|
} finally {
|
|
5067
5141
|
clearTimeout(timer);
|
|
5068
5142
|
}
|
|
@@ -79,10 +79,10 @@ export declare const VoiceProviderSimulationControls: import("vue").DefineCompon
|
|
|
79
79
|
}>> & Readonly<{}>, {
|
|
80
80
|
kind: string;
|
|
81
81
|
title: string;
|
|
82
|
+
failureMessage: string;
|
|
82
83
|
fallbackRequiredProvider: string;
|
|
83
84
|
fallbackRequiredMessage: string;
|
|
84
85
|
failureProviders: readonly string[] | undefined;
|
|
85
|
-
failureMessage: string;
|
|
86
86
|
pathPrefix: string;
|
|
87
87
|
class: string;
|
|
88
88
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/voice",
|
|
3
|
-
"version": "0.0.22-beta.
|
|
3
|
+
"version": "0.0.22-beta.618",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/absolutejs/voice.git"
|
|
8
8
|
},
|
|
9
|
+
"homepage": "https://github.com/absolutejs/voice",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/absolutejs/voice/issues"
|
|
12
|
+
},
|
|
9
13
|
"files": [
|
|
10
14
|
"dist",
|
|
11
15
|
"README.md"
|