mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-19 04:17:44 +00:00
Merge branch 'main' of https://github.com/jambonz/jambonz-feature-server into feat/aws_s2s
This commit is contained in:
@@ -163,5 +163,16 @@
|
|||||||
"wird sich bei Ihnen melden",
|
"wird sich bei Ihnen melden",
|
||||||
"ich melde mich bei dir",
|
"ich melde mich bei dir",
|
||||||
"wir können nicht"
|
"wir können nicht"
|
||||||
|
],
|
||||||
|
"it-IT": [
|
||||||
|
"segreteria telefonica",
|
||||||
|
"risponde la segreteria telefonica",
|
||||||
|
"lascia un messaggio",
|
||||||
|
"puoi lasciare un messaggio dopo il segnale",
|
||||||
|
"dopo il segnale acustico",
|
||||||
|
"il numero chiamato non è raggiungibile",
|
||||||
|
"non è raggiungibile",
|
||||||
|
"lascia pure un messaggio",
|
||||||
|
"puoi lasciare un messaggio"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ const customSanitizeFunction = (value) => {
|
|||||||
/* trims characters at the beginning and at the end of a string */
|
/* trims characters at the beginning and at the end of a string */
|
||||||
value = value.trim();
|
value = value.trim();
|
||||||
|
|
||||||
/* Verify strings including 'http' via new URL */
|
// Only attempt to parse if the whole string is a URL
|
||||||
if (value.includes('http')) {
|
if (/^https?:\/\/\S+$/.test(value)) {
|
||||||
value = new URL(value).toString();
|
value = new URL(value).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,6 +220,18 @@ class CallSession extends Emitter {
|
|||||||
this._synthesizer = synth;
|
this._synthesizer = synth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Say stream enabled
|
||||||
|
*/
|
||||||
|
|
||||||
|
get autoStreamTts() {
|
||||||
|
return this._autoStreamTts || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set autoStreamTts(i) {
|
||||||
|
this._autoStreamTts = i;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASR TTS fallback
|
* ASR TTS fallback
|
||||||
*/
|
*/
|
||||||
@@ -1799,6 +1811,10 @@ Duration=${duration} `
|
|||||||
.catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskStatus - Error sending'));
|
.catch((err) => this.logger.debug({err}, 'CallSession:_notifyTaskStatus - Error sending'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _internalTtsStreamingBufferTokens(tokens) {
|
||||||
|
return await this.ttsStreamingBuffer?.bufferTokens(tokens) || {status: 'failed', reason: 'no tts streaming buffer'};
|
||||||
|
}
|
||||||
|
|
||||||
_lccTtsFlush(opts) {
|
_lccTtsFlush(opts) {
|
||||||
this.ttsStreamingBuffer?.flush(opts);
|
this.ttsStreamingBuffer?.flush(opts);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ class TaskConfig extends Task {
|
|||||||
'actionHookDelayAction',
|
'actionHookDelayAction',
|
||||||
'boostAudioSignal',
|
'boostAudioSignal',
|
||||||
'vad',
|
'vad',
|
||||||
'ttsStream'
|
'ttsStream',
|
||||||
|
'autoStreamTts'
|
||||||
].forEach((k) => this[k] = this.data[k] || {});
|
].forEach((k) => this[k] = this.data[k] || {});
|
||||||
|
|
||||||
if ('notifyEvents' in this.data) {
|
if ('notifyEvents' in this.data) {
|
||||||
@@ -117,6 +118,7 @@ class TaskConfig extends Task {
|
|||||||
if (this.hasTtsStream) {
|
if (this.hasTtsStream) {
|
||||||
phrase.push(`${this.ttsStream.enable ? 'enable' : 'disable'} ttsStream`);
|
phrase.push(`${this.ttsStream.enable ? 'enable' : 'disable'} ttsStream`);
|
||||||
}
|
}
|
||||||
|
if ('autoStreamTts' in this.data) phrase.push(`enable Say.stream value ${this.data.autoStreamTts ? 'on' : 'off'}`);
|
||||||
return `${this.name}{${phrase.join(',')}}`;
|
return `${this.name}{${phrase.join(',')}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +298,11 @@ class TaskConfig extends Task {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('autoStreamTts' in this.data) {
|
||||||
|
this.logger.info(`Config: autoStreamTts set to ${this.data.autoStreamTts}`);
|
||||||
|
cs.autoStreamTts = this.data.autoStreamTts;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasFillerNoise) {
|
if (this.hasFillerNoise) {
|
||||||
const {enable, ...opts} = this.fillerNoise;
|
const {enable, ...opts} = this.fillerNoise;
|
||||||
this.logger.info({fillerNoise: this.fillerNoise}, 'Config: fillerNoise');
|
this.logger.info({fillerNoise: this.fillerNoise}, 'Config: fillerNoise');
|
||||||
@@ -330,7 +337,9 @@ class TaskConfig extends Task {
|
|||||||
};
|
};
|
||||||
this.logger.info({opts: this.gatherOpts}, 'Config: enabling ttsStream');
|
this.logger.info({opts: this.gatherOpts}, 'Config: enabling ttsStream');
|
||||||
cs.enableBackgroundTtsStream(this.sayOpts);
|
cs.enableBackgroundTtsStream(this.sayOpts);
|
||||||
} else if (!this.ttsStream.enable) {
|
}
|
||||||
|
// only disable ttsStream if it specifically set to false
|
||||||
|
else if (this.ttsStream.enable === false) {
|
||||||
this.logger.info('Config: disabling ttsStream');
|
this.logger.info('Config: disabling ttsStream');
|
||||||
cs.disableTtsStream();
|
cs.disableTtsStream();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -271,7 +271,12 @@ class TaskDial extends Task {
|
|||||||
}
|
}
|
||||||
this._removeDtmfDetection(cs.dlg);
|
this._removeDtmfDetection(cs.dlg);
|
||||||
this._removeDtmfDetection(this.dlg);
|
this._removeDtmfDetection(this.dlg);
|
||||||
await this._killOutdials();
|
try {
|
||||||
|
await this._killOutdials();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.logger.info({err}, 'Dial:kill - error killing outdials');
|
||||||
|
}
|
||||||
if (this.sd) {
|
if (this.sd) {
|
||||||
const byeReasonHeader = this.killReason === KillReason.MediaTimeout ? 'Media Timeout' : undefined;
|
const byeReasonHeader = this.killReason === KillReason.MediaTimeout ? 'Media Timeout' : undefined;
|
||||||
this.sd.kill(byeReasonHeader);
|
this.sd.kill(byeReasonHeader);
|
||||||
@@ -281,13 +286,22 @@ class TaskDial extends Task {
|
|||||||
}
|
}
|
||||||
if (this.callSid) sessionTracker.remove(this.callSid);
|
if (this.callSid) sessionTracker.remove(this.callSid);
|
||||||
if (this.listenTask) {
|
if (this.listenTask) {
|
||||||
await this.listenTask.kill(cs);
|
try {
|
||||||
this.listenTask.span.end();
|
await this.listenTask.kill(cs);
|
||||||
|
this.listenTask?.span?.end();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.logger.error({err}, 'Dial:kill - error killing listen task');
|
||||||
|
}
|
||||||
this.listenTask = null;
|
this.listenTask = null;
|
||||||
}
|
}
|
||||||
if (this.transcribeTask) {
|
if (this.transcribeTask) {
|
||||||
await this.transcribeTask.kill(cs);
|
try {
|
||||||
this.transcribeTask.span.end();
|
await this.transcribeTask.kill(cs);
|
||||||
|
this.transcribeTask?.span?.end();
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error({err}, 'Dial:kill - error killing transcribe task');
|
||||||
|
}
|
||||||
this.transcribeTask = null;
|
this.transcribeTask = null;
|
||||||
}
|
}
|
||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
|
|||||||
@@ -3,24 +3,32 @@ const TtsTask = require('./tts-task');
|
|||||||
const {TaskName, TaskPreconditions} = require('../utils/constants');
|
const {TaskName, TaskPreconditions} = require('../utils/constants');
|
||||||
const pollySSMLSplit = require('polly-ssml-split');
|
const pollySSMLSplit = require('polly-ssml-split');
|
||||||
const { SpeechCredentialError } = require('../utils/error');
|
const { SpeechCredentialError } = require('../utils/error');
|
||||||
|
const { sleepFor } = require('../utils/helpers');
|
||||||
|
|
||||||
const breakLengthyTextIfNeeded = (logger, text) => {
|
const breakLengthyTextIfNeeded = (logger, text) => {
|
||||||
const chunkSize = 1000;
|
// As The text can be used for tts streaming, we need to break lengthy text into smaller chunks
|
||||||
|
// HIGH_WATER_BUFFER_SIZE defined in tts-streaming-buffer.js
|
||||||
|
const chunkSize = 900;
|
||||||
const isSSML = text.startsWith('<speak>');
|
const isSSML = text.startsWith('<speak>');
|
||||||
if (text.length <= chunkSize || !isSSML) return [text];
|
|
||||||
const options = {
|
const options = {
|
||||||
// MIN length
|
|
||||||
softLimit: 100,
|
softLimit: 100,
|
||||||
// MAX length, exclude 15 characters <speak></speak>
|
|
||||||
hardLimit: chunkSize - 15,
|
hardLimit: chunkSize - 15,
|
||||||
// Set of extra split characters (Optional property)
|
|
||||||
extraSplitChars: ',;!?',
|
extraSplitChars: ',;!?',
|
||||||
};
|
};
|
||||||
pollySSMLSplit.configure(options);
|
pollySSMLSplit.configure(options);
|
||||||
try {
|
try {
|
||||||
return pollySSMLSplit.split(text);
|
if (text.length <= chunkSize) return [text];
|
||||||
|
if (isSSML) {
|
||||||
|
return pollySSMLSplit.split(text);
|
||||||
|
} else {
|
||||||
|
// Wrap with <speak> and split
|
||||||
|
const wrapped = `<speak>${text}</speak>`;
|
||||||
|
const splitArr = pollySSMLSplit.split(wrapped);
|
||||||
|
// Remove <speak> and </speak> from each chunk
|
||||||
|
return splitArr.map((str) => str.replace(/^<speak>/, '').replace(/<\/speak>$/, ''));
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.info({err}, 'Error spliting SSML long text');
|
logger.info({err}, 'Error splitting SSML long text');
|
||||||
return [text];
|
return [text];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -39,6 +47,9 @@ class TaskSay extends TtsTask {
|
|||||||
assert.ok((typeof this.data.text === 'string' || Array.isArray(this.data.text)) || this.data.stream === true,
|
assert.ok((typeof this.data.text === 'string' || Array.isArray(this.data.text)) || this.data.stream === true,
|
||||||
'Say: either text or stream:true is required');
|
'Say: either text or stream:true is required');
|
||||||
|
|
||||||
|
this.text = this.data.text ? (Array.isArray(this.data.text) ? this.data.text : [this.data.text])
|
||||||
|
.map((t) => breakLengthyTextIfNeeded(this.logger, t))
|
||||||
|
.flat() : [];
|
||||||
|
|
||||||
if (this.data.stream === true) {
|
if (this.data.stream === true) {
|
||||||
this._isStreamingTts = true;
|
this._isStreamingTts = true;
|
||||||
@@ -46,10 +57,6 @@ class TaskSay extends TtsTask {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._isStreamingTts = false;
|
this._isStreamingTts = false;
|
||||||
this.text = (Array.isArray(this.data.text) ? this.data.text : [this.data.text])
|
|
||||||
.map((t) => breakLengthyTextIfNeeded(this.logger, t))
|
|
||||||
.flat();
|
|
||||||
|
|
||||||
this.loop = this.data.loop || 1;
|
this.loop = this.data.loop || 1;
|
||||||
this.isHandledByPrimaryProvider = true;
|
this.isHandledByPrimaryProvider = true;
|
||||||
}
|
}
|
||||||
@@ -85,6 +92,10 @@ class TaskSay extends TtsTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this._isStreamingTts = this._isStreamingTts || cs.autoStreamTts;
|
||||||
|
if (this.isStreamingTts) {
|
||||||
|
this.closeOnStreamEmpty = this.closeOnStreamEmpty || this.text.length !== 0;
|
||||||
|
}
|
||||||
if (this.isStreamingTts) await this.handlingStreaming(cs, obj);
|
if (this.isStreamingTts) await this.handlingStreaming(cs, obj);
|
||||||
else await this.handling(cs, obj);
|
else await this.handling(cs, obj);
|
||||||
this.emit('playDone');
|
this.emit('playDone');
|
||||||
@@ -116,6 +127,54 @@ class TaskSay extends TtsTask {
|
|||||||
|
|
||||||
cs.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_open'})
|
cs.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_open'})
|
||||||
.catch((err) => this.logger.info({err}, 'TaskSay:handlingStreaming - Error sending'));
|
.catch((err) => this.logger.info({err}, 'TaskSay:handlingStreaming - Error sending'));
|
||||||
|
|
||||||
|
if (this.text.length !== 0) {
|
||||||
|
this.logger.info('TaskSay:handlingStreaming - sending text to TTS stream');
|
||||||
|
for (const t of this.text) {
|
||||||
|
const result = await cs._internalTtsStreamingBufferTokens(t);
|
||||||
|
if (result?.status === 'failed') {
|
||||||
|
if (result.reason === 'full') {
|
||||||
|
// Retry logic for full buffer
|
||||||
|
const maxRetries = 5;
|
||||||
|
let backoffMs = 1000;
|
||||||
|
for (let retryCount = 0; retryCount < maxRetries && !this.killed; retryCount++) {
|
||||||
|
this.logger.info(
|
||||||
|
`TaskSay:handlingStreaming - retry ${retryCount + 1}/${maxRetries} after ${backoffMs}ms`);
|
||||||
|
await sleepFor(backoffMs);
|
||||||
|
|
||||||
|
const retryResult = await cs._internalTtsStreamingBufferTokens(t);
|
||||||
|
|
||||||
|
// Exit retry loop on success
|
||||||
|
if (retryResult?.status !== 'failed') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle failure for reason other than full buffer
|
||||||
|
if (retryResult.reason !== 'full') {
|
||||||
|
this.logger.info(
|
||||||
|
{result: retryResult}, 'TaskSay:handlingStreaming - TTS stream failed to buffer tokens');
|
||||||
|
throw new Error(`TTS stream failed to buffer tokens: ${retryResult.reason}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last retry attempt failed
|
||||||
|
if (retryCount === maxRetries - 1) {
|
||||||
|
this.logger.info('TaskSay:handlingStreaming - Maximum retries exceeded for full buffer');
|
||||||
|
throw new Error('TTS stream buffer full - maximum retries exceeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase backoff for next retry
|
||||||
|
backoffMs = Math.min(backoffMs * 1.5, 10000);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Immediate failure for non-full buffer issues
|
||||||
|
this.logger.info({result}, 'TaskSay:handlingStreaming - TTS stream failed to buffer tokens');
|
||||||
|
throw new Error(`TTS stream failed to buffer tokens: ${result.reason}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await cs._lccTtsFlush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.info({err}, 'TaskSay:handlingStreaming - Error setting channel vars');
|
this.logger.info({err}, 'TaskSay:handlingStreaming - Error setting channel vars');
|
||||||
cs.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_closed'})
|
cs.requestor?.request('tts:streaming-event', '/streaming-event', {event_type: 'stream_closed'})
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ class WsRequestor extends BaseRequestor {
|
|||||||
|
|
||||||
while (retryCount <= this.maxReconnects) {
|
while (retryCount <= this.maxReconnects) {
|
||||||
try {
|
try {
|
||||||
this.logger.error({retryCount, maxReconnects: this.maxReconnects},
|
this.logger.debug({retryCount, maxReconnects: this.maxReconnects},
|
||||||
'WsRequestor:request - attempting connection');
|
'WsRequestor:request - attempting connection retry');
|
||||||
|
|
||||||
// Ensure clean state before each connection attempt
|
// Ensure clean state before each connection attempt
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
@@ -141,38 +141,29 @@ class WsRequestor extends BaseRequestor {
|
|||||||
this.ws = null;
|
this.ws = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.error({retryCount}, 'WsRequestor:request - calling _connect()');
|
|
||||||
const startAt = process.hrtime();
|
const startAt = process.hrtime();
|
||||||
await this._connect();
|
await this._connect();
|
||||||
const rtt = this._roundTrip(startAt);
|
const rtt = this._roundTrip(startAt);
|
||||||
this.stats.histogram('app.hook.connect_time', rtt, ['hook_type:app']);
|
this.stats.histogram('app.hook.connect_time', rtt, ['hook_type:app']);
|
||||||
this.logger.error({retryCount}, 'WsRequestor:request - connection successful, exiting retry loop');
|
|
||||||
lastError = null;
|
lastError = null;
|
||||||
break;
|
break;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastError = error;
|
lastError = error;
|
||||||
retryCount++;
|
retryCount++;
|
||||||
this.logger.error({error: error.message, retryCount, maxReconnects: this.maxReconnects},
|
|
||||||
'WsRequestor:request - connection attempt failed');
|
|
||||||
|
|
||||||
if (retryCount <= this.maxReconnects &&
|
if (retryCount <= this.maxReconnects &&
|
||||||
this.retryPolicyValues?.length &&
|
this.retryPolicyValues?.length &&
|
||||||
this._shouldRetry(error, this.retryPolicyValues)) {
|
this._shouldRetry(error, this.retryPolicyValues)) {
|
||||||
|
|
||||||
this.logger.error(
|
|
||||||
{url, error, retryCount, maxRetries: this.maxReconnects},
|
|
||||||
`WsRequestor:request - connection failed, retrying (${retryCount}/${this.maxReconnects})`
|
|
||||||
);
|
|
||||||
|
|
||||||
const delay = this.backoffMs;
|
const delay = this.backoffMs;
|
||||||
this.backoffMs = this.backoffMs < 2000 ? this.backoffMs * 2 : (this.backoffMs + 2000);
|
this.backoffMs = this.backoffMs < 2000 ? this.backoffMs * 2 : (this.backoffMs + 2000);
|
||||||
this.logger.error({delay}, 'WsRequestor:request - waiting before retry');
|
this.logger.debug({delay}, 'WsRequestor:request - waiting before retry');
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
this.logger.error('WsRequestor:request - retry delay complete, attempting retry');
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.logger.error({lastError: lastError.message, retryCount, maxReconnects: this.maxReconnects},
|
|
||||||
'WsRequestor:request - throwing last error');
|
this.logger.error({error: error.message, retryCount, maxReconnects: this.maxReconnects},
|
||||||
|
'WsRequestor:request - all connection attempts failed');
|
||||||
throw lastError;
|
throw lastError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,7 +361,7 @@ class WsRequestor extends BaseRequestor {
|
|||||||
|
|
||||||
this
|
this
|
||||||
.once('ready', (ws) => {
|
.once('ready', (ws) => {
|
||||||
this.logger.error({retryCount: 'unknown'}, 'WsRequestor:_connect - ready event fired, resolving Promise');
|
this.logger.debug('WsRequestor:_connect - ready event fired, resolving Promise');
|
||||||
this.removeAllListeners('not-ready');
|
this.removeAllListeners('not-ready');
|
||||||
if (this.connections > 1) this.request('session:reconnect', this.url);
|
if (this.connections > 1) this.request('session:reconnect', this.url);
|
||||||
resolve();
|
resolve();
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -15,10 +15,10 @@
|
|||||||
"@jambonz/http-health-check": "^0.0.1",
|
"@jambonz/http-health-check": "^0.0.1",
|
||||||
"@jambonz/mw-registrar": "^0.2.7",
|
"@jambonz/mw-registrar": "^0.2.7",
|
||||||
"@jambonz/realtimedb-helpers": "^0.8.13",
|
"@jambonz/realtimedb-helpers": "^0.8.13",
|
||||||
"@jambonz/speech-utils": "^0.2.11",
|
"@jambonz/speech-utils": "^0.2.12",
|
||||||
"@jambonz/stats-collector": "^0.1.10",
|
"@jambonz/stats-collector": "^0.1.10",
|
||||||
"@jambonz/time-series": "^0.2.13",
|
"@jambonz/time-series": "^0.2.13",
|
||||||
"@jambonz/verb-specifications": "^0.0.104",
|
"@jambonz/verb-specifications": "^0.0.105",
|
||||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||||
"@opentelemetry/api": "^1.8.0",
|
"@opentelemetry/api": "^1.8.0",
|
||||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||||
@@ -1466,9 +1466,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jambonz/speech-utils": {
|
"node_modules/@jambonz/speech-utils": {
|
||||||
"version": "0.2.11",
|
"version": "0.2.12",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.2.12.tgz",
|
||||||
"integrity": "sha512-5V+OJUUnK1CpKKrB0PrAbGVDcwRGQYH/ZPHFMBayW67XaNRpiL3b9jDpMvq35yzx8MGY18JpzCPgVKHTxERlqQ==",
|
"integrity": "sha512-1xik/ZRUtPE2SOztxweGI+RTXUbiUXRShJ8G/l7VJJBkSWbfKKerYIRfHicAPumHicaUrbqSzZ6hr0eghv80KA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"23": "^0.0.0",
|
"23": "^0.0.0",
|
||||||
"@aws-sdk/client-polly": "^3.496.0",
|
"@aws-sdk/client-polly": "^3.496.0",
|
||||||
@@ -1504,9 +1504,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jambonz/verb-specifications": {
|
"node_modules/@jambonz/verb-specifications": {
|
||||||
"version": "0.0.104",
|
"version": "0.0.105",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.104.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.105.tgz",
|
||||||
"integrity": "sha512-G1LjK6ISujdg0zALudtUvdaPXmvA4FU6x3s8S9MwUbWbFo2WERMUcNOgQAutDZwOMrLH9DnbPL8ZIdnTCKnlkA==",
|
"integrity": "sha512-MD6RMJyXMoHpR7Wl3xmYmU54P0eF/9LNywRNNsdkAmSf0EogFqSJft4xD/yGeRWlO5O6eAYZEJdaMQeLSxitcg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
|||||||
@@ -31,10 +31,10 @@
|
|||||||
"@jambonz/http-health-check": "^0.0.1",
|
"@jambonz/http-health-check": "^0.0.1",
|
||||||
"@jambonz/mw-registrar": "^0.2.7",
|
"@jambonz/mw-registrar": "^0.2.7",
|
||||||
"@jambonz/realtimedb-helpers": "^0.8.13",
|
"@jambonz/realtimedb-helpers": "^0.8.13",
|
||||||
"@jambonz/speech-utils": "^0.2.11",
|
"@jambonz/speech-utils": "^0.2.12",
|
||||||
"@jambonz/stats-collector": "^0.1.10",
|
"@jambonz/stats-collector": "^0.1.10",
|
||||||
"@jambonz/time-series": "^0.2.13",
|
"@jambonz/time-series": "^0.2.13",
|
||||||
"@jambonz/verb-specifications": "^0.0.104",
|
"@jambonz/verb-specifications": "^0.0.105",
|
||||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||||
"@opentelemetry/api": "^1.8.0",
|
"@opentelemetry/api": "^1.8.0",
|
||||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user