diff --git a/.env.demo b/.env.demo
index fea3dc7..554a865 100644
--- a/.env.demo
+++ b/.env.demo
@@ -6,7 +6,9 @@
# SERVER_PORT_HTTP=8080
ENV_NAME=demo
-TTS_API_URL=https://api.tts.d-popov.com/asr
+# TTS_API_URL=https://api.tts.d-popov.com/asr
+# TTS_API_URL=https://api.tts.d-popov.com/asr
+# TTS_BACKEND=http://192.168.0.11:9009/asr
# LLN_MODEL=qwen2
# LNN_API_URL=https://ollama.d-popov.com/api/generate
@@ -17,7 +19,7 @@ LNN_API_URL=https://ollama.d-popov.com/api/generate
GROQ_API_KEY=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
OPENAI_API_KEY=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
-WS_URL=wss://tts.d-popov.com
-PUBLIC_HOSTNAME=tts.d-popov.com
-SERVER_PORT_HTTP=8080
-SERVER_PORT_WS=8081
\ No newline at end of file
+# PUBLIC_WS_URL=wss://ws.tts.d-popov.com
+# PUBLIC_HOSTNAME=tts.d-popov.com
+# SERVER_PORT_HTTP=28080
+# SERVER_PORT_WS=28081
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 188cd7f..707c6db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,3 +16,4 @@ agent-mAId/output.wav
agent-mAId/build/*
agent-mAId/dist/main.exe
agent-mAId/output.wav
+.node-persist/storage/*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 3cb3638..5cb5705 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -90,5 +90,6 @@ RUN . /venv/bin/activate && pip install --upgrade pip && pip install -r agent-py
# CMD ["npm", "start"]
-CMD ["npm", "run", "start:demo"]
+# CMD ["npm", "run", "start:demo"]
+CMD ["npm", "run", "start:demo-chat"]
#CMD ["npm", "run", "start:tele"]
\ No newline at end of file
diff --git a/_doc/_notes/readme.md b/_doc/_notes/readme.md
index 25e9ed8..1179cb1 100644
--- a/_doc/_notes/readme.md
+++ b/_doc/_notes/readme.md
@@ -1,3 +1,7 @@
+# .10: /mnt/apps/DEV/REPOS/git.d-popov.com/ai-kevin/
+# run
+export NODE_ENV=demo && npm run start:demo-chat
+
# build:
docker build -t kevin-ai .
# start the project in container:
diff --git a/crypto/sol/app.py b/crypto/sol/app.py
index f334c9d..5c895de 100644
--- a/crypto/sol/app.py
+++ b/crypto/sol/app.py
@@ -1,25 +1,157 @@
from flask import Flask, render_template, request, jsonify
-from solana.rpc.api import Client
+from solana.rpc.async_api import AsyncClient
+from solana.rpc.commitment import Confirmed
+from solders.pubkey import Pubkey
+from dexscreener import DexscreenerClient
+import asyncio
+from telegram import Bot
+from telegram.constants import ParseMode
+import datetime
app = Flask(__name__)
-solana_client = Client("https://api.mainnet-beta.solana.com")
+
+# Use the production Solana RPC endpoint
+solana_client = AsyncClient("https://api.mainnet-beta.solana.com")
+dexscreener_client = DexscreenerClient()
+
+# Configuration
+DEVELOPER_CHAT_ID = "777826553"
+FOLLOWED_WALLET = "9U7D916zuQ8qcL9kQZqkcroWhHGho5vD8VNekvztrutN"
+YOUR_WALLET = "65nzyZXTLC81MthTo52a2gRJjqryTizWVqpK2fDKLye5"
+TELEGRAM_BOT_TOKEN = "6805059978:AAHNJKuOeazMSJHc3-BXRCsFfEVyFHeFnjw"
+
+# Initialize Telegram Bot
+bot = Bot(token=TELEGRAM_BOT_TOKEN)
+
+# Token addresses (to be populated dynamically)
+TOKEN_ADDRESSES = {}
+
+async def get_token_balance(wallet_address, token_address):
+ try:
+ balance = await solana_client.get_token_account_balance(
+ Pubkey.from_string(token_address),
+ commitment=Confirmed
+ )
+ return float(balance['result']['value']['uiAmount'])
+ except Exception as e:
+ print(f"Error getting balance for {token_address}: {str(e)}")
+ return 0
+
+async def send_telegram_message(message):
+ try:
+ await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=message, parse_mode=ParseMode.HTML)
+ except Exception as e:
+ print(f"Error sending Telegram message: {str(e)}")
@app.route('/')
def index():
return render_template('index.html')
@app.route('/tokens', methods=['GET'])
-def get_tokens():
- # Here you would add logic to fetch new tokens or token data
- return jsonify(['SOL', 'USDC']) # Example token list
+async def get_tokens():
+ balances = await get_wallet_balances(YOUR_WALLET)
+ return jsonify(list(balances.keys()))
-@app.route('/swap', methods=['POST'])
-def swap_tokens():
- data = request.json
- token_name = data['token_name']
- amount = data['amount']
- # Here you would add logic to perform the token swap
- return jsonify({'status': 'success', 'message': f'Swapped {amount} of {token_name}'})
+@app.route('/balances', methods=['GET'])
+async def get_balances():
+ balances = await get_wallet_balances(YOUR_WALLET)
+ return jsonify(balances)
+
+@app.route('/follow_move', methods=['POST'])
+async def follow_move():
+ move = request.json
+ followed_balances = await get_wallet_balances(FOLLOWED_WALLET)
+ your_balances = await get_wallet_balances(YOUR_WALLET)
+
+ if move['token'] not in followed_balances or move['token'] not in your_balances:
+ return jsonify({'status': 'error', 'message': 'Invalid token'})
+
+ followed_balance = followed_balances[move['token']]
+ your_balance = your_balances[move['token']]
+
+ proportion = your_balance / followed_balance if followed_balance > 0 else 0
+ amount_to_swap = move['amount'] * proportion
+
+ if your_balance >= amount_to_swap:
+ # Implement actual swap logic here
+ pair = dexscreener_client.get_token_pair("solana", move['token'])
+ price = float(pair['priceUsd'])
+ received_amount = amount_to_swap * price
+
+ await send_telegram_message(
+ f"Move Followed:\n"
+ f"Swapped {amount_to_swap:.6f} {move['token']} "
+ f"for {received_amount:.6f} {move['to_token']}"
+ )
+
+ return jsonify({
+ 'status': 'success',
+ 'message': f"Swapped {amount_to_swap:.6f} {move['token']} for {received_amount:.6f} {move['to_token']}"
+ })
+ else:
+ await send_telegram_message(
+ f"Move Failed:\n"
+ f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}"
+ )
+
+ return jsonify({
+ 'status': 'error',
+ 'message': f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}"
+ })
+
+async def get_wallet_balances(wallet_address):
+ balances = {}
+ for token, address in TOKEN_ADDRESSES.items():
+ balances[token] = await get_token_balance(wallet_address, address)
+ return balances
+
+async def get_non_zero_token_balances(wallet_address):
+ non_zero_balances = {}
+ for token, address in TOKEN_ADDRESSES.items():
+ balance = await get_token_balance(wallet_address, address)
+ if balance > 0:
+ non_zero_balances[token] = address
+ return non_zero_balances
+
+async def list_initial_wallet_states():
+ global TOKEN_ADDRESSES
+
+ followed_wallet_balances = await get_wallet_balances(FOLLOWED_WALLET)
+ your_wallet_balances = await get_wallet_balances(YOUR_WALLET)
+
+ followed_non_zero = await get_non_zero_token_balances(FOLLOWED_WALLET)
+ your_non_zero = await get_non_zero_token_balances(YOUR_WALLET)
+
+ TOKEN_ADDRESSES = {**followed_non_zero, **your_non_zero}
+
+ followed_wallet_state = "\n".join([f"{token}: {amount:.6f}" for token, amount in followed_wallet_balances.items() if amount > 0])
+ your_wallet_state = "\n".join([f"{token}: {amount:.6f}" for token, amount in your_wallet_balances.items() if amount > 0])
+
+ message = (
+ "Initial Wallet States (Non-zero balances):\n\n"
+ f"Followed Wallet ({FOLLOWED_WALLET}):\n"
+ f"{followed_wallet_state}\n\n"
+ f"Your Wallet ({YOUR_WALLET}):\n"
+ f"{your_wallet_state}\n\n"
+ f"Monitored Tokens:\n"
+ f"{', '.join(TOKEN_ADDRESSES.keys())}"
+ )
+
+ await send_telegram_message(message)
+
+async def send_startup_message():
+ current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ message = (
+ f"Solana Agent Application Started\n\n"
+ f"Startup Time: {current_time}\n"
+ f"Followed Wallet: {FOLLOWED_WALLET}\n"
+ f"Your Wallet: {YOUR_WALLET}\n\n"
+ "The application is now running and ready to monitor wallet activities."
+ )
+ await send_telegram_message(message)
if __name__ == '__main__':
- app.run(debug=True)
+ asyncio.run(send_startup_message())
+ asyncio.run(list_initial_wallet_states())
+ print(f"Monitored Tokens: {', '.join(TOKEN_ADDRESSES.keys())}")
+ app.run(host='0.0.0.0', port=3009)
\ No newline at end of file
diff --git a/crypto/sol/readme.md b/crypto/sol/readme.md
new file mode 100644
index 0000000..130732b
--- /dev/null
+++ b/crypto/sol/readme.md
@@ -0,0 +1,14 @@
+`Conda activate trade`
+To run this Python Solana agent:
+
+Install the required libraries:
+
+`pip install flask solana dexscreener python-telegram-bot`
+
+Replace REPLACE_WITH_WALLET_ADDRESS with the wallet address you want to follow.
+Replace REPLACE_WITH_YOUR_WALLET_ADDRESS with your own wallet address.
+Save the code in a file, e.g., solana_agent.py.
+
+Run the Flask application:
+
+`python app.py`
\ No newline at end of file
diff --git a/docker-compose.demo.yml b/docker-compose.demo.yml
index 0cc78d3..03ab51c 100644
--- a/docker-compose.demo.yml
+++ b/docker-compose.demo.yml
@@ -1,15 +1,22 @@
-version: '3.8'
-
services:
- node-app:
- container_name: node-voice-chat
- build:
- context: .
- dockerfile: web/deploy/demo.Dockerfile
- ports:
- - "8880:8080" # Exposes port 3000 on the host and maps it to port 3000 on the container
+ chat-server:
+ image: node:20-alpine
+ container_name: ml-voice-chat-server
+ working_dir: /usr/src/app
volumes:
- - .:/usr/src/app # Mounts the current directory to /usr/src/app in the container
+ - /mnt/apps/DEV/REPOS/git.d-popov.com/ai-kevin:/usr/src/app
+ command: >
+ sh -c "npm install && node web/chat-server.js"
environment:
- NODE_ENV: demo # Sets the environment variable NODE_ENV to development
- command: npm run start:demo-chat # Runs npm start when the container starts
+ NODE_ENV: demo
+ #TTS_BACKEND_URL: https://api.tts.d-popov.com/asr
+ TTS_API_URL: http://192.168.0.11:9009/asr
+ WS_URL: wss://ws.tts.d-popov.com
+ SERVER_PORT_HTTP: 8080
+ SERVER_PORT_WS: 8082
+ ports:
+ - 28080:8080
+ - 28081:8082
+ dns:
+ - 8.8.8.8
+ - 8.8.4.4
\ No newline at end of file
diff --git a/web/chat-server.js b/web/chat-server.js
index e102945..2082c16 100644
--- a/web/chat-server.js
+++ b/web/chat-server.js
@@ -15,30 +15,37 @@ const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
// Load environment variables
-dotenv.config({ path: `.env${process.env.NODE_ENV === 'development' ? '.development' :'.'+ process.env.NODE_ENV }` });
+dotenv.config({
+ path: `.env${
+ process.env.NODE_ENV === 'development'
+ ? '.development'
+ : '.' + process.env.NODE_ENV
+ }`
+})
console.log(`loaded env file: ${process.env.NODE_ENV}`)
// Initialize services
-const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
-const groq = new Groq({ apiKey: process.env.GROQ_API_KEY });
+const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
+const groq = new Groq({ apiKey: process.env.GROQ_API_KEY })
// Express setup
-const app = express();
-app.use(bodyParser.json());
+const app = express()
+app.use(bodyParser.json())
// Configuration constants
-const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000;
-const PORT_WS = process.env.SERVER_PORT_WS || 8080;
-const TTS_API_URL = process.env.TTS_API_URL;
-const LNN_API_URL = process.env.LNN_API_URL;
-const LLN_MODEL = process.env.LLN_MODEL;
+const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000
+const PORT_WS = process.env.SERVER_PORT_WS || 8082
+const TTS_API_URL = process.env.TTS_API_URL
+const LNN_API_URL = process.env.LNN_API_URL
+const LLN_MODEL = process.env.LLN_MODEL
+console.log(`TTS API URL: ${TTS_API_URL}`)
-let language = "en";
-let storeRecordings = false;
-let queueCounter = 0;
+let language = 'en'
+let storeRecordings = false
+let queueCounter = 0
-const sessions = new Map();
-const chats = new Map(); // Store chat rooms
+const sessions = new Map()
+const chats = new Map() // Store chat rooms
@@ -70,58 +77,73 @@ app.post('/register', async (req, res) => {
});
+// // Initialize storage and load initial values
+// async function initStorage () {
+// await storage.init()
+// language = (await storage.getItem('language')) || language
+// storeRecordings =
+// (await storage.getItem('storeRecordings')) || storeRecordings
+
+// const storedChats = (await storage.getItem('chats')) || []
+// storedChats.forEach(chat => chats.set(chat.id, chat))
+
+// const storedSessions = (await storage.getItem('sessions')) || []
+// storedSessions.forEach(session => sessions.set(session.sessionId, session))
+// }
+
+// initStorage()
// WebSocket Server
-const wss = new WebSocket.Server({ port: PORT_WS });
+const wss = new WebSocket.Server({ port: PORT_WS })
wss.on('connection', ws => {
- ws.on('message', async message => handleMessage(ws, message));
- ws.on('close', () => handleClose(ws));
-});
+ ws.on('message', async message => handleMessage(ws, message))
+ ws.on('close', () => handleClose(ws))
+})
// Handle WebSocket messages
-async function handleMessage(ws, message) {
- let data;
- try {
- data = JSON.parse(message);
- } catch {
- return handleAudioData(ws, message);
- }
+async function handleMessage (ws, message) {
+ let data
+ try {
+ data = JSON.parse(message)
+ } catch {
+ return handleAudioData(ws, message)
+ }
- try {
- switch (data.type) {
- case 'sessionId':
- await handleSessionId(ws);
- break;
- case 'join':
- await handleJoin(ws, data);
- break;
- case 'startChat':
- await handleStartChat(ws, data);
- break;
- case 'enterChat':
- await handleEnterChat(ws, data);
- break;
- case 'reconnect':
- await handleReconnect(ws, data);
- break;
- default:
- console.log('Unknown message type:', data.type);
- }
- } catch (err) {
- console.error('Failed to handle message', err);
+ try {
+ switch (data.type) {
+ case 'sessionId':
+ await handleSessionId(ws)
+ break
+ case 'join':
+ await handleJoin(ws, data)
+ break
+ case 'startChat':
+ await handleStartChat(ws, data)
+ break
+ case 'enterChat':
+ await handleEnterChat(ws, data)
+ break
+ case 'reconnect':
+ await handleReconnect(ws, data)
+ break
+ default:
+ console.log('Unknown message type:', data.type)
}
+ } catch (err) {
+ console.error('Failed to handle message', err)
+ }
}
-function handleClose(ws) {
- sessions.delete(ws.sessionId);
- broadcastUserList();
+function handleClose (ws) {
+ sessions.delete(ws.sessionId)
+ broadcastUserList()
}
// Handlers for specific message types
-async function handleSessionId(ws) {
- ws.sessionId = generateSessionId();
- sessions.set(ws.sessionId, { language: 'en' });
- await storage.setItem('sessions', Array.from(sessions.values()));
+async function handleSessionId (ws) {
+ ws.sessionId = generateSessionId()
+ sessions.set(ws.sessionId, { language: 'en' })
+ await storage.setItem('sessions', Array.from(sessions.values()))
}
// Modified handleJoin function
@@ -177,26 +199,28 @@ async function handleEnterChat(ws, { chatId }) {
}
}
-async function handleReconnect(ws, { sessionId }) {
- const userSession = sessions.get(sessionId);
- if (userSession) {
- sessions.set(ws.sessionId, userSession);
- ws.sessionId = sessionId;
- const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(ws.sessionId));
- ws.send(JSON.stringify({ type: 'chats', chats: userChats }));
- } else {
- console.log('Session not found:', sessionId);
- }
- broadcastUserList();
+async function handleReconnect (ws, { sessionId }) {
+ const userSession = sessions.get(sessionId)
+ if (userSession) {
+ sessions.set(ws.sessionId, userSession)
+ ws.sessionId = sessionId
+ const userChats = Array.from(chats.values()).filter(chat =>
+ chat.participants.includes(ws.sessionId)
+ )
+ ws.send(JSON.stringify({ type: 'chats', chats: userChats }))
+ } else {
+ console.log('Session not found:', sessionId)
+ }
+ broadcastUserList()
}
// Utility functions
-function generateSessionId() {
- return Math.random().toString(36).substring(2);
+function generateSessionId () {
+ return Math.random().toString(36).substring(2)
}
-function generateChatId() {
- return Math.random().toString(36).substring(2);
+function generateChatId () {
+ return Math.random().toString(36).substring(2)
}
async function broadcastUserList() {
@@ -229,220 +253,272 @@ function notifyParticipants(participants) {
});
}
-async function handleAudioData(ws, data) {
- const sessionData = sessions.get(ws.sessionId);
- let { language, task } = sessionData;
+async function handleAudioData (ws, data) {
+ const sessionData = sessions.get(ws.sessionId)
+ let { language, task } = sessionData
- const formData = {
- task: task || 'transcribe',
- language,
- vad_filter: 'true',
- output: 'json',
- audio_file: {
- value: data,
- options: { filename: 'audio.ogg', contentType: 'audio/ogg' }
- }
- };
-
- if (!language || language === 'auto') {
- await detectLanguage(ws, formData);
- } else {
- await transcribeAudio(ws, formData, sessionData);
+ const formData = {
+ task: task || 'transcribe',
+ language,
+ vad_filter: 'true',
+ output: 'json',
+ audio_file: {
+ value: data,
+ options: { filename: 'audio.ogg', contentType: 'audio/ogg' }
}
+ }
+
+ if (!language || language === 'auto') {
+ await detectLanguage(ws, formData)
+ } else {
+ await transcribeAudio(ws, formData, sessionData)
+ }
}
-async function detectLanguage(ws, formData) {
- try {
- const result = await requestPromise({
- method: 'POST',
- url: TTS_API_URL.replace('/asr', '/detect-language'),
- formData
- });
- const { language_code } = JSON.parse(result);
- if (language_code) {
- const sessionData = sessions.get(ws.sessionId);
- sessionData.language = language_code;
- ws.send(JSON.stringify({ type: 'languageDetected', languageDetected: language_code }));
- await transcribeAudio(ws, formData, sessionData);
- }
- } catch (err) {
- console.error('Language detection failed:', err);
+async function detectLanguage (ws, formData) {
+ try {
+ const result = await requestPromise({
+ method: 'POST',
+ url: TTS_API_URL.replace('/asr', '/detect-language'),
+ formData
+ })
+ const { language_code } = JSON.parse(result)
+ if (language_code) {
+ const sessionData = sessions.get(ws.sessionId)
+ sessionData.language = language_code
+ ws.send(
+ JSON.stringify({
+ type: 'languageDetected',
+ languageDetected: language_code
+ })
+ )
+ await transcribeAudio(ws, formData, sessionData)
}
+ } catch (err) {
+ console.error('Language detection failed:', err)
+ }
}
-async function transcribeAudio(ws, formData, sessionData) {
- const start = new Date().getTime();
- queueCounter++;
+async function transcribeAudio (ws, formData, sessionData) {
+ const start = new Date().getTime()
+ queueCounter++
- try {
- if(sessionData.language) {
- formData.language = sessionData.language;
- }
- formData.vad_filter = 'true';
- const body = await requestPromise({ method: 'POST', url: TTS_API_URL, formData });
- queueCounter--;
-
- const duration = new Date().getTime() - start;
- ws.send(JSON.stringify({
- type: 'text',
- queueCounter,
- duration,
- language: sessionData.language,
- text: body
- }));
-
- await handleChatTranscription(ws, body, sessionData);
-
- } catch (err) {
- console.error('Transcription failed:', err);
+ try {
+ if (sessionData.language) {
+ formData.language = sessionData.language
}
+ formData.vad_filter = 'true'
+ const body = await requestPromise({
+ method: 'POST',
+ url: TTS_API_URL,
+ formData
+ })
+ queueCounter--
- if (storeRecordings) {
- const timestamp = Date.now();
- fs.mkdir('rec', { recursive: true }, err => {
- if (err) console.error(err);
- else {
- fs.writeFile(`rec/audio${timestamp}.ogg`, formData.audio_file.value, err => {
- if (err) console.error(err);
- else console.log(`Audio data saved to rec/audio${timestamp}.ogg`);
- });
+ const duration = new Date().getTime() - start
+ ws.send(
+ JSON.stringify({
+ type: 'text',
+ queueCounter,
+ duration,
+ language: sessionData.language,
+ text: body
+ })
+ )
+
+ await handleChatTranscription(ws, body, sessionData)
+ } catch (err) {
+ console.error('Transcription failed:', err)
+ }
+
+ if (storeRecordings) {
+ const timestamp = Date.now()
+ fs.mkdir('rec', { recursive: true }, err => {
+ if (err) console.error(err)
+ else {
+ fs.writeFile(
+ `rec/audio${timestamp}.ogg`,
+ formData.audio_file.value,
+ err => {
+ if (err) console.error(err)
+ else console.log(`Audio data saved to rec/audio${timestamp}.ogg`)
+ }
+ )
+ }
+ })
+ }
+}
+
+async function handleChatTranscription (ws, body, sessionData) {
+ if (sessionData.currentChat) {
+ const chat = chats.get(sessionData.currentChat)
+ if (chat) {
+ let msg = { sender: sessionData.username, text: body, translations: [] }
+ chat.messages.push(msg)
+
+ for (let sessionId of chat.participants) {
+ if (sessionId !== ws.sessionId) {
+ const targetLang = sessions.get(sessionId)?.language || 'en'
+ if (targetLang !== sessionData.language) {
+ const translation = await translateText(
+ body,
+ sessionData.language,
+ targetLang
+ )
+ msg.translations.push({ language: targetLang, text: translation })
+
+ const participantSocket = Array.from(wss.clients).find(
+ client => client.sessionId === sessionId
+ )
+ if (
+ participantSocket &&
+ participantSocket.readyState === WebSocket.OPEN
+ ) {
+ participantSocket.send(
+ JSON.stringify({
+ type: 'text',
+ text: `${sessionData.username}: ${translation}`
+ })
+ )
+ const audioBuffer = await generateSpeech(translation)
+ participantSocket.send(
+ JSON.stringify({
+ type: 'audio',
+ audio: audioBuffer.toString('base64')
+ })
+ )
}
- });
- }
-}
-
-async function handleChatTranscription(ws, body, sessionData) {
- if (sessionData.currentChat) {
- const chat = chats.get(sessionData.currentChat);
- if (chat) {
- let msg = { sender: sessionData.username, text: body, translations: [] };
- chat.messages.push(msg);
-
- for (let sessionId of chat.participants) {
- if (sessionId !== ws.sessionId) {
- const targetLang = sessions.get(sessionId)?.language || 'en';
- if (targetLang !== sessionData.language) {
- const translation = await translateText(body, sessionData.language, targetLang);
- msg.translations.push({ language: targetLang, text: translation });
-
- const participantSocket = Array.from(wss.clients).find(client => client.sessionId === sessionId);
- if (participantSocket && participantSocket.readyState === WebSocket.OPEN) {
- participantSocket.send(JSON.stringify({ type: 'text', text: `${sessionData.username}: ${translation}` }));
- const audioBuffer = await generateSpeech(translation);
- participantSocket.send(JSON.stringify({ type: 'audio', audio: audioBuffer.toString('base64') }));
- }
- } else {
- const participantSocket = Array.from(wss.clients).find(client => client.sessionId === sessionId);
- if (participantSocket && participantSocket.readyState === WebSocket.OPEN) {
- participantSocket.send(JSON.stringify({ type: 'text', text: `${sessionData.username}: ${body}` }));
- participantSocket.send(JSON.stringify({ type: 'audio', audio: formData.toString('base64') }));
- }
- }
- }
+ } else {
+ const participantSocket = Array.from(wss.clients).find(
+ client => client.sessionId === sessionId
+ )
+ if (
+ participantSocket &&
+ participantSocket.readyState === WebSocket.OPEN
+ ) {
+ participantSocket.send(
+ JSON.stringify({
+ type: 'text',
+ text: `${sessionData.username}: ${body}`
+ })
+ )
+ participantSocket.send(
+ JSON.stringify({
+ type: 'audio',
+ audio: formData.toString('base64')
+ })
+ )
}
+ }
}
+ }
}
+ }
}
-async function translateText(originalText, originalLanguage, targetLanguage) {
- const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}`;
+async function translateText (originalText, originalLanguage, targetLanguage) {
+ const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}`
- const response = await groq.chat.completions.create({
- messages: [
- {
- role: "system",
- content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.`,
- },
- {
- role: "user",
- content: originalText,
- },
- ],
- model: "llama3-8b-8192",
- });
+ const response = await groq.chat.completions.create({
+ messages: [
+ {
+ role: 'system',
+ content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.`
+ },
+ {
+ role: 'user',
+ content: originalText
+ }
+ ],
+ model: 'llama3-8b-8192'
+ })
- return response.choices[0]?.message?.content || "";
+ return response.choices[0]?.message?.content || ''
}
-async function generateSpeech(text) {
- const mp3 = await openai.audio.speech.create({
- model: "tts-1",
- voice: "alloy",
- input: text,
- });
- return Buffer.from(await mp3.arrayBuffer());
+async function generateSpeech (text) {
+ const mp3 = await openai.audio.speech.create({
+ model: 'tts-1',
+ voice: 'alloy',
+ input: text
+ })
+ return Buffer.from(await mp3.arrayBuffer())
}
// HTTP Server
app.get('/', (req, res) => {
- res.sendFile(path.join(__dirname, 'chat-client.html'));
-});
+ res.sendFile(path.join(__dirname, 'chat-client.html'))
+})
app.get('/audio.js', (req, res) => {
- res.sendFile(path.join(__dirname, 'audio.js'));
-});
+ res.sendFile(path.join(__dirname, 'audio.js'))
+})
app.post('/log', (req, res) => {
- console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`);
- res.status(200).send('OK');
-});
+ console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`)
+ res.status(200).send('OK')
+})
app.get('/wsurl', (req, res) => {
- if(process.env.PUBLIC_HOSTNAME){
- process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}`
- }
- console.log('Request for WS URL resolved with:', process.env.WS_URL );
- res.status(200).send(process.env.WS_URL);
-});
+ // if(process.env.PUBLIC_HOSTNAME){
+ // process.env.WS_URL = `wss://${process.env.PUBLIC_HOSTNAME}`
+ // }
+ console.log('Request for WS URL resolved with:', process.env.WS_URL)
+ res.status(200).send(process.env.WS_URL)
+})
app.get('/settings', async (req, res) => {
- if (req.query.language) {
- language = req.query.language;
- await storage.setItem('language', language);
- }
- if (req.query.storeRecordings) {
- storeRecordings = req.query.storeRecordings;
- await storage.setItem('storeRecordings', storeRecordings);
- }
- res.status(200).send({ language, storeRecordings });
-});
+ if (req.query.language) {
+ language = req.query.language
+ await storage.setItem('language', language)
+ }
+ if (req.query.storeRecordings) {
+ storeRecordings = req.query.storeRecordings
+ await storage.setItem('storeRecordings', storeRecordings)
+ }
+ res.status(200).send({ language, storeRecordings })
+})
app.post('/settings', async (req, res) => {
- const { sessionId, language, storeRecordings, task } = req.body;
- const sessionData = sessions.get(sessionId);
- if (language) sessionData.language = language;
- if (storeRecordings !== undefined) sessionData.storeRecordings = storeRecordings;
- if (task) sessionData.task = task;
- res.status(200).send('OK');
-});
+ const { sessionId, language, storeRecordings, task } = req.body
+ const sessionData = sessions.get(sessionId)
+ if (language) sessionData.language = language
+ if (storeRecordings !== undefined)
+ sessionData.storeRecordings = storeRecordings
+ if (task) sessionData.task = task
+ res.status(200).send('OK')
+})
app.post('/upload', (req, res) => {
- const timestamp = Date.now();
- console.log('Received audio data:', timestamp);
- fs.mkdir('rec', { recursive: true }, err => {
- if (err) return res.status(500).send('ERROR');
- const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`);
- req.pipe(file);
- file.on('finish', () => res.status(200).send('OK'));
- });
-});
+ const timestamp = Date.now()
+ console.log('Received audio data:', timestamp)
+ fs.mkdir('rec', { recursive: true }, err => {
+ if (err) return res.status(500).send('ERROR')
+ const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`)
+ req.pipe(file)
+ file.on('finish', () => res.status(200).send('OK'))
+ })
+})
app.get('/chats', (req, res) => {
- const { username } = req.query;
- const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(username));
- res.status(200).send({ chats: userChats });
-});
+ const { username } = req.query
+ const userChats = Array.from(chats.values()).filter(chat =>
+ chat.participants.includes(username)
+ )
+ res.status(200).send({ chats: userChats })
+})
app.listen(PORT_HTTP, () => {
- console.log(`Server listening on port ${PORT_HTTP}`);
-});
+ console.log(`Server listening on port ${PORT_HTTP}`)
+ console.log(process.env.TTS_BACKEND_URL)
+})
// Helper to wrap request in a promise
-function requestPromise(options) {
- return new Promise((resolve, reject) => {
- request(options, (error, response, body) => {
- if (error) return reject(error);
- resolve(body);
- });
- });
+function requestPromise (options) {
+ return new Promise((resolve, reject) => {
+ request(options, (error, response, body) => {
+ if (error) return reject(error)
+ resolve(body)
+ })
+ })
}
diff --git a/web/deploy/demo.Dockerfile b/web/deploy/demo.Dockerfile
index 484a3ad..48a281c 100644
--- a/web/deploy/demo.Dockerfile
+++ b/web/deploy/demo.Dockerfile
@@ -4,6 +4,8 @@ FROM node:20
# Create and change to the app directory
WORKDIR /usr/src/app
+ENV NODE_ENV=demo
+
# Copy the package.json and package-lock.json files
COPY package*.json ./
@@ -13,8 +15,8 @@ RUN npm install
# Copy the rest of the application code
COPY . .
-# Expose port 3000
EXPOSE 8880
+EXPOSE 8881
# Start the application
CMD ["npm", "run", "start:demo-chat"]