@absolutejs/voice 0.0.22-beta.475 → 0.0.22-beta.476
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/aiVoiceModel.d.ts +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +99 -0
- package/dist/testing/index.js +31 -3
- package/dist/types.d.ts +4 -0
- package/package.json +7 -2
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AIProviderConfig } from "@absolutejs/ai";
|
|
2
|
+
import type { VoiceAgentModel } from "./agent";
|
|
3
|
+
import type { VoiceSessionRecord } from "./types";
|
|
4
|
+
export type CreateAIVoiceModelOptions = {
|
|
5
|
+
model: string;
|
|
6
|
+
provider: AIProviderConfig;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
systemPrompt?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const createAIVoiceModel: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: CreateAIVoiceModelOptions) => VoiceAgentModel<TContext, TSession, TResult>;
|
package/dist/index.d.ts
CHANGED
|
@@ -69,6 +69,8 @@ export { assertVoiceSimulationSuiteEvidence, createVoiceSimulationSuiteRoutes, e
|
|
|
69
69
|
export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult, } from "./workflowContract";
|
|
70
70
|
export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceProviderFallbackRecovery, summarizeVoiceSessions, summarizeVoiceSessionReplay, } from "./sessionReplay";
|
|
71
71
|
export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool, } from "./agent";
|
|
72
|
+
export { createAIVoiceModel } from "./aiVoiceModel";
|
|
73
|
+
export type { CreateAIVoiceModelOptions } from "./aiVoiceModel";
|
|
72
74
|
export { createVoiceRAGTool } from "./ragTool";
|
|
73
75
|
export type { VoiceRAGCollectionLike, VoiceRAGQueryResult, VoiceRAGSearchInput, VoiceRAGToolArgs, VoiceRAGToolOptions, VoiceRAGToolResult, } from "./ragTool";
|
|
74
76
|
export { createVoiceApiRequestTool, createVoiceDTMFTool, createVoiceEndCallTool, createVoiceTransferCallTool, createVoiceVoicemailDetectionTool, } from "./agentTools";
|
package/dist/index.js
CHANGED
|
@@ -3369,6 +3369,9 @@ var buildTurnText = (transcripts, partialText, options = {}) => {
|
|
|
3369
3369
|
return selectPreferredTranscriptText(finalText, nextPartial);
|
|
3370
3370
|
};
|
|
3371
3371
|
|
|
3372
|
+
// src/types.ts
|
|
3373
|
+
var ttsAdapterSessionCanCancel = (session) => typeof session.cancel === "function";
|
|
3374
|
+
|
|
3372
3375
|
// src/session.ts
|
|
3373
3376
|
var DEFAULT_RECONNECT_TIMEOUT = 30000;
|
|
3374
3377
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
|
|
@@ -3789,6 +3792,27 @@ var createVoiceSession = (options) => {
|
|
|
3789
3792
|
});
|
|
3790
3793
|
}
|
|
3791
3794
|
};
|
|
3795
|
+
const cancelActiveTTS = async (reason) => {
|
|
3796
|
+
const activeSession = ttsSession;
|
|
3797
|
+
const cancelledTurnId = activeTTSTurnId;
|
|
3798
|
+
if (!activeSession || cancelledTurnId === undefined) {
|
|
3799
|
+
return;
|
|
3800
|
+
}
|
|
3801
|
+
activeTTSTurnId = undefined;
|
|
3802
|
+
if (!ttsAdapterSessionCanCancel(activeSession)) {
|
|
3803
|
+
return;
|
|
3804
|
+
}
|
|
3805
|
+
try {
|
|
3806
|
+
await activeSession.cancel(reason);
|
|
3807
|
+
} catch (error) {
|
|
3808
|
+
logger.warn("voice tts adapter cancel failed", {
|
|
3809
|
+
error: toError(error).message,
|
|
3810
|
+
reason,
|
|
3811
|
+
sessionId: options.id,
|
|
3812
|
+
turnId: cancelledTurnId
|
|
3813
|
+
});
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3792
3816
|
const sendAssistantAudio = async (chunk, input) => {
|
|
3793
3817
|
const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
|
|
3794
3818
|
await send({
|
|
@@ -5097,6 +5121,9 @@ var createVoiceSession = (options) => {
|
|
|
5097
5121
|
pushTurnAudio(conditionedAudio);
|
|
5098
5122
|
}
|
|
5099
5123
|
if (audioLevel >= turnDetection.speechThreshold) {
|
|
5124
|
+
if (!speechDetected && activeTTSTurnId !== undefined) {
|
|
5125
|
+
cancelActiveTTS("barge-in");
|
|
5126
|
+
}
|
|
5100
5127
|
speechDetected = true;
|
|
5101
5128
|
clearSilenceTimer();
|
|
5102
5129
|
} else if (speechDetected) {
|
|
@@ -34559,6 +34586,76 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
34559
34586
|
return result;
|
|
34560
34587
|
};
|
|
34561
34588
|
};
|
|
34589
|
+
// src/aiVoiceModel.ts
|
|
34590
|
+
var toProviderMessages = (messages) => {
|
|
34591
|
+
const out = [];
|
|
34592
|
+
for (const message of messages) {
|
|
34593
|
+
if (message.role === "tool") {
|
|
34594
|
+
out.push({
|
|
34595
|
+
content: [
|
|
34596
|
+
{
|
|
34597
|
+
content: message.content,
|
|
34598
|
+
tool_use_id: message.toolCallId ?? message.name ?? "",
|
|
34599
|
+
type: "tool_result"
|
|
34600
|
+
}
|
|
34601
|
+
],
|
|
34602
|
+
role: "user"
|
|
34603
|
+
});
|
|
34604
|
+
continue;
|
|
34605
|
+
}
|
|
34606
|
+
if (message.role === "system") {
|
|
34607
|
+
out.push({ content: message.content, role: "user" });
|
|
34608
|
+
continue;
|
|
34609
|
+
}
|
|
34610
|
+
out.push({ content: message.content, role: message.role });
|
|
34611
|
+
}
|
|
34612
|
+
return out;
|
|
34613
|
+
};
|
|
34614
|
+
var toProviderTools = (tools) => {
|
|
34615
|
+
if (tools.length === 0) {
|
|
34616
|
+
return;
|
|
34617
|
+
}
|
|
34618
|
+
return tools.map((tool) => ({
|
|
34619
|
+
description: tool.description ?? "",
|
|
34620
|
+
input_schema: tool.parameters ?? {
|
|
34621
|
+
properties: {},
|
|
34622
|
+
type: "object"
|
|
34623
|
+
},
|
|
34624
|
+
name: tool.name
|
|
34625
|
+
}));
|
|
34626
|
+
};
|
|
34627
|
+
var createAIVoiceModel = (options) => ({
|
|
34628
|
+
generate: async (input) => {
|
|
34629
|
+
const systemPrompt = input.system ?? options.systemPrompt;
|
|
34630
|
+
const stream = options.provider.stream({
|
|
34631
|
+
messages: toProviderMessages(input.messages),
|
|
34632
|
+
model: options.model,
|
|
34633
|
+
signal: options.signal,
|
|
34634
|
+
systemPrompt,
|
|
34635
|
+
tools: toProviderTools(input.tools)
|
|
34636
|
+
});
|
|
34637
|
+
let assistantText = "";
|
|
34638
|
+
const toolCalls = [];
|
|
34639
|
+
for await (const chunk of stream) {
|
|
34640
|
+
if (chunk.type === "text") {
|
|
34641
|
+
assistantText += chunk.content;
|
|
34642
|
+
} else if (chunk.type === "tool_use") {
|
|
34643
|
+
toolCalls.push({
|
|
34644
|
+
args: chunk.input ?? {},
|
|
34645
|
+
id: chunk.id,
|
|
34646
|
+
name: chunk.name
|
|
34647
|
+
});
|
|
34648
|
+
}
|
|
34649
|
+
}
|
|
34650
|
+
const output = {
|
|
34651
|
+
assistantText
|
|
34652
|
+
};
|
|
34653
|
+
if (toolCalls.length > 0) {
|
|
34654
|
+
output.toolCalls = toolCalls;
|
|
34655
|
+
}
|
|
34656
|
+
return output;
|
|
34657
|
+
}
|
|
34658
|
+
});
|
|
34562
34659
|
// src/ragTool.ts
|
|
34563
34660
|
var DEFAULT_TOOL_NAME = "searchKnowledgeBase";
|
|
34564
34661
|
var DEFAULT_DESCRIPTION = "Search the knowledge base and return short grounded citations. Use this whenever the caller asks a question that may be answered by indexed reference material.";
|
|
@@ -44991,6 +45088,7 @@ export {
|
|
|
44991
45088
|
verifyVoiceOpsWebhookSignature,
|
|
44992
45089
|
validateVoiceWorkflowRouteResult,
|
|
44993
45090
|
validateVoiceObservabilityExportRecord,
|
|
45091
|
+
ttsAdapterSessionCanCancel,
|
|
44994
45092
|
transcodeTwilioInboundPayloadToPCM16,
|
|
44995
45093
|
transcodePCMToTwilioOutboundPayload,
|
|
44996
45094
|
summarizeVoiceTurnQuality,
|
|
@@ -45614,6 +45712,7 @@ export {
|
|
|
45614
45712
|
createDomainPhraseHints,
|
|
45615
45713
|
createDomainLexicon,
|
|
45616
45714
|
createAnthropicVoiceAssistantModel,
|
|
45715
|
+
createAIVoiceModel,
|
|
45617
45716
|
conditionAudioChunk,
|
|
45618
45717
|
completeVoiceOpsTask,
|
|
45619
45718
|
compareVoiceEvalBaseline,
|
package/dist/testing/index.js
CHANGED
|
@@ -5337,6 +5337,9 @@ var resolveLogger = (logger) => ({
|
|
|
5337
5337
|
...logger
|
|
5338
5338
|
});
|
|
5339
5339
|
|
|
5340
|
+
// src/types.ts
|
|
5341
|
+
var ttsAdapterSessionCanCancel = (session) => typeof session.cancel === "function";
|
|
5342
|
+
|
|
5340
5343
|
// src/session.ts
|
|
5341
5344
|
var DEFAULT_RECONNECT_TIMEOUT = 30000;
|
|
5342
5345
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS2 = 10;
|
|
@@ -5757,6 +5760,27 @@ var createVoiceSession = (options) => {
|
|
|
5757
5760
|
});
|
|
5758
5761
|
}
|
|
5759
5762
|
};
|
|
5763
|
+
const cancelActiveTTS = async (reason) => {
|
|
5764
|
+
const activeSession = ttsSession;
|
|
5765
|
+
const cancelledTurnId = activeTTSTurnId;
|
|
5766
|
+
if (!activeSession || cancelledTurnId === undefined) {
|
|
5767
|
+
return;
|
|
5768
|
+
}
|
|
5769
|
+
activeTTSTurnId = undefined;
|
|
5770
|
+
if (!ttsAdapterSessionCanCancel(activeSession)) {
|
|
5771
|
+
return;
|
|
5772
|
+
}
|
|
5773
|
+
try {
|
|
5774
|
+
await activeSession.cancel(reason);
|
|
5775
|
+
} catch (error) {
|
|
5776
|
+
logger.warn("voice tts adapter cancel failed", {
|
|
5777
|
+
error: toError(error).message,
|
|
5778
|
+
reason,
|
|
5779
|
+
sessionId: options.id,
|
|
5780
|
+
turnId: cancelledTurnId
|
|
5781
|
+
});
|
|
5782
|
+
}
|
|
5783
|
+
};
|
|
5760
5784
|
const sendAssistantAudio = async (chunk, input) => {
|
|
5761
5785
|
const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
|
|
5762
5786
|
await send({
|
|
@@ -7065,6 +7089,9 @@ var createVoiceSession = (options) => {
|
|
|
7065
7089
|
pushTurnAudio(conditionedAudio);
|
|
7066
7090
|
}
|
|
7067
7091
|
if (audioLevel >= turnDetection.speechThreshold) {
|
|
7092
|
+
if (!speechDetected && activeTTSTurnId !== undefined) {
|
|
7093
|
+
cancelActiveTTS("barge-in");
|
|
7094
|
+
}
|
|
7068
7095
|
speechDetected = true;
|
|
7069
7096
|
clearSilenceTimer();
|
|
7070
7097
|
} else if (speechDetected) {
|
|
@@ -13154,8 +13181,9 @@ var runTTSAdapterFixture = async (adapter, fixture, options = {}) => {
|
|
|
13154
13181
|
sessionId: `tts-benchmark:${fixture.id}`,
|
|
13155
13182
|
...openOptions ?? {}
|
|
13156
13183
|
});
|
|
13184
|
+
const sessionOn = session.on;
|
|
13157
13185
|
const unsubscribers = [
|
|
13158
|
-
|
|
13186
|
+
sessionOn("audio", ({ chunk, format, receivedAt }) => {
|
|
13159
13187
|
const normalizedChunk = chunk instanceof Uint8Array ? chunk : chunk instanceof ArrayBuffer ? new Uint8Array(chunk) : new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
13160
13188
|
audioChunkCount += 1;
|
|
13161
13189
|
totalAudioBytes += normalizedChunk.byteLength;
|
|
@@ -13175,10 +13203,10 @@ var runTTSAdapterFixture = async (adapter, fixture, options = {}) => {
|
|
|
13175
13203
|
}, options.interruptAfterFirstAudioMs);
|
|
13176
13204
|
}
|
|
13177
13205
|
}),
|
|
13178
|
-
|
|
13206
|
+
sessionOn("error", () => {
|
|
13179
13207
|
errorCount += 1;
|
|
13180
13208
|
}),
|
|
13181
|
-
|
|
13209
|
+
sessionOn("close", () => {
|
|
13182
13210
|
closeCount += 1;
|
|
13183
13211
|
closed = true;
|
|
13184
13212
|
closedAt = Date.now();
|
package/dist/types.d.ts
CHANGED
|
@@ -166,8 +166,12 @@ export type TTSSessionEventMap = {
|
|
|
166
166
|
export type TTSAdapterSession = {
|
|
167
167
|
on: <K extends keyof TTSSessionEventMap>(event: K, handler: (payload: TTSSessionEventMap[K]) => void | Promise<void>) => () => void;
|
|
168
168
|
send: (text: string) => Promise<void>;
|
|
169
|
+
cancel?: (reason?: string) => Promise<void>;
|
|
169
170
|
close: (reason?: string) => Promise<void>;
|
|
170
171
|
};
|
|
172
|
+
export declare const ttsAdapterSessionCanCancel: (session: TTSAdapterSession) => session is TTSAdapterSession & {
|
|
173
|
+
cancel: (reason?: string) => Promise<void>;
|
|
174
|
+
};
|
|
171
175
|
export type TTSAdapterOpenOptions = {
|
|
172
176
|
sessionId: string;
|
|
173
177
|
lexicon?: VoiceLexiconEntry[];
|
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.476",
|
|
4
4
|
"description": "Voice primitives and Elysia plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"bench:stt": "bun run ./scripts/benchmark-stt.ts all",
|
|
161
161
|
"bench:assemblyai:sessions": "bun run ./scripts/benchmark-session.ts assemblyai",
|
|
162
162
|
"bench:openai:sessions": "bun run ./scripts/benchmark-session.ts openai",
|
|
163
|
-
"build": "bun run ./scripts/build-htmx-bootstrap-asset.ts && rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/media && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",
|
|
163
|
+
"build": "bun run ./scripts/build-htmx-bootstrap-asset.ts && rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",
|
|
164
164
|
"format": "prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\"",
|
|
165
165
|
"lint": "eslint ./src",
|
|
166
166
|
"release": "bun run format && bun run build && bun publish",
|
|
@@ -229,12 +229,16 @@
|
|
|
229
229
|
},
|
|
230
230
|
"peerDependencies": {
|
|
231
231
|
"@absolutejs/absolute": ">=0.19.0-beta.646",
|
|
232
|
+
"@absolutejs/ai": ">=0.0.5",
|
|
232
233
|
"@angular/core": ">=21.0.0",
|
|
233
234
|
"elysia": ">=1.4.18",
|
|
234
235
|
"react": ">=19.0.0",
|
|
235
236
|
"vue": ">=3.5.0"
|
|
236
237
|
},
|
|
237
238
|
"peerDependenciesMeta": {
|
|
239
|
+
"@absolutejs/ai": {
|
|
240
|
+
"optional": true
|
|
241
|
+
},
|
|
238
242
|
"@angular/core": {
|
|
239
243
|
"optional": true
|
|
240
244
|
},
|
|
@@ -250,6 +254,7 @@
|
|
|
250
254
|
},
|
|
251
255
|
"devDependencies": {
|
|
252
256
|
"@absolutejs/absolute": "0.19.0-beta.646",
|
|
257
|
+
"@absolutejs/ai": "0.0.5",
|
|
253
258
|
"@angular/core": "^21.0.0",
|
|
254
259
|
"@types/bun": "1.3.9",
|
|
255
260
|
"@types/react": "19.2.0",
|