@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.
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ export { setupMemoryNamespace } from './memory';
2
+ export { setupAudioNamespace } from './audio';
3
+ export { setupEventsNamespace } from './events';