This commit is contained in:
Dobromir Popov 2024-10-02 17:34:32 +03:00
commit 2440c0286e
9 changed files with 515 additions and 276 deletions

View File

@ -6,7 +6,9 @@
# SERVER_PORT_HTTP=8080 # SERVER_PORT_HTTP=8080
ENV_NAME=demo 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 # LLN_MODEL=qwen2
# LNN_API_URL=https://ollama.d-popov.com/api/generate # 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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 GROQ_API_KEY=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
OPENAI_API_KEY=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 OPENAI_API_KEY=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
WS_URL=wss://tts.d-popov.com # PUBLIC_WS_URL=wss://ws.tts.d-popov.com
PUBLIC_HOSTNAME=tts.d-popov.com # PUBLIC_HOSTNAME=tts.d-popov.com
SERVER_PORT_HTTP=8080 # SERVER_PORT_HTTP=28080
SERVER_PORT_WS=8081 # SERVER_PORT_WS=28081

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ agent-mAId/output.wav
agent-mAId/build/* agent-mAId/build/*
agent-mAId/dist/main.exe agent-mAId/dist/main.exe
agent-mAId/output.wav agent-mAId/output.wav
.node-persist/storage/*

View File

@ -90,5 +90,6 @@ RUN . /venv/bin/activate && pip install --upgrade pip && pip install -r agent-py
# CMD ["npm", "start"] # CMD ["npm", "start"]
CMD ["npm", "run", "start:demo"] # CMD ["npm", "run", "start:demo"]
CMD ["npm", "run", "start:demo-chat"]
#CMD ["npm", "run", "start:tele"] #CMD ["npm", "run", "start:tele"]

View File

@ -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: # build:
docker build -t kevin-ai . docker build -t kevin-ai .
# start the project in container: # start the project in container:

View File

@ -1,25 +1,157 @@
from flask import Flask, render_template, request, jsonify 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__) 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('/') @app.route('/')
def index(): def index():
return render_template('index.html') return render_template('index.html')
@app.route('/tokens', methods=['GET']) @app.route('/tokens', methods=['GET'])
def get_tokens(): async def get_tokens():
# Here you would add logic to fetch new tokens or token data balances = await get_wallet_balances(YOUR_WALLET)
return jsonify(['SOL', 'USDC']) # Example token list return jsonify(list(balances.keys()))
@app.route('/swap', methods=['POST']) @app.route('/balances', methods=['GET'])
def swap_tokens(): async def get_balances():
data = request.json balances = await get_wallet_balances(YOUR_WALLET)
token_name = data['token_name'] return jsonify(balances)
amount = data['amount']
# Here you would add logic to perform the token swap @app.route('/follow_move', methods=['POST'])
return jsonify({'status': 'success', 'message': f'Swapped {amount} of {token_name}'}) 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"<b>Move Followed:</b>\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"<b>Move Failed:</b>\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 = (
"<b>Initial Wallet States (Non-zero balances):</b>\n\n"
f"<b>Followed Wallet ({FOLLOWED_WALLET}):</b>\n"
f"{followed_wallet_state}\n\n"
f"<b>Your Wallet ({YOUR_WALLET}):</b>\n"
f"{your_wallet_state}\n\n"
f"<b>Monitored Tokens:</b>\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"<b>Solana Agent Application Started</b>\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__': 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)

14
crypto/sol/readme.md Normal file
View File

@ -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`

View File

@ -1,15 +1,22 @@
version: '3.8'
services: services:
node-app: chat-server:
container_name: node-voice-chat image: node:20-alpine
build: container_name: ml-voice-chat-server
context: . working_dir: /usr/src/app
dockerfile: web/deploy/demo.Dockerfile
ports:
- "8880:8080" # Exposes port 3000 on the host and maps it to port 3000 on the container
volumes: 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: environment:
NODE_ENV: demo # Sets the environment variable NODE_ENV to development NODE_ENV: demo
command: npm run start:demo-chat # Runs npm start when the container starts #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

View File

@ -15,30 +15,37 @@ const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient(); const prisma = new PrismaClient();
// Load environment variables // 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}`) console.log(`loaded env file: ${process.env.NODE_ENV}`)
// Initialize services // Initialize services
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
const groq = new Groq({ apiKey: process.env.GROQ_API_KEY }); const groq = new Groq({ apiKey: process.env.GROQ_API_KEY })
// Express setup // Express setup
const app = express(); const app = express()
app.use(bodyParser.json()); app.use(bodyParser.json())
// Configuration constants // Configuration constants
const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000; const PORT_HTTP = process.env.SERVER_PORT_HTTP || 3000
const PORT_WS = process.env.SERVER_PORT_WS || 8080; const PORT_WS = process.env.SERVER_PORT_WS || 8082
const TTS_API_URL = process.env.TTS_API_URL; const TTS_API_URL = process.env.TTS_API_URL
const LNN_API_URL = process.env.LNN_API_URL; const LNN_API_URL = process.env.LNN_API_URL
const LLN_MODEL = process.env.LLN_MODEL; const LLN_MODEL = process.env.LLN_MODEL
console.log(`TTS API URL: ${TTS_API_URL}`)
let language = "en"; let language = 'en'
let storeRecordings = false; let storeRecordings = false
let queueCounter = 0; let queueCounter = 0
const sessions = new Map(); const sessions = new Map()
const chats = new Map(); // Store chat rooms 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 // WebSocket Server
const wss = new WebSocket.Server({ port: PORT_WS }); const wss = new WebSocket.Server({ port: PORT_WS })
wss.on('connection', ws => { wss.on('connection', ws => {
ws.on('message', async message => handleMessage(ws, message)); ws.on('message', async message => handleMessage(ws, message))
ws.on('close', () => handleClose(ws)); ws.on('close', () => handleClose(ws))
}); })
// Handle WebSocket messages // Handle WebSocket messages
async function handleMessage(ws, message) { async function handleMessage (ws, message) {
let data; let data
try { try {
data = JSON.parse(message); data = JSON.parse(message)
} catch { } catch {
return handleAudioData(ws, message); return handleAudioData(ws, message)
} }
try { try {
switch (data.type) { switch (data.type) {
case 'sessionId': case 'sessionId':
await handleSessionId(ws); await handleSessionId(ws)
break; break
case 'join': case 'join':
await handleJoin(ws, data); await handleJoin(ws, data)
break; break
case 'startChat': case 'startChat':
await handleStartChat(ws, data); await handleStartChat(ws, data)
break; break
case 'enterChat': case 'enterChat':
await handleEnterChat(ws, data); await handleEnterChat(ws, data)
break; break
case 'reconnect': case 'reconnect':
await handleReconnect(ws, data); await handleReconnect(ws, data)
break; break
default: default:
console.log('Unknown message type:', data.type); console.log('Unknown message type:', data.type)
}
} catch (err) {
console.error('Failed to handle message', err);
} }
} catch (err) {
console.error('Failed to handle message', err)
}
} }
function handleClose(ws) { function handleClose (ws) {
sessions.delete(ws.sessionId); sessions.delete(ws.sessionId)
broadcastUserList(); broadcastUserList()
} }
// Handlers for specific message types // Handlers for specific message types
async function handleSessionId(ws) { async function handleSessionId (ws) {
ws.sessionId = generateSessionId(); ws.sessionId = generateSessionId()
sessions.set(ws.sessionId, { language: 'en' }); sessions.set(ws.sessionId, { language: 'en' })
await storage.setItem('sessions', Array.from(sessions.values())); await storage.setItem('sessions', Array.from(sessions.values()))
} }
// Modified handleJoin function // Modified handleJoin function
@ -177,26 +199,28 @@ async function handleEnterChat(ws, { chatId }) {
} }
} }
async function handleReconnect(ws, { sessionId }) { async function handleReconnect (ws, { sessionId }) {
const userSession = sessions.get(sessionId); const userSession = sessions.get(sessionId)
if (userSession) { if (userSession) {
sessions.set(ws.sessionId, userSession); sessions.set(ws.sessionId, userSession)
ws.sessionId = sessionId; ws.sessionId = sessionId
const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(ws.sessionId)); const userChats = Array.from(chats.values()).filter(chat =>
ws.send(JSON.stringify({ type: 'chats', chats: userChats })); chat.participants.includes(ws.sessionId)
} else { )
console.log('Session not found:', sessionId); ws.send(JSON.stringify({ type: 'chats', chats: userChats }))
} } else {
broadcastUserList(); console.log('Session not found:', sessionId)
}
broadcastUserList()
} }
// Utility functions // Utility functions
function generateSessionId() { function generateSessionId () {
return Math.random().toString(36).substring(2); return Math.random().toString(36).substring(2)
} }
function generateChatId() { function generateChatId () {
return Math.random().toString(36).substring(2); return Math.random().toString(36).substring(2)
} }
async function broadcastUserList() { async function broadcastUserList() {
@ -229,220 +253,272 @@ function notifyParticipants(participants) {
}); });
} }
async function handleAudioData(ws, data) { async function handleAudioData (ws, data) {
const sessionData = sessions.get(ws.sessionId); const sessionData = sessions.get(ws.sessionId)
let { language, task } = sessionData; let { language, task } = sessionData
const formData = { const formData = {
task: task || 'transcribe', task: task || 'transcribe',
language, language,
vad_filter: 'true', vad_filter: 'true',
output: 'json', output: 'json',
audio_file: { audio_file: {
value: data, value: data,
options: { filename: 'audio.ogg', contentType: 'audio/ogg' } options: { filename: 'audio.ogg', contentType: 'audio/ogg' }
}
};
if (!language || language === 'auto') {
await detectLanguage(ws, formData);
} else {
await transcribeAudio(ws, formData, sessionData);
} }
}
if (!language || language === 'auto') {
await detectLanguage(ws, formData)
} else {
await transcribeAudio(ws, formData, sessionData)
}
} }
async function detectLanguage(ws, formData) { async function detectLanguage (ws, formData) {
try { try {
const result = await requestPromise({ const result = await requestPromise({
method: 'POST', method: 'POST',
url: TTS_API_URL.replace('/asr', '/detect-language'), url: TTS_API_URL.replace('/asr', '/detect-language'),
formData formData
}); })
const { language_code } = JSON.parse(result); const { language_code } = JSON.parse(result)
if (language_code) { if (language_code) {
const sessionData = sessions.get(ws.sessionId); const sessionData = sessions.get(ws.sessionId)
sessionData.language = language_code; sessionData.language = language_code
ws.send(JSON.stringify({ type: 'languageDetected', languageDetected: language_code })); ws.send(
await transcribeAudio(ws, formData, sessionData); JSON.stringify({
} type: 'languageDetected',
} catch (err) { languageDetected: language_code
console.error('Language detection failed:', err); })
)
await transcribeAudio(ws, formData, sessionData)
} }
} catch (err) {
console.error('Language detection failed:', err)
}
} }
async function transcribeAudio(ws, formData, sessionData) { async function transcribeAudio (ws, formData, sessionData) {
const start = new Date().getTime(); const start = new Date().getTime()
queueCounter++; queueCounter++
try { try {
if(sessionData.language) { if (sessionData.language) {
formData.language = 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);
} }
formData.vad_filter = 'true'
const body = await requestPromise({
method: 'POST',
url: TTS_API_URL,
formData
})
queueCounter--
if (storeRecordings) { const duration = new Date().getTime() - start
const timestamp = Date.now(); ws.send(
fs.mkdir('rec', { recursive: true }, err => { JSON.stringify({
if (err) console.error(err); type: 'text',
else { queueCounter,
fs.writeFile(`rec/audio${timestamp}.ogg`, formData.audio_file.value, err => { duration,
if (err) console.error(err); language: sessionData.language,
else console.log(`Audio data saved to rec/audio${timestamp}.ogg`); 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')
})
)
} }
}); } else {
} const participantSocket = Array.from(wss.clients).find(
} client => client.sessionId === sessionId
)
async function handleChatTranscription(ws, body, sessionData) { if (
if (sessionData.currentChat) { participantSocket &&
const chat = chats.get(sessionData.currentChat); participantSocket.readyState === WebSocket.OPEN
if (chat) { ) {
let msg = { sender: sessionData.username, text: body, translations: [] }; participantSocket.send(
chat.messages.push(msg); JSON.stringify({
type: 'text',
for (let sessionId of chat.participants) { text: `${sessionData.username}: ${body}`
if (sessionId !== ws.sessionId) { })
const targetLang = sessions.get(sessionId)?.language || 'en'; )
if (targetLang !== sessionData.language) { participantSocket.send(
const translation = await translateText(body, sessionData.language, targetLang); JSON.stringify({
msg.translations.push({ language: targetLang, text: translation }); type: 'audio',
audio: formData.toString('base64')
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') }));
}
}
}
} }
}
} }
}
} }
}
} }
async function translateText(originalText, originalLanguage, targetLanguage) { async function translateText (originalText, originalLanguage, targetLanguage) {
const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}`; const prompt = `Translate this text from ${originalLanguage} to ${targetLanguage}: ${originalText}`
const response = await groq.chat.completions.create({ const response = await groq.chat.completions.create({
messages: [ messages: [
{ {
role: "system", role: 'system',
content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.`, content: `You are translating voice transcriptions from '${originalLanguage}' to '${targetLanguage}'. Reply with just the translation.`
}, },
{ {
role: "user", role: 'user',
content: originalText, content: originalText
}, }
], ],
model: "llama3-8b-8192", model: 'llama3-8b-8192'
}); })
return response.choices[0]?.message?.content || ""; return response.choices[0]?.message?.content || ''
} }
async function generateSpeech(text) { async function generateSpeech (text) {
const mp3 = await openai.audio.speech.create({ const mp3 = await openai.audio.speech.create({
model: "tts-1", model: 'tts-1',
voice: "alloy", voice: 'alloy',
input: text, input: text
}); })
return Buffer.from(await mp3.arrayBuffer()); return Buffer.from(await mp3.arrayBuffer())
} }
// HTTP Server // HTTP Server
app.get('/', (req, res) => { 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) => { 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) => { app.post('/log', (req, res) => {
console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`); console.log(`[LOG ${new Date().toISOString()}] ${req.body.message}`)
res.status(200).send('OK'); res.status(200).send('OK')
}); })
app.get('/wsurl', (req, res) => { app.get('/wsurl', (req, res) => {
if(process.env.PUBLIC_HOSTNAME){ // if(process.env.PUBLIC_HOSTNAME){
process.env.WS_URL = `wss://${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 ); console.log('Request for WS URL resolved with:', process.env.WS_URL)
res.status(200).send(process.env.WS_URL); res.status(200).send(process.env.WS_URL)
}); })
app.get('/settings', async (req, res) => { app.get('/settings', async (req, res) => {
if (req.query.language) { if (req.query.language) {
language = req.query.language; language = req.query.language
await storage.setItem('language', language); await storage.setItem('language', language)
} }
if (req.query.storeRecordings) { if (req.query.storeRecordings) {
storeRecordings = req.query.storeRecordings; storeRecordings = req.query.storeRecordings
await storage.setItem('storeRecordings', storeRecordings); await storage.setItem('storeRecordings', storeRecordings)
} }
res.status(200).send({ language, storeRecordings }); res.status(200).send({ language, storeRecordings })
}); })
app.post('/settings', async (req, res) => { app.post('/settings', async (req, res) => {
const { sessionId, language, storeRecordings, task } = req.body; const { sessionId, language, storeRecordings, task } = req.body
const sessionData = sessions.get(sessionId); const sessionData = sessions.get(sessionId)
if (language) sessionData.language = language; if (language) sessionData.language = language
if (storeRecordings !== undefined) sessionData.storeRecordings = storeRecordings; if (storeRecordings !== undefined)
if (task) sessionData.task = task; sessionData.storeRecordings = storeRecordings
res.status(200).send('OK'); if (task) sessionData.task = task
}); res.status(200).send('OK')
})
app.post('/upload', (req, res) => { app.post('/upload', (req, res) => {
const timestamp = Date.now(); const timestamp = Date.now()
console.log('Received audio data:', timestamp); console.log('Received audio data:', timestamp)
fs.mkdir('rec', { recursive: true }, err => { fs.mkdir('rec', { recursive: true }, err => {
if (err) return res.status(500).send('ERROR'); if (err) return res.status(500).send('ERROR')
const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`); const file = fs.createWriteStream(`rec/audio_slice_${timestamp}.ogg`)
req.pipe(file); req.pipe(file)
file.on('finish', () => res.status(200).send('OK')); file.on('finish', () => res.status(200).send('OK'))
}); })
}); })
app.get('/chats', (req, res) => { app.get('/chats', (req, res) => {
const { username } = req.query; const { username } = req.query
const userChats = Array.from(chats.values()).filter(chat => chat.participants.includes(username)); const userChats = Array.from(chats.values()).filter(chat =>
res.status(200).send({ chats: userChats }); chat.participants.includes(username)
}); )
res.status(200).send({ chats: userChats })
})
app.listen(PORT_HTTP, () => { 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 // Helper to wrap request in a promise
function requestPromise(options) { function requestPromise (options) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request(options, (error, response, body) => { request(options, (error, response, body) => {
if (error) return reject(error); if (error) return reject(error)
resolve(body); resolve(body)
}); })
}); })
} }

View File

@ -4,6 +4,8 @@ FROM node:20
# Create and change to the app directory # Create and change to the app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
ENV NODE_ENV=demo
# Copy the package.json and package-lock.json files # Copy the package.json and package-lock.json files
COPY package*.json ./ COPY package*.json ./
@ -13,8 +15,8 @@ RUN npm install
# Copy the rest of the application code # Copy the rest of the application code
COPY . . COPY . .
# Expose port 3000
EXPOSE 8880 EXPOSE 8880
EXPOSE 8881
# Start the application # Start the application
CMD ["npm", "run", "start:demo-chat"] CMD ["npm", "run", "start:demo-chat"]