@absolutejs/voice 0.0.22-beta.582 → 0.0.22-beta.583

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.
@@ -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,7 @@ 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";
74
75
  export { createVoiceMCPToolset } from "./core/mcpToolset";
75
76
  export type { CreateVoiceMCPToolsetOptions, MCPClientLike, MCPToolCallResult, MCPToolContentBlock, MCPToolDefinition, VoiceMCPToolResult, } from "./core/mcpToolset";
76
77
  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,
@@ -45368,6 +45396,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
45368
45396
  const timeoutMs = options.timeoutMs ?? 60000;
45369
45397
  return {
45370
45398
  generate: async (input) => {
45399
+ const stamp = startVoiceTimer(input.session.id);
45371
45400
  const ac = new AbortController;
45372
45401
  const timer = setTimeout(() => {
45373
45402
  ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
@@ -45408,6 +45437,10 @@ var createOpenAIVoiceAssistantModel = (options) => {
45408
45437
  clearTimeout(timer);
45409
45438
  throw error;
45410
45439
  }
45440
+ stamp("openai.fetch-returned", {
45441
+ messages: input.messages.length,
45442
+ status: response.status
45443
+ });
45411
45444
  if (!response.ok) {
45412
45445
  clearTimeout(timer);
45413
45446
  throw createHTTPError("OpenAI", response);
@@ -45415,11 +45448,23 @@ var createOpenAIVoiceAssistantModel = (options) => {
45415
45448
  let assistantText;
45416
45449
  let toolCalls;
45417
45450
  let usage;
45451
+ let firstDeltaSeen = false;
45452
+ const onTextDelta = input.onTextDelta ? (delta) => {
45453
+ if (!firstDeltaSeen) {
45454
+ firstDeltaSeen = true;
45455
+ stamp("openai.first-delta");
45456
+ }
45457
+ input.onTextDelta?.(delta);
45458
+ } : undefined;
45418
45459
  try {
45419
- ({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, input.onTextDelta, {
45460
+ ({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, onTextDelta, {
45420
45461
  signal: ac.signal,
45421
45462
  inactivityMs: 1e4
45422
45463
  }));
45464
+ stamp("openai.stream-done", {
45465
+ textChars: assistantText?.length ?? 0,
45466
+ toolCalls: toolCalls.length
45467
+ });
45423
45468
  } finally {
45424
45469
  clearTimeout(timer);
45425
45470
  }
@@ -52391,6 +52436,7 @@ export {
52391
52436
  wrapVoiceHTMLInHTMXContainer,
52392
52437
  withVoiceOpsTaskId,
52393
52438
  withVoiceIntegrationEventId,
52439
+ voiceTimingEnabled,
52394
52440
  voiceTelephonyOutcomeToRouteResult,
52395
52441
  voiceObservabilityExportSchemaVersion,
52396
52442
  voiceObservabilityExportSchemaId,
@@ -52448,6 +52494,7 @@ export {
52448
52494
  summarizeVoiceAuditSinkDeliveries,
52449
52495
  summarizeVoiceAssistantRuns,
52450
52496
  summarizeVoiceAssistantHealth,
52497
+ startVoiceTimer,
52451
52498
  startVoiceOpsTask,
52452
52499
  signVoiceWebhookBody,
52453
52500
  signVoiceTwilioWebhook,
@@ -52648,6 +52695,7 @@ export {
52648
52695
  matchesVoiceOpsTaskAssignmentRule,
52649
52696
  markVoiceOpsTaskSLABreached,
52650
52697
  mapVoiceProofTargetsWithConcurrency,
52698
+ logVoiceTiming,
52651
52699
  loadVoiceRealCallProfileEvidenceFromTraceStore,
52652
52700
  loadVoiceRealCallProfileEvidenceFromStore,
52653
52701
  loadVoiceObservabilityExportReplaySource,
@@ -4195,6 +4195,21 @@ 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
+
4198
4213
  // src/core/modelAdapters.ts
4199
4214
  var isVoiceProviderRoutingPolicyPreset = (value) => value === "balanced" || value === "cost-cap" || value === "cost-first" || value === "latency-first" || value === "quality-first";
4200
4215
  var resolveVoiceProviderRoutingPolicyPreset = (preset, options = {}) => {
@@ -4905,6 +4920,7 @@ var createOpenAIVoiceAssistantModel = (options) => {
4905
4920
  const timeoutMs = options.timeoutMs ?? 60000;
4906
4921
  return {
4907
4922
  generate: async (input) => {
4923
+ const stamp = startVoiceTimer(input.session.id);
4908
4924
  const ac = new AbortController;
4909
4925
  const timer = setTimeout(() => {
4910
4926
  ac.abort(new Error(`OpenAI /responses timed out after ${timeoutMs}ms (no completion event received)`));
@@ -4945,6 +4961,10 @@ var createOpenAIVoiceAssistantModel = (options) => {
4945
4961
  clearTimeout(timer);
4946
4962
  throw error;
4947
4963
  }
4964
+ stamp("openai.fetch-returned", {
4965
+ messages: input.messages.length,
4966
+ status: response.status
4967
+ });
4948
4968
  if (!response.ok) {
4949
4969
  clearTimeout(timer);
4950
4970
  throw createHTTPError("OpenAI", response);
@@ -4952,11 +4972,23 @@ var createOpenAIVoiceAssistantModel = (options) => {
4952
4972
  let assistantText;
4953
4973
  let toolCalls;
4954
4974
  let usage;
4975
+ let firstDeltaSeen = false;
4976
+ const onTextDelta = input.onTextDelta ? (delta) => {
4977
+ if (!firstDeltaSeen) {
4978
+ firstDeltaSeen = true;
4979
+ stamp("openai.first-delta");
4980
+ }
4981
+ input.onTextDelta?.(delta);
4982
+ } : undefined;
4955
4983
  try {
4956
- ({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, input.onTextDelta, {
4984
+ ({ assistantText, toolCalls, usage } = await consumeOpenAIResponsesStream(response, onTextDelta, {
4957
4985
  signal: ac.signal,
4958
4986
  inactivityMs: 1e4
4959
4987
  }));
4988
+ stamp("openai.stream-done", {
4989
+ textChars: assistantText?.length ?? 0,
4990
+ toolCalls: toolCalls.length
4991
+ });
4960
4992
  } finally {
4961
4993
  clearTimeout(timer);
4962
4994
  }
@@ -7676,6 +7708,7 @@ var createVoiceSession = (options) => {
7676
7708
  const onTurnTimeoutMs = options.routeOnTurnTimeoutMs ?? 45000;
7677
7709
  let committedOutput;
7678
7710
  const onTurnStartedAt = Date.now();
7711
+ logVoiceTiming(session.id, "session.commit-to-onturn", onTurnStartedAt - (turn.committedAt || onTurnStartedAt), { fillerScheduled: fillerTimer !== null });
7679
7712
  try {
7680
7713
  const onTurnPromise = options.route.onTurn({
7681
7714
  api,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.582",
3
+ "version": "0.0.22-beta.583",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",