feat(proxy-engine): add live TTS streaming interactions and incoming number range support
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: 'siprouter',
|
||||
version: '1.24.0',
|
||||
version: '1.25.0',
|
||||
description: 'undefined'
|
||||
}
|
||||
|
||||
29
ts/config.ts
29
ts/config.ts
@@ -48,6 +48,24 @@ export interface IDeviceConfig {
|
||||
extension: string;
|
||||
}
|
||||
|
||||
export type TIncomingNumberMode = 'single' | 'range' | 'regex';
|
||||
|
||||
export interface IIncomingNumberConfig {
|
||||
id: string;
|
||||
label: string;
|
||||
providerId?: string;
|
||||
mode: TIncomingNumberMode;
|
||||
countryCode?: string;
|
||||
areaCode?: string;
|
||||
localNumber?: string;
|
||||
rangeEnd?: string;
|
||||
pattern?: string;
|
||||
|
||||
// Legacy persisted fields kept for migration compatibility.
|
||||
number?: string;
|
||||
rangeStart?: string;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Match/Action routing model
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -66,7 +84,7 @@ export interface ISipRouteMatch {
|
||||
*
|
||||
* Inbound: matches the provider-delivered DID / Request-URI user part.
|
||||
* Outbound: matches the normalized dialed digits.
|
||||
* Supports: exact string, prefix with trailing '*' (e.g. "+4930*"), or regex ("/^\\+49/").
|
||||
* Supports: exact string, numeric range `start..end`, prefix with trailing '*' (e.g. "+4930*"), or regex ("/^\\+49/").
|
||||
*/
|
||||
numberPattern?: string;
|
||||
|
||||
@@ -234,6 +252,7 @@ export interface IAppConfig {
|
||||
proxy: IProxyConfig;
|
||||
providers: IProviderConfig[];
|
||||
devices: IDeviceConfig[];
|
||||
incomingNumbers?: IIncomingNumberConfig[];
|
||||
routing: IRoutingConfig;
|
||||
contacts: IContact[];
|
||||
voiceboxes?: IVoiceboxConfig[];
|
||||
@@ -288,6 +307,14 @@ export function loadConfig(): IAppConfig {
|
||||
d.extension ??= '100';
|
||||
}
|
||||
|
||||
cfg.incomingNumbers ??= [];
|
||||
for (const incoming of cfg.incomingNumbers) {
|
||||
if (!incoming.id) incoming.id = `incoming-${Date.now()}`;
|
||||
incoming.label ??= incoming.id;
|
||||
incoming.mode ??= incoming.pattern ? 'regex' : incoming.rangeStart || incoming.rangeEnd ? 'range' : 'single';
|
||||
incoming.countryCode ??= incoming.mode === 'regex' ? undefined : '+49';
|
||||
}
|
||||
|
||||
cfg.routing ??= { routes: [] };
|
||||
cfg.routing.routes ??= [];
|
||||
|
||||
|
||||
@@ -266,6 +266,7 @@ async function handleRequest(
|
||||
if (existing && ud.displayName !== undefined) existing.displayName = ud.displayName;
|
||||
}
|
||||
}
|
||||
if (updates.incomingNumbers !== undefined) cfg.incomingNumbers = updates.incomingNumbers;
|
||||
if (updates.routing) {
|
||||
if (updates.routing.routes) {
|
||||
cfg.routing.routes = updates.routing.routes;
|
||||
|
||||
@@ -82,6 +82,19 @@ type TProxyCommands = {
|
||||
};
|
||||
result: { result: 'digit' | 'timeout' | 'cancelled'; digit?: string };
|
||||
};
|
||||
start_tts_interaction: {
|
||||
params: {
|
||||
call_id: string;
|
||||
leg_id: string;
|
||||
text: string;
|
||||
voice?: string;
|
||||
model?: string;
|
||||
voices?: string;
|
||||
expected_digits: string;
|
||||
timeout_ms: number;
|
||||
};
|
||||
result: { result: 'digit' | 'timeout' | 'cancelled'; digit?: string };
|
||||
};
|
||||
add_tool_leg: {
|
||||
params: {
|
||||
call_id: string;
|
||||
@@ -446,6 +459,40 @@ export async function startInteraction(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a live TTS interaction on a specific leg. The first chunk is rendered
|
||||
* up front and the rest streams into the mixer while playback is already live.
|
||||
*/
|
||||
export async function startTtsInteraction(
|
||||
callId: string,
|
||||
legId: string,
|
||||
text: string,
|
||||
expectedDigits: string,
|
||||
timeoutMs: number,
|
||||
options?: {
|
||||
voice?: string;
|
||||
model?: string;
|
||||
voices?: string;
|
||||
},
|
||||
): Promise<{ result: 'digit' | 'timeout' | 'cancelled'; digit?: string } | null> {
|
||||
if (!bridge || !initialized) return null;
|
||||
try {
|
||||
return await sendProxyCommand('start_tts_interaction', {
|
||||
call_id: callId,
|
||||
leg_id: legId,
|
||||
text,
|
||||
expected_digits: expectedDigits,
|
||||
timeout_ms: timeoutMs,
|
||||
voice: options?.voice,
|
||||
model: options?.model,
|
||||
voices: options?.voices,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
logFn?.(`[proxy-engine] start_tts_interaction error: ${errorMessage(error)}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tool leg (recording or transcription) to a call.
|
||||
* Tool legs receive per-source unmerged audio from all participants.
|
||||
|
||||
Reference in New Issue
Block a user