mirror of
https://github.com/jambonz/verb-specifications.git
synced 2026-04-14 21:57:01 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bb1313b5b | ||
|
|
f92c3429d2 | ||
|
|
bb21d7d71f | ||
|
|
d0f77421fe | ||
|
|
161695ed43 | ||
|
|
b46cfe0c55 | ||
|
|
6f39ad2e2e | ||
|
|
66b63d8912 | ||
|
|
de5a2fdd1c | ||
|
|
8cb79d47cc | ||
|
|
11a0dce5ba | ||
|
|
ecf59041c5 | ||
|
|
abcb3ffa5d | ||
|
|
f5e7adcf66 | ||
|
|
f29b214208 | ||
|
|
2bf7561980 | ||
|
|
96db93fcdc | ||
|
|
142718104a | ||
|
|
9ac144ef71 | ||
|
|
337114035d | ||
|
|
253bd8a49c | ||
|
|
7a5c094bfc | ||
|
|
228f8773d3 | ||
|
|
c9cd50c559 | ||
|
|
fe095be5c8 | ||
|
|
71caf6bb53 | ||
|
|
887320fd5d | ||
|
|
f0ffdee9c6 | ||
|
|
1013db46d3 | ||
|
|
8771e3f22f | ||
|
|
ff757d3177 | ||
|
|
5745cc9a29 | ||
|
|
2b81fe20f0 | ||
|
|
60a33e11eb | ||
|
|
9717ce6d58 | ||
|
|
49ffe5f526 | ||
|
|
61590b66cc | ||
|
|
d8cf684bf6 | ||
|
|
e039261eb5 | ||
|
|
23e726ed3f | ||
|
|
e1bc5adc55 | ||
|
|
76e2486535 | ||
|
|
dfcf1de210 | ||
|
|
7d19707dec | ||
|
|
b4856e9d05 | ||
|
|
577191ed0d | ||
|
|
ee2ffff20d | ||
|
|
4c5b795498 | ||
|
|
7ecd46393c | ||
|
|
8fffe39195 | ||
|
|
23cb499244 | ||
|
|
fb94dc94cb | ||
|
|
30677b6cd1 | ||
|
|
4ea9cda0f8 | ||
|
|
a0b9f963c2 | ||
|
|
841a471faa | ||
|
|
44112f67b3 | ||
|
|
1f7c98c0ce | ||
|
|
276a55e672 | ||
|
|
840db61042 | ||
|
|
85e1b64ee4 | ||
|
|
8ed54e2d51 | ||
|
|
c24fb7996c | ||
|
|
6c24ad1145 | ||
|
|
af3955cb35 | ||
|
|
1ee29749c3 | ||
|
|
dea57e0910 | ||
|
|
f444a57bcd | ||
|
|
a9a2339821 | ||
|
|
95f3a06625 | ||
|
|
985476e8b1 | ||
|
|
1782db362e | ||
|
|
e654ee9e10 | ||
|
|
38d1d04c4a | ||
|
|
5cbd3886d1 | ||
|
|
de8dfc9202 | ||
|
|
637431468d | ||
|
|
4a817c97b2 |
@@ -4,6 +4,27 @@ const _specData = require('./specs');
|
|||||||
const specs = new Map();
|
const specs = new Map();
|
||||||
for (const key in _specData) { specs.set(key, _specData[key]); }
|
for (const key in _specData) { specs.set(key, _specData[key]); }
|
||||||
|
|
||||||
|
/* verb synonyms and shortcuts: maps alias verb names to their canonical form,
|
||||||
|
optionally injecting properties (e.g. vendor) into the verb data */
|
||||||
|
const verbTransforms = new Map([
|
||||||
|
['stream', {verb: 'listen'}],
|
||||||
|
['s2s', {verb: 'llm'}],
|
||||||
|
['openai_s2s', {verb: 'llm', properties: {vendor: 'openai'}}],
|
||||||
|
['microsoft_s2s', {verb: 'llm', properties: {vendor: 'microsoft'}}],
|
||||||
|
['google_s2s', {verb: 'llm', properties: {vendor: 'google'}}],
|
||||||
|
['elevenlabs_s2s', {verb: 'llm', properties: {vendor: 'elevenlabs'}}],
|
||||||
|
['deepgram_s2s', {verb: 'llm', properties: {vendor: 'deepgram'}}],
|
||||||
|
['voiceagent_s2s', {verb: 'llm', properties: {vendor: 'voiceagent'}}],
|
||||||
|
['ultravox_s2s', {verb: 'llm', properties: {vendor: 'ultravox'}}],
|
||||||
|
]);
|
||||||
|
|
||||||
|
function applyVerbTransform(name, data) {
|
||||||
|
const transform = verbTransforms.get(name);
|
||||||
|
if (!transform) return {name, data};
|
||||||
|
const newData = transform.properties ? {...transform.properties, ...data} : data;
|
||||||
|
return {name: transform.verb, data: newData};
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeJambones(logger, obj) {
|
function normalizeJambones(logger, obj) {
|
||||||
if (!Array.isArray(obj)) {
|
if (!Array.isArray(obj)) {
|
||||||
throw new Error('malformed jambonz payload: must be array');
|
throw new Error('malformed jambonz payload: must be array');
|
||||||
@@ -13,18 +34,22 @@ function normalizeJambones(logger, obj) {
|
|||||||
if (typeof tdata !== 'object') throw new Error('malformed jambonz payload: must be array of objects');
|
if (typeof tdata !== 'object') throw new Error('malformed jambonz payload: must be array of objects');
|
||||||
if ('verb' in tdata) {
|
if ('verb' in tdata) {
|
||||||
// {verb: 'say', text: 'foo..bar'..}
|
// {verb: 'say', text: 'foo..bar'..}
|
||||||
const name = tdata.verb;
|
|
||||||
const o = {};
|
const o = {};
|
||||||
Object.keys(tdata)
|
Object.keys(tdata)
|
||||||
.filter((k) => k !== 'verb')
|
.filter((k) => k !== 'verb')
|
||||||
.forEach((k) => o[k] = tdata[k]);
|
.forEach((k) => o[k] = tdata[k]);
|
||||||
|
const {name, data} = applyVerbTransform(tdata.verb, o);
|
||||||
const o2 = {};
|
const o2 = {};
|
||||||
o2[name] = o;
|
o2[name] = data;
|
||||||
document.push(o2);
|
document.push(o2);
|
||||||
}
|
}
|
||||||
else if (Object.keys(tdata).length === 1) {
|
else if (Object.keys(tdata).length === 1) {
|
||||||
// {'say': {..}}
|
// {'say': {..}}
|
||||||
document.push(tdata);
|
const key = Object.keys(tdata)[0];
|
||||||
|
const {name, data} = applyVerbTransform(key, tdata[key]);
|
||||||
|
const o2 = {};
|
||||||
|
o2[name] = data;
|
||||||
|
document.push(o2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger.info(tdata, 'malformed jambonz payload: missing verb property');
|
logger.info(tdata, 'malformed jambonz payload: missing verb property');
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@jambonz/verb-specifications",
|
"name": "@jambonz/verb-specifications",
|
||||||
"version": "0.0.101",
|
"version": "0.1.7",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@jambonz/verb-specifications",
|
"name": "@jambonz/verb-specifications",
|
||||||
"version": "0.0.101",
|
"version": "0.1.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@jambonz/verb-specifications",
|
"name": "@jambonz/verb-specifications",
|
||||||
"version": "0.0.101",
|
"version": "0.1.7",
|
||||||
"description": "Jambonz Verb Specification Utilities",
|
"description": "Jambonz Verb Specification Utilities",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
238
specs.json
238
specs.json
@@ -1,4 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"alert" : {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"message": "string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"message"
|
||||||
|
]
|
||||||
|
},
|
||||||
"answer": {
|
"answer": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "string"
|
"id": "string"
|
||||||
@@ -57,6 +66,7 @@
|
|||||||
"amd": "#amd",
|
"amd": "#amd",
|
||||||
"fillerNoise": "#fillerNoise",
|
"fillerNoise": "#fillerNoise",
|
||||||
"notifyEvents": "boolean",
|
"notifyEvents": "boolean",
|
||||||
|
"notifySttLatency": "boolean",
|
||||||
"reset": "string|array",
|
"reset": "string|array",
|
||||||
"onHoldMusic": "string",
|
"onHoldMusic": "string",
|
||||||
"actionHookDelayAction": "#actionHookDelayAction",
|
"actionHookDelayAction": "#actionHookDelayAction",
|
||||||
@@ -64,7 +74,12 @@
|
|||||||
"boostAudioSignal": "number|string",
|
"boostAudioSignal": "number|string",
|
||||||
"vad":"#vad",
|
"vad":"#vad",
|
||||||
"referHook": "object|string",
|
"referHook": "object|string",
|
||||||
"earlyMedia": "boolean"
|
"earlyMedia": "boolean",
|
||||||
|
"autoStreamTts": "boolean",
|
||||||
|
"disableTtsCache": "boolean",
|
||||||
|
"trackTtsPlayout": "boolean",
|
||||||
|
"noiseIsolation": "#noiseIsolation",
|
||||||
|
"turnTaking": "#turnTaking"
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
},
|
},
|
||||||
@@ -213,6 +228,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": "string",
|
"id": "string",
|
||||||
"text": "string|array",
|
"text": "string|array",
|
||||||
|
"instructions": "string",
|
||||||
"stream": "boolean",
|
"stream": "boolean",
|
||||||
"loop": "number|string",
|
"loop": "number|string",
|
||||||
"synthesizer": "#synthesizer",
|
"synthesizer": "#synthesizer",
|
||||||
@@ -266,7 +282,9 @@
|
|||||||
"statusEvents": "array",
|
"statusEvents": "array",
|
||||||
"statusHook": "object|string",
|
"statusHook": "object|string",
|
||||||
"enterHook": "object|string",
|
"enterHook": "object|string",
|
||||||
"record": "#record"
|
"record": "#record",
|
||||||
|
"listen": "#listen",
|
||||||
|
"distributeDtmf": "boolean"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"name"
|
"name"
|
||||||
@@ -298,7 +316,8 @@
|
|||||||
"transcribe": "#transcribe",
|
"transcribe": "#transcribe",
|
||||||
"amd": "#amd",
|
"amd": "#amd",
|
||||||
"dub": ["#dub"],
|
"dub": ["#dub"],
|
||||||
"tag": "object"
|
"tag": "object",
|
||||||
|
"forwardPAI": "boolean"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"target"
|
"target"
|
||||||
@@ -314,7 +333,7 @@
|
|||||||
"region": "string",
|
"region": "string",
|
||||||
"model": {
|
"model": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["es", "cx"]
|
"enum": ["es", "cx", "ces"]
|
||||||
},
|
},
|
||||||
"lang": "string",
|
"lang": "string",
|
||||||
"actionHook": "object|string",
|
"actionHook": "object|string",
|
||||||
@@ -409,33 +428,6 @@
|
|||||||
"url"
|
"url"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"stream": {
|
|
||||||
"properties": {
|
|
||||||
"id": "string",
|
|
||||||
"actionHook": "object|string",
|
|
||||||
"auth": "#auth",
|
|
||||||
"finishOnKey": "string",
|
|
||||||
"maxLength": "number",
|
|
||||||
"metadata": "object",
|
|
||||||
"mixType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": ["mono", "stereo", "mixed"]
|
|
||||||
},
|
|
||||||
"passDtmf": "boolean",
|
|
||||||
"playBeep": "boolean",
|
|
||||||
"disableBidirectionalAudio": "boolean",
|
|
||||||
"bidirectionalAudio": "#bidirectionalAudio",
|
|
||||||
"sampleRate": "number",
|
|
||||||
"timeout": "number",
|
|
||||||
"transcribe": "#transcribe",
|
|
||||||
"url": "string",
|
|
||||||
"wsAuth": "#auth",
|
|
||||||
"earlyMedia": "boolean"
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"url"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"llm": {
|
"llm": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "string",
|
"id": "string",
|
||||||
@@ -443,6 +435,7 @@
|
|||||||
"model": "string",
|
"model": "string",
|
||||||
"auth": "object",
|
"auth": "object",
|
||||||
"connectOptions": "object",
|
"connectOptions": "object",
|
||||||
|
"mcpServers": ["#mcpServer"],
|
||||||
"actionHook": "object|string",
|
"actionHook": "object|string",
|
||||||
"eventHook": "object|string",
|
"eventHook": "object|string",
|
||||||
"toolHook": "object|string",
|
"toolHook": "object|string",
|
||||||
@@ -451,11 +444,19 @@
|
|||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"vendor",
|
"vendor",
|
||||||
"model",
|
|
||||||
"auth",
|
|
||||||
"llmOptions"
|
"llmOptions"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"mcpServer": {
|
||||||
|
"properties": {
|
||||||
|
"url": "string",
|
||||||
|
"auth": "object",
|
||||||
|
"roots": ["#root"]
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
"message": {
|
"message": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "string",
|
"id": "string",
|
||||||
@@ -510,6 +511,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["startCallRecording", "stopCallRecording", "pauseCallRecording", "resumeCallRecording"]
|
"enum": ["startCallRecording", "stopCallRecording", "pauseCallRecording", "resumeCallRecording"]
|
||||||
},
|
},
|
||||||
|
"type" : {
|
||||||
|
"type" : "string",
|
||||||
|
"enum" : ["cloud", "siprec"]
|
||||||
|
|
||||||
|
},
|
||||||
"recordingID": "string",
|
"recordingID": "string",
|
||||||
"siprecServerURL": "string|array",
|
"siprecServerURL": "string|array",
|
||||||
"headers": "object"
|
"headers": "object"
|
||||||
@@ -521,7 +527,8 @@
|
|||||||
"redirect": {
|
"redirect": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": "string",
|
"id": "string",
|
||||||
"actionHook": "object|string"
|
"actionHook": "object|string",
|
||||||
|
"statusHook": "object|string"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"actionHook"
|
"actionHook"
|
||||||
@@ -577,7 +584,6 @@
|
|||||||
"channel": "number"
|
"channel": "number"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"transcriptionHook"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"target": {
|
"target": {
|
||||||
@@ -658,6 +664,7 @@
|
|||||||
"fallbackLabel": "string",
|
"fallbackLabel": "string",
|
||||||
"fallbackLanguage": "string",
|
"fallbackLanguage": "string",
|
||||||
"vad": "#vad",
|
"vad": "#vad",
|
||||||
|
"autogeneratePrompt": "boolean",
|
||||||
"hints": "array",
|
"hints": "array",
|
||||||
"hintsBoost": "number",
|
"hintsBoost": "number",
|
||||||
"altLanguages": "array",
|
"altLanguages": "array",
|
||||||
@@ -734,7 +741,10 @@
|
|||||||
"customOptions": "#customOptions",
|
"customOptions": "#customOptions",
|
||||||
"verbioOptions": "#verbioOptions",
|
"verbioOptions": "#verbioOptions",
|
||||||
"speechmaticsOptions": "#speechmaticsOptions",
|
"speechmaticsOptions": "#speechmaticsOptions",
|
||||||
"openaiOptions": "#openaiOptions"
|
"openaiOptions": "#openaiOptions",
|
||||||
|
"houndifyOptions": "#houndifyOptions",
|
||||||
|
"gladiaOptions": "object",
|
||||||
|
"elevenlabsOptions": "#elevenlabsOptions"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"vendor"
|
"vendor"
|
||||||
@@ -777,6 +787,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"speechSegmentationSilenceTimeoutMs": "number",
|
"speechSegmentationSilenceTimeoutMs": "number",
|
||||||
"postProcessing" : "string",
|
"postProcessing" : "string",
|
||||||
|
"audioLogging" : "boolean",
|
||||||
"languageIdMode": {
|
"languageIdMode": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -812,6 +823,62 @@
|
|||||||
"transcriptNormalization": "array"
|
"transcriptNormalization": "array"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"houndifyOptions": {
|
||||||
|
"properties": {
|
||||||
|
"requestInfo": "object",
|
||||||
|
"sampleRate": "number",
|
||||||
|
"latitude": "number",
|
||||||
|
"longitude": "number",
|
||||||
|
"city": "string",
|
||||||
|
"state": "string",
|
||||||
|
"country": "string",
|
||||||
|
"timeZone": "string",
|
||||||
|
"domain": "string",
|
||||||
|
"audioEndpoint": "string",
|
||||||
|
"maxSilenceSeconds": "number",
|
||||||
|
"maxSilenceAfterFullQuerySeconds": "number",
|
||||||
|
"maxSilenceAfterPartialQuerySeconds": "number",
|
||||||
|
"vadSensitivity": "number",
|
||||||
|
"vadTimeout": "number",
|
||||||
|
"vadMode": "string",
|
||||||
|
"vadVoiceMs": "number",
|
||||||
|
"vadSilenceMs": "number",
|
||||||
|
"vadDebug": "boolean",
|
||||||
|
"audioFormat": "string",
|
||||||
|
"enableNoiseReduction": "boolean",
|
||||||
|
"enableProfanityFilter": "boolean",
|
||||||
|
"enablePunctuation": "boolean",
|
||||||
|
"enableCapitalization": "boolean",
|
||||||
|
"confidenceThreshold": "number",
|
||||||
|
"enableDisfluencyFilter": "boolean",
|
||||||
|
"maxResults": "number",
|
||||||
|
"enableWordTimestamps": "boolean",
|
||||||
|
"maxAlternatives": "number",
|
||||||
|
"partialTranscriptInterval": "number",
|
||||||
|
"sessionTimeout": "number",
|
||||||
|
"connectionTimeout": "number",
|
||||||
|
"customVocabulary": "array",
|
||||||
|
"languageModel": "string",
|
||||||
|
"audioQueryAbsoluteTimeout": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"elevenlabsOptions": {
|
||||||
|
"properties": {
|
||||||
|
"includeTimestamps": "boolean",
|
||||||
|
"commitStrategy": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"manual",
|
||||||
|
"vad"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"vadSilenceThresholdSecs": "number",
|
||||||
|
"vadThreshold": "number",
|
||||||
|
"minSpeechDurationMs": "number",
|
||||||
|
"minSilenceDurationMs": "number",
|
||||||
|
"enableLogging": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cobaltOptions": {
|
"cobaltOptions": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"serverUri": "string",
|
"serverUri": "string",
|
||||||
@@ -891,7 +958,12 @@
|
|||||||
"shortUtterance": "boolean",
|
"shortUtterance": "boolean",
|
||||||
"vadTurnoff": "number",
|
"vadTurnoff": "number",
|
||||||
"tag": "string",
|
"tag": "string",
|
||||||
"fillerWords" : "boolean"
|
"fillerWords" : "boolean",
|
||||||
|
"eotThreshold": "number",
|
||||||
|
"eotTimeoutMs": "number",
|
||||||
|
"mipOptOut": "boolean",
|
||||||
|
"entityPrompt": "string",
|
||||||
|
"eagerEotThreshold":"number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sonioxOptions": {
|
"sonioxOptions": {
|
||||||
@@ -992,6 +1064,7 @@
|
|||||||
"additional_vocab": "array",
|
"additional_vocab": "array",
|
||||||
"diarization": "string",
|
"diarization": "string",
|
||||||
"speaker_diarization_config": "#sm_speakerDiarizationConfig",
|
"speaker_diarization_config": "#sm_speakerDiarizationConfig",
|
||||||
|
"conversation_config": "#sm_conversationConfig",
|
||||||
"enable_partials": "boolean",
|
"enable_partials": "boolean",
|
||||||
"max_delay": "number",
|
"max_delay": "number",
|
||||||
"max_delay_mode": {
|
"max_delay_mode": {
|
||||||
@@ -1019,6 +1092,13 @@
|
|||||||
"required": [
|
"required": [
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"sm_conversationConfig": {
|
||||||
|
"properties": {
|
||||||
|
"end_of_utterance_silence_trigger": "number"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
]
|
||||||
|
},
|
||||||
"sm_puctuationOverrides": {
|
"sm_puctuationOverrides": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"permitted_marks": "array",
|
"permitted_marks": "array",
|
||||||
@@ -1125,7 +1205,25 @@
|
|||||||
},
|
},
|
||||||
"assemblyAiOptions": {
|
"assemblyAiOptions": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"apiKey": "string"
|
"apiKey": "string",
|
||||||
|
"serviceVersion": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"v2",
|
||||||
|
"v3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"speechModel": "string",
|
||||||
|
"formatTurns": "boolean",
|
||||||
|
"endOfTurnConfidenceThreshold": "number",
|
||||||
|
"minEndOfTurnSilenceWhenConfident": "number",
|
||||||
|
"maxTurnSilence": "number",
|
||||||
|
"minTurnSilence": "number",
|
||||||
|
"keyterms": "array",
|
||||||
|
"prompt": "string",
|
||||||
|
"languageDetection": "boolean",
|
||||||
|
"vadThreshold": "number",
|
||||||
|
"inactivityTimeout": "number"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resource": {
|
"resource": {
|
||||||
@@ -1206,10 +1304,18 @@
|
|||||||
"voiceMs": "number",
|
"voiceMs": "number",
|
||||||
"silenceMs": "number",
|
"silenceMs": "number",
|
||||||
"strategy": "string",
|
"strategy": "string",
|
||||||
"mode": "number"
|
"mode": "number",
|
||||||
|
"vendor": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"webrtc",
|
||||||
|
"silero"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"threshold": "number",
|
||||||
|
"speechPadMs": "number"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"enable"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"amd": {
|
"amd": {
|
||||||
@@ -1248,5 +1354,57 @@
|
|||||||
"streaming": "boolean",
|
"streaming": "boolean",
|
||||||
"sampleRate": "number"
|
"sampleRate": "number"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"pipeline": {
|
||||||
|
"properties": {
|
||||||
|
"id": "string",
|
||||||
|
"stt": "#recognizer",
|
||||||
|
"tts": "#synthesizer",
|
||||||
|
"llm": "#llm",
|
||||||
|
"turnDetection": "string|object",
|
||||||
|
"bargeIn": "#bargeInPipeline",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string",
|
||||||
|
"toolHook": "object|string",
|
||||||
|
"greeting": "boolean",
|
||||||
|
"earlyGeneration": "boolean",
|
||||||
|
"noiseIsolation": "string|#noiseIsolationPipeline"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"llm"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"bargeInPipeline": {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"minSpeechDuration": "number",
|
||||||
|
"sticky": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"noiseIsolationPipeline": {
|
||||||
|
"properties": {
|
||||||
|
"mode": "string",
|
||||||
|
"level": "number",
|
||||||
|
"direction": {
|
||||||
|
"enum": ["read", "write"]
|
||||||
|
},
|
||||||
|
"model": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"noiseIsolation" : {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"vendor": "string",
|
||||||
|
"level": "number",
|
||||||
|
"model": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"turnTaking": {
|
||||||
|
"properties": {
|
||||||
|
"enable": "boolean",
|
||||||
|
"vendor": "string",
|
||||||
|
"threshold": "number",
|
||||||
|
"model": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const test = require('tape');
|
const test = require('tape');
|
||||||
const logger = require('pino')({level: process.env.JAMBONES_LOGLEVEL || 'error'});
|
const logger = require('pino')({level: process.env.JAMBONES_LOGLEVEL || 'error'});
|
||||||
const { validate } = require('..');
|
const { validate, normalizeJambones } = require('..');
|
||||||
|
|
||||||
test("validate correct verbs", async (t) => {
|
test("validate correct verbs", async (t) => {
|
||||||
|
|
||||||
@@ -72,12 +72,18 @@ test("validate correct verbs", async (t) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"verb": "config",
|
"verb": "config",
|
||||||
|
"notifySttLatency": true,
|
||||||
"recognizer": {
|
"recognizer": {
|
||||||
"vendor": "google",
|
"vendor": "google",
|
||||||
"language": "de-DE",
|
"language": "de-DE",
|
||||||
"label": "label1",
|
"label": "label1",
|
||||||
"assemblyAiOptions": {
|
"assemblyAiOptions": {
|
||||||
"apiKey": "apikey"
|
"apiKey": "apikey",
|
||||||
|
"serviceVersion": "v3",
|
||||||
|
"formatTurns": true,
|
||||||
|
"endOfTurnConfidenceThreshold": 0.5,
|
||||||
|
"minEndOfTurnSilenceWhenConfident": 500,
|
||||||
|
"maxTurnSilence": 2000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -207,6 +213,10 @@ test("validate correct verbs", async (t) => {
|
|||||||
"deepgramOptions": {
|
"deepgramOptions": {
|
||||||
"endpointing": 500,
|
"endpointing": 500,
|
||||||
"noDelay": true,
|
"noDelay": true,
|
||||||
|
"eotThreshold": 500,
|
||||||
|
"eotTimeoutMs": 5000,
|
||||||
|
"eagerEotThreshold": 200,
|
||||||
|
"mipOptOut": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"say": {
|
"say": {
|
||||||
@@ -221,6 +231,10 @@ test("validate correct verbs", async (t) => {
|
|||||||
"fallbackVoice": "voice"
|
"fallbackVoice": "voice"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"say": {
|
||||||
|
"text": "To speak to Sales press 1 or say Sales. To speak to customer support press 2 or say Support",
|
||||||
|
"instructions": "Voice: High-energy, upbeat, and encouraging, projecting enthusiasm and motivation."
|
||||||
|
},
|
||||||
"say": {
|
"say": {
|
||||||
"text": "To speak to Sales press 1 or say Sales. To speak to customer support press 2 or say Support",
|
"text": "To speak to Sales press 1 or say Sales. To speak to customer support press 2 or say Support",
|
||||||
"synthesizer": {
|
"synthesizer": {
|
||||||
@@ -265,6 +279,70 @@ test("validate correct verbs", async (t) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"verb": "gather",
|
||||||
|
"actionHook": "http://example.com/collect",
|
||||||
|
"input": ["digits", "speech"],
|
||||||
|
"bargein": true,
|
||||||
|
"dtmfBargein": true,
|
||||||
|
"finishOnKey": "#",
|
||||||
|
"numDigits": 5,
|
||||||
|
"timeout": 8,
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "houndify",
|
||||||
|
"language": "en-US",
|
||||||
|
"houndifyOptions": {
|
||||||
|
"requestInfo": {
|
||||||
|
"Latitude": 30.6,
|
||||||
|
"Longitude": 30.6,
|
||||||
|
"City": "Huston"
|
||||||
|
},
|
||||||
|
"sampleRate": 16000,
|
||||||
|
"latitude": 30.6,
|
||||||
|
"longitude": 30.6,
|
||||||
|
"city": "Huston",
|
||||||
|
"state": "CA",
|
||||||
|
"country": "US",
|
||||||
|
"timeZone": "GMT",
|
||||||
|
"domain": "Voice",
|
||||||
|
"audioEndpoint": "https://api.houndify.com/v1/audio",
|
||||||
|
"maxSilenceSeconds": 5,
|
||||||
|
"maxSilenceAfterFullQuerySeconds": 3,
|
||||||
|
"maxSilenceAfterPartialQuerySeconds": 2,
|
||||||
|
"vadSensitivity": 0.5,
|
||||||
|
"vadTimeout": 1000,
|
||||||
|
"vadMode": "auto",
|
||||||
|
"vadVoiceMs": 250,
|
||||||
|
"vadSilenceMs": 500,
|
||||||
|
"vadDebug": true,
|
||||||
|
"audioFormat": "PCM16",
|
||||||
|
"enableNoiseReduction": true,
|
||||||
|
"enableProfanityFilter": false,
|
||||||
|
"enablePunctuation": true,
|
||||||
|
"enableCapitalization": true,
|
||||||
|
"confidenceThreshold": 0.7,
|
||||||
|
"enableDisfluencyFilter": true,
|
||||||
|
"maxResults": 5,
|
||||||
|
"enableWordTimestamps": true,
|
||||||
|
"maxAlternatives": 3,
|
||||||
|
"partialTranscriptInterval": 100,
|
||||||
|
"sessionTimeout": 30000,
|
||||||
|
"connectionTimeout": 5000,
|
||||||
|
"customVocabulary": ["jambonz", "telephony", "voip"],
|
||||||
|
"languageModel": "enhanced",
|
||||||
|
"audioQueryAbsoluteTimeout": 5
|
||||||
|
},
|
||||||
|
"gladiaOptions": {
|
||||||
|
"post_processing": {
|
||||||
|
"summarization": false,
|
||||||
|
"summarization_config": {
|
||||||
|
"type": "general"
|
||||||
|
},
|
||||||
|
"chapterization": false
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"verb": "gather",
|
"verb": "gather",
|
||||||
"actionHook": "http://example.com/collect",
|
"actionHook": "http://example.com/collect",
|
||||||
@@ -374,6 +452,10 @@ test("validate correct verbs", async (t) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"verb": "config",
|
||||||
|
"autoStreamTts": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"verb": "config",
|
"verb": "config",
|
||||||
"vad": {
|
"vad": {
|
||||||
@@ -381,7 +463,28 @@ test("validate correct verbs", async (t) => {
|
|||||||
"voiceMs": 250,
|
"voiceMs": 250,
|
||||||
"silenceMs": 150,
|
"silenceMs": 150,
|
||||||
"strategy": "one-shot",
|
"strategy": "one-shot",
|
||||||
"mode": 2
|
"mode": 2,
|
||||||
|
"vendor": "webrtc",
|
||||||
|
"threshold": 0.5,
|
||||||
|
"speechPadMs": 1000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "config",
|
||||||
|
"noiseIsolation": {
|
||||||
|
"enable": true,
|
||||||
|
"vendor": "krisp",
|
||||||
|
"level": 3,
|
||||||
|
"model": "custom-model"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "config",
|
||||||
|
"turnTaking": {
|
||||||
|
"enable": true,
|
||||||
|
"vendor": "krisp",
|
||||||
|
"threshold": 0.5,
|
||||||
|
"model": "turn-taking-model"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -465,6 +568,24 @@ test("validate correct verbs", async (t) => {
|
|||||||
"interim": true
|
"interim": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"verb": "transcribe",
|
||||||
|
"transcriptionHook": "http://example.com/transcribe",
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "nvidia",
|
||||||
|
"language" : "en-US",
|
||||||
|
"interim": true,
|
||||||
|
"elevenlabsOptions": {
|
||||||
|
"includeTimestamps": true,
|
||||||
|
"commitStrategy": "vad",
|
||||||
|
"vadSilenceThresholdSecs": 0.8,
|
||||||
|
"vadThreshold": 0.5,
|
||||||
|
"minSpeechDurationMs": 300,
|
||||||
|
"minSilenceDurationMs": 500,
|
||||||
|
"enableLogging": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"verb": "transcribe",
|
"verb": "transcribe",
|
||||||
"transcriptionHook": "http://example.com/transcribe",
|
"transcriptionHook": "http://example.com/transcribe",
|
||||||
@@ -522,6 +643,94 @@ test("validate correct verbs", async (t) => {
|
|||||||
},
|
},
|
||||||
"dual_streams": true,
|
"dual_streams": true,
|
||||||
"timeLimit" : 10
|
"timeLimit" : 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "llm",
|
||||||
|
"vendor": 'ultravox',
|
||||||
|
"model": 'fixie-ai/ultravox',
|
||||||
|
"auth": {
|
||||||
|
"apiKey": "sk-1234567890abcdefg"
|
||||||
|
},
|
||||||
|
"llmOptions": {
|
||||||
|
"firstSpeaker": 'FIRST_SPEAKER_AGENT',
|
||||||
|
},
|
||||||
|
"mcpServers": [
|
||||||
|
{
|
||||||
|
"url": 'https://mcp.example.com',
|
||||||
|
"auth": {
|
||||||
|
"username": 'username',
|
||||||
|
"password": 'password'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "s2s",
|
||||||
|
"vendor": "openai",
|
||||||
|
"llmOptions": {
|
||||||
|
"model": "gpt-4o-realtime"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "openai_s2s",
|
||||||
|
"llmOptions": {
|
||||||
|
"model": "gpt-4o-realtime"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "google_s2s",
|
||||||
|
"llmOptions": {
|
||||||
|
"model": "gemini-2.0-flash"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "elevenlabs_s2s",
|
||||||
|
"llmOptions": {
|
||||||
|
"agentId": "agent-123"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "stream",
|
||||||
|
"url": "wss://myrecorder.example.com/calls",
|
||||||
|
"mixType": "stereo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "pipeline",
|
||||||
|
"stt": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US"
|
||||||
|
},
|
||||||
|
"tts": {
|
||||||
|
"vendor": "google",
|
||||||
|
"language": "en-US"
|
||||||
|
},
|
||||||
|
"llm": {
|
||||||
|
"vendor": "openai",
|
||||||
|
"llmOptions": {
|
||||||
|
"model": "gpt-4o"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"actionHook": "/pipeline/action",
|
||||||
|
"eventHook": "/pipeline/event",
|
||||||
|
"toolHook": "/pipeline/tool"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verb": "transcribe",
|
||||||
|
"transcriptionHook": "http://example.com/transcribe",
|
||||||
|
"recognizer": {
|
||||||
|
"vendor": "speechmatics",
|
||||||
|
"language": "en",
|
||||||
|
"speechmaticsOptions": {
|
||||||
|
"transcription_config": {
|
||||||
|
"language": "en",
|
||||||
|
"enable_partials": true,
|
||||||
|
"max_delay": 2,
|
||||||
|
"conversation_config": {
|
||||||
|
"end_of_utterance_silence_trigger": 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
try {
|
try {
|
||||||
@@ -551,4 +760,108 @@ test('invalid test', async (t) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
})
|
});
|
||||||
|
|
||||||
|
test('verb synonyms: stream is synonym for listen', async (t) => {
|
||||||
|
// "verb" format
|
||||||
|
const result1 = normalizeJambones(logger, [
|
||||||
|
{"verb": "stream", "url": "wss://example.com/calls", "mixType": "stereo"}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result1[0])[0], 'listen', 'stream verb is rewritten to listen');
|
||||||
|
t.equal(result1[0].listen.url, 'wss://example.com/calls', 'data is preserved');
|
||||||
|
|
||||||
|
// object-key format
|
||||||
|
const result2 = normalizeJambones(logger, [
|
||||||
|
{"stream": {"url": "wss://example.com/calls"}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result2[0])[0], 'listen', 'stream key is rewritten to listen');
|
||||||
|
|
||||||
|
// validate passes
|
||||||
|
try {
|
||||||
|
validate(logger, [{"verb": "stream", "url": "wss://example.com/calls"}]);
|
||||||
|
t.pass('stream verb validates successfully');
|
||||||
|
} catch (err) {
|
||||||
|
t.fail('stream verb should validate: ' + err);
|
||||||
|
}
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('verb synonyms: s2s is synonym for llm', async (t) => {
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"verb": "s2s", "vendor": "openai", "llmOptions": {"model": "gpt-4o"}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result[0])[0], 'llm', 's2s verb is rewritten to llm');
|
||||||
|
t.equal(result[0].llm.vendor, 'openai', 'vendor is preserved');
|
||||||
|
t.equal(result[0].llm.llmOptions.model, 'gpt-4o', 'llmOptions preserved');
|
||||||
|
|
||||||
|
try {
|
||||||
|
validate(logger, [{"verb": "s2s", "vendor": "openai", "llmOptions": {"model": "gpt-4o"}}]);
|
||||||
|
t.pass('s2s verb validates successfully');
|
||||||
|
} catch (err) {
|
||||||
|
t.fail('s2s verb should validate: ' + err);
|
||||||
|
}
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vendor shortcuts: openai_s2s injects vendor', async (t) => {
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"verb": "openai_s2s", "llmOptions": {"model": "gpt-4o-realtime"}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result[0])[0], 'llm', 'openai_s2s is rewritten to llm');
|
||||||
|
t.equal(result[0].llm.vendor, 'openai', 'vendor is injected');
|
||||||
|
t.equal(result[0].llm.llmOptions.model, 'gpt-4o-realtime', 'llmOptions preserved');
|
||||||
|
|
||||||
|
try {
|
||||||
|
validate(logger, [{"verb": "openai_s2s", "llmOptions": {"model": "gpt-4o-realtime"}}]);
|
||||||
|
t.pass('openai_s2s validates successfully');
|
||||||
|
} catch (err) {
|
||||||
|
t.fail('openai_s2s should validate: ' + err);
|
||||||
|
}
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vendor shortcuts: all vendors work', async (t) => {
|
||||||
|
const vendors = [
|
||||||
|
'openai', 'microsoft', 'google', 'elevenlabs', 'deepgram', 'voiceagent', 'ultravox'
|
||||||
|
];
|
||||||
|
for (const vendor of vendors) {
|
||||||
|
const verbName = `${vendor}_s2s`;
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"verb": verbName, "llmOptions": {}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result[0])[0], 'llm', `${verbName} rewrites to llm`);
|
||||||
|
t.equal(result[0].llm.vendor, vendor, `${verbName} injects vendor=${vendor}`);
|
||||||
|
}
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vendor shortcuts: object-key format works', async (t) => {
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"google_s2s": {"llmOptions": {"model": "gemini-2.0-flash"}}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result[0])[0], 'llm', 'google_s2s key is rewritten to llm');
|
||||||
|
t.equal(result[0].llm.vendor, 'google', 'vendor is injected');
|
||||||
|
t.equal(result[0].llm.llmOptions.model, 'gemini-2.0-flash', 'llmOptions preserved');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('vendor shortcuts: explicit vendor in data overrides injected vendor', async (t) => {
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"verb": "openai_s2s", "vendor": "custom", "llmOptions": {}}
|
||||||
|
]);
|
||||||
|
t.equal(result[0].llm.vendor, 'custom', 'explicit vendor takes precedence');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('non-synonym verbs are unchanged', async (t) => {
|
||||||
|
const result = normalizeJambones(logger, [
|
||||||
|
{"verb": "say", "text": "hello"}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result[0])[0], 'say', 'say verb is not transformed');
|
||||||
|
|
||||||
|
const result2 = normalizeJambones(logger, [
|
||||||
|
{"llm": {"vendor": "openai", "llmOptions": {}}}
|
||||||
|
]);
|
||||||
|
t.equal(Object.keys(result2[0])[0], 'llm', 'llm verb is not transformed');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user