@lantanios/agentic-server 0.1.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/.env.example +16 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +40 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/namespaces/audio.d.ts +8 -0
- package/dist/namespaces/audio.d.ts.map +1 -0
- package/dist/namespaces/audio.js +126 -0
- package/dist/namespaces/audio.js.map +1 -0
- package/dist/namespaces/events.d.ts +3 -0
- package/dist/namespaces/events.d.ts.map +1 -0
- package/dist/namespaces/events.js +125 -0
- package/dist/namespaces/events.js.map +1 -0
- package/dist/namespaces/index.d.ts +4 -0
- package/dist/namespaces/index.d.ts.map +1 -0
- package/dist/namespaces/index.js +10 -0
- package/dist/namespaces/index.js.map +1 -0
- package/dist/namespaces/memory.d.ts +3 -0
- package/dist/namespaces/memory.d.ts.map +1 -0
- package/dist/namespaces/memory.js +139 -0
- package/dist/namespaces/memory.js.map +1 -0
- package/package.json +35 -0
- package/src/config.ts +40 -0
- package/src/index.ts +143 -0
- package/src/namespaces/audio.ts +142 -0
- package/src/namespaces/events.ts +144 -0
- package/src/namespaces/index.ts +3 -0
- package/src/namespaces/memory.ts +149 -0
- package/tsconfig.json +13 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.setupMemoryNamespace = setupMemoryNamespace;
|
|
4
|
+
const agentic_memory_1 = require("@lantanios/agentic-memory");
|
|
5
|
+
function getAgentId(socket) {
|
|
6
|
+
return socket.data.agentId;
|
|
7
|
+
}
|
|
8
|
+
function setupMemoryNamespace(io) {
|
|
9
|
+
io.use((socket, next) => {
|
|
10
|
+
const agentId = socket.handshake.auth.agentId;
|
|
11
|
+
if (!agentId) {
|
|
12
|
+
return next(new Error('agentId is required'));
|
|
13
|
+
}
|
|
14
|
+
socket.data.agentId = agentId;
|
|
15
|
+
next();
|
|
16
|
+
});
|
|
17
|
+
io.on('connection', (socket) => {
|
|
18
|
+
const agentId = getAgentId(socket);
|
|
19
|
+
console.log(`[memory] Agent connected: ${agentId}`);
|
|
20
|
+
// Create memory
|
|
21
|
+
socket.on('create', async (data, callback) => {
|
|
22
|
+
try {
|
|
23
|
+
const memory = await (0, agentic_memory_1.createMemory)({
|
|
24
|
+
agentId,
|
|
25
|
+
type: data.type,
|
|
26
|
+
content: data.content,
|
|
27
|
+
metadata: data.metadata,
|
|
28
|
+
expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
|
|
29
|
+
});
|
|
30
|
+
callback({ success: true, memory });
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
callback({ success: false, error: error.message });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// List memories
|
|
37
|
+
socket.on('list', async (data, callback) => {
|
|
38
|
+
try {
|
|
39
|
+
const result = await (0, agentic_memory_1.listMemories)({
|
|
40
|
+
agentId,
|
|
41
|
+
...data,
|
|
42
|
+
since: data.since ? new Date(data.since) : undefined,
|
|
43
|
+
until: data.until ? new Date(data.until) : undefined,
|
|
44
|
+
});
|
|
45
|
+
callback({ success: true, ...result });
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
callback({ success: false, error: error.message });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Get single memory
|
|
52
|
+
socket.on('get', async (id, callback) => {
|
|
53
|
+
try {
|
|
54
|
+
const memory = await (0, agentic_memory_1.getMemory)(agentId, id);
|
|
55
|
+
if (!memory) {
|
|
56
|
+
return callback({ success: false, error: 'Memory not found' });
|
|
57
|
+
}
|
|
58
|
+
callback({ success: true, memory });
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
callback({ success: false, error: error.message });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// Update memory
|
|
65
|
+
socket.on('update', async (data, callback) => {
|
|
66
|
+
try {
|
|
67
|
+
const memory = await (0, agentic_memory_1.updateMemory)(agentId, data.id, {
|
|
68
|
+
content: data.content,
|
|
69
|
+
type: data.type,
|
|
70
|
+
metadata: data.metadata,
|
|
71
|
+
});
|
|
72
|
+
if (!memory) {
|
|
73
|
+
return callback({ success: false, error: 'Memory not found' });
|
|
74
|
+
}
|
|
75
|
+
callback({ success: true, memory });
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
callback({ success: false, error: error.message });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// Delete memory
|
|
82
|
+
socket.on('delete', async (id, callback) => {
|
|
83
|
+
try {
|
|
84
|
+
const memory = await (0, agentic_memory_1.deleteMemory)(agentId, id);
|
|
85
|
+
if (!memory) {
|
|
86
|
+
return callback({ success: false, error: 'Memory not found' });
|
|
87
|
+
}
|
|
88
|
+
callback({ success: true, memory });
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
callback({ success: false, error: error.message });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
// Vector search
|
|
95
|
+
socket.on('search', async (data, callback) => {
|
|
96
|
+
try {
|
|
97
|
+
const results = await (0, agentic_memory_1.vectorSearch)({
|
|
98
|
+
agentId,
|
|
99
|
+
query: data.query,
|
|
100
|
+
type: data.type,
|
|
101
|
+
limit: data.limit,
|
|
102
|
+
minScore: data.minScore,
|
|
103
|
+
});
|
|
104
|
+
callback({ success: true, results });
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
callback({ success: false, error: error.message });
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Recall context
|
|
111
|
+
socket.on('recall', async (data, callback) => {
|
|
112
|
+
try {
|
|
113
|
+
const result = await (0, agentic_memory_1.recall)({
|
|
114
|
+
agentId,
|
|
115
|
+
context: data.context,
|
|
116
|
+
limit: data.limit,
|
|
117
|
+
});
|
|
118
|
+
callback({ success: true, ...result });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
callback({ success: false, error: error.message });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Get summary stats
|
|
125
|
+
socket.on('summary', async (callback) => {
|
|
126
|
+
try {
|
|
127
|
+
const summary = await (0, agentic_memory_1.getSummary)(agentId);
|
|
128
|
+
callback({ success: true, summary });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
callback({ success: false, error: error.message });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
socket.on('disconnect', () => {
|
|
135
|
+
console.log(`[memory] Agent disconnected: ${agentId}`);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/namespaces/memory.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lantanios/agentic-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Real-time server with Socket.IO for memory and audio streaming",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"dev": "ts-node-dev --respawn src/index.ts",
|
|
10
|
+
"start": "node dist/index.js",
|
|
11
|
+
"lint": "eslint src --ext .ts"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["socket.io", "realtime", "audio", "memory", "ai"],
|
|
14
|
+
"author": "Lantanios",
|
|
15
|
+
"license": "UNLICENSED",
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "restricted"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@lantanios/agentic-audio": "workspace:*",
|
|
21
|
+
"@lantanios/agentic-memory": "workspace:*",
|
|
22
|
+
"cors": "^2.8.5",
|
|
23
|
+
"dotenv": "^16.0.0",
|
|
24
|
+
"express": "^4.18.0",
|
|
25
|
+
"mongoose": "^8.0.0",
|
|
26
|
+
"socket.io": "^4.7.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/cors": "^2.8.0",
|
|
30
|
+
"@types/express": "^4.17.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"ts-node-dev": "^2.0.0",
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
export const config = {
|
|
5
|
+
port: parseInt(process.env.PORT || '3847', 10),
|
|
6
|
+
|
|
7
|
+
// MongoDB
|
|
8
|
+
mongoUri: process.env.MONGODB_URI || '',
|
|
9
|
+
|
|
10
|
+
// OpenAI (for embeddings)
|
|
11
|
+
openaiApiKey: process.env.OPENAI_API_KEY || '',
|
|
12
|
+
|
|
13
|
+
// CORS
|
|
14
|
+
corsOrigins: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:3000'],
|
|
15
|
+
|
|
16
|
+
// Audio providers
|
|
17
|
+
audio: {
|
|
18
|
+
stt: {
|
|
19
|
+
localEndpoint: process.env.LOCAL_STT_ENDPOINT || 'http://localhost:8001',
|
|
20
|
+
deepgramApiKey: process.env.DEEPGRAM_API_KEY || '',
|
|
21
|
+
},
|
|
22
|
+
tts: {
|
|
23
|
+
localEndpoint: process.env.LOCAL_TTS_ENDPOINT || 'http://localhost:8001',
|
|
24
|
+
openaiApiKey: process.env.OPENAI_API_KEY || '',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function validateConfig(): string[] {
|
|
30
|
+
const errors: string[] = [];
|
|
31
|
+
|
|
32
|
+
if (!config.mongoUri) {
|
|
33
|
+
errors.push('MONGODB_URI is required');
|
|
34
|
+
}
|
|
35
|
+
if (!config.openaiApiKey) {
|
|
36
|
+
errors.push('OPENAI_API_KEY is required for embeddings');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return errors;
|
|
40
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import express, { Application } from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { createServer } from 'http';
|
|
4
|
+
import { Server } from 'socket.io';
|
|
5
|
+
import mongoose from 'mongoose';
|
|
6
|
+
|
|
7
|
+
import { config, validateConfig } from './config';
|
|
8
|
+
import { setupMemoryNamespace, setupAudioNamespace, setupEventsNamespace } from './namespaces';
|
|
9
|
+
import { AudioService, AudioServiceConfig } from '@lantanios/agentic-audio';
|
|
10
|
+
|
|
11
|
+
const app: Application = express();
|
|
12
|
+
app.use(cors({ origin: config.corsOrigins }));
|
|
13
|
+
app.use(express.json());
|
|
14
|
+
|
|
15
|
+
const httpServer = createServer(app);
|
|
16
|
+
|
|
17
|
+
const io = new Server(httpServer, {
|
|
18
|
+
cors: {
|
|
19
|
+
origin: config.corsOrigins,
|
|
20
|
+
methods: ['GET', 'POST'],
|
|
21
|
+
},
|
|
22
|
+
maxHttpBufferSize: 10 * 1024 * 1024, // 10MB for audio chunks
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Create audio service with priority-based providers
|
|
26
|
+
function createAudioService(): AudioService {
|
|
27
|
+
const audioConfig: AudioServiceConfig = {
|
|
28
|
+
stt: {
|
|
29
|
+
providers: [
|
|
30
|
+
{
|
|
31
|
+
name: 'local-whisper',
|
|
32
|
+
type: 'local-whisper',
|
|
33
|
+
priority: 1,
|
|
34
|
+
endpoint: config.audio.stt.localEndpoint,
|
|
35
|
+
model: 'large-v3',
|
|
36
|
+
timeout: 30000,
|
|
37
|
+
},
|
|
38
|
+
...(config.audio.stt.deepgramApiKey
|
|
39
|
+
? [{
|
|
40
|
+
name: 'deepgram',
|
|
41
|
+
type: 'deepgram' as const,
|
|
42
|
+
priority: 2,
|
|
43
|
+
apiKey: config.audio.stt.deepgramApiKey,
|
|
44
|
+
timeout: 30000,
|
|
45
|
+
}]
|
|
46
|
+
: []),
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
tts: {
|
|
50
|
+
providers: [
|
|
51
|
+
{
|
|
52
|
+
name: 'local-piper',
|
|
53
|
+
type: 'local-piper',
|
|
54
|
+
priority: 1,
|
|
55
|
+
endpoint: config.audio.tts.localEndpoint,
|
|
56
|
+
voice: 'en_US-lessac-medium',
|
|
57
|
+
timeout: 30000,
|
|
58
|
+
},
|
|
59
|
+
...(config.audio.tts.openaiApiKey
|
|
60
|
+
? [{
|
|
61
|
+
name: 'openai-tts',
|
|
62
|
+
type: 'openai-tts' as const,
|
|
63
|
+
priority: 2,
|
|
64
|
+
apiKey: config.audio.tts.openaiApiKey,
|
|
65
|
+
voice: 'nova' as const,
|
|
66
|
+
}]
|
|
67
|
+
: []),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return new AudioService(audioConfig);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Health check
|
|
76
|
+
app.get('/health', async (_req, res) => {
|
|
77
|
+
const mongoStatus = mongoose.connection.readyState === 1 ? 'connected' : 'disconnected';
|
|
78
|
+
|
|
79
|
+
res.json({
|
|
80
|
+
status: 'ok',
|
|
81
|
+
timestamp: new Date().toISOString(),
|
|
82
|
+
mongodb: mongoStatus,
|
|
83
|
+
sockets: io.engine.clientsCount,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Provider status
|
|
88
|
+
app.get('/providers', async (_req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const audioService = createAudioService();
|
|
91
|
+
const status = await audioService.checkAllProviders();
|
|
92
|
+
res.json({ status: 'ok', providers: status });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
res.status(500).json({ status: 'error', error: (error as Error).message });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
async function start() {
|
|
99
|
+
// Validate config
|
|
100
|
+
const errors = validateConfig();
|
|
101
|
+
if (errors.length > 0) {
|
|
102
|
+
console.error('Configuration errors:');
|
|
103
|
+
errors.forEach(e => console.error(` - ${e}`));
|
|
104
|
+
console.log('\nStarting anyway with available features...');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Connect to MongoDB
|
|
108
|
+
if (config.mongoUri) {
|
|
109
|
+
try {
|
|
110
|
+
console.log('Connecting to MongoDB...');
|
|
111
|
+
await mongoose.connect(config.mongoUri);
|
|
112
|
+
console.log('✓ Connected to MongoDB');
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error('✗ MongoDB connection failed:', err);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Create audio service
|
|
119
|
+
const audioService = createAudioService();
|
|
120
|
+
|
|
121
|
+
// Setup Socket.IO namespaces
|
|
122
|
+
setupMemoryNamespace(io.of('/memory'));
|
|
123
|
+
setupAudioNamespace(io.of('/audio'), { audioService });
|
|
124
|
+
setupEventsNamespace(io.of('/events'));
|
|
125
|
+
|
|
126
|
+
console.log('✓ Socket.IO namespaces configured');
|
|
127
|
+
|
|
128
|
+
// Start server
|
|
129
|
+
httpServer.listen(config.port, () => {
|
|
130
|
+
console.log(`\n✓ @lantanios/agentic-server running on http://localhost:${config.port}`);
|
|
131
|
+
console.log('\nNamespaces:');
|
|
132
|
+
console.log(' /memory - Memory CRUD + vector search');
|
|
133
|
+
console.log(' /audio - STT/TTS streaming');
|
|
134
|
+
console.log(' /events - Pub/sub + direct messaging');
|
|
135
|
+
console.log('\nREST endpoints:');
|
|
136
|
+
console.log(' GET /health - Health check');
|
|
137
|
+
console.log(' GET /providers - Audio provider status');
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
start().catch(console.error);
|
|
142
|
+
|
|
143
|
+
export { io, app, httpServer };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Namespace, Socket } from 'socket.io';
|
|
2
|
+
import { AudioService } from '@lantanios/agentic-audio';
|
|
3
|
+
|
|
4
|
+
interface AudioNamespaceOptions {
|
|
5
|
+
audioService: AudioService;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getAgentId(socket: Socket): string {
|
|
9
|
+
return socket.data.agentId as string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function setupAudioNamespace(io: Namespace, options: AudioNamespaceOptions) {
|
|
13
|
+
const { audioService } = options;
|
|
14
|
+
|
|
15
|
+
io.use((socket, next) => {
|
|
16
|
+
const agentId = socket.handshake.auth.agentId;
|
|
17
|
+
if (!agentId) {
|
|
18
|
+
return next(new Error('agentId is required'));
|
|
19
|
+
}
|
|
20
|
+
socket.data.agentId = agentId;
|
|
21
|
+
socket.data.audioBuffer = [];
|
|
22
|
+
socket.data.isRecording = false;
|
|
23
|
+
next();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
io.on('connection', (socket) => {
|
|
27
|
+
const agentId = getAgentId(socket);
|
|
28
|
+
console.log(`[audio] Agent connected: ${agentId}`);
|
|
29
|
+
|
|
30
|
+
// Start audio stream
|
|
31
|
+
socket.on('stream:start', (options, callback) => {
|
|
32
|
+
socket.data.audioBuffer = [];
|
|
33
|
+
socket.data.isRecording = true;
|
|
34
|
+
console.log(`[audio] Stream started for ${agentId}`);
|
|
35
|
+
callback?.({ success: true });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Receive audio chunk
|
|
39
|
+
socket.on('stream:chunk', (chunk: Buffer | ArrayBuffer) => {
|
|
40
|
+
if (!socket.data.isRecording) return;
|
|
41
|
+
|
|
42
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
43
|
+
(socket.data.audioBuffer as Buffer[]).push(buffer);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Stop stream and transcribe
|
|
47
|
+
socket.on('stream:stop', async (options, callback) => {
|
|
48
|
+
if (!socket.data.isRecording) {
|
|
49
|
+
return callback?.({ success: false, error: 'Not recording' });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
socket.data.isRecording = false;
|
|
53
|
+
const audioBuffer = socket.data.audioBuffer as Buffer[];
|
|
54
|
+
const audio = Buffer.concat(audioBuffer);
|
|
55
|
+
socket.data.audioBuffer = [];
|
|
56
|
+
|
|
57
|
+
if (audio.length === 0) {
|
|
58
|
+
return callback?.({ success: false, error: 'No audio data' });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
console.log(`[audio] Transcribing ${audio.length} bytes for ${agentId}`);
|
|
63
|
+
const result = await audioService.stt.transcribe(audio, {
|
|
64
|
+
language: options?.language,
|
|
65
|
+
provider: options?.provider,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
callback?.({ success: true, result });
|
|
69
|
+
socket.emit('transcription', result);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`[audio] Transcription error:`, error);
|
|
72
|
+
callback?.({ success: false, error: (error as Error).message });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// One-shot transcription
|
|
77
|
+
socket.on('transcribe', async (data, callback) => {
|
|
78
|
+
try {
|
|
79
|
+
const audio = Buffer.isBuffer(data.audio) ? data.audio : Buffer.from(data.audio);
|
|
80
|
+
const result = await audioService.stt.transcribe(audio, {
|
|
81
|
+
language: data.language,
|
|
82
|
+
provider: data.provider,
|
|
83
|
+
});
|
|
84
|
+
callback({ success: true, result });
|
|
85
|
+
} catch (error) {
|
|
86
|
+
callback({ success: false, error: (error as Error).message });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Text to speech
|
|
91
|
+
socket.on('speak', async (data, callback) => {
|
|
92
|
+
try {
|
|
93
|
+
const audio = await audioService.tts.speak(data.text, {
|
|
94
|
+
voice: data.voice,
|
|
95
|
+
speed: data.speed,
|
|
96
|
+
format: data.format || 'mp3',
|
|
97
|
+
provider: data.provider,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
callback({ success: true, audio });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
callback({ success: false, error: (error as Error).message });
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Stream TTS
|
|
107
|
+
socket.on('speak:stream', async (data) => {
|
|
108
|
+
try {
|
|
109
|
+
socket.emit('speak:start');
|
|
110
|
+
|
|
111
|
+
for await (const chunk of audioService.tts.speakStream(data.text, {
|
|
112
|
+
voice: data.voice,
|
|
113
|
+
speed: data.speed,
|
|
114
|
+
format: data.format || 'mp3',
|
|
115
|
+
provider: data.provider,
|
|
116
|
+
})) {
|
|
117
|
+
socket.emit('speak:chunk', chunk);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
socket.emit('speak:end');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
socket.emit('speak:error', { error: (error as Error).message });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Check provider status
|
|
127
|
+
socket.on('providers:status', async (callback) => {
|
|
128
|
+
try {
|
|
129
|
+
const status = await audioService.checkAllProviders();
|
|
130
|
+
callback({ success: true, status });
|
|
131
|
+
} catch (error) {
|
|
132
|
+
callback({ success: false, error: (error as Error).message });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
socket.on('disconnect', () => {
|
|
137
|
+
socket.data.isRecording = false;
|
|
138
|
+
socket.data.audioBuffer = [];
|
|
139
|
+
console.log(`[audio] Agent disconnected: ${agentId}`);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Namespace, Socket } from 'socket.io';
|
|
2
|
+
|
|
3
|
+
// Simple pub/sub for cross-agent events
|
|
4
|
+
const channels = new Map<string, Set<Socket>>();
|
|
5
|
+
|
|
6
|
+
function getAgentId(socket: Socket): string {
|
|
7
|
+
return socket.data.agentId as string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getSubscriptions(socket: Socket): Set<string> {
|
|
11
|
+
if (!socket.data.subscriptions) {
|
|
12
|
+
socket.data.subscriptions = new Set<string>();
|
|
13
|
+
}
|
|
14
|
+
return socket.data.subscriptions as Set<string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function setupEventsNamespace(io: Namespace) {
|
|
18
|
+
io.use((socket, next) => {
|
|
19
|
+
const agentId = socket.handshake.auth.agentId;
|
|
20
|
+
if (!agentId) {
|
|
21
|
+
return next(new Error('agentId is required'));
|
|
22
|
+
}
|
|
23
|
+
socket.data.agentId = agentId;
|
|
24
|
+
socket.data.subscriptions = new Set<string>();
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
io.on('connection', (socket) => {
|
|
29
|
+
const agentId = getAgentId(socket);
|
|
30
|
+
console.log(`[events] Agent connected: ${agentId}`);
|
|
31
|
+
|
|
32
|
+
// Subscribe to a channel
|
|
33
|
+
socket.on('subscribe', (channel: string, callback) => {
|
|
34
|
+
if (!channels.has(channel)) {
|
|
35
|
+
channels.set(channel, new Set());
|
|
36
|
+
}
|
|
37
|
+
channels.get(channel)!.add(socket);
|
|
38
|
+
getSubscriptions(socket).add(channel);
|
|
39
|
+
|
|
40
|
+
console.log(`[events] ${agentId} subscribed to ${channel}`);
|
|
41
|
+
callback?.({ success: true, channel });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Unsubscribe from a channel
|
|
45
|
+
socket.on('unsubscribe', (channel: string, callback) => {
|
|
46
|
+
channels.get(channel)?.delete(socket);
|
|
47
|
+
getSubscriptions(socket).delete(channel);
|
|
48
|
+
|
|
49
|
+
console.log(`[events] ${agentId} unsubscribed from ${channel}`);
|
|
50
|
+
callback?.({ success: true, channel });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Publish to a channel
|
|
54
|
+
socket.on('publish', (data: { channel: string; event: string; payload: unknown }, callback) => {
|
|
55
|
+
const { channel, event, payload } = data;
|
|
56
|
+
const subscribers = channels.get(channel);
|
|
57
|
+
|
|
58
|
+
if (!subscribers || subscribers.size === 0) {
|
|
59
|
+
return callback?.({ success: true, delivered: 0 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let delivered = 0;
|
|
63
|
+
for (const subscriber of subscribers) {
|
|
64
|
+
if (subscriber.id !== socket.id) { // Don't send to self
|
|
65
|
+
subscriber.emit('event', {
|
|
66
|
+
channel,
|
|
67
|
+
event,
|
|
68
|
+
payload,
|
|
69
|
+
from: agentId,
|
|
70
|
+
timestamp: Date.now(),
|
|
71
|
+
});
|
|
72
|
+
delivered++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(`[events] ${agentId} published ${event} to ${channel} (${delivered} recipients)`);
|
|
77
|
+
callback?.({ success: true, delivered });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Broadcast to all connected agents (except self)
|
|
81
|
+
socket.on('broadcast', (data: { event: string; payload: unknown }, callback) => {
|
|
82
|
+
const { event, payload } = data;
|
|
83
|
+
|
|
84
|
+
let delivered = 0;
|
|
85
|
+
for (const [, s] of io.sockets) {
|
|
86
|
+
if (s.id !== socket.id) {
|
|
87
|
+
s.emit('broadcast', {
|
|
88
|
+
event,
|
|
89
|
+
payload,
|
|
90
|
+
from: agentId,
|
|
91
|
+
timestamp: Date.now(),
|
|
92
|
+
});
|
|
93
|
+
delivered++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`[events] ${agentId} broadcast ${event} (${delivered} recipients)`);
|
|
98
|
+
callback?.({ success: true, delivered });
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Direct message to another agent
|
|
102
|
+
socket.on('dm', (data: { to: string; event: string; payload: unknown }, callback) => {
|
|
103
|
+
const { to, event, payload } = data;
|
|
104
|
+
|
|
105
|
+
let delivered = false;
|
|
106
|
+
for (const [, s] of io.sockets) {
|
|
107
|
+
if (getAgentId(s) === to) {
|
|
108
|
+
s.emit('dm', {
|
|
109
|
+
event,
|
|
110
|
+
payload,
|
|
111
|
+
from: agentId,
|
|
112
|
+
timestamp: Date.now(),
|
|
113
|
+
});
|
|
114
|
+
delivered = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(`[events] ${agentId} DM to ${to}: ${event} (${delivered ? 'delivered' : 'not found'})`);
|
|
120
|
+
callback?.({ success: delivered, delivered: delivered ? 1 : 0 });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// List active agents
|
|
124
|
+
socket.on('agents:list', (callback) => {
|
|
125
|
+
const agents: string[] = [];
|
|
126
|
+
for (const [, s] of io.sockets) {
|
|
127
|
+
const id = getAgentId(s);
|
|
128
|
+
if (id) {
|
|
129
|
+
agents.push(id);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
callback?.({ success: true, agents });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
socket.on('disconnect', () => {
|
|
136
|
+
// Cleanup subscriptions
|
|
137
|
+
for (const channel of getSubscriptions(socket)) {
|
|
138
|
+
channels.get(channel)?.delete(socket);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`[events] Agent disconnected: ${agentId}`);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|