@absolutejs/voice 0.0.22-beta.485 → 0.0.22-beta.486

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/index.d.ts CHANGED
@@ -81,6 +81,8 @@ export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assist
81
81
  export type { VoiceAssistantMode, VoiceAssistantModality, VoiceAssistantModeDescriptor, VoiceSemanticVADConfig, } from "./assistantMode";
82
82
  export { createPunctuationSemanticTurnDetector, createRegexSemanticTurnDetector, } from "./semanticTurn";
83
83
  export { VOICE_WEBHOOK_SIGNATURE_HEADER, VOICE_WEBHOOK_TIMESTAMP_HEADER, extractVoiceWebhookSignatureFromHeaders, signVoiceWebhookBody, verifyVoiceWebhookSignature, } from "./webhookVerification";
84
+ export { aggregateVoiceTurnLatencySpans, buildOTELSpanId, buildOTELTraceId, buildVoiceOTELPayload, createVoiceOTELHTTPExporter, } from "./otelExporter";
85
+ export type { VoiceOTELAttribute, VoiceOTELExporter, VoiceOTELExporterOptions, VoiceOTELPayload, VoiceOTELResourceSpans, VoiceOTELSpan, VoiceTurnLatencySpanSet, VoiceTurnLatencySpanStage, } from "./otelExporter";
84
86
  export type { VoiceWebhookVerificationInput, VoiceWebhookVerificationReason, VoiceWebhookVerificationResult, } from "./webhookVerification";
85
87
  export type { CreatePunctuationSemanticTurnDetectorOptions, CreateRegexSemanticTurnDetectorOptions, VoiceSemanticTurnDetector, VoiceSemanticTurnInput, VoiceSemanticTurnVerdict, } from "./semanticTurn";
86
88
  export { createMonologueAMDDetector } from "./amdDetector";
package/dist/index.js CHANGED
@@ -35475,6 +35475,143 @@ var extractVoiceWebhookSignatureFromHeaders = (headers) => {
35475
35475
  timestamp: get(VOICE_WEBHOOK_TIMESTAMP_HEADER)
35476
35476
  };
35477
35477
  };
35478
+ // src/otelExporter.ts
35479
+ var SCOPE_NAME = "@absolutejs/voice";
35480
+ var SPAN_KIND_INTERNAL = 1;
35481
+ var STATUS_OK = 1;
35482
+ var HEX_CHARS = "0123456789abcdef";
35483
+ var hashToHex = (input, length) => {
35484
+ let hash = 0xcbf29ce4_84222325n;
35485
+ const prime = 0x100000001_b3n;
35486
+ for (let index = 0;index < input.length; index += 1) {
35487
+ hash ^= BigInt(input.charCodeAt(index));
35488
+ hash = hash * prime & 0xffffffffffffffffn;
35489
+ }
35490
+ let hex = hash.toString(16).padStart(16, "0");
35491
+ while (hex.length < length) {
35492
+ let next = 0n;
35493
+ for (const ch of hex) {
35494
+ next = next * 31n + BigInt(HEX_CHARS.indexOf(ch)) & 0xffffffffffffffffn;
35495
+ }
35496
+ hex += next.toString(16).padStart(16, "0");
35497
+ }
35498
+ return hex.slice(0, length);
35499
+ };
35500
+ var buildOTELTraceId = (sessionId) => hashToHex(`voice-trace:${sessionId}`, 32);
35501
+ var buildOTELSpanId = (sessionId, suffix) => hashToHex(`voice-span:${sessionId}:${suffix}`, 16);
35502
+ var toUnixNano = (ms) => `${Math.trunc(ms * 1e6)}`;
35503
+ var stringAttr = (key, value) => ({
35504
+ key,
35505
+ value: { stringValue: value }
35506
+ });
35507
+ var aggregateVoiceTurnLatencySpans = (events) => {
35508
+ const byTurn = new Map;
35509
+ for (const event of events) {
35510
+ if (event.type !== "turn_latency.stage" || !event.turnId) {
35511
+ continue;
35512
+ }
35513
+ const stage = typeof event.payload?.stage === "string" ? event.payload.stage : undefined;
35514
+ if (!stage) {
35515
+ continue;
35516
+ }
35517
+ const key = `${event.sessionId}::${event.turnId}`;
35518
+ const existing = byTurn.get(key);
35519
+ if (!existing) {
35520
+ byTurn.set(key, {
35521
+ endedAt: event.at,
35522
+ scenarioId: event.scenarioId,
35523
+ sessionId: event.sessionId,
35524
+ stages: [{ at: event.at, stage }],
35525
+ startedAt: event.at,
35526
+ turnId: event.turnId
35527
+ });
35528
+ continue;
35529
+ }
35530
+ existing.stages.push({ at: event.at, stage });
35531
+ existing.startedAt = Math.min(existing.startedAt, event.at);
35532
+ existing.endedAt = Math.max(existing.endedAt, event.at);
35533
+ }
35534
+ return Array.from(byTurn.values()).sort((left, right) => left.startedAt - right.startedAt);
35535
+ };
35536
+ var buildVoiceOTELPayload = (spanSets, options = {}) => {
35537
+ const resourceAttributes = [
35538
+ stringAttr("service.name", options.serviceName ?? "absolutejs-voice")
35539
+ ];
35540
+ for (const [key, value] of Object.entries(options.resourceAttributes ?? {})) {
35541
+ resourceAttributes.push(stringAttr(key, value));
35542
+ }
35543
+ const spans = [];
35544
+ for (const set of spanSets) {
35545
+ const traceId = buildOTELTraceId(set.sessionId);
35546
+ const parentSpanId = buildOTELSpanId(set.sessionId, `turn:${set.turnId}`);
35547
+ spans.push({
35548
+ attributes: [
35549
+ stringAttr("voice.session_id", set.sessionId),
35550
+ stringAttr("voice.turn_id", set.turnId),
35551
+ ...set.scenarioId ? [stringAttr("voice.scenario_id", set.scenarioId)] : []
35552
+ ],
35553
+ endTimeUnixNano: toUnixNano(set.endedAt),
35554
+ kind: SPAN_KIND_INTERNAL,
35555
+ name: "voice.turn",
35556
+ spanId: parentSpanId,
35557
+ startTimeUnixNano: toUnixNano(set.startedAt),
35558
+ status: { code: STATUS_OK },
35559
+ traceId
35560
+ });
35561
+ for (let index = 0;index < set.stages.length; index += 1) {
35562
+ const stage = set.stages[index];
35563
+ const next = set.stages[index + 1];
35564
+ const endsAt = next ? next.at : set.endedAt;
35565
+ spans.push({
35566
+ attributes: [
35567
+ stringAttr("voice.session_id", set.sessionId),
35568
+ stringAttr("voice.turn_id", set.turnId),
35569
+ stringAttr("voice.stage", stage.stage)
35570
+ ],
35571
+ endTimeUnixNano: toUnixNano(endsAt),
35572
+ kind: SPAN_KIND_INTERNAL,
35573
+ name: `voice.turn.stage.${stage.stage}`,
35574
+ parentSpanId,
35575
+ spanId: buildOTELSpanId(set.sessionId, `${set.turnId}:${stage.stage}:${index}`),
35576
+ startTimeUnixNano: toUnixNano(stage.at),
35577
+ status: { code: STATUS_OK },
35578
+ traceId
35579
+ });
35580
+ }
35581
+ }
35582
+ return {
35583
+ resourceSpans: [
35584
+ {
35585
+ resource: { attributes: resourceAttributes },
35586
+ scopeSpans: [{ scope: { name: SCOPE_NAME }, spans }]
35587
+ }
35588
+ ]
35589
+ };
35590
+ };
35591
+ var createVoiceOTELHTTPExporter = (options) => {
35592
+ const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
35593
+ return {
35594
+ export: async (events) => {
35595
+ const spanSets = aggregateVoiceTurnLatencySpans(events);
35596
+ if (spanSets.length === 0) {
35597
+ return { ok: true };
35598
+ }
35599
+ const payload = buildVoiceOTELPayload(spanSets, {
35600
+ resourceAttributes: options.resourceAttributes,
35601
+ serviceName: options.serviceName
35602
+ });
35603
+ const response = await fetchImpl(options.url, {
35604
+ body: JSON.stringify(payload),
35605
+ headers: {
35606
+ "content-type": "application/json",
35607
+ ...options.headers
35608
+ },
35609
+ method: "POST"
35610
+ });
35611
+ return { ok: response.ok, status: response.status };
35612
+ }
35613
+ };
35614
+ };
35478
35615
  // src/amdDetector.ts
35479
35616
  var createMonologueAMDDetector = (options = {}) => {
35480
35617
  const minMonologueMs = options.minMonologueMs ?? 8000;
@@ -46540,6 +46677,7 @@ export {
46540
46677
  createVoiceObservabilityExportSchema,
46541
46678
  createVoiceObservabilityExportRoutes,
46542
46679
  createVoiceObservabilityExportReplayRoutes,
46680
+ createVoiceOTELHTTPExporter,
46543
46681
  createVoiceMonitorWebhookNotifier,
46544
46682
  createVoiceMonitorSession,
46545
46683
  createVoiceMonitorRuntimeBinding,
@@ -46758,6 +46896,7 @@ export {
46758
46896
  buildVoiceObservabilityExportDeliveryHistory,
46759
46897
  buildVoiceObservabilityExport,
46760
46898
  buildVoiceObservabilityArtifactIndex,
46899
+ buildVoiceOTELPayload,
46761
46900
  buildVoiceMultilingualProofReadinessCheck,
46762
46901
  buildVoiceMonitorRunReport,
46763
46902
  buildVoiceMonitorPlan,
@@ -46788,6 +46927,8 @@ export {
46788
46927
  buildVoiceAuditTrailReport,
46789
46928
  buildVoiceAuditExport,
46790
46929
  buildVoiceAuditDeliveryReport,
46930
+ buildOTELTraceId,
46931
+ buildOTELSpanId,
46791
46932
  buildEmptyVoiceProofTrendReport,
46792
46933
  assignVoiceOpsTask,
46793
46934
  assertVoiceToolContractEvidence,
@@ -46838,6 +46979,7 @@ export {
46838
46979
  appendVoiceRealCallProfileRecoveryEvidence,
46839
46980
  appendVoiceProviderRouterTraceEvent,
46840
46981
  appendVoiceIOProviderRouterTraceEvent,
46982
+ aggregateVoiceTurnLatencySpans,
46841
46983
  acknowledgeVoiceMonitorIssue,
46842
46984
  VOICE_WEBHOOK_TIMESTAMP_HEADER,
46843
46985
  VOICE_WEBHOOK_SIGNATURE_HEADER,
@@ -0,0 +1,83 @@
1
+ import type { StoredVoiceTraceEvent } from "./trace";
2
+ export type VoiceOTELAttribute = {
3
+ key: string;
4
+ value: {
5
+ boolValue: boolean;
6
+ };
7
+ } | {
8
+ key: string;
9
+ value: {
10
+ doubleValue: number;
11
+ };
12
+ } | {
13
+ key: string;
14
+ value: {
15
+ intValue: string;
16
+ };
17
+ } | {
18
+ key: string;
19
+ value: {
20
+ stringValue: string;
21
+ };
22
+ };
23
+ export type VoiceOTELSpan = {
24
+ attributes: VoiceOTELAttribute[];
25
+ endTimeUnixNano: string;
26
+ kind: number;
27
+ name: string;
28
+ parentSpanId?: string;
29
+ spanId: string;
30
+ startTimeUnixNano: string;
31
+ status: {
32
+ code: number;
33
+ };
34
+ traceId: string;
35
+ };
36
+ export type VoiceOTELResourceSpans = {
37
+ resource: {
38
+ attributes: VoiceOTELAttribute[];
39
+ };
40
+ scopeSpans: ReadonlyArray<{
41
+ scope: {
42
+ name: string;
43
+ version?: string;
44
+ };
45
+ spans: VoiceOTELSpan[];
46
+ }>;
47
+ };
48
+ export type VoiceOTELPayload = {
49
+ resourceSpans: VoiceOTELResourceSpans[];
50
+ };
51
+ export type VoiceTurnLatencySpanStage = {
52
+ at: number;
53
+ stage: string;
54
+ };
55
+ export type VoiceTurnLatencySpanSet = {
56
+ endedAt: number;
57
+ scenarioId?: string;
58
+ sessionId: string;
59
+ stages: VoiceTurnLatencySpanStage[];
60
+ startedAt: number;
61
+ turnId: string;
62
+ };
63
+ export declare const buildOTELTraceId: (sessionId: string) => string;
64
+ export declare const buildOTELSpanId: (sessionId: string, suffix: string) => string;
65
+ export declare const aggregateVoiceTurnLatencySpans: (events: StoredVoiceTraceEvent[]) => VoiceTurnLatencySpanSet[];
66
+ export type VoiceOTELExporterOptions = {
67
+ fetch?: typeof fetch;
68
+ headers?: Record<string, string>;
69
+ resourceAttributes?: Record<string, string>;
70
+ serviceName?: string;
71
+ url: string;
72
+ };
73
+ export declare const buildVoiceOTELPayload: (spanSets: VoiceTurnLatencySpanSet[], options?: {
74
+ resourceAttributes?: Record<string, string>;
75
+ serviceName?: string;
76
+ }) => VoiceOTELPayload;
77
+ export type VoiceOTELExporter = {
78
+ export: (events: StoredVoiceTraceEvent[]) => Promise<{
79
+ ok: boolean;
80
+ status?: number;
81
+ }>;
82
+ };
83
+ export declare const createVoiceOTELHTTPExporter: (options: VoiceOTELExporterOptions) => VoiceOTELExporter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.485",
3
+ "version": "0.0.22-beta.486",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",