@absolutejs/voice 0.0.22-beta.554 → 0.0.22-beta.555
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 +8 -0
- package/dist/index.js +83 -39
- package/dist/testing/index.js +83 -39
- package/package.json +1 -1
|
@@ -13,6 +13,14 @@ export type OpenAIVoiceAssistantModelOptions = {
|
|
|
13
13
|
model?: string;
|
|
14
14
|
onUsage?: (usage: Record<string, unknown>) => Promise<void> | void;
|
|
15
15
|
temperature?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Hard cap on a single /responses stream. Aborts via AbortController if
|
|
18
|
+
* the stream doesn't complete in time. Default 60s — generous for typical
|
|
19
|
+
* gpt-4.1-mini conversational turns (1-3s) but catches infinite hangs
|
|
20
|
+
* (server-side stream stalls observed on rare complex inputs). On timeout
|
|
21
|
+
* the agent loop falls through to default-silent-turn-ack.
|
|
22
|
+
*/
|
|
23
|
+
timeoutMs?: number;
|
|
16
24
|
};
|
|
17
25
|
export type AnthropicVoiceAssistantModelOptions = {
|
|
18
26
|
apiKey: string;
|
package/dist/index.js
CHANGED
|
@@ -5303,17 +5303,41 @@ var createVoiceSession = (options) => {
|
|
|
5303
5303
|
});
|
|
5304
5304
|
}, fillerDelayMs);
|
|
5305
5305
|
}
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5306
|
+
let committedOutput;
|
|
5307
|
+
const onTurnStartedAt = Date.now();
|
|
5308
|
+
try {
|
|
5309
|
+
committedOutput = await options.route.onTurn({
|
|
5310
|
+
api,
|
|
5311
|
+
context: options.context,
|
|
5312
|
+
liveOps: liveOpsControl ? {
|
|
5313
|
+
control: liveOpsControl,
|
|
5314
|
+
injectedInstruction
|
|
5315
|
+
} : undefined,
|
|
5316
|
+
onTextDelta: ttsStreamer?.push,
|
|
5317
|
+
session,
|
|
5318
|
+
turn
|
|
5319
|
+
});
|
|
5320
|
+
} catch (error) {
|
|
5321
|
+
const message = toError(error).message;
|
|
5322
|
+
logger.warn("voice route.onTurn failed", {
|
|
5323
|
+
elapsedMs: Date.now() - onTurnStartedAt,
|
|
5324
|
+
error: message,
|
|
5325
|
+
sessionId: options.id,
|
|
5326
|
+
turnId: turn.id
|
|
5327
|
+
});
|
|
5328
|
+
console.error(`[voice] onTurn failed for session ${options.id} turn ${turn.id} after ${Date.now() - onTurnStartedAt}ms:`, message);
|
|
5329
|
+
await appendTrace({
|
|
5330
|
+
payload: {
|
|
5331
|
+
elapsedMs: Date.now() - onTurnStartedAt,
|
|
5332
|
+
error: message,
|
|
5333
|
+
stage: "route.onTurn"
|
|
5334
|
+
},
|
|
5335
|
+
session,
|
|
5336
|
+
turnId: turn.id,
|
|
5337
|
+
type: "session.error"
|
|
5338
|
+
});
|
|
5339
|
+
committedOutput = undefined;
|
|
5340
|
+
}
|
|
5317
5341
|
const output = {
|
|
5318
5342
|
assistantText: committedOutput?.assistantText,
|
|
5319
5343
|
citations: committedOutput?.citations,
|
|
@@ -45106,41 +45130,61 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
45106
45130
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
45107
45131
|
const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
|
|
45108
45132
|
const model = options.model ?? "gpt-4.1-mini";
|
|
45133
|
+
const timeoutMs = options.timeoutMs ?? 60000;
|
|
45109
45134
|
return {
|
|
45110
45135
|
generate: async (input) => {
|
|
45111
|
-
const
|
|
45112
|
-
|
|
45113
|
-
|
|
45114
|
-
|
|
45136
|
+
const ac = new AbortController;
|
|
45137
|
+
const timer = setTimeout(() => {
|
|
45138
|
+
ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
|
|
45139
|
+
}, timeoutMs);
|
|
45140
|
+
let response;
|
|
45141
|
+
try {
|
|
45142
|
+
response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
|
|
45143
|
+
body: JSON.stringify({
|
|
45144
|
+
input: messagesToOpenAIInput(input.messages),
|
|
45145
|
+
instructions: [input.system, VOICE_SYSTEM_INSTRUCTIONS].filter(Boolean).join(`
|
|
45115
45146
|
|
|
45116
45147
|
`),
|
|
45117
|
-
|
|
45118
|
-
|
|
45119
|
-
|
|
45120
|
-
|
|
45121
|
-
|
|
45122
|
-
|
|
45123
|
-
|
|
45124
|
-
|
|
45125
|
-
|
|
45126
|
-
|
|
45127
|
-
|
|
45128
|
-
|
|
45129
|
-
|
|
45130
|
-
|
|
45131
|
-
|
|
45132
|
-
|
|
45133
|
-
|
|
45134
|
-
|
|
45135
|
-
|
|
45136
|
-
|
|
45137
|
-
|
|
45138
|
-
|
|
45139
|
-
|
|
45148
|
+
max_output_tokens: options.maxOutputTokens,
|
|
45149
|
+
model,
|
|
45150
|
+
stream: true,
|
|
45151
|
+
temperature: options.temperature,
|
|
45152
|
+
tool_choice: input.tools.length ? "auto" : "none",
|
|
45153
|
+
tools: input.tools.map((tool) => ({
|
|
45154
|
+
description: tool.description,
|
|
45155
|
+
name: tool.name,
|
|
45156
|
+
parameters: tool.parameters ?? {
|
|
45157
|
+
additionalProperties: true,
|
|
45158
|
+
type: "object"
|
|
45159
|
+
},
|
|
45160
|
+
strict: false,
|
|
45161
|
+
type: "function"
|
|
45162
|
+
}))
|
|
45163
|
+
}),
|
|
45164
|
+
headers: {
|
|
45165
|
+
accept: "text/event-stream",
|
|
45166
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
45167
|
+
"content-type": "application/json"
|
|
45168
|
+
},
|
|
45169
|
+
method: "POST",
|
|
45170
|
+
signal: ac.signal
|
|
45171
|
+
});
|
|
45172
|
+
} catch (error) {
|
|
45173
|
+
clearTimeout(timer);
|
|
45174
|
+
throw error;
|
|
45175
|
+
}
|
|
45140
45176
|
if (!response.ok) {
|
|
45177
|
+
clearTimeout(timer);
|
|
45141
45178
|
throw createHTTPError("OpenAI", response);
|
|
45142
45179
|
}
|
|
45143
|
-
|
|
45180
|
+
let assistantText;
|
|
45181
|
+
let toolCalls;
|
|
45182
|
+
let usage;
|
|
45183
|
+
try {
|
|
45184
|
+
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, input.onTextDelta));
|
|
45185
|
+
} finally {
|
|
45186
|
+
clearTimeout(timer);
|
|
45187
|
+
}
|
|
45144
45188
|
if (usage) {
|
|
45145
45189
|
await options.onUsage?.(usage);
|
|
45146
45190
|
}
|
package/dist/testing/index.js
CHANGED
|
@@ -4600,41 +4600,61 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
4600
4600
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
4601
4601
|
const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
|
|
4602
4602
|
const model = options.model ?? "gpt-4.1-mini";
|
|
4603
|
+
const timeoutMs = options.timeoutMs ?? 60000;
|
|
4603
4604
|
return {
|
|
4604
4605
|
generate: async (input) => {
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4606
|
+
const ac = new AbortController;
|
|
4607
|
+
const timer = setTimeout(() => {
|
|
4608
|
+
ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
|
|
4609
|
+
}, timeoutMs);
|
|
4610
|
+
let response;
|
|
4611
|
+
try {
|
|
4612
|
+
response = await fetchImpl(`${baseUrl.replace(/\/$/, "")}/responses`, {
|
|
4613
|
+
body: JSON.stringify({
|
|
4614
|
+
input: messagesToOpenAIInput(input.messages),
|
|
4615
|
+
instructions: [input.system, VOICE_SYSTEM_INSTRUCTIONS].filter(Boolean).join(`
|
|
4609
4616
|
|
|
4610
4617
|
`),
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4618
|
+
max_output_tokens: options.maxOutputTokens,
|
|
4619
|
+
model,
|
|
4620
|
+
stream: true,
|
|
4621
|
+
temperature: options.temperature,
|
|
4622
|
+
tool_choice: input.tools.length ? "auto" : "none",
|
|
4623
|
+
tools: input.tools.map((tool) => ({
|
|
4624
|
+
description: tool.description,
|
|
4625
|
+
name: tool.name,
|
|
4626
|
+
parameters: tool.parameters ?? {
|
|
4627
|
+
additionalProperties: true,
|
|
4628
|
+
type: "object"
|
|
4629
|
+
},
|
|
4630
|
+
strict: false,
|
|
4631
|
+
type: "function"
|
|
4632
|
+
}))
|
|
4633
|
+
}),
|
|
4634
|
+
headers: {
|
|
4635
|
+
accept: "text/event-stream",
|
|
4636
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
4637
|
+
"content-type": "application/json"
|
|
4638
|
+
},
|
|
4639
|
+
method: "POST",
|
|
4640
|
+
signal: ac.signal
|
|
4641
|
+
});
|
|
4642
|
+
} catch (error) {
|
|
4643
|
+
clearTimeout(timer);
|
|
4644
|
+
throw error;
|
|
4645
|
+
}
|
|
4634
4646
|
if (!response.ok) {
|
|
4647
|
+
clearTimeout(timer);
|
|
4635
4648
|
throw createHTTPError("OpenAI", response);
|
|
4636
4649
|
}
|
|
4637
|
-
|
|
4650
|
+
let assistantText;
|
|
4651
|
+
let toolCalls;
|
|
4652
|
+
let usage;
|
|
4653
|
+
try {
|
|
4654
|
+
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, input.onTextDelta));
|
|
4655
|
+
} finally {
|
|
4656
|
+
clearTimeout(timer);
|
|
4657
|
+
}
|
|
4638
4658
|
if (usage) {
|
|
4639
4659
|
await options.onUsage?.(usage);
|
|
4640
4660
|
}
|
|
@@ -7120,17 +7140,41 @@ var createVoiceSession = (options) => {
|
|
|
7120
7140
|
});
|
|
7121
7141
|
}, fillerDelayMs);
|
|
7122
7142
|
}
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7143
|
+
let committedOutput;
|
|
7144
|
+
const onTurnStartedAt = Date.now();
|
|
7145
|
+
try {
|
|
7146
|
+
committedOutput = await options.route.onTurn({
|
|
7147
|
+
api,
|
|
7148
|
+
context: options.context,
|
|
7149
|
+
liveOps: liveOpsControl ? {
|
|
7150
|
+
control: liveOpsControl,
|
|
7151
|
+
injectedInstruction
|
|
7152
|
+
} : undefined,
|
|
7153
|
+
onTextDelta: ttsStreamer?.push,
|
|
7154
|
+
session,
|
|
7155
|
+
turn
|
|
7156
|
+
});
|
|
7157
|
+
} catch (error) {
|
|
7158
|
+
const message = toError(error).message;
|
|
7159
|
+
logger.warn("voice route.onTurn failed", {
|
|
7160
|
+
elapsedMs: Date.now() - onTurnStartedAt,
|
|
7161
|
+
error: message,
|
|
7162
|
+
sessionId: options.id,
|
|
7163
|
+
turnId: turn.id
|
|
7164
|
+
});
|
|
7165
|
+
console.error(`[voice] onTurn failed for session ${options.id} turn ${turn.id} after ${Date.now() - onTurnStartedAt}ms:`, message);
|
|
7166
|
+
await appendTrace({
|
|
7167
|
+
payload: {
|
|
7168
|
+
elapsedMs: Date.now() - onTurnStartedAt,
|
|
7169
|
+
error: message,
|
|
7170
|
+
stage: "route.onTurn"
|
|
7171
|
+
},
|
|
7172
|
+
session,
|
|
7173
|
+
turnId: turn.id,
|
|
7174
|
+
type: "session.error"
|
|
7175
|
+
});
|
|
7176
|
+
committedOutput = undefined;
|
|
7177
|
+
}
|
|
7134
7178
|
const output = {
|
|
7135
7179
|
assistantText: committedOutput?.assistantText,
|
|
7136
7180
|
citations: committedOutput?.citations,
|