aillom-vox-client 1.0.0
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/LICENSE +15 -0
- package/README.md +272 -0
- package/dist/AillomVox.d.ts +36 -0
- package/dist/AillomVox.js +152 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.js +2 -0
- package/docs/ASTERISK.md +411 -0
- package/docs/PROTOCOL.md +156 -0
- package/docs/PROVIDERS.md +40 -0
- package/docs/TOOLS.md +314 -0
- package/docs/TROUBLESHOOTING.md +86 -0
- package/docs/VOICES.md +219 -0
- package/docs/providers/AILLOMVOX.md +185 -0
- package/docs/providers/AWS.md +32 -0
- package/docs/providers/GEMINI.md +33 -0
- package/docs/providers/GROK.md +25 -0
- package/docs/providers/OPENAI.md +39 -0
- package/docs/providers/QWEN.md +27 -0
- package/docs/providers/ULTRAVOX.md +29 -0
- package/examples/01-basic/app.js +196 -0
- package/examples/01-basic/index.html +27 -0
- package/examples/02-advanced-dashboard/app.js +465 -0
- package/examples/02-advanced-dashboard/index.html +200 -0
- package/examples/02-advanced-dashboard/style.css +501 -0
- package/examples/03-smart-home/index.html +377 -0
- package/examples/04-customer-support/index.html +474 -0
- package/examples/sdk-usage.ts +44 -0
- package/integrations/n8n-nodes-aillomvox/README.md +56 -0
- package/integrations/n8n-nodes-aillomvox/credentials/AillomVoxApi.credentials.ts +29 -0
- package/integrations/n8n-nodes-aillomvox/dist/credentials/AillomVoxApi.credentials.js +30 -0
- package/integrations/n8n-nodes-aillomvox/dist/nodes/AillomVox/AillomVox.node.js +219 -0
- package/integrations/n8n-nodes-aillomvox/dist/nodes/AillomVox/aillomvox.svg +6 -0
- package/integrations/n8n-nodes-aillomvox/gulpfile.js +10 -0
- package/integrations/n8n-nodes-aillomvox/nodes/AillomVox/AillomVox.node.ts +229 -0
- package/integrations/n8n-nodes-aillomvox/nodes/AillomVox/aillomvox.svg +6 -0
- package/integrations/n8n-nodes-aillomvox/package-lock.json +11741 -0
- package/integrations/n8n-nodes-aillomvox/package.json +56 -0
- package/integrations/n8n-nodes-aillomvox/tsconfig.json +32 -0
- package/package.json +55 -0
- package/src/AillomVox.ts +169 -0
- package/src/index.ts +2 -0
- package/src/types.ts +50 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-aillomvox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Official AillomVox node for n8n. Easy integration with Voice Gateway, Recordings Management, and AI Providers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"aillomvox",
|
|
8
|
+
"voice",
|
|
9
|
+
"ai",
|
|
10
|
+
"gateway"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://vox.aillom.com",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Aillom AI",
|
|
16
|
+
"email": "dev@aillom.com"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/aillom/aillom-vox-client.git"
|
|
21
|
+
},
|
|
22
|
+
"main": "index.js",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && gulp build:icons",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"format": "prettier nodes/**/*.ts --write",
|
|
27
|
+
"lint": "eslint nodes package.json",
|
|
28
|
+
"lintfix": "eslint nodes package.json --fix",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"n8n": {
|
|
35
|
+
"nodes": [
|
|
36
|
+
"dist/nodes/AillomVox/AillomVox.node.js"
|
|
37
|
+
],
|
|
38
|
+
"credentials": [
|
|
39
|
+
"dist/credentials/AillomVoxApi.credentials.js"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/express": "^4.17.6",
|
|
44
|
+
"@types/request-promise-native": "~1.0.15",
|
|
45
|
+
"@typescript-eslint/parser": "~5.45",
|
|
46
|
+
"eslint-plugin-n8n-nodes-base": "^1.11.0",
|
|
47
|
+
"gulp": "^4.0.2",
|
|
48
|
+
"n8n-core": "^1.122.14",
|
|
49
|
+
"n8n-workflow": "*",
|
|
50
|
+
"prettier": "^2.7.1",
|
|
51
|
+
"typescript": "^5.0.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"n8n-workflow": "*"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"rootDirs": [
|
|
4
|
+
"nodes",
|
|
5
|
+
"credentials"
|
|
6
|
+
],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"target": "es2019",
|
|
9
|
+
"lib": [
|
|
10
|
+
"es2019",
|
|
11
|
+
"dom"
|
|
12
|
+
],
|
|
13
|
+
"module": "commonjs",
|
|
14
|
+
"moduleResolution": "node",
|
|
15
|
+
"allowJs": true,
|
|
16
|
+
"checkJs": false,
|
|
17
|
+
"strict": false,
|
|
18
|
+
"noImplicitAny": false, // Relaxed for easier build in mixed envs
|
|
19
|
+
"esModuleInterop": true,
|
|
20
|
+
"resolveJsonModule": true,
|
|
21
|
+
"skipLibCheck": true,
|
|
22
|
+
"forceConsistentCasingInFileNames": true
|
|
23
|
+
},
|
|
24
|
+
"include": [
|
|
25
|
+
"nodes/**/*",
|
|
26
|
+
"credentials/**/*"
|
|
27
|
+
],
|
|
28
|
+
"exclude": [
|
|
29
|
+
"node_modules",
|
|
30
|
+
"dist"
|
|
31
|
+
]
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aillom-vox-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Enterprise-Grade Voice AI SDK for Speech-to-Speech, Audio-to-Audio, and Realtime Multimodal applications.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "tsc --watch",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"isomorphic-ws": "^5.0.0",
|
|
14
|
+
"ws": "^8.16.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/ws": "^8.5.10",
|
|
18
|
+
"typescript": "^5.3.3",
|
|
19
|
+
"ts-node": "^10.9.2",
|
|
20
|
+
"@types/node": "^20.11.19"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/aillom/aillom-vox-client.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"voice-ai",
|
|
28
|
+
"speech-to-speech",
|
|
29
|
+
"audio-to-audio",
|
|
30
|
+
"realtime-api",
|
|
31
|
+
"websocket-client",
|
|
32
|
+
"openai-realtime",
|
|
33
|
+
"gemini-multimodal",
|
|
34
|
+
"low-latency",
|
|
35
|
+
"voice-gateway",
|
|
36
|
+
"sip-trunking",
|
|
37
|
+
"asterisk",
|
|
38
|
+
"inworld-ai",
|
|
39
|
+
"gpt-4o-realtime",
|
|
40
|
+
"telephony",
|
|
41
|
+
"ultravox",
|
|
42
|
+
"nova-sonic"
|
|
43
|
+
],
|
|
44
|
+
"author": "Aillom Technologies <contato@aillom.com.br>",
|
|
45
|
+
"license": "ISC",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/aillom/aillom-vox-client/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://vox.aillom.com",
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^25.2.2",
|
|
52
|
+
"ts-node": "^10.9.2",
|
|
53
|
+
"typescript": "^5.9.3"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/AillomVox.ts
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { AillomVoxConfig, TranscriptEvent, ToolCallEvent, EventHandler } from './types';
|
|
2
|
+
import WebSocket from 'isomorphic-ws';
|
|
3
|
+
|
|
4
|
+
export class AillomVox {
|
|
5
|
+
private ws: WebSocket | null = null;
|
|
6
|
+
private config: AillomVoxConfig;
|
|
7
|
+
private eventListeners: Map<string, EventHandler[]> = new Map();
|
|
8
|
+
private isConnected: boolean = false;
|
|
9
|
+
private url: string = 'wss://vox.aillom.com/ws';
|
|
10
|
+
|
|
11
|
+
constructor(config: AillomVoxConfig) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
if (!this.config.apiKey) {
|
|
14
|
+
throw new Error('AillomVox: apiKey is required');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Connects to the AillomVox Gateway
|
|
20
|
+
*/
|
|
21
|
+
public connect(): Promise<void> {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
try {
|
|
24
|
+
this.ws = new WebSocket(this.url);
|
|
25
|
+
this.ws.binaryType = 'arraybuffer';
|
|
26
|
+
|
|
27
|
+
this.ws.onopen = () => {
|
|
28
|
+
this.isConnected = true;
|
|
29
|
+
this.sendConfig();
|
|
30
|
+
this.emit('connected', {});
|
|
31
|
+
resolve();
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.ws.onmessage = (event: WebSocket.MessageEvent) => {
|
|
35
|
+
this.handleMessage(event);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
this.ws.onerror = (error: WebSocket.ErrorEvent) => {
|
|
39
|
+
this.emit('error', error);
|
|
40
|
+
if (!this.isConnected) reject(error);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
this.ws.onclose = (event: WebSocket.CloseEvent) => {
|
|
44
|
+
this.isConnected = false;
|
|
45
|
+
this.emit('disconnected', { code: event.code, reason: event.reason });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
} catch (err) {
|
|
49
|
+
reject(err);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sends audio chunk (PCM 16-bit) to the AI
|
|
56
|
+
*/
|
|
57
|
+
public sendAudio(chunk: ArrayBuffer | Int16Array | Buffer): void {
|
|
58
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
59
|
+
this.ws.send(chunk);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Sends a tool result back to the AI
|
|
64
|
+
*/
|
|
65
|
+
public sendToolResult(callId: string, result: any): void {
|
|
66
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
67
|
+
this.ws.send(JSON.stringify({
|
|
68
|
+
type: 'tool_result',
|
|
69
|
+
call_id: callId,
|
|
70
|
+
result: result
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Disconnects the session
|
|
76
|
+
*/
|
|
77
|
+
public disconnect(): void {
|
|
78
|
+
if (this.ws) {
|
|
79
|
+
this.ws.close();
|
|
80
|
+
this.ws = null;
|
|
81
|
+
this.isConnected = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Subscribes to an event
|
|
87
|
+
*/
|
|
88
|
+
public on(event: 'audio', handler: (data: ArrayBuffer) => void): void;
|
|
89
|
+
public on(event: 'transcript', handler: (data: TranscriptEvent) => void): void;
|
|
90
|
+
public on(event: 'tool_call', handler: (data: ToolCallEvent) => void): void;
|
|
91
|
+
public on(event: 'error', handler: (error: any) => void): void;
|
|
92
|
+
public on(event: 'connected' | 'disconnected' | 'interruption', handler: (data: any) => void): void;
|
|
93
|
+
public on(event: string, handler: EventHandler): void {
|
|
94
|
+
if (!this.eventListeners.has(event)) {
|
|
95
|
+
this.eventListeners.set(event, []);
|
|
96
|
+
}
|
|
97
|
+
this.eventListeners.get(event)?.push(handler);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private sendConfig() {
|
|
101
|
+
if (!this.ws) return;
|
|
102
|
+
|
|
103
|
+
const payload = {
|
|
104
|
+
type: 'config',
|
|
105
|
+
apikey: this.config.apiKey,
|
|
106
|
+
provider: this.config.provider || 'aillomvox',
|
|
107
|
+
voice: this.config.voice || 'Edward',
|
|
108
|
+
language: this.config.language || 'en-US',
|
|
109
|
+
sample_rate: this.config.sampleRate || 16000,
|
|
110
|
+
system_prompt: this.config.systemPrompt,
|
|
111
|
+
tools: this.config.tools,
|
|
112
|
+
webhook_url: this.config.webhookUrl,
|
|
113
|
+
max_duration: this.config.maxDuration
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (this.config.debug) {
|
|
117
|
+
console.log('[AillomVox] Sending config:', JSON.stringify(payload, null, 2));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.ws.send(JSON.stringify(payload));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private handleMessage(event: WebSocket.MessageEvent) {
|
|
124
|
+
const data = event.data;
|
|
125
|
+
|
|
126
|
+
// Handle Binary Audio
|
|
127
|
+
if (data instanceof ArrayBuffer || (typeof Buffer !== 'undefined' && Buffer.isBuffer(data))) {
|
|
128
|
+
this.emit('audio', data);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Handle JSON Control Messages
|
|
133
|
+
if (typeof data === 'string') {
|
|
134
|
+
try {
|
|
135
|
+
const msg = JSON.parse(data);
|
|
136
|
+
|
|
137
|
+
switch (msg.type) {
|
|
138
|
+
case 'transcript':
|
|
139
|
+
this.emit('transcript', msg);
|
|
140
|
+
break;
|
|
141
|
+
case 'tool_call':
|
|
142
|
+
this.emit('tool_call', msg);
|
|
143
|
+
break;
|
|
144
|
+
case 'error':
|
|
145
|
+
this.emit('error', msg);
|
|
146
|
+
break;
|
|
147
|
+
case 'interruption':
|
|
148
|
+
this.emit('interruption', {});
|
|
149
|
+
break;
|
|
150
|
+
case 'hangup':
|
|
151
|
+
this.disconnect();
|
|
152
|
+
this.emit('disconnected', { reason: 'agent_hangup' });
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
if (this.config.debug) console.log('[AillomVox] Data:', msg);
|
|
156
|
+
}
|
|
157
|
+
} catch (e) {
|
|
158
|
+
console.error('[AillomVox] Failed to parse message:', e);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private emit(event: string, data: any) {
|
|
164
|
+
const listeners = this.eventListeners.get(event);
|
|
165
|
+
if (listeners) {
|
|
166
|
+
listeners.forEach(handler => handler(data));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type ProviderType =
|
|
2
|
+
| 'aillomvox'
|
|
3
|
+
| 'gemini'
|
|
4
|
+
| 'openai'
|
|
5
|
+
| 'aws'
|
|
6
|
+
| 'qwen'
|
|
7
|
+
| 'grok'
|
|
8
|
+
| 'ultravox';
|
|
9
|
+
|
|
10
|
+
export interface MicrophoneConfig {
|
|
11
|
+
sampleRate?: 8000 | 16000 | 24000;
|
|
12
|
+
inputSampleRate?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ClientTool {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
parameters: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface AillomVoxConfig {
|
|
22
|
+
apiKey: string;
|
|
23
|
+
provider?: ProviderType;
|
|
24
|
+
voice?: string;
|
|
25
|
+
language?: string;
|
|
26
|
+
systemPrompt?: string;
|
|
27
|
+
sampleRate?: 8000 | 16000 | 24000;
|
|
28
|
+
debug?: boolean;
|
|
29
|
+
tools?: ClientTool[];
|
|
30
|
+
webhookUrl?: string;
|
|
31
|
+
maxDuration?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TranscriptEvent {
|
|
35
|
+
role: 'user' | 'assistant';
|
|
36
|
+
text: string;
|
|
37
|
+
final: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ToolCallEvent {
|
|
41
|
+
call_id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
args: any;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AudioEvent {
|
|
47
|
+
buffer: ArrayBuffer;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type EventHandler<T = any> = (data: T) => void;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": [
|
|
6
|
+
"es2020",
|
|
7
|
+
"dom"
|
|
8
|
+
],
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"src/**/*"
|
|
18
|
+
],
|
|
19
|
+
"exclude": [
|
|
20
|
+
"node_modules",
|
|
21
|
+
"dist"
|
|
22
|
+
]
|
|
23
|
+
}
|