@absolutejs/voice 0.0.22-beta.312 → 0.0.22-beta.314
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 +0 -2
- package/dist/index.js +29 -143
- package/dist/mediaPipelineRoutes.d.ts +16 -11
- package/package.json +4 -1
- package/dist/mediaPipeline.d.ts +0 -169
package/dist/index.d.ts
CHANGED
|
@@ -15,8 +15,6 @@ 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 { buildVoiceMediaTransportReport, buildVoiceMediaInterruptionReport, buildVoiceMediaPipelineCalibrationReport, buildVoiceMediaResamplingPlan, buildVoiceMediaVadReport, createVoiceMediaFrame, createVoiceMediaFrameTransformPipeline, createVoiceMediaTransport } from './mediaPipeline';
|
|
19
|
-
export type { VoiceMediaFrame, VoiceMediaFrameKind, VoiceMediaFrameSource, VoiceMediaFrameTransform, VoiceMediaFrameTransformPipeline, VoiceMediaInterruptionInput, VoiceMediaInterruptionReport, VoiceMediaPipelineCalibrationInput, VoiceMediaPipelineCalibrationIssue, VoiceMediaPipelineCalibrationReport, VoiceMediaPipelineStatus, VoiceMediaResamplingPlan, VoiceMediaTransport, VoiceMediaTransportAdapter, VoiceMediaTransportEvent, VoiceMediaTransportEventKind, VoiceMediaTransportOptions, VoiceMediaTransportReport, VoiceMediaTransportState, VoiceMediaVadInput, VoiceMediaVadReport, VoiceMediaVadSegment } from './mediaPipeline';
|
|
20
18
|
export { assertVoiceMediaPipelineEvidence, buildVoiceMediaPipelineReport, createVoiceMediaPipelineRoutes, evaluateVoiceMediaPipelineEvidence, renderVoiceMediaPipelineHTML, renderVoiceMediaPipelineMarkdown } from './mediaPipelineRoutes';
|
|
21
19
|
export type { VoiceMediaPipelineAssertionInput, VoiceMediaPipelineAssertionReport, VoiceMediaPipelineReport, VoiceMediaPipelineReportOptions, VoiceMediaPipelineRoutesOptions } from './mediaPipelineRoutes';
|
|
22
20
|
export { buildVoiceDemoReadyReport, createVoiceDemoReadyRoutes, renderVoiceDemoReadyHTML } from './demoReadyRoutes';
|
package/dist/index.js
CHANGED
|
@@ -11609,7 +11609,10 @@ var createVoiceDiagnosticsRoutes = (options) => {
|
|
|
11609
11609
|
});
|
|
11610
11610
|
return routes;
|
|
11611
11611
|
};
|
|
11612
|
-
// src/
|
|
11612
|
+
// src/mediaPipelineRoutes.ts
|
|
11613
|
+
import { Elysia as Elysia12 } from "elysia";
|
|
11614
|
+
|
|
11615
|
+
// node_modules/@absolutejs/media/dist/index.js
|
|
11613
11616
|
var formatLabel2 = (format) => `${format.container}/${format.encoding}/${String(format.sampleRateHz)}hz/${String(format.channels)}ch`;
|
|
11614
11617
|
var formatMatches2 = (actual, expected) => actual.container === expected.container && actual.encoding === expected.encoding && actual.sampleRateHz === expected.sampleRateHz && actual.channels === expected.channels;
|
|
11615
11618
|
var pushIssue = (issues, severity, code, message) => {
|
|
@@ -11619,97 +11622,7 @@ var numericMetadata = (frame, key) => {
|
|
|
11619
11622
|
const value = frame.metadata?.[key];
|
|
11620
11623
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
11621
11624
|
};
|
|
11622
|
-
var
|
|
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
|
-
};
|
|
11712
|
-
var buildVoiceMediaResamplingPlan = (input) => {
|
|
11625
|
+
var buildMediaResamplingPlan = (input) => {
|
|
11713
11626
|
const required = !formatMatches2(input.inputFormat, input.outputFormat);
|
|
11714
11627
|
return {
|
|
11715
11628
|
inputFormat: input.inputFormat,
|
|
@@ -11719,39 +11632,6 @@ var buildVoiceMediaResamplingPlan = (input) => {
|
|
|
11719
11632
|
status: input.inputFormat.container === input.outputFormat.container && input.inputFormat.encoding === input.outputFormat.encoding && input.inputFormat.channels === input.outputFormat.channels ? "pass" : "warn"
|
|
11720
11633
|
};
|
|
11721
11634
|
};
|
|
11722
|
-
var createVoiceMediaFrameTransformPipeline = (input = {}) => {
|
|
11723
|
-
const transforms = input.transforms ?? [];
|
|
11724
|
-
const push = async (frame) => {
|
|
11725
|
-
let frames = [frame];
|
|
11726
|
-
for (const transform of transforms) {
|
|
11727
|
-
const nextFrames = [];
|
|
11728
|
-
for (const current of frames) {
|
|
11729
|
-
const transformed = await transform.transform(current);
|
|
11730
|
-
if (transformed === undefined) {
|
|
11731
|
-
continue;
|
|
11732
|
-
}
|
|
11733
|
-
if (Array.isArray(transformed)) {
|
|
11734
|
-
nextFrames.push(...transformed);
|
|
11735
|
-
} else {
|
|
11736
|
-
nextFrames.push(transformed);
|
|
11737
|
-
}
|
|
11738
|
-
}
|
|
11739
|
-
frames = nextFrames;
|
|
11740
|
-
}
|
|
11741
|
-
return frames;
|
|
11742
|
-
};
|
|
11743
|
-
return {
|
|
11744
|
-
push,
|
|
11745
|
-
pushMany: async (frames) => {
|
|
11746
|
-
const output = [];
|
|
11747
|
-
for (const frame of frames) {
|
|
11748
|
-
output.push(...await push(frame));
|
|
11749
|
-
}
|
|
11750
|
-
return output;
|
|
11751
|
-
},
|
|
11752
|
-
transforms
|
|
11753
|
-
};
|
|
11754
|
-
};
|
|
11755
11635
|
var speechProbability = (frame) => {
|
|
11756
11636
|
if (frame.metadata?.isSpeech === true) {
|
|
11757
11637
|
return 1;
|
|
@@ -11767,7 +11647,7 @@ var speechProbability = (frame) => {
|
|
|
11767
11647
|
}
|
|
11768
11648
|
return 0;
|
|
11769
11649
|
};
|
|
11770
|
-
var
|
|
11650
|
+
var buildMediaVadReport = (input = {}) => {
|
|
11771
11651
|
const frames = (input.frames ?? []).filter((frame) => frame.kind === "input-audio");
|
|
11772
11652
|
const speechStartThreshold = input.speechStartThreshold ?? 0.6;
|
|
11773
11653
|
const speechEndThreshold = input.speechEndThreshold ?? 0.35;
|
|
@@ -11825,7 +11705,7 @@ var buildVoiceMediaVadReport = (input = {}) => {
|
|
|
11825
11705
|
status: frames.length === 0 ? "warn" : "pass"
|
|
11826
11706
|
};
|
|
11827
11707
|
};
|
|
11828
|
-
var
|
|
11708
|
+
var buildMediaInterruptionReport = (input = {}) => {
|
|
11829
11709
|
const issues = [];
|
|
11830
11710
|
const interruptionFrames = (input.frames ?? []).filter((frame) => frame.kind === "interruption");
|
|
11831
11711
|
const latenciesMs = interruptionFrames.map((frame) => frame.latencyMs).filter((latency) => typeof latency === "number");
|
|
@@ -11844,7 +11724,7 @@ var buildVoiceMediaInterruptionReport = (input = {}) => {
|
|
|
11844
11724
|
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass"
|
|
11845
11725
|
};
|
|
11846
11726
|
};
|
|
11847
|
-
var
|
|
11727
|
+
var buildMediaPipelineCalibrationReport = (input = {}) => {
|
|
11848
11728
|
const frames = input.frames ?? [];
|
|
11849
11729
|
const issues = [];
|
|
11850
11730
|
const inputFrames = frames.filter((frame) => frame.kind === "input-audio");
|
|
@@ -11902,13 +11782,13 @@ var buildVoiceMediaPipelineCalibrationReport = (input = {}) => {
|
|
|
11902
11782
|
resamplingRequired,
|
|
11903
11783
|
resamplingTargetHz,
|
|
11904
11784
|
status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
|
|
11905
|
-
surface: input.surface ?? "
|
|
11785
|
+
surface: input.surface ?? "media-pipeline",
|
|
11906
11786
|
traceLinkedFrames,
|
|
11907
11787
|
turnCommitFrames: turnCommitFrames.length
|
|
11908
11788
|
};
|
|
11909
11789
|
};
|
|
11790
|
+
|
|
11910
11791
|
// src/mediaPipelineRoutes.ts
|
|
11911
|
-
import { Elysia as Elysia12 } from "elysia";
|
|
11912
11792
|
var escapeHtml15 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11913
11793
|
var statusRank2 = {
|
|
11914
11794
|
pass: 0,
|
|
@@ -11918,19 +11798,19 @@ var statusRank2 = {
|
|
|
11918
11798
|
var worstStatus = (statuses) => statuses.reduce((worst, status) => statusRank2[status] > statusRank2[worst] ? status : worst, "pass");
|
|
11919
11799
|
var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
11920
11800
|
const frames = options.frames ?? [];
|
|
11921
|
-
const calibration =
|
|
11922
|
-
const vad =
|
|
11801
|
+
const calibration = buildMediaPipelineCalibrationReport(options);
|
|
11802
|
+
const vad = buildMediaVadReport({
|
|
11923
11803
|
frames,
|
|
11924
11804
|
maxSilenceFrames: options.maxSilenceFrames,
|
|
11925
11805
|
minSpeechFrames: options.minSpeechFrames,
|
|
11926
11806
|
speechEndThreshold: options.speechEndThreshold,
|
|
11927
11807
|
speechStartThreshold: options.speechStartThreshold
|
|
11928
11808
|
});
|
|
11929
|
-
const interruption =
|
|
11809
|
+
const interruption = buildMediaInterruptionReport({
|
|
11930
11810
|
frames,
|
|
11931
11811
|
maxInterruptionLatencyMs: options.maxInterruptionLatencyMs
|
|
11932
11812
|
});
|
|
11933
|
-
const resampling = calibration.inputFormat && calibration.outputFormat ?
|
|
11813
|
+
const resampling = calibration.inputFormat && calibration.outputFormat ? buildMediaResamplingPlan({
|
|
11934
11814
|
inputFormat: calibration.inputFormat,
|
|
11935
11815
|
outputFormat: calibration.outputFormat
|
|
11936
11816
|
}) : undefined;
|
|
@@ -11939,6 +11819,7 @@ var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
|
11939
11819
|
vad.status,
|
|
11940
11820
|
interruption.status,
|
|
11941
11821
|
resampling?.status ?? "pass",
|
|
11822
|
+
options.processorGraph?.status ?? "pass",
|
|
11942
11823
|
options.transport?.status ?? "pass"
|
|
11943
11824
|
]);
|
|
11944
11825
|
return {
|
|
@@ -11947,6 +11828,7 @@ var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
|
11947
11828
|
frames: frames.length,
|
|
11948
11829
|
interruption,
|
|
11949
11830
|
ok: status === "pass",
|
|
11831
|
+
processorGraph: options.processorGraph,
|
|
11950
11832
|
resampling,
|
|
11951
11833
|
status,
|
|
11952
11834
|
surface: options.surface ?? "voice-media-pipeline",
|
|
@@ -11983,6 +11865,15 @@ var evaluateVoiceMediaPipelineEvidence = (report, input = {}) => {
|
|
|
11983
11865
|
if (input.requireResamplingReady && report.calibration.resamplingRequired && !report.resampling) {
|
|
11984
11866
|
issues.push("Expected resampling plan when calibration requires resampling.");
|
|
11985
11867
|
}
|
|
11868
|
+
if (input.requireProcessorGraph && !report.processorGraph) {
|
|
11869
|
+
issues.push("Expected media processor graph evidence.");
|
|
11870
|
+
}
|
|
11871
|
+
if (input.minProcessorGraphNodes !== undefined && (report.processorGraph?.nodes.length ?? 0) < input.minProcessorGraphNodes) {
|
|
11872
|
+
issues.push(`Expected at least ${String(input.minProcessorGraphNodes)} media processor node(s), found ${String(report.processorGraph?.nodes.length ?? 0)}.`);
|
|
11873
|
+
}
|
|
11874
|
+
if (input.minProcessorGraphEmittedFrames !== undefined && (report.processorGraph?.emittedFrames ?? 0) < input.minProcessorGraphEmittedFrames) {
|
|
11875
|
+
issues.push(`Expected at least ${String(input.minProcessorGraphEmittedFrames)} processor graph output frame(s), found ${String(report.processorGraph?.emittedFrames ?? 0)}.`);
|
|
11876
|
+
}
|
|
11986
11877
|
if (input.requireTransportConnected && !report.transport?.connected) {
|
|
11987
11878
|
issues.push("Expected connected media transport evidence.");
|
|
11988
11879
|
}
|
|
@@ -12022,6 +11913,9 @@ var renderVoiceMediaPipelineMarkdown = (report) => [
|
|
|
12022
11913
|
`- Resampling required: ${report.calibration.resamplingRequired ? "yes" : "no"}`,
|
|
12023
11914
|
`- VAD segments: ${String(report.vad.segments.length)}`,
|
|
12024
11915
|
`- Interruption frames: ${String(report.interruption.interruptionFrames)}`,
|
|
11916
|
+
`- Processor graph: ${report.processorGraph ? `${report.processorGraph.name} (${String(report.processorGraph.nodes.length)} nodes)` : "n/a"}`,
|
|
11917
|
+
`- Processor graph emitted frames: ${String(report.processorGraph?.emittedFrames ?? 0)}`,
|
|
11918
|
+
`- Processor graph dropped frames: ${String(report.processorGraph?.droppedFrames ?? 0)}`,
|
|
12025
11919
|
`- Transport: ${report.transport ? `${report.transport.name} (${report.transport.state})` : "n/a"}`,
|
|
12026
11920
|
`- Transport input frames: ${String(report.transport?.inputFrames ?? 0)}`,
|
|
12027
11921
|
`- Transport output frames: ${String(report.transport?.outputFrames ?? 0)}`,
|
|
@@ -12039,7 +11933,7 @@ var renderVoiceMediaPipelineMarkdown = (report) => [
|
|
|
12039
11933
|
var renderVoiceMediaPipelineHTML = (report, title = "Voice Media Pipeline Proof") => {
|
|
12040
11934
|
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("");
|
|
12041
11935
|
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("");
|
|
12042
|
-
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><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
|
+
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>`;
|
|
12043
11937
|
};
|
|
12044
11938
|
var createVoiceMediaPipelineRoutes = (options = {}) => {
|
|
12045
11939
|
const path = options.path ?? "/api/voice/media-pipeline";
|
|
@@ -34870,10 +34764,7 @@ export {
|
|
|
34870
34764
|
createVoiceMemoryAuditSinkDeliveryStore,
|
|
34871
34765
|
createVoiceMemoryAuditEventStore,
|
|
34872
34766
|
createVoiceMemoryAssistantMemoryStore,
|
|
34873
|
-
createVoiceMediaTransport,
|
|
34874
34767
|
createVoiceMediaPipelineRoutes,
|
|
34875
|
-
createVoiceMediaFrameTransformPipeline,
|
|
34876
|
-
createVoiceMediaFrame,
|
|
34877
34768
|
createVoiceLiveOpsRoutes,
|
|
34878
34769
|
createVoiceLiveOpsController,
|
|
34879
34770
|
createVoiceLiveLatencyRoutes,
|
|
@@ -35022,12 +34913,7 @@ export {
|
|
|
35022
34913
|
buildVoiceObservabilityExport,
|
|
35023
34914
|
buildVoiceObservabilityArtifactIndex,
|
|
35024
34915
|
buildVoiceMonitorRunReport,
|
|
35025
|
-
buildVoiceMediaVadReport,
|
|
35026
|
-
buildVoiceMediaTransportReport,
|
|
35027
|
-
buildVoiceMediaResamplingPlan,
|
|
35028
34916
|
buildVoiceMediaPipelineReport,
|
|
35029
|
-
buildVoiceMediaPipelineCalibrationReport,
|
|
35030
|
-
buildVoiceMediaInterruptionReport,
|
|
35031
34917
|
buildVoiceLiveOpsControlState,
|
|
35032
34918
|
buildVoiceLatencySLOGate,
|
|
35033
34919
|
buildVoiceIncidentBundle,
|
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
-
import { type
|
|
3
|
-
export type VoiceMediaPipelineReportOptions =
|
|
4
|
-
frames?: readonly
|
|
2
|
+
import { type MediaFrame, type MediaInterruptionReport, type MediaPipelineCalibrationInput, type MediaPipelineCalibrationReport, type MediaPipelineStatus, type MediaProcessorGraphReport, type MediaResamplingPlan, type MediaTransportReport, type MediaVadReport } from '@absolutejs/media';
|
|
3
|
+
export type VoiceMediaPipelineReportOptions = MediaPipelineCalibrationInput & {
|
|
4
|
+
frames?: readonly MediaFrame[];
|
|
5
5
|
maxInterruptionLatencyMs?: number;
|
|
6
6
|
maxSilenceFrames?: number;
|
|
7
7
|
minSpeechFrames?: number;
|
|
8
|
+
processorGraph?: MediaProcessorGraphReport;
|
|
8
9
|
speechEndThreshold?: number;
|
|
9
10
|
speechStartThreshold?: number;
|
|
10
|
-
transport?:
|
|
11
|
+
transport?: MediaTransportReport;
|
|
11
12
|
};
|
|
12
13
|
export type VoiceMediaPipelineReport = {
|
|
13
|
-
calibration:
|
|
14
|
+
calibration: MediaPipelineCalibrationReport;
|
|
14
15
|
checkedAt: number;
|
|
15
16
|
frames: number;
|
|
16
|
-
interruption:
|
|
17
|
+
interruption: MediaInterruptionReport;
|
|
17
18
|
ok: boolean;
|
|
18
|
-
resampling?:
|
|
19
|
-
|
|
19
|
+
resampling?: MediaResamplingPlan;
|
|
20
|
+
processorGraph?: MediaProcessorGraphReport;
|
|
21
|
+
status: MediaPipelineStatus;
|
|
20
22
|
surface: string;
|
|
21
|
-
transport?:
|
|
22
|
-
vad:
|
|
23
|
+
transport?: MediaTransportReport;
|
|
24
|
+
vad: MediaVadReport;
|
|
23
25
|
};
|
|
24
26
|
export type VoiceMediaPipelineAssertionInput = {
|
|
25
27
|
maxFirstAudioLatencyMs?: number;
|
|
26
28
|
maxInterruptionLatencyMs?: number;
|
|
27
29
|
minAssistantAudioFrames?: number;
|
|
28
30
|
minInputAudioFrames?: number;
|
|
31
|
+
minProcessorGraphEmittedFrames?: number;
|
|
32
|
+
minProcessorGraphNodes?: number;
|
|
29
33
|
minTransportInputFrames?: number;
|
|
30
34
|
minTransportOutputFrames?: number;
|
|
31
35
|
minTraceLinkedFrames?: number;
|
|
@@ -33,13 +37,14 @@ export type VoiceMediaPipelineAssertionInput = {
|
|
|
33
37
|
maxTransportBackpressureEvents?: number;
|
|
34
38
|
requireInterruptionFrame?: boolean;
|
|
35
39
|
requirePass?: boolean;
|
|
40
|
+
requireProcessorGraph?: boolean;
|
|
36
41
|
requireResamplingReady?: boolean;
|
|
37
42
|
requireTransportConnected?: boolean;
|
|
38
43
|
};
|
|
39
44
|
export type VoiceMediaPipelineAssertionReport = {
|
|
40
45
|
issues: string[];
|
|
41
46
|
ok: boolean;
|
|
42
|
-
status:
|
|
47
|
+
status: MediaPipelineStatus;
|
|
43
48
|
surface: string;
|
|
44
49
|
};
|
|
45
50
|
export type VoiceMediaPipelineRoutesOptions = VoiceMediaPipelineReportOptions & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absolutejs/voice",
|
|
3
|
-
"version": "0.0.22-beta.
|
|
3
|
+
"version": "0.0.22-beta.314",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -245,6 +245,9 @@
|
|
|
245
245
|
"optional": true
|
|
246
246
|
}
|
|
247
247
|
},
|
|
248
|
+
"dependencies": {
|
|
249
|
+
"@absolutejs/media": "0.0.1-beta.1"
|
|
250
|
+
},
|
|
248
251
|
"devDependencies": {
|
|
249
252
|
"@absolutejs/absolute": "0.19.0-beta.646",
|
|
250
253
|
"@angular/core": "^21.0.0",
|
package/dist/mediaPipeline.d.ts
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import type { AudioFormat } from './types';
|
|
2
|
-
export type VoiceMediaFrameKind = 'assistant-audio' | 'input-audio' | 'interruption' | 'metadata' | 'transcript' | 'turn-commit';
|
|
3
|
-
export type VoiceMediaFrameSource = 'browser' | 'provider' | 'telephony' | 'voice-runtime';
|
|
4
|
-
export type VoiceMediaPipelineStatus = 'fail' | 'pass' | 'warn';
|
|
5
|
-
export type VoiceMediaResamplingPlan = {
|
|
6
|
-
inputFormat: AudioFormat;
|
|
7
|
-
outputFormat: AudioFormat;
|
|
8
|
-
ratio: number;
|
|
9
|
-
required: boolean;
|
|
10
|
-
status: VoiceMediaPipelineStatus;
|
|
11
|
-
};
|
|
12
|
-
export type VoiceMediaFrame = {
|
|
13
|
-
at?: number;
|
|
14
|
-
audio?: ArrayBuffer | ArrayBufferView;
|
|
15
|
-
durationMs?: number;
|
|
16
|
-
format?: AudioFormat;
|
|
17
|
-
id: string;
|
|
18
|
-
kind: VoiceMediaFrameKind;
|
|
19
|
-
latencyMs?: number;
|
|
20
|
-
metadata?: Record<string, unknown>;
|
|
21
|
-
sessionId?: string;
|
|
22
|
-
source: VoiceMediaFrameSource | (string & {});
|
|
23
|
-
traceEventId?: string;
|
|
24
|
-
turnId?: string;
|
|
25
|
-
};
|
|
26
|
-
export type VoiceMediaFrameTransform = {
|
|
27
|
-
inputFormat?: AudioFormat;
|
|
28
|
-
name: string;
|
|
29
|
-
outputFormat?: AudioFormat;
|
|
30
|
-
transform: (frame: VoiceMediaFrame) => VoiceMediaFrame | readonly VoiceMediaFrame[] | undefined | Promise<VoiceMediaFrame | readonly VoiceMediaFrame[] | undefined>;
|
|
31
|
-
};
|
|
32
|
-
export type VoiceMediaFrameTransformPipeline = {
|
|
33
|
-
push: (frame: VoiceMediaFrame) => Promise<readonly VoiceMediaFrame[]>;
|
|
34
|
-
pushMany: (frames: readonly VoiceMediaFrame[]) => Promise<readonly VoiceMediaFrame[]>;
|
|
35
|
-
transforms: readonly VoiceMediaFrameTransform[];
|
|
36
|
-
};
|
|
37
|
-
export type VoiceMediaTransportAdapter = {
|
|
38
|
-
close?: () => Promise<void> | void;
|
|
39
|
-
connect?: () => Promise<void> | void;
|
|
40
|
-
inputFormat?: AudioFormat;
|
|
41
|
-
name: string;
|
|
42
|
-
onFrame?: (handler: (frame: VoiceMediaFrame) => Promise<void> | void) => () => void;
|
|
43
|
-
outputFormat?: AudioFormat;
|
|
44
|
-
send: (frame: VoiceMediaFrame) => Promise<void> | void;
|
|
45
|
-
};
|
|
46
|
-
export type VoiceMediaTransportState = 'closed' | 'closing' | 'failed' | 'idle' | 'open';
|
|
47
|
-
export type VoiceMediaTransportEventKind = 'backpressure' | 'close' | 'connect' | 'error' | 'frame-in' | 'frame-out';
|
|
48
|
-
export type VoiceMediaTransportEvent = {
|
|
49
|
-
at: number;
|
|
50
|
-
bufferedFrames?: number;
|
|
51
|
-
error?: string;
|
|
52
|
-
frameId?: string;
|
|
53
|
-
kind: VoiceMediaTransportEventKind;
|
|
54
|
-
state: VoiceMediaTransportState;
|
|
55
|
-
};
|
|
56
|
-
export type VoiceMediaTransportReport = {
|
|
57
|
-
backpressureEvents: number;
|
|
58
|
-
checkedAt: number;
|
|
59
|
-
closed: boolean;
|
|
60
|
-
connected: boolean;
|
|
61
|
-
events: readonly VoiceMediaTransportEvent[];
|
|
62
|
-
failed: boolean;
|
|
63
|
-
inputFrames: number;
|
|
64
|
-
name: string;
|
|
65
|
-
outputFrames: number;
|
|
66
|
-
state: VoiceMediaTransportState;
|
|
67
|
-
status: VoiceMediaPipelineStatus;
|
|
68
|
-
};
|
|
69
|
-
export type VoiceMediaTransport = VoiceMediaTransportAdapter & {
|
|
70
|
-
events: () => readonly VoiceMediaTransportEvent[];
|
|
71
|
-
receive: (frame: VoiceMediaFrame) => Promise<void>;
|
|
72
|
-
report: () => VoiceMediaTransportReport;
|
|
73
|
-
state: () => VoiceMediaTransportState;
|
|
74
|
-
};
|
|
75
|
-
export type VoiceMediaTransportOptions = {
|
|
76
|
-
inputFormat?: AudioFormat;
|
|
77
|
-
maxBufferedFrames?: number;
|
|
78
|
-
name: string;
|
|
79
|
-
onClose?: () => Promise<void> | void;
|
|
80
|
-
onConnect?: () => Promise<void> | void;
|
|
81
|
-
onSend?: (frame: VoiceMediaFrame) => Promise<void> | void;
|
|
82
|
-
outputFormat?: AudioFormat;
|
|
83
|
-
};
|
|
84
|
-
export type VoiceMediaPipelineCalibrationInput = {
|
|
85
|
-
expectedInputFormat?: AudioFormat;
|
|
86
|
-
expectedOutputFormat?: AudioFormat;
|
|
87
|
-
frames?: readonly VoiceMediaFrame[];
|
|
88
|
-
inputFormat?: AudioFormat;
|
|
89
|
-
maxBackpressureFrames?: number;
|
|
90
|
-
maxFirstAudioLatencyMs?: number;
|
|
91
|
-
maxJitterMs?: number;
|
|
92
|
-
outputFormat?: AudioFormat;
|
|
93
|
-
requireInterruptionFrame?: boolean;
|
|
94
|
-
requireTraceEvidence?: boolean;
|
|
95
|
-
surface?: string;
|
|
96
|
-
};
|
|
97
|
-
export type VoiceMediaPipelineCalibrationIssue = {
|
|
98
|
-
code: string;
|
|
99
|
-
message: string;
|
|
100
|
-
severity: 'error' | 'warning';
|
|
101
|
-
};
|
|
102
|
-
export type VoiceMediaPipelineCalibrationReport = {
|
|
103
|
-
assistantAudioFrames: number;
|
|
104
|
-
backpressureFrames: number;
|
|
105
|
-
checkedAt: number;
|
|
106
|
-
firstAudioLatencyMs?: number;
|
|
107
|
-
inputAudioFrames: number;
|
|
108
|
-
inputFormat?: AudioFormat;
|
|
109
|
-
interruptionFrames: number;
|
|
110
|
-
issues: VoiceMediaPipelineCalibrationIssue[];
|
|
111
|
-
jitterMs?: number;
|
|
112
|
-
outputFormat?: AudioFormat;
|
|
113
|
-
resamplingRequired: boolean;
|
|
114
|
-
resamplingTargetHz?: number;
|
|
115
|
-
status: VoiceMediaPipelineStatus;
|
|
116
|
-
surface: string;
|
|
117
|
-
traceLinkedFrames: number;
|
|
118
|
-
turnCommitFrames: number;
|
|
119
|
-
};
|
|
120
|
-
export type VoiceMediaVadInput = {
|
|
121
|
-
frames?: readonly VoiceMediaFrame[];
|
|
122
|
-
maxSilenceFrames?: number;
|
|
123
|
-
minSpeechFrames?: number;
|
|
124
|
-
speechEndThreshold?: number;
|
|
125
|
-
speechStartThreshold?: number;
|
|
126
|
-
};
|
|
127
|
-
export type VoiceMediaVadSegment = {
|
|
128
|
-
durationMs?: number;
|
|
129
|
-
endAt?: number;
|
|
130
|
-
frameCount: number;
|
|
131
|
-
segmentId: string;
|
|
132
|
-
sessionId?: string;
|
|
133
|
-
startAt?: number;
|
|
134
|
-
turnId?: string;
|
|
135
|
-
};
|
|
136
|
-
export type VoiceMediaVadReport = {
|
|
137
|
-
checkedAt: number;
|
|
138
|
-
inputAudioFrames: number;
|
|
139
|
-
segments: VoiceMediaVadSegment[];
|
|
140
|
-
status: VoiceMediaPipelineStatus;
|
|
141
|
-
};
|
|
142
|
-
export type VoiceMediaInterruptionInput = {
|
|
143
|
-
frames?: readonly VoiceMediaFrame[];
|
|
144
|
-
maxInterruptionLatencyMs?: number;
|
|
145
|
-
};
|
|
146
|
-
export type VoiceMediaInterruptionReport = {
|
|
147
|
-
checkedAt: number;
|
|
148
|
-
interruptionFrames: number;
|
|
149
|
-
issues: VoiceMediaPipelineCalibrationIssue[];
|
|
150
|
-
latenciesMs: number[];
|
|
151
|
-
status: VoiceMediaPipelineStatus;
|
|
152
|
-
};
|
|
153
|
-
export declare const createVoiceMediaFrame: (frame: VoiceMediaFrame) => VoiceMediaFrame;
|
|
154
|
-
export declare const buildVoiceMediaTransportReport: (input: {
|
|
155
|
-
events?: readonly VoiceMediaTransportEvent[];
|
|
156
|
-
name: string;
|
|
157
|
-
state?: VoiceMediaTransportState;
|
|
158
|
-
}) => VoiceMediaTransportReport;
|
|
159
|
-
export declare const createVoiceMediaTransport: (options: VoiceMediaTransportOptions) => VoiceMediaTransport;
|
|
160
|
-
export declare const buildVoiceMediaResamplingPlan: (input: {
|
|
161
|
-
inputFormat: AudioFormat;
|
|
162
|
-
outputFormat: AudioFormat;
|
|
163
|
-
}) => VoiceMediaResamplingPlan;
|
|
164
|
-
export declare const createVoiceMediaFrameTransformPipeline: (input?: {
|
|
165
|
-
transforms?: readonly VoiceMediaFrameTransform[];
|
|
166
|
-
}) => VoiceMediaFrameTransformPipeline;
|
|
167
|
-
export declare const buildVoiceMediaVadReport: (input?: VoiceMediaVadInput) => VoiceMediaVadReport;
|
|
168
|
-
export declare const buildVoiceMediaInterruptionReport: (input?: VoiceMediaInterruptionInput) => VoiceMediaInterruptionReport;
|
|
169
|
-
export declare const buildVoiceMediaPipelineCalibrationReport: (input?: VoiceMediaPipelineCalibrationInput) => VoiceMediaPipelineCalibrationReport;
|