@absolutejs/voice 0.0.22-beta.582 → 0.0.22-beta.584
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/debugTiming.d.ts +11 -0
- package/dist/core/hardenedFetch.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +91 -4
- package/dist/testing/index.js +76 -4
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const logVoiceTiming: (sessionId: string, stage: string, elapsedMs: number, detail?: Record<string, unknown>) => void;
|
|
2
|
+
/**
|
|
3
|
+
* A per-turn stopwatch. `stamp(stage, detail?)` logs the elapsed since the timer
|
|
4
|
+
* was created, so one timer per turn lays every stage out on a single timeline:
|
|
5
|
+
*
|
|
6
|
+
* const stamp = startVoiceTimer(session.id);
|
|
7
|
+
* stamp("agent.system-resolved", { chars });
|
|
8
|
+
* stamp("agent.round0.generate-done", { ms });
|
|
9
|
+
*/
|
|
10
|
+
export declare const startVoiceTimer: (sessionId: string) => (stage: string, detail?: Record<string, unknown>) => void;
|
|
11
|
+
export declare const voiceTimingEnabled: () => boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -71,6 +71,8 @@ export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, crea
|
|
|
71
71
|
export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool, } from "./core/agent";
|
|
72
72
|
export { createPersonaVoiceCaller, createScriptedVoiceCaller, renderVoiceSimulationTranscript, runVoiceConversationSimulation, } from "./core/conversationSimulator";
|
|
73
73
|
export type { RunVoiceConversationSimulationInput, VoiceConversationSimulationEndedReason, VoiceConversationSimulationResult, VoicePersonaCallerCompletion, VoiceScriptedCallerStep, VoiceSimulatedSpeaker, VoiceSimulatedTurn, VoiceSimulatorCaller, VoiceSimulatorCallerModel, VoiceSimulatorCallerReply, } from "./core/conversationSimulator";
|
|
74
|
+
export { logVoiceTiming, startVoiceTimer, voiceTimingEnabled, } from "./core/debugTiming";
|
|
75
|
+
export { hardenFetch } from "./core/hardenedFetch";
|
|
74
76
|
export { createVoiceMCPToolset } from "./core/mcpToolset";
|
|
75
77
|
export type { CreateVoiceMCPToolsetOptions, MCPClientLike, MCPToolCallResult, MCPToolContentBlock, MCPToolDefinition, VoiceMCPToolResult, } from "./core/mcpToolset";
|
|
76
78
|
export { createAIVoiceModel } from "./core/aiVoiceModel";
|
package/dist/index.js
CHANGED
|
@@ -3091,6 +3091,21 @@ var toVoiceSessionSummary = (session) => ({
|
|
|
3091
3091
|
// src/core/session.ts
|
|
3092
3092
|
import { Buffer as Buffer2 } from "buffer";
|
|
3093
3093
|
|
|
3094
|
+
// src/core/debugTiming.ts
|
|
3095
|
+
var timingEnabled = () => process.env.ABSOLUTEJS_VOICE_TIMING === "1" || process.env.ABSOLUTEJS_VOICE_TIMING === "true";
|
|
3096
|
+
var emitTiming = (sessionId, stage, elapsedMs, detail) => {
|
|
3097
|
+
if (!timingEnabled())
|
|
3098
|
+
return;
|
|
3099
|
+
const extra = detail ? ` ${JSON.stringify(detail)}` : "";
|
|
3100
|
+
console.info(`[voice][timing] session=${sessionId} ${stage} +${Math.round(elapsedMs)}ms${extra}`);
|
|
3101
|
+
};
|
|
3102
|
+
var logVoiceTiming = (sessionId, stage, elapsedMs, detail) => emitTiming(sessionId, stage, elapsedMs, detail);
|
|
3103
|
+
var startVoiceTimer = (sessionId) => {
|
|
3104
|
+
const startedAt = Date.now();
|
|
3105
|
+
return (stage, detail) => emitTiming(sessionId, stage, Date.now() - startedAt, detail);
|
|
3106
|
+
};
|
|
3107
|
+
var voiceTimingEnabled = () => timingEnabled();
|
|
3108
|
+
|
|
3094
3109
|
// src/core/backchannel.ts
|
|
3095
3110
|
var DEFAULT_CUES = [
|
|
3096
3111
|
{ text: "mm-hmm" },
|
|
@@ -5534,6 +5549,7 @@ var createVoiceSession = (options) => {
|
|
|
5534
5549
|
const onTurnTimeoutMs = options.routeOnTurnTimeoutMs ?? 45000;
|
|
5535
5550
|
let committedOutput;
|
|
5536
5551
|
const onTurnStartedAt = Date.now();
|
|
5552
|
+
logVoiceTiming(session.id, "session.commit-to-onturn", onTurnStartedAt - (turn.committedAt || onTurnStartedAt), { fillerScheduled: fillerTimer !== null });
|
|
5537
5553
|
try {
|
|
5538
5554
|
const onTurnPromise = options.route.onTurn({
|
|
5539
5555
|
api,
|
|
@@ -7446,7 +7462,9 @@ var createVoiceAgent = (options) => {
|
|
|
7446
7462
|
const maxToolRounds = Math.max(0, options.maxToolRounds ?? 2);
|
|
7447
7463
|
const audit = resolveVoiceAgentAuditLogger(options.audit);
|
|
7448
7464
|
const run = async (input) => {
|
|
7465
|
+
const stamp = startVoiceTimer(input.session.id);
|
|
7449
7466
|
const messages = input.messages ?? createHistoryMessages(input.session, input.turn);
|
|
7467
|
+
stamp("agent.history-built", { messages: messages.length });
|
|
7450
7468
|
const toolResults = [];
|
|
7451
7469
|
const baseSystem = typeof options.system === "function" ? await options.system({
|
|
7452
7470
|
context: input.context,
|
|
@@ -7456,9 +7474,11 @@ var createVoiceAgent = (options) => {
|
|
|
7456
7474
|
const system = [baseSystem, input.system].filter((value) => Boolean(value?.trim())).join(`
|
|
7457
7475
|
|
|
7458
7476
|
`) || undefined;
|
|
7477
|
+
stamp("agent.system-resolved", { systemChars: system?.length ?? 0 });
|
|
7459
7478
|
let output = {};
|
|
7460
7479
|
for (let round = 0;round <= maxToolRounds; round += 1) {
|
|
7461
7480
|
const modelStartedAt = Date.now();
|
|
7481
|
+
stamp(`agent.round${round}.generate-start`);
|
|
7462
7482
|
try {
|
|
7463
7483
|
output = await options.model.generate({
|
|
7464
7484
|
agentId: options.id,
|
|
@@ -7474,6 +7494,11 @@ var createVoiceAgent = (options) => {
|
|
|
7474
7494
|
})),
|
|
7475
7495
|
turn: input.turn
|
|
7476
7496
|
});
|
|
7497
|
+
stamp(`agent.round${round}.generate-done`, {
|
|
7498
|
+
ms: Date.now() - modelStartedAt,
|
|
7499
|
+
textChars: output.assistantText?.length ?? 0,
|
|
7500
|
+
toolCalls: output.toolCalls?.length ?? 0
|
|
7501
|
+
});
|
|
7477
7502
|
await audit?.providerCall({
|
|
7478
7503
|
actor: {
|
|
7479
7504
|
id: options.id,
|
|
@@ -7487,6 +7512,9 @@ var createVoiceAgent = (options) => {
|
|
|
7487
7512
|
sessionId: input.session.id
|
|
7488
7513
|
});
|
|
7489
7514
|
} catch (error) {
|
|
7515
|
+
stamp(`agent.round${round}.generate-error`, {
|
|
7516
|
+
ms: Date.now() - modelStartedAt
|
|
7517
|
+
});
|
|
7490
7518
|
await audit?.providerCall({
|
|
7491
7519
|
actor: {
|
|
7492
7520
|
id: options.id,
|
|
@@ -40814,6 +40842,44 @@ Respond with only your spoken line. When your goal is met or you want to hang up
|
|
|
40814
40842
|
persona: options.persona
|
|
40815
40843
|
};
|
|
40816
40844
|
};
|
|
40845
|
+
// src/core/hardenedFetch.ts
|
|
40846
|
+
var ATTEMPT_TIMEOUT_MS = 6000;
|
|
40847
|
+
var isBun = "Bun" in globalThis;
|
|
40848
|
+
var oneAttempt = async (baseFetch, input, init) => {
|
|
40849
|
+
const controller = new AbortController;
|
|
40850
|
+
const callerSignal = init?.signal ?? undefined;
|
|
40851
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason);
|
|
40852
|
+
if (callerSignal?.aborted)
|
|
40853
|
+
controller.abort(callerSignal.reason);
|
|
40854
|
+
else
|
|
40855
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
40856
|
+
const timer = setTimeout(() => {
|
|
40857
|
+
controller.abort(new Error(`fetch exceeded ${ATTEMPT_TIMEOUT_MS}ms before response headers (stale Bun keep-alive socket?)`));
|
|
40858
|
+
}, ATTEMPT_TIMEOUT_MS);
|
|
40859
|
+
const headers = new Headers(init?.headers);
|
|
40860
|
+
if (isBun)
|
|
40861
|
+
headers.set("Connection", "close");
|
|
40862
|
+
try {
|
|
40863
|
+
return await baseFetch(input, {
|
|
40864
|
+
...init,
|
|
40865
|
+
headers,
|
|
40866
|
+
signal: controller.signal
|
|
40867
|
+
});
|
|
40868
|
+
} finally {
|
|
40869
|
+
clearTimeout(timer);
|
|
40870
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
40871
|
+
}
|
|
40872
|
+
};
|
|
40873
|
+
var hardenFetch = (baseFetch = globalThis.fetch) => Object.assign(async (input, init) => {
|
|
40874
|
+
try {
|
|
40875
|
+
return await oneAttempt(baseFetch, input, init);
|
|
40876
|
+
} catch (error) {
|
|
40877
|
+
if (init?.signal?.aborted)
|
|
40878
|
+
throw error;
|
|
40879
|
+
console.warn(`[voice] hardened fetch retrying on a fresh connection: ${error instanceof Error ? error.message : String(error)}`);
|
|
40880
|
+
return oneAttempt(baseFetch, input, init);
|
|
40881
|
+
}
|
|
40882
|
+
}, { preconnect: baseFetch.preconnect.bind(baseFetch) });
|
|
40817
40883
|
// src/core/mcpToolset.ts
|
|
40818
40884
|
var flattenContent = (result) => {
|
|
40819
40885
|
const blocks = result.content ?? [];
|
|
@@ -45362,12 +45428,13 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
45362
45428
|
return { assistantText, toolCalls: finalizeToolCalls(calls), usage };
|
|
45363
45429
|
};
|
|
45364
45430
|
var createOpenAIVoiceAssistantModel = (options) => {
|
|
45365
|
-
const fetchImpl = options.fetch
|
|
45431
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
45366
45432
|
const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
|
|
45367
45433
|
const model = options.model ?? "gpt-4.1-mini";
|
|
45368
45434
|
const timeoutMs = options.timeoutMs ?? 60000;
|
|
45369
45435
|
return {
|
|
45370
45436
|
generate: async (input) => {
|
|
45437
|
+
const stamp = startVoiceTimer(input.session.id);
|
|
45371
45438
|
const ac = new AbortController;
|
|
45372
45439
|
const timer = setTimeout(() => {
|
|
45373
45440
|
ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
|
|
@@ -45408,6 +45475,10 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
45408
45475
|
clearTimeout(timer);
|
|
45409
45476
|
throw error;
|
|
45410
45477
|
}
|
|
45478
|
+
stamp("openai.fetch-returned", {
|
|
45479
|
+
messages: input.messages.length,
|
|
45480
|
+
status: response.status
|
|
45481
|
+
});
|
|
45411
45482
|
if (!response.ok) {
|
|
45412
45483
|
clearTimeout(timer);
|
|
45413
45484
|
throw createHTTPError("OpenAI", response);
|
|
@@ -45415,11 +45486,23 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
45415
45486
|
let assistantText;
|
|
45416
45487
|
let toolCalls;
|
|
45417
45488
|
let usage;
|
|
45489
|
+
let firstDeltaSeen = false;
|
|
45490
|
+
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
45491
|
+
if (!firstDeltaSeen) {
|
|
45492
|
+
firstDeltaSeen = true;
|
|
45493
|
+
stamp("openai.first-delta");
|
|
45494
|
+
}
|
|
45495
|
+
input.onTextDelta?.(delta);
|
|
45496
|
+
} : undefined;
|
|
45418
45497
|
try {
|
|
45419
|
-
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response,
|
|
45498
|
+
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
45420
45499
|
signal: ac.signal,
|
|
45421
45500
|
inactivityMs: 1e4
|
|
45422
45501
|
}));
|
|
45502
|
+
stamp("openai.stream-done", {
|
|
45503
|
+
textChars: assistantText?.length ?? 0,
|
|
45504
|
+
toolCalls: toolCalls.length
|
|
45505
|
+
});
|
|
45423
45506
|
} finally {
|
|
45424
45507
|
clearTimeout(timer);
|
|
45425
45508
|
}
|
|
@@ -45470,7 +45553,7 @@ var consumeAnthropicStream = async (response, onTextDelta) => {
|
|
|
45470
45553
|
return { assistantText, toolCalls: finalizeToolCalls(calls), usage };
|
|
45471
45554
|
};
|
|
45472
45555
|
var createAnthropicVoiceAssistantModel = (options) => {
|
|
45473
|
-
const fetchImpl = options.fetch
|
|
45556
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
45474
45557
|
const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
|
|
45475
45558
|
const model = options.model ?? "claude-sonnet-4-5";
|
|
45476
45559
|
return {
|
|
@@ -45556,7 +45639,7 @@ var consumeGeminiStream = async (response, onTextDelta) => {
|
|
|
45556
45639
|
return { assistantText, toolCalls, usage };
|
|
45557
45640
|
};
|
|
45558
45641
|
var createGeminiVoiceAssistantModel = (options) => {
|
|
45559
|
-
const fetchImpl = options.fetch
|
|
45642
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
45560
45643
|
const baseUrl = options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
|
|
45561
45644
|
const model = options.model ?? "gemini-2.5-flash";
|
|
45562
45645
|
const maxRetries = Math.max(0, options.maxRetries ?? 2);
|
|
@@ -52391,6 +52474,7 @@ export {
|
|
|
52391
52474
|
wrapVoiceHTMLInHTMXContainer,
|
|
52392
52475
|
withVoiceOpsTaskId,
|
|
52393
52476
|
withVoiceIntegrationEventId,
|
|
52477
|
+
voiceTimingEnabled,
|
|
52394
52478
|
voiceTelephonyOutcomeToRouteResult,
|
|
52395
52479
|
voiceObservabilityExportSchemaVersion,
|
|
52396
52480
|
voiceObservabilityExportSchemaId,
|
|
@@ -52448,6 +52532,7 @@ export {
|
|
|
52448
52532
|
summarizeVoiceAuditSinkDeliveries,
|
|
52449
52533
|
summarizeVoiceAssistantRuns,
|
|
52450
52534
|
summarizeVoiceAssistantHealth,
|
|
52535
|
+
startVoiceTimer,
|
|
52451
52536
|
startVoiceOpsTask,
|
|
52452
52537
|
signVoiceWebhookBody,
|
|
52453
52538
|
signVoiceTwilioWebhook,
|
|
@@ -52648,6 +52733,7 @@ export {
|
|
|
52648
52733
|
matchesVoiceOpsTaskAssignmentRule,
|
|
52649
52734
|
markVoiceOpsTaskSLABreached,
|
|
52650
52735
|
mapVoiceProofTargetsWithConcurrency,
|
|
52736
|
+
logVoiceTiming,
|
|
52651
52737
|
loadVoiceRealCallProfileEvidenceFromTraceStore,
|
|
52652
52738
|
loadVoiceRealCallProfileEvidenceFromStore,
|
|
52653
52739
|
loadVoiceObservabilityExportReplaySource,
|
|
@@ -52663,6 +52749,7 @@ export {
|
|
|
52663
52749
|
importVoiceCampaignRecipients,
|
|
52664
52750
|
heartbeatVoiceOpsTask,
|
|
52665
52751
|
hasVoiceOpsTaskSLABreach,
|
|
52752
|
+
hardenFetch,
|
|
52666
52753
|
getVoiceProofTargetLogicalFailure,
|
|
52667
52754
|
getVoiceLiveOpsControlStatus,
|
|
52668
52755
|
getVoiceCampaignDialerProofStatus,
|
package/dist/testing/index.js
CHANGED
|
@@ -4195,6 +4195,60 @@ var createVoiceIOProviderFailureSimulator = (options) => {
|
|
|
4195
4195
|
run
|
|
4196
4196
|
};
|
|
4197
4197
|
};
|
|
4198
|
+
// src/core/debugTiming.ts
|
|
4199
|
+
var timingEnabled = () => process.env.ABSOLUTEJS_VOICE_TIMING === "1" || process.env.ABSOLUTEJS_VOICE_TIMING === "true";
|
|
4200
|
+
var emitTiming = (sessionId, stage, elapsedMs, detail) => {
|
|
4201
|
+
if (!timingEnabled())
|
|
4202
|
+
return;
|
|
4203
|
+
const extra = detail ? ` ${JSON.stringify(detail)}` : "";
|
|
4204
|
+
console.info(`[voice][timing] session=${sessionId} ${stage} +${Math.round(elapsedMs)}ms${extra}`);
|
|
4205
|
+
};
|
|
4206
|
+
var logVoiceTiming = (sessionId, stage, elapsedMs, detail) => emitTiming(sessionId, stage, elapsedMs, detail);
|
|
4207
|
+
var startVoiceTimer = (sessionId) => {
|
|
4208
|
+
const startedAt = Date.now();
|
|
4209
|
+
return (stage, detail) => emitTiming(sessionId, stage, Date.now() - startedAt, detail);
|
|
4210
|
+
};
|
|
4211
|
+
var voiceTimingEnabled = () => timingEnabled();
|
|
4212
|
+
|
|
4213
|
+
// src/core/hardenedFetch.ts
|
|
4214
|
+
var ATTEMPT_TIMEOUT_MS = 6000;
|
|
4215
|
+
var isBun = "Bun" in globalThis;
|
|
4216
|
+
var oneAttempt = async (baseFetch, input, init) => {
|
|
4217
|
+
const controller = new AbortController;
|
|
4218
|
+
const callerSignal = init?.signal ?? undefined;
|
|
4219
|
+
const onCallerAbort = () => controller.abort(callerSignal?.reason);
|
|
4220
|
+
if (callerSignal?.aborted)
|
|
4221
|
+
controller.abort(callerSignal.reason);
|
|
4222
|
+
else
|
|
4223
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
4224
|
+
const timer = setTimeout(() => {
|
|
4225
|
+
controller.abort(new Error(`fetch exceeded ${ATTEMPT_TIMEOUT_MS}ms before response headers (stale Bun keep-alive socket?)`));
|
|
4226
|
+
}, ATTEMPT_TIMEOUT_MS);
|
|
4227
|
+
const headers = new Headers(init?.headers);
|
|
4228
|
+
if (isBun)
|
|
4229
|
+
headers.set("Connection", "close");
|
|
4230
|
+
try {
|
|
4231
|
+
return await baseFetch(input, {
|
|
4232
|
+
...init,
|
|
4233
|
+
headers,
|
|
4234
|
+
signal: controller.signal
|
|
4235
|
+
});
|
|
4236
|
+
} finally {
|
|
4237
|
+
clearTimeout(timer);
|
|
4238
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
4239
|
+
}
|
|
4240
|
+
};
|
|
4241
|
+
var hardenFetch = (baseFetch = globalThis.fetch) => Object.assign(async (input, init) => {
|
|
4242
|
+
try {
|
|
4243
|
+
return await oneAttempt(baseFetch, input, init);
|
|
4244
|
+
} catch (error) {
|
|
4245
|
+
if (init?.signal?.aborted)
|
|
4246
|
+
throw error;
|
|
4247
|
+
console.warn(`[voice] hardened fetch retrying on a fresh connection: ${error instanceof Error ? error.message : String(error)}`);
|
|
4248
|
+
return oneAttempt(baseFetch, input, init);
|
|
4249
|
+
}
|
|
4250
|
+
}, { preconnect: baseFetch.preconnect.bind(baseFetch) });
|
|
4251
|
+
|
|
4198
4252
|
// src/core/modelAdapters.ts
|
|
4199
4253
|
var isVoiceProviderRoutingPolicyPreset = (value) => value === "balanced" || value === "cost-cap" || value === "cost-first" || value === "latency-first" || value === "quality-first";
|
|
4200
4254
|
var resolveVoiceProviderRoutingPolicyPreset = (preset, options = {}) => {
|
|
@@ -4899,12 +4953,13 @@ var consumeOpenAIResponsesStream = async (response, onTextDelta, abortOptions) =
|
|
|
4899
4953
|
return { assistantText, toolCalls: finalizeToolCalls(calls), usage };
|
|
4900
4954
|
};
|
|
4901
4955
|
var createOpenAIVoiceAssistantModel = (options) => {
|
|
4902
|
-
const fetchImpl = options.fetch
|
|
4956
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
4903
4957
|
const baseUrl = options.baseUrl ?? "https://api.openai.com/v1";
|
|
4904
4958
|
const model = options.model ?? "gpt-4.1-mini";
|
|
4905
4959
|
const timeoutMs = options.timeoutMs ?? 60000;
|
|
4906
4960
|
return {
|
|
4907
4961
|
generate: async (input) => {
|
|
4962
|
+
const stamp = startVoiceTimer(input.session.id);
|
|
4908
4963
|
const ac = new AbortController;
|
|
4909
4964
|
const timer = setTimeout(() => {
|
|
4910
4965
|
ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
|
|
@@ -4945,6 +5000,10 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
4945
5000
|
clearTimeout(timer);
|
|
4946
5001
|
throw error;
|
|
4947
5002
|
}
|
|
5003
|
+
stamp("openai.fetch-returned", {
|
|
5004
|
+
messages: input.messages.length,
|
|
5005
|
+
status: response.status
|
|
5006
|
+
});
|
|
4948
5007
|
if (!response.ok) {
|
|
4949
5008
|
clearTimeout(timer);
|
|
4950
5009
|
throw createHTTPError("OpenAI", response);
|
|
@@ -4952,11 +5011,23 @@ var createOpenAIVoiceAssistantModel = (options) => {
|
|
|
4952
5011
|
let assistantText;
|
|
4953
5012
|
let toolCalls;
|
|
4954
5013
|
let usage;
|
|
5014
|
+
let firstDeltaSeen = false;
|
|
5015
|
+
const onTextDelta = input.onTextDelta ? (delta) => {
|
|
5016
|
+
if (!firstDeltaSeen) {
|
|
5017
|
+
firstDeltaSeen = true;
|
|
5018
|
+
stamp("openai.first-delta");
|
|
5019
|
+
}
|
|
5020
|
+
input.onTextDelta?.(delta);
|
|
5021
|
+
} : undefined;
|
|
4955
5022
|
try {
|
|
4956
|
-
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response,
|
|
5023
|
+
({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, onTextDelta, {
|
|
4957
5024
|
signal: ac.signal,
|
|
4958
5025
|
inactivityMs: 1e4
|
|
4959
5026
|
}));
|
|
5027
|
+
stamp("openai.stream-done", {
|
|
5028
|
+
textChars: assistantText?.length ?? 0,
|
|
5029
|
+
toolCalls: toolCalls.length
|
|
5030
|
+
});
|
|
4960
5031
|
} finally {
|
|
4961
5032
|
clearTimeout(timer);
|
|
4962
5033
|
}
|
|
@@ -5007,7 +5078,7 @@ var consumeAnthropicStream = async (response, onTextDelta) => {
|
|
|
5007
5078
|
return { assistantText, toolCalls: finalizeToolCalls(calls), usage };
|
|
5008
5079
|
};
|
|
5009
5080
|
var createAnthropicVoiceAssistantModel = (options) => {
|
|
5010
|
-
const fetchImpl = options.fetch
|
|
5081
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
5011
5082
|
const baseUrl = options.baseUrl ?? "https://api.anthropic.com/v1";
|
|
5012
5083
|
const model = options.model ?? "claude-sonnet-4-5";
|
|
5013
5084
|
return {
|
|
@@ -5093,7 +5164,7 @@ var consumeGeminiStream = async (response, onTextDelta) => {
|
|
|
5093
5164
|
return { assistantText, toolCalls, usage };
|
|
5094
5165
|
};
|
|
5095
5166
|
var createGeminiVoiceAssistantModel = (options) => {
|
|
5096
|
-
const fetchImpl = options.fetch
|
|
5167
|
+
const fetchImpl = hardenFetch(options.fetch);
|
|
5097
5168
|
const baseUrl = options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta";
|
|
5098
5169
|
const model = options.model ?? "gemini-2.5-flash";
|
|
5099
5170
|
const maxRetries = Math.max(0, options.maxRetries ?? 2);
|
|
@@ -7676,6 +7747,7 @@ var createVoiceSession = (options) => {
|
|
|
7676
7747
|
const onTurnTimeoutMs = options.routeOnTurnTimeoutMs ?? 45000;
|
|
7677
7748
|
let committedOutput;
|
|
7678
7749
|
const onTurnStartedAt = Date.now();
|
|
7750
|
+
logVoiceTiming(session.id, "session.commit-to-onturn", onTurnStartedAt - (turn.committedAt || onTurnStartedAt), { fillerScheduled: fillerTimer !== null });
|
|
7679
7751
|
try {
|
|
7680
7752
|
const onTurnPromise = options.route.onTurn({
|
|
7681
7753
|
api,
|