@absolutejs/voice 0.0.22-beta.311 → 0.0.22-beta.313
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 +2 -2
- package/dist/index.js +214 -2
- package/dist/mediaPipeline.d.ts +94 -0
- package/dist/mediaPipelineRoutes.d.ts +12 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -15,8 +15,8 @@ export type { VoiceRealtimeChannelAssertionInput, VoiceRealtimeChannelAssertionR
|
|
|
15
15
|
export { assertVoiceRealtimeProviderContractEvidence, buildVoiceRealtimeProviderContractMatrix, createVoiceRealtimeProviderContractMatrixPreset, createVoiceRealtimeProviderContractRoutes, evaluateVoiceRealtimeProviderContractEvidence, renderVoiceRealtimeProviderContractHTML } from './realtimeProviderContracts';
|
|
16
16
|
export type { VoiceRealtimeProviderContractAssertionInput, VoiceRealtimeProviderContractAssertionReport, VoiceRealtimeProviderContractCapability, VoiceRealtimeProviderContractCheck, VoiceRealtimeProviderContractDefinition, VoiceRealtimeProviderContractMatrixPresetOptions, VoiceRealtimeProviderContractMatrixInput, VoiceRealtimeProviderContractMatrixReport, VoiceRealtimeProviderContractRoutesOptions, VoiceRealtimeProviderContractRow, VoiceRealtimeProviderPresetProvider, VoiceRealtimeProviderContractStatus } from './realtimeProviderContracts';
|
|
17
17
|
export { buildVoiceDiagnosticsMarkdown, createVoiceDiagnosticsRoutes, resolveVoiceDiagnosticsTraceFilter } from './diagnosticsRoutes';
|
|
18
|
-
export { buildVoiceMediaInterruptionReport, buildVoiceMediaPipelineCalibrationReport, buildVoiceMediaResamplingPlan, buildVoiceMediaVadReport, createVoiceMediaFrame, createVoiceMediaFrameTransformPipeline } from './mediaPipeline';
|
|
19
|
-
export type { VoiceMediaFrame, VoiceMediaFrameKind, VoiceMediaFrameSource, VoiceMediaFrameTransform, VoiceMediaFrameTransformPipeline, VoiceMediaInterruptionInput, VoiceMediaInterruptionReport, VoiceMediaPipelineCalibrationInput, VoiceMediaPipelineCalibrationIssue, VoiceMediaPipelineCalibrationReport, VoiceMediaPipelineStatus, VoiceMediaResamplingPlan, VoiceMediaTransportAdapter, VoiceMediaVadInput, VoiceMediaVadReport, VoiceMediaVadSegment } from './mediaPipeline';
|
|
18
|
+
export { buildVoiceMediaTransportReport, buildVoiceMediaInterruptionReport, buildVoiceMediaPipelineCalibrationReport, buildVoiceMediaResamplingPlan, buildVoiceMediaProcessorGraphReport, buildVoiceMediaVadReport, createVoiceMediaFrame, createVoiceMediaFrameTransformPipeline, createVoiceMediaProcessorGraph, createVoiceMediaTransport } from './mediaPipeline';
|
|
19
|
+
export type { VoiceMediaFrame, VoiceMediaFrameKind, VoiceMediaFrameSource, VoiceMediaFrameTransform, VoiceMediaFrameTransformPipeline, VoiceMediaInterruptionInput, VoiceMediaInterruptionReport, VoiceMediaPipelineCalibrationInput, VoiceMediaPipelineCalibrationIssue, VoiceMediaPipelineCalibrationReport, VoiceMediaPipelineStatus, VoiceMediaProcessorGraph, VoiceMediaProcessorGraphReport, VoiceMediaProcessorNode, VoiceMediaProcessorNodeEvent, VoiceMediaProcessorNodeKind, VoiceMediaProcessorNodeReport, VoiceMediaResamplingPlan, VoiceMediaTransport, VoiceMediaTransportAdapter, VoiceMediaTransportEvent, VoiceMediaTransportEventKind, VoiceMediaTransportOptions, VoiceMediaTransportReport, VoiceMediaTransportState, VoiceMediaVadInput, VoiceMediaVadReport, VoiceMediaVadSegment } from './mediaPipeline';
|
|
20
20
|
export { assertVoiceMediaPipelineEvidence, buildVoiceMediaPipelineReport, createVoiceMediaPipelineRoutes, evaluateVoiceMediaPipelineEvidence, renderVoiceMediaPipelineHTML, renderVoiceMediaPipelineMarkdown } from './mediaPipelineRoutes';
|
|
21
21
|
export type { VoiceMediaPipelineAssertionInput, VoiceMediaPipelineAssertionReport, VoiceMediaPipelineReport, VoiceMediaPipelineReportOptions, VoiceMediaPipelineRoutesOptions } from './mediaPipelineRoutes';
|
|
22
22
|
export { buildVoiceDemoReadyReport, createVoiceDemoReadyRoutes, renderVoiceDemoReadyHTML } from './demoReadyRoutes';
|
package/dist/index.js
CHANGED
|
@@ -11620,6 +11620,95 @@ var numericMetadata = (frame, key) => {
|
|
|
11620
11620
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11621
11621
|
};
|
|
11622
11622
|
var createVoiceMediaFrame = (frame) => frame;
|
|
11623
|
+
var buildVoiceMediaTransportReport = (input) => {
|
|
11624
|
+
const events = input.events ?? [];
|
|
11625
|
+
const state = input.state ?? events.at(-1)?.state ?? "idle";
|
|
11626
|
+
const backpressureEvents = events.filter((event) => event.kind === "backpressure").length;
|
|
11627
|
+
const failed = state === "failed" || events.some((event) => event.kind === "error");
|
|
11628
|
+
return {
|
|
11629
|
+
backpressureEvents,
|
|
11630
|
+
checkedAt: Date.now(),
|
|
11631
|
+
closed: state === "closed",
|
|
11632
|
+
connected: state === "open",
|
|
11633
|
+
events,
|
|
11634
|
+
failed,
|
|
11635
|
+
inputFrames: events.filter((event) => event.kind === "frame-in").length,
|
|
11636
|
+
name: input.name,
|
|
11637
|
+
outputFrames: events.filter((event) => event.kind === "frame-out").length,
|
|
11638
|
+
state,
|
|
11639
|
+
status: failed ? "fail" : backpressureEvents > 0 ? "warn" : "pass"
|
|
11640
|
+
};
|
|
11641
|
+
};
|
|
11642
|
+
var createVoiceMediaTransport = (options) => {
|
|
11643
|
+
let state = "idle";
|
|
11644
|
+
const events = [];
|
|
11645
|
+
const frameHandlers = new Set;
|
|
11646
|
+
const record = (event) => {
|
|
11647
|
+
events.push({ ...event, at: Date.now(), state });
|
|
11648
|
+
};
|
|
11649
|
+
return {
|
|
11650
|
+
close: async () => {
|
|
11651
|
+
state = "closing";
|
|
11652
|
+
await options.onClose?.();
|
|
11653
|
+
state = "closed";
|
|
11654
|
+
record({ kind: "close" });
|
|
11655
|
+
},
|
|
11656
|
+
connect: async () => {
|
|
11657
|
+
try {
|
|
11658
|
+
await options.onConnect?.();
|
|
11659
|
+
state = "open";
|
|
11660
|
+
record({ kind: "connect" });
|
|
11661
|
+
} catch (error) {
|
|
11662
|
+
state = "failed";
|
|
11663
|
+
record({
|
|
11664
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11665
|
+
kind: "error"
|
|
11666
|
+
});
|
|
11667
|
+
throw error;
|
|
11668
|
+
}
|
|
11669
|
+
},
|
|
11670
|
+
events: () => [...events],
|
|
11671
|
+
inputFormat: options.inputFormat,
|
|
11672
|
+
name: options.name,
|
|
11673
|
+
onFrame: (handler) => {
|
|
11674
|
+
frameHandlers.add(handler);
|
|
11675
|
+
return () => frameHandlers.delete(handler);
|
|
11676
|
+
},
|
|
11677
|
+
outputFormat: options.outputFormat,
|
|
11678
|
+
receive: async (frame) => {
|
|
11679
|
+
record({ frameId: frame.id, kind: "frame-in" });
|
|
11680
|
+
if (options.maxBufferedFrames !== undefined && events.filter((event) => event.kind === "frame-in").length > options.maxBufferedFrames) {
|
|
11681
|
+
record({
|
|
11682
|
+
bufferedFrames: events.filter((event) => event.kind === "frame-in").length,
|
|
11683
|
+
kind: "backpressure"
|
|
11684
|
+
});
|
|
11685
|
+
}
|
|
11686
|
+
for (const handler of frameHandlers) {
|
|
11687
|
+
await handler(frame);
|
|
11688
|
+
}
|
|
11689
|
+
},
|
|
11690
|
+
report: () => buildVoiceMediaTransportReport({
|
|
11691
|
+
events,
|
|
11692
|
+
name: options.name,
|
|
11693
|
+
state
|
|
11694
|
+
}),
|
|
11695
|
+
send: async (frame) => {
|
|
11696
|
+
try {
|
|
11697
|
+
await options.onSend?.(frame);
|
|
11698
|
+
record({ frameId: frame.id, kind: "frame-out" });
|
|
11699
|
+
} catch (error) {
|
|
11700
|
+
state = "failed";
|
|
11701
|
+
record({
|
|
11702
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11703
|
+
frameId: frame.id,
|
|
11704
|
+
kind: "error"
|
|
11705
|
+
});
|
|
11706
|
+
throw error;
|
|
11707
|
+
}
|
|
11708
|
+
},
|
|
11709
|
+
state: () => state
|
|
11710
|
+
};
|
|
11711
|
+
};
|
|
11623
11712
|
var buildVoiceMediaResamplingPlan = (input) => {
|
|
11624
11713
|
const required = !formatMatches2(input.inputFormat, input.outputFormat);
|
|
11625
11714
|
return {
|
|
@@ -11663,6 +11752,93 @@ var createVoiceMediaFrameTransformPipeline = (input = {}) => {
|
|
|
11663
11752
|
transforms
|
|
11664
11753
|
};
|
|
11665
11754
|
};
|
|
11755
|
+
var normalizeProcessorResult = (frame, result) => {
|
|
11756
|
+
if (result === false || result === undefined) {
|
|
11757
|
+
return [];
|
|
11758
|
+
}
|
|
11759
|
+
if (result === true) {
|
|
11760
|
+
return [frame];
|
|
11761
|
+
}
|
|
11762
|
+
if (Array.isArray(result)) {
|
|
11763
|
+
return result;
|
|
11764
|
+
}
|
|
11765
|
+
return [result];
|
|
11766
|
+
};
|
|
11767
|
+
var buildVoiceMediaProcessorGraphReport = (input) => {
|
|
11768
|
+
const events = input.events ?? [];
|
|
11769
|
+
const nodes = input.nodes.map((node) => {
|
|
11770
|
+
const nodeEvents = events.filter((event) => event.node === node.name);
|
|
11771
|
+
const droppedFrames2 = nodeEvents.reduce((total, event) => total + event.dropped, 0);
|
|
11772
|
+
const emittedFrames2 = nodeEvents.reduce((total, event) => total + event.emitted, 0);
|
|
11773
|
+
const inputFrames2 = nodeEvents.reduce((total, event) => total + event.inputs, 0);
|
|
11774
|
+
return {
|
|
11775
|
+
droppedFrames: droppedFrames2,
|
|
11776
|
+
emittedFrames: emittedFrames2,
|
|
11777
|
+
events: nodeEvents,
|
|
11778
|
+
inputFrames: inputFrames2,
|
|
11779
|
+
kind: node.kind ?? "processor",
|
|
11780
|
+
name: node.name,
|
|
11781
|
+
status: inputFrames2 > 0 && emittedFrames2 === 0 && node.kind !== "sink" ? "warn" : "pass"
|
|
11782
|
+
};
|
|
11783
|
+
});
|
|
11784
|
+
const inputFrames = events.filter((event) => event.node === input.nodes[0]?.name).length;
|
|
11785
|
+
const droppedFrames = events.reduce((total, event) => total + event.dropped, 0);
|
|
11786
|
+
const emittedFrames = input.nodes.at(-1) ? events.filter((event) => event.node === input.nodes.at(-1)?.name).reduce((total, event) => total + event.emitted, 0) : 0;
|
|
11787
|
+
const status = nodes.some((node) => node.status === "warn") ? "warn" : "pass";
|
|
11788
|
+
return {
|
|
11789
|
+
checkedAt: Date.now(),
|
|
11790
|
+
droppedFrames,
|
|
11791
|
+
emittedFrames,
|
|
11792
|
+
events,
|
|
11793
|
+
inputFrames,
|
|
11794
|
+
name: input.name,
|
|
11795
|
+
nodes,
|
|
11796
|
+
status
|
|
11797
|
+
};
|
|
11798
|
+
};
|
|
11799
|
+
var createVoiceMediaProcessorGraph = (input = {}) => {
|
|
11800
|
+
const nodes = input.nodes ?? [];
|
|
11801
|
+
const events = [];
|
|
11802
|
+
const process = async (frame) => {
|
|
11803
|
+
let frames = [frame];
|
|
11804
|
+
for (const node of nodes) {
|
|
11805
|
+
const nextFrames = [];
|
|
11806
|
+
for (const current of frames) {
|
|
11807
|
+
const output = normalizeProcessorResult(current, await node.process(current));
|
|
11808
|
+
events.push({
|
|
11809
|
+
at: Date.now(),
|
|
11810
|
+
dropped: output.length === 0 ? 1 : 0,
|
|
11811
|
+
emitted: output.length,
|
|
11812
|
+
frameId: current.id,
|
|
11813
|
+
inputs: 1,
|
|
11814
|
+
node: node.name
|
|
11815
|
+
});
|
|
11816
|
+
nextFrames.push(...output);
|
|
11817
|
+
}
|
|
11818
|
+
frames = nextFrames;
|
|
11819
|
+
if (frames.length === 0) {
|
|
11820
|
+
break;
|
|
11821
|
+
}
|
|
11822
|
+
}
|
|
11823
|
+
return frames;
|
|
11824
|
+
};
|
|
11825
|
+
return {
|
|
11826
|
+
nodes,
|
|
11827
|
+
process,
|
|
11828
|
+
processMany: async (frames) => {
|
|
11829
|
+
const output = [];
|
|
11830
|
+
for (const frame of frames) {
|
|
11831
|
+
output.push(...await process(frame));
|
|
11832
|
+
}
|
|
11833
|
+
return output;
|
|
11834
|
+
},
|
|
11835
|
+
report: () => buildVoiceMediaProcessorGraphReport({
|
|
11836
|
+
events,
|
|
11837
|
+
name: input.name ?? "voice-media-processor-graph",
|
|
11838
|
+
nodes
|
|
11839
|
+
})
|
|
11840
|
+
};
|
|
11841
|
+
};
|
|
11666
11842
|
var speechProbability = (frame) => {
|
|
11667
11843
|
if (frame.metadata?.isSpeech === true) {
|
|
11668
11844
|
return 1;
|
|
@@ -11849,7 +12025,9 @@ var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
|
11849
12025
|
calibration.status,
|
|
11850
12026
|
vad.status,
|
|
11851
12027
|
interruption.status,
|
|
11852
|
-
resampling?.status ?? "pass"
|
|
12028
|
+
resampling?.status ?? "pass",
|
|
12029
|
+
options.processorGraph?.status ?? "pass",
|
|
12030
|
+
options.transport?.status ?? "pass"
|
|
11853
12031
|
]);
|
|
11854
12032
|
return {
|
|
11855
12033
|
calibration,
|
|
@@ -11857,9 +12035,11 @@ var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
|
11857
12035
|
frames: frames.length,
|
|
11858
12036
|
interruption,
|
|
11859
12037
|
ok: status === "pass",
|
|
12038
|
+
processorGraph: options.processorGraph,
|
|
11860
12039
|
resampling,
|
|
11861
12040
|
status,
|
|
11862
12041
|
surface: options.surface ?? "voice-media-pipeline",
|
|
12042
|
+
transport: options.transport,
|
|
11863
12043
|
vad
|
|
11864
12044
|
};
|
|
11865
12045
|
};
|
|
@@ -11892,6 +12072,27 @@ var evaluateVoiceMediaPipelineEvidence = (report, input = {}) => {
|
|
|
11892
12072
|
if (input.requireResamplingReady && report.calibration.resamplingRequired && !report.resampling) {
|
|
11893
12073
|
issues.push("Expected resampling plan when calibration requires resampling.");
|
|
11894
12074
|
}
|
|
12075
|
+
if (input.requireProcessorGraph && !report.processorGraph) {
|
|
12076
|
+
issues.push("Expected media processor graph evidence.");
|
|
12077
|
+
}
|
|
12078
|
+
if (input.minProcessorGraphNodes !== undefined && (report.processorGraph?.nodes.length ?? 0) < input.minProcessorGraphNodes) {
|
|
12079
|
+
issues.push(`Expected at least ${String(input.minProcessorGraphNodes)} media processor node(s), found ${String(report.processorGraph?.nodes.length ?? 0)}.`);
|
|
12080
|
+
}
|
|
12081
|
+
if (input.minProcessorGraphEmittedFrames !== undefined && (report.processorGraph?.emittedFrames ?? 0) < input.minProcessorGraphEmittedFrames) {
|
|
12082
|
+
issues.push(`Expected at least ${String(input.minProcessorGraphEmittedFrames)} processor graph output frame(s), found ${String(report.processorGraph?.emittedFrames ?? 0)}.`);
|
|
12083
|
+
}
|
|
12084
|
+
if (input.requireTransportConnected && !report.transport?.connected) {
|
|
12085
|
+
issues.push("Expected connected media transport evidence.");
|
|
12086
|
+
}
|
|
12087
|
+
if (input.minTransportInputFrames !== undefined && (report.transport?.inputFrames ?? 0) < input.minTransportInputFrames) {
|
|
12088
|
+
issues.push(`Expected at least ${String(input.minTransportInputFrames)} transport input frame(s), found ${String(report.transport?.inputFrames ?? 0)}.`);
|
|
12089
|
+
}
|
|
12090
|
+
if (input.minTransportOutputFrames !== undefined && (report.transport?.outputFrames ?? 0) < input.minTransportOutputFrames) {
|
|
12091
|
+
issues.push(`Expected at least ${String(input.minTransportOutputFrames)} transport output frame(s), found ${String(report.transport?.outputFrames ?? 0)}.`);
|
|
12092
|
+
}
|
|
12093
|
+
if (input.maxTransportBackpressureEvents !== undefined && (report.transport?.backpressureEvents ?? 0) > input.maxTransportBackpressureEvents) {
|
|
12094
|
+
issues.push(`Expected at most ${String(input.maxTransportBackpressureEvents)} transport backpressure event(s), found ${String(report.transport?.backpressureEvents ?? 0)}.`);
|
|
12095
|
+
}
|
|
11895
12096
|
return {
|
|
11896
12097
|
issues,
|
|
11897
12098
|
ok: issues.length === 0,
|
|
@@ -11919,6 +12120,13 @@ var renderVoiceMediaPipelineMarkdown = (report) => [
|
|
|
11919
12120
|
`- Resampling required: ${report.calibration.resamplingRequired ? "yes" : "no"}`,
|
|
11920
12121
|
`- VAD segments: ${String(report.vad.segments.length)}`,
|
|
11921
12122
|
`- Interruption frames: ${String(report.interruption.interruptionFrames)}`,
|
|
12123
|
+
`- Processor graph: ${report.processorGraph ? `${report.processorGraph.name} (${String(report.processorGraph.nodes.length)} nodes)` : "n/a"}`,
|
|
12124
|
+
`- Processor graph emitted frames: ${String(report.processorGraph?.emittedFrames ?? 0)}`,
|
|
12125
|
+
`- Processor graph dropped frames: ${String(report.processorGraph?.droppedFrames ?? 0)}`,
|
|
12126
|
+
`- Transport: ${report.transport ? `${report.transport.name} (${report.transport.state})` : "n/a"}`,
|
|
12127
|
+
`- Transport input frames: ${String(report.transport?.inputFrames ?? 0)}`,
|
|
12128
|
+
`- Transport output frames: ${String(report.transport?.outputFrames ?? 0)}`,
|
|
12129
|
+
`- Transport backpressure events: ${String(report.transport?.backpressureEvents ?? 0)}`,
|
|
11922
12130
|
"",
|
|
11923
12131
|
"## Issues",
|
|
11924
12132
|
"",
|
|
@@ -11932,7 +12140,7 @@ var renderVoiceMediaPipelineMarkdown = (report) => [
|
|
|
11932
12140
|
var renderVoiceMediaPipelineHTML = (report, title = "Voice Media Pipeline Proof") => {
|
|
11933
12141
|
const issues = [...report.calibration.issues, ...report.interruption.issues].map((issue) => `<li class="${escapeHtml15(issue.severity)}"><strong>${escapeHtml15(issue.code)}</strong>: ${escapeHtml15(issue.message)}</li>`).join("");
|
|
11934
12142
|
const segments = report.vad.segments.map((segment) => `<tr><td>${escapeHtml15(segment.segmentId)}</td><td>${escapeHtml15(segment.frameCount)}</td><td>${escapeHtml15(segment.durationMs ?? "n/a")}</td><td>${escapeHtml15(segment.turnId ?? "n/a")}</td></tr>`).join("");
|
|
11935
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml15(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}.metric{background:#101814;border:1px solid #2e3d36;border-radius:18px;padding:14px}.metric span{color:#a8b5ad;display:block;font-size:.78rem;text-transform:uppercase}.metric strong{display:block;font-size:1.65rem;margin-top:5px}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:7px 11px}.pass{color:#86efac}.warn,.warning{color:#fde68a}.fail,.error{color:#fecaca}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3d36;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Native media pipeline</p><h1>${escapeHtml15(title)}</h1><p class="status ${escapeHtml15(report.status)}">${escapeHtml15(report.status)}</p><p>${escapeHtml15(report.surface)}</p><section class="summary"><div class="metric"><span>Frames</span><strong>${String(report.frames)}</strong></div><div class="metric"><span>Input audio</span><strong>${String(report.calibration.inputAudioFrames)}</strong></div><div class="metric"><span>Assistant audio</span><strong>${String(report.calibration.assistantAudioFrames)}</strong></div><div class="metric"><span>Trace linked</span><strong>${String(report.calibration.traceLinkedFrames)}</strong></div><div class="metric"><span>First audio</span><strong>${escapeHtml15(report.calibration.firstAudioLatencyMs ?? "n/a")}ms</strong></div><div class="metric"><span>VAD segments</span><strong>${String(report.vad.segments.length)}</strong></div><div class="metric"><span>Interruptions</span><strong>${String(report.interruption.interruptionFrames)}</strong></div><div class="metric"><span>Resampling</span><strong>${report.calibration.resamplingRequired ? "required" : "not required"}</strong></div></section></section><section class="card"><h2>Issues</h2><ul>${issues || '<li class="pass">No media pipeline issues.</li>'}</ul></section><section class="card"><h2>VAD Segments</h2><table><thead><tr><th>Segment</th><th>Frames</th><th>Duration ms</th><th>Turn</th></tr></thead><tbody>${segments || '<tr><td colspan="4">No VAD segments.</td></tr>'}</tbody></table></section></main></body></html>`;
|
|
12143
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml15(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}.metric{background:#101814;border:1px solid #2e3d36;border-radius:18px;padding:14px}.metric span{color:#a8b5ad;display:block;font-size:.78rem;text-transform:uppercase}.metric strong{display:block;font-size:1.65rem;margin-top:5px}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:7px 11px}.pass{color:#86efac}.warn,.warning{color:#fde68a}.fail,.error{color:#fecaca}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3d36;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Native media pipeline</p><h1>${escapeHtml15(title)}</h1><p class="status ${escapeHtml15(report.status)}">${escapeHtml15(report.status)}</p><p>${escapeHtml15(report.surface)}</p><section class="summary"><div class="metric"><span>Frames</span><strong>${String(report.frames)}</strong></div><div class="metric"><span>Input audio</span><strong>${String(report.calibration.inputAudioFrames)}</strong></div><div class="metric"><span>Assistant audio</span><strong>${String(report.calibration.assistantAudioFrames)}</strong></div><div class="metric"><span>Trace linked</span><strong>${String(report.calibration.traceLinkedFrames)}</strong></div><div class="metric"><span>First audio</span><strong>${escapeHtml15(report.calibration.firstAudioLatencyMs ?? "n/a")}ms</strong></div><div class="metric"><span>VAD segments</span><strong>${String(report.vad.segments.length)}</strong></div><div class="metric"><span>Interruptions</span><strong>${String(report.interruption.interruptionFrames)}</strong></div><div class="metric"><span>Processor graph</span><strong>${String(report.processorGraph?.nodes.length ?? 0)} nodes</strong></div><div class="metric"><span>Graph out/drop</span><strong>${String(report.processorGraph?.emittedFrames ?? 0)}/${String(report.processorGraph?.droppedFrames ?? 0)}</strong></div><div class="metric"><span>Resampling</span><strong>${report.calibration.resamplingRequired ? "required" : "not required"}</strong></div><div class="metric"><span>Transport</span><strong>${escapeHtml15(report.transport?.state ?? "n/a")}</strong></div><div class="metric"><span>Transport in/out</span><strong>${String(report.transport?.inputFrames ?? 0)}/${String(report.transport?.outputFrames ?? 0)}</strong></div><div class="metric"><span>Backpressure</span><strong>${String(report.transport?.backpressureEvents ?? 0)}</strong></div></section></section><section class="card"><h2>Issues</h2><ul>${issues || '<li class="pass">No media pipeline issues.</li>'}</ul></section><section class="card"><h2>VAD Segments</h2><table><thead><tr><th>Segment</th><th>Frames</th><th>Duration ms</th><th>Turn</th></tr></thead><tbody>${segments || '<tr><td colspan="4">No VAD segments.</td></tr>'}</tbody></table></section></main></body></html>`;
|
|
11936
12144
|
};
|
|
11937
12145
|
var createVoiceMediaPipelineRoutes = (options = {}) => {
|
|
11938
12146
|
const path = options.path ?? "/api/voice/media-pipeline";
|
|
@@ -34763,6 +34971,8 @@ export {
|
|
|
34763
34971
|
createVoiceMemoryAuditSinkDeliveryStore,
|
|
34764
34972
|
createVoiceMemoryAuditEventStore,
|
|
34765
34973
|
createVoiceMemoryAssistantMemoryStore,
|
|
34974
|
+
createVoiceMediaTransport,
|
|
34975
|
+
createVoiceMediaProcessorGraph,
|
|
34766
34976
|
createVoiceMediaPipelineRoutes,
|
|
34767
34977
|
createVoiceMediaFrameTransformPipeline,
|
|
34768
34978
|
createVoiceMediaFrame,
|
|
@@ -34915,7 +35125,9 @@ export {
|
|
|
34915
35125
|
buildVoiceObservabilityArtifactIndex,
|
|
34916
35126
|
buildVoiceMonitorRunReport,
|
|
34917
35127
|
buildVoiceMediaVadReport,
|
|
35128
|
+
buildVoiceMediaTransportReport,
|
|
34918
35129
|
buildVoiceMediaResamplingPlan,
|
|
35130
|
+
buildVoiceMediaProcessorGraphReport,
|
|
34919
35131
|
buildVoiceMediaPipelineReport,
|
|
34920
35132
|
buildVoiceMediaPipelineCalibrationReport,
|
|
34921
35133
|
buildVoiceMediaInterruptionReport,
|
package/dist/mediaPipeline.d.ts
CHANGED
|
@@ -34,6 +34,47 @@ export type VoiceMediaFrameTransformPipeline = {
|
|
|
34
34
|
pushMany: (frames: readonly VoiceMediaFrame[]) => Promise<readonly VoiceMediaFrame[]>;
|
|
35
35
|
transforms: readonly VoiceMediaFrameTransform[];
|
|
36
36
|
};
|
|
37
|
+
export type VoiceMediaProcessorNodeKind = 'branch' | 'filter' | 'processor' | 'sink';
|
|
38
|
+
export type VoiceMediaProcessorNodeEvent = {
|
|
39
|
+
at: number;
|
|
40
|
+
dropped: number;
|
|
41
|
+
emitted: number;
|
|
42
|
+
frameId: string;
|
|
43
|
+
inputs: number;
|
|
44
|
+
node: string;
|
|
45
|
+
};
|
|
46
|
+
export type VoiceMediaProcessorNodeReport = {
|
|
47
|
+
droppedFrames: number;
|
|
48
|
+
emittedFrames: number;
|
|
49
|
+
events: readonly VoiceMediaProcessorNodeEvent[];
|
|
50
|
+
inputFrames: number;
|
|
51
|
+
kind: VoiceMediaProcessorNodeKind;
|
|
52
|
+
name: string;
|
|
53
|
+
status: VoiceMediaPipelineStatus;
|
|
54
|
+
};
|
|
55
|
+
export type VoiceMediaProcessorGraphReport = {
|
|
56
|
+
checkedAt: number;
|
|
57
|
+
droppedFrames: number;
|
|
58
|
+
emittedFrames: number;
|
|
59
|
+
events: readonly VoiceMediaProcessorNodeEvent[];
|
|
60
|
+
inputFrames: number;
|
|
61
|
+
name: string;
|
|
62
|
+
nodes: readonly VoiceMediaProcessorNodeReport[];
|
|
63
|
+
status: VoiceMediaPipelineStatus;
|
|
64
|
+
};
|
|
65
|
+
export type VoiceMediaProcessorNode = {
|
|
66
|
+
inputFormat?: AudioFormat;
|
|
67
|
+
kind?: VoiceMediaProcessorNodeKind;
|
|
68
|
+
name: string;
|
|
69
|
+
outputFormat?: AudioFormat;
|
|
70
|
+
process: (frame: VoiceMediaFrame) => boolean | VoiceMediaFrame | readonly VoiceMediaFrame[] | undefined | Promise<boolean | VoiceMediaFrame | readonly VoiceMediaFrame[] | undefined>;
|
|
71
|
+
};
|
|
72
|
+
export type VoiceMediaProcessorGraph = {
|
|
73
|
+
nodes: readonly VoiceMediaProcessorNode[];
|
|
74
|
+
process: (frame: VoiceMediaFrame) => Promise<readonly VoiceMediaFrame[]>;
|
|
75
|
+
processMany: (frames: readonly VoiceMediaFrame[]) => Promise<readonly VoiceMediaFrame[]>;
|
|
76
|
+
report: () => VoiceMediaProcessorGraphReport;
|
|
77
|
+
};
|
|
37
78
|
export type VoiceMediaTransportAdapter = {
|
|
38
79
|
close?: () => Promise<void> | void;
|
|
39
80
|
connect?: () => Promise<void> | void;
|
|
@@ -43,6 +84,44 @@ export type VoiceMediaTransportAdapter = {
|
|
|
43
84
|
outputFormat?: AudioFormat;
|
|
44
85
|
send: (frame: VoiceMediaFrame) => Promise<void> | void;
|
|
45
86
|
};
|
|
87
|
+
export type VoiceMediaTransportState = 'closed' | 'closing' | 'failed' | 'idle' | 'open';
|
|
88
|
+
export type VoiceMediaTransportEventKind = 'backpressure' | 'close' | 'connect' | 'error' | 'frame-in' | 'frame-out';
|
|
89
|
+
export type VoiceMediaTransportEvent = {
|
|
90
|
+
at: number;
|
|
91
|
+
bufferedFrames?: number;
|
|
92
|
+
error?: string;
|
|
93
|
+
frameId?: string;
|
|
94
|
+
kind: VoiceMediaTransportEventKind;
|
|
95
|
+
state: VoiceMediaTransportState;
|
|
96
|
+
};
|
|
97
|
+
export type VoiceMediaTransportReport = {
|
|
98
|
+
backpressureEvents: number;
|
|
99
|
+
checkedAt: number;
|
|
100
|
+
closed: boolean;
|
|
101
|
+
connected: boolean;
|
|
102
|
+
events: readonly VoiceMediaTransportEvent[];
|
|
103
|
+
failed: boolean;
|
|
104
|
+
inputFrames: number;
|
|
105
|
+
name: string;
|
|
106
|
+
outputFrames: number;
|
|
107
|
+
state: VoiceMediaTransportState;
|
|
108
|
+
status: VoiceMediaPipelineStatus;
|
|
109
|
+
};
|
|
110
|
+
export type VoiceMediaTransport = VoiceMediaTransportAdapter & {
|
|
111
|
+
events: () => readonly VoiceMediaTransportEvent[];
|
|
112
|
+
receive: (frame: VoiceMediaFrame) => Promise<void>;
|
|
113
|
+
report: () => VoiceMediaTransportReport;
|
|
114
|
+
state: () => VoiceMediaTransportState;
|
|
115
|
+
};
|
|
116
|
+
export type VoiceMediaTransportOptions = {
|
|
117
|
+
inputFormat?: AudioFormat;
|
|
118
|
+
maxBufferedFrames?: number;
|
|
119
|
+
name: string;
|
|
120
|
+
onClose?: () => Promise<void> | void;
|
|
121
|
+
onConnect?: () => Promise<void> | void;
|
|
122
|
+
onSend?: (frame: VoiceMediaFrame) => Promise<void> | void;
|
|
123
|
+
outputFormat?: AudioFormat;
|
|
124
|
+
};
|
|
46
125
|
export type VoiceMediaPipelineCalibrationInput = {
|
|
47
126
|
expectedInputFormat?: AudioFormat;
|
|
48
127
|
expectedOutputFormat?: AudioFormat;
|
|
@@ -113,6 +192,12 @@ export type VoiceMediaInterruptionReport = {
|
|
|
113
192
|
status: VoiceMediaPipelineStatus;
|
|
114
193
|
};
|
|
115
194
|
export declare const createVoiceMediaFrame: (frame: VoiceMediaFrame) => VoiceMediaFrame;
|
|
195
|
+
export declare const buildVoiceMediaTransportReport: (input: {
|
|
196
|
+
events?: readonly VoiceMediaTransportEvent[];
|
|
197
|
+
name: string;
|
|
198
|
+
state?: VoiceMediaTransportState;
|
|
199
|
+
}) => VoiceMediaTransportReport;
|
|
200
|
+
export declare const createVoiceMediaTransport: (options: VoiceMediaTransportOptions) => VoiceMediaTransport;
|
|
116
201
|
export declare const buildVoiceMediaResamplingPlan: (input: {
|
|
117
202
|
inputFormat: AudioFormat;
|
|
118
203
|
outputFormat: AudioFormat;
|
|
@@ -120,6 +205,15 @@ export declare const buildVoiceMediaResamplingPlan: (input: {
|
|
|
120
205
|
export declare const createVoiceMediaFrameTransformPipeline: (input?: {
|
|
121
206
|
transforms?: readonly VoiceMediaFrameTransform[];
|
|
122
207
|
}) => VoiceMediaFrameTransformPipeline;
|
|
208
|
+
export declare const buildVoiceMediaProcessorGraphReport: (input: {
|
|
209
|
+
events?: readonly VoiceMediaProcessorNodeEvent[];
|
|
210
|
+
name: string;
|
|
211
|
+
nodes: readonly VoiceMediaProcessorNode[];
|
|
212
|
+
}) => VoiceMediaProcessorGraphReport;
|
|
213
|
+
export declare const createVoiceMediaProcessorGraph: (input?: {
|
|
214
|
+
name?: string;
|
|
215
|
+
nodes?: readonly VoiceMediaProcessorNode[];
|
|
216
|
+
}) => VoiceMediaProcessorGraph;
|
|
123
217
|
export declare const buildVoiceMediaVadReport: (input?: VoiceMediaVadInput) => VoiceMediaVadReport;
|
|
124
218
|
export declare const buildVoiceMediaInterruptionReport: (input?: VoiceMediaInterruptionInput) => VoiceMediaInterruptionReport;
|
|
125
219
|
export declare const buildVoiceMediaPipelineCalibrationReport: (input?: VoiceMediaPipelineCalibrationInput) => VoiceMediaPipelineCalibrationReport;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
-
import { type VoiceMediaFrame, type VoiceMediaInterruptionReport, type VoiceMediaPipelineCalibrationInput, type VoiceMediaPipelineCalibrationReport, type VoiceMediaPipelineStatus, type VoiceMediaResamplingPlan, type VoiceMediaVadReport } from './mediaPipeline';
|
|
2
|
+
import { type VoiceMediaFrame, type VoiceMediaInterruptionReport, type VoiceMediaPipelineCalibrationInput, type VoiceMediaPipelineCalibrationReport, type VoiceMediaPipelineStatus, type VoiceMediaProcessorGraphReport, type VoiceMediaResamplingPlan, type VoiceMediaTransportReport, type VoiceMediaVadReport } from './mediaPipeline';
|
|
3
3
|
export type VoiceMediaPipelineReportOptions = VoiceMediaPipelineCalibrationInput & {
|
|
4
4
|
frames?: readonly VoiceMediaFrame[];
|
|
5
5
|
maxInterruptionLatencyMs?: number;
|
|
6
6
|
maxSilenceFrames?: number;
|
|
7
7
|
minSpeechFrames?: number;
|
|
8
|
+
processorGraph?: VoiceMediaProcessorGraphReport;
|
|
8
9
|
speechEndThreshold?: number;
|
|
9
10
|
speechStartThreshold?: number;
|
|
11
|
+
transport?: VoiceMediaTransportReport;
|
|
10
12
|
};
|
|
11
13
|
export type VoiceMediaPipelineReport = {
|
|
12
14
|
calibration: VoiceMediaPipelineCalibrationReport;
|
|
@@ -15,8 +17,10 @@ export type VoiceMediaPipelineReport = {
|
|
|
15
17
|
interruption: VoiceMediaInterruptionReport;
|
|
16
18
|
ok: boolean;
|
|
17
19
|
resampling?: VoiceMediaResamplingPlan;
|
|
20
|
+
processorGraph?: VoiceMediaProcessorGraphReport;
|
|
18
21
|
status: VoiceMediaPipelineStatus;
|
|
19
22
|
surface: string;
|
|
23
|
+
transport?: VoiceMediaTransportReport;
|
|
20
24
|
vad: VoiceMediaVadReport;
|
|
21
25
|
};
|
|
22
26
|
export type VoiceMediaPipelineAssertionInput = {
|
|
@@ -24,11 +28,18 @@ export type VoiceMediaPipelineAssertionInput = {
|
|
|
24
28
|
maxInterruptionLatencyMs?: number;
|
|
25
29
|
minAssistantAudioFrames?: number;
|
|
26
30
|
minInputAudioFrames?: number;
|
|
31
|
+
minProcessorGraphEmittedFrames?: number;
|
|
32
|
+
minProcessorGraphNodes?: number;
|
|
33
|
+
minTransportInputFrames?: number;
|
|
34
|
+
minTransportOutputFrames?: number;
|
|
27
35
|
minTraceLinkedFrames?: number;
|
|
28
36
|
minVadSegments?: number;
|
|
37
|
+
maxTransportBackpressureEvents?: number;
|
|
29
38
|
requireInterruptionFrame?: boolean;
|
|
30
39
|
requirePass?: boolean;
|
|
40
|
+
requireProcessorGraph?: boolean;
|
|
31
41
|
requireResamplingReady?: boolean;
|
|
42
|
+
requireTransportConnected?: boolean;
|
|
32
43
|
};
|
|
33
44
|
export type VoiceMediaPipelineAssertionReport = {
|
|
34
45
|
issues: string[];
|