benjolo commited on
Commit
133a8f5
β€’
1 Parent(s): e6ecf03

Update backend/main.py

Browse files
Files changed (1) hide show
  1. backend/main.py +329 -349
backend/main.py CHANGED
@@ -1,368 +1,348 @@
1
- # from operator import itemgetter
2
- # import os
3
- # from datetime import datetime
4
- # import uvicorn
5
- # from typing import Any, Optional, Tuple, Dict, TypedDict
6
- # from urllib import parse
7
- # from uuid import uuid4
8
- # import logging
9
- # from fastapi.logger import logger as fastapi_logger
10
- # import sys
11
- # # sys.path.append('/Users/benolojo/DCU/CA4/ca400_FinalYearProject/2024-ca400-olojob2-majdap2/src/backend/')
12
-
13
- # from fastapi import FastAPI
14
- # from fastapi.middleware.cors import CORSMiddleware
15
- # from fastapi import APIRouter, Body, Request, status
16
- # from pymongo import MongoClient
17
- # from dotenv import dotenv_values
18
- # from routes import router as api_router
19
- # from contextlib import asynccontextmanager
20
- # import requests
21
-
22
- # from typing import List
23
- # from datetime import date
24
- # from mongodb.operations.calls import *
25
- # from mongodb.models.calls import UserCall, UpdateCall
26
- # # from mongodb.endpoints.calls import *
27
-
28
- # # from transformers import AutoProcessor, SeamlessM4Tv2Model
29
-
30
- # # from seamless_communication.inference import Translator
31
- # from Client import Client
32
- # #----------------------------------
33
- # # base seamless imports
34
- # # ---------------------------------
35
- # import numpy as np
36
- # import torch
37
- # # ---------------------------------
38
- # import socketio
39
-
40
- # ###############################################
41
- # # Configure logger
42
-
43
- # gunicorn_error_logger = logging.getLogger("gunicorn.error")
44
- # gunicorn_logger = logging.getLogger("gunicorn")
45
- # uvicorn_access_logger = logging.getLogger("uvicorn.access")
46
-
47
- # gunicorn_error_logger.propagate = True
48
- # gunicorn_logger.propagate = True
49
- # uvicorn_access_logger.propagate = True
50
-
51
- # uvicorn_access_logger.handlers = gunicorn_error_logger.handlers
52
- # fastapi_logger.handlers = gunicorn_error_logger.handlers
53
-
54
- # ###############################################
55
-
56
- # # sio is the main socket.io entrypoint
57
- # sio = socketio.AsyncServer(
58
- # async_mode="asgi",
59
- # cors_allowed_origins="*",
60
- # logger=gunicorn_logger,
61
- # engineio_logger=gunicorn_logger,
62
- # )
63
- # # sio.logger.setLevel(logging.DEBUG)
64
- # socketio_app = socketio.ASGIApp(sio)
65
- # # app.mount("/", socketio_app)
66
-
67
- # config = dotenv_values(".env")
68
-
69
- # # Read connection string from environment vars
70
- # # uri = os.environ['MONGODB_URI']
71
-
72
- # # Read connection string from .env file
73
- # uri = config['MONGODB_URI']
74
-
75
- # # Set transformers cache
76
- # # os.environ['HF_HOME'] = './.cache/'
77
- # # os.environ['SENTENCE_TRANSFORMERS_HOME'] = './.cache'
78
-
79
- # # MongoDB Connection Lifespan Events
80
- # @asynccontextmanager
81
- # async def lifespan(app: FastAPI):
82
- # # startup logic
83
- # app.mongodb_client = MongoClient(uri)
84
- # app.database = app.mongodb_client['IT-Cluster1'] #connect to interpretalk primary db
85
- # try:
86
- # app.mongodb_client.admin.command('ping')
87
- # print("MongoDB Connection Established...")
88
- # except Exception as e:
89
- # print(e)
90
 
91
- # yield
 
 
 
 
92
 
93
- # # shutdown logic
94
- # print("Closing MongoDB Connection...")
95
- # app.mongodb_client.close()
96
 
97
- # app = FastAPI(lifespan=lifespan, logger=gunicorn_logger)
 
 
 
 
 
 
 
98
 
99
- # # New CORS funcitonality
100
- # app.add_middleware(
101
- # CORSMiddleware,
102
- # allow_origins=["*"], # configured node app port
103
- # allow_credentials=True,
104
- # allow_methods=["*"],
105
- # allow_headers=["*"],
106
- # )
107
 
108
- # app.include_router(api_router) # include routers for user, calls and transcripts operations
109
 
110
- # DEBUG = True
111
 
112
- # ESCAPE_HATCH_SERVER_LOCK_RELEASE_NAME = "remove_server_lock"
 
113
 
114
- # TARGET_SAMPLING_RATE = 16000
115
- # MAX_BYTES_BUFFER = 480_000
 
116
 
117
- # print("")
118
- # print("")
119
- # print("=" * 20 + " ⭐️ Starting Server... ⭐️ " + "=" * 20)
120
 
121
- # ###############################################
122
- # # Configure socketio server
123
- # ###############################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
- # # TODO PM - change this to the actual path
126
- # # seamless remnant code
127
- # # CLIENT_BUILD_PATH = "../streaming-react-app/dist/"
128
- # # static_files = {
129
- # # "/": CLIENT_BUILD_PATH,
130
- # # "/assets/seamless-db6a2555.svg": {
131
- # # "filename": CLIENT_BUILD_PATH + "assets/seamless-db6a2555.svg",
132
- # # "content_type": "image/svg+xml",
133
- # # },
134
- # # }
135
- # device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
136
- # # processor = AutoProcessor.from_pretrained("facebook/seamless-m4t-v2-large", force_download=True)
137
- # #cache_dir="/.cache"
138
-
139
- # # PM - hardcoding temporarily as my GPU doesnt have enough vram
140
- # # model = SeamlessM4Tv2Model.from_pretrained("facebook/seamless-m4t-v2-large").to("cpu")
141
- # # model = SeamlessM4Tv2Model.from_pretrained("facebook/seamless-m4t-v2-large", force_download=True).to(device)
142
-
143
-
144
- # bytes_data = bytearray()
145
- # model_name = "seamlessM4T_v2_large"
146
- # # vocoder_name = "vocoder_v2" if model_name == "seamlessM4T_v2_large" else "vocoder_36langs"
147
-
148
- # clients = {}
149
- # rooms = {}
150
-
151
- # import torch
152
- # from transformers import pipeline
153
- # translator = pipeline("automatic-speech-recognition",
154
- # "facebook/seamless-m4t-v2-large",
155
- # torch_dtype=torch.float32,
156
- # device="cpu")
157
-
158
- # converter = pipeline("translation",
159
- # "facebook/seamless-m4t-v2-large",
160
- # torch_dtype=torch.float32,
161
- # device="cpu")
162
-
163
-
164
- # def get_collection_users():
165
- # return app.database["user_records"]
166
-
167
- # def get_collection_calls():
168
- # # return app.database["call_records"]
169
- # return app.database["call_test"]
170
-
171
-
172
- # @app.get("/test/", response_description="Welcome User")
173
- # def test():
174
-
175
- # return {"message": "Welcome to InterpreTalk!"}
176
-
177
-
178
- # @app.post("/test_post/", response_description="List more test call records")
179
- # def test_post():
180
- # request_data = {
181
- # "call_id": "TESTID000001"
182
- # }
183
-
184
- # result = create_calls(get_collection_calls(), request_data)
185
-
186
- # # return {"message": "Welcome to InterpreTalk!"}
187
- # return result
188
-
189
- # @app.put("/test_put/", response_description="List test call records")
190
- # def test_put():
191
-
192
- # # result = list_calls(get_collection_calls(), 100)
193
- # # result = send_captions("TEST", "TEST", "TEST", "oUjUxTYTQFVVjEarIcZ0")
194
- # result = send_captions("TEST", "TEST", "TEST", "TESTID000001")
195
-
196
- # print(result)
197
- # return result
198
-
199
-
200
- # async def send_translated_text(client_id, original_text, translated_text, room_id):
201
- # print('SEND_TRANSLATED_TEXT IS WOKRING IN FASTAPI BACKEND...')
202
- # print(rooms)
203
- # print(clients)
204
-
205
- # data = {
206
- # "author": str(client_id),
207
- # "original_text": str(original_text),
208
- # "translated_text": str(translated_text),
209
- # "timestamp": str(datetime.now())
210
- # }
211
- # gunicorn_logger.info("SENDING TRANSLATED TEXT TO CLIENT")
212
- # await sio.emit("translated_text", data, room=room_id)
213
- # gunicorn_logger.info("SUCCESSFULLY SEND AUDIO TO FRONTEND")
214
-
215
- # @sio.on("connect")
216
- # async def connect(sid, environ):
217
- # print(f"πŸ“₯ [event: connected] sid={sid}")
218
- # query_params = dict(parse.parse_qsl(environ["QUERY_STRING"]))
219
- # client_id = query_params.get("client_id")
220
- # gunicorn_logger.info(f"πŸ“₯ [event: connected] sid={sid}, client_id={client_id}")
221
- # # sid = socketid, client_id = client specific ID ,always the same for same user
222
- # clients[sid] = Client(sid, client_id)
223
- # gunicorn_logger.warning(f"Client connected: {sid}")
224
- # gunicorn_logger.warning(clients)
225
-
226
- # @sio.on("disconnect")
227
- # async def disconnect(sid): # BO -Β also pass call id as parameter for updating MongoDB
228
- # gunicorn_logger.debug(f"πŸ“€ [event: disconnected] sid={sid}")
229
- # clients.pop(sid, None)
230
- # # BO -> Update Call record with call duration, key terms
231
-
232
- # @sio.on("target_language")
233
- # async def target_language(sid, target_lang):
234
- # gunicorn_logger.info(f"πŸ“₯ [event: target_language] sid={sid}, target_lang={target_lang}")
235
- # clients[sid].target_language = target_lang
236
-
237
- # @sio.on("call_user")
238
- # async def call_user(sid, call_id):
239
- # clients[sid].call_id = call_id
240
- # gunicorn_logger.info(f"CALL {sid}: entering room {call_id}")
241
- # rooms[call_id] = rooms.get(call_id, [])
242
- # if sid not in rooms[call_id] and len(rooms[call_id]) < 2:
243
- # rooms[call_id].append(sid)
244
- # sio.enter_room(sid, call_id)
245
- # else:
246
- # gunicorn_logger.info(f"CALL {sid}: room {call_id} is full")
247
- # # await sio.emit("room_full", room=call_id, to=sid)
248
-
249
- # # # BO - Get call id from dictionary created during socketio connection
250
- # # client_id = clients[sid].client_id
251
 
252
- # # gunicorn_logger.warning(f"NOW TRYING TO CREATE DB RECORD FOR Caller with ID: {client_id} for call: {call_id}")
253
- # # # # BO -> Create Call Record with Caller and call_id field (None for callee, duration, terms..)
254
- # # request_data = {
255
- # # "call_id": str(call_id),
256
- # # "caller_id": str(client_id),
257
- # # "creation_date": str(datetime.now())
258
- # # }
259
-
260
- # # response = create_calls(get_collection_calls(), request_data)
261
- # # print(response) # BO - print created db call record
262
-
263
- # @sio.on("audio_config")
264
- # async def audio_config(sid, sample_rate):
265
- # clients[sid].original_sr = sample_rate
266
-
267
-
268
- # @sio.on("answer_call")
269
- # async def answer_call(sid, call_id):
270
-
271
- # clients[sid].call_id = call_id
272
- # gunicorn_logger.info(f"ANSWER {sid}: entering room {call_id}")
273
- # rooms[call_id] = rooms.get(call_id, [])
274
- # if sid not in rooms[call_id] and len(rooms[call_id]) < 2:
275
- # rooms[call_id].append(sid)
276
- # sio.enter_room(sid, call_id)
277
- # else:
278
- # gunicorn_logger.info(f"ANSWER {sid}: room {call_id} is full")
279
- # # await sio.emit("room_full", room=call_id, to=sid)
280
-
281
-
282
- # # # BO - Get call id from dictionary created during socketio connection
283
- # # client_id = clients[sid].client_id
284
 
285
- # # # BO -> Update Call Record with Callee field based on call_id
286
- # # gunicorn_logger.warning(f"NOW UPDATING MongoDB RECORD FOR Caller with ID: {client_id} for call: {call_id}")
287
- # # # # BO -> Create Call Record with callee_id field (None for callee, duration, terms..)
288
- # # request_data = {
289
- # # "callee_id": client_id
290
- # # }
291
-
292
- # # response = update_calls(get_collection_calls(), call_id, request_data)
293
- # # print(response) # BO - print created db call record
294
 
295
 
296
- # @sio.on("incoming_audio")
297
- # async def incoming_audio(sid, data, call_id):
298
- # try:
299
- # clients[sid].add_bytes(data)
300
-
301
- # if clients[sid].get_length() >= MAX_BYTES_BUFFER:
302
- # gunicorn_logger.info('Buffer full, now outputting...')
303
- # output_path = clients[sid].output_path
304
- # vad_result, resampled_audio = clients[sid].resample_and_write_to_file()
305
- # # source lang is speakers tgt language πŸ˜ƒ
306
- # src_lang = clients[sid].target_language
307
- # if vad_result:
308
- # gunicorn_logger.info('Speech detected, now processing audio.....')
309
- # tgt_sid = next(id for id in rooms[call_id] if id != sid)
310
- # tgt_lang = clients[tgt_sid].target_language
311
- # # following example from https://github.com/facebookresearch/seamless_communication/blob/main/docs/m4t/README.md#transformers-usage
312
- # # output_tokens = processor(audios=resampled_audio, src_lang=src_lang, return_tensors="pt")
313
- # # model_output = model.generate(**output_tokens, tgt_lang=src_lang, generate_speech=False)[0].tolist()[0]
314
- # # asr_text = processor.decode(model_output, skip_special_tokens=True)
315
- # asr_text = translator(resampled_audio, generate_kwargs={"tgt_lang": src_lang})['text']
316
- # print(f"ASR TEXT = {asr_text}")
317
- # # ASR TEXT => ORIGINAL TEXT
318
-
319
- # # t2t_tokens = processor(text=asr_text, src_lang=src_lang, tgt_lang=tgt_lang, return_tensors="pt")
320
- # # print(f"FIRST TYPE = {type(output_tokens)}, SECOND TYPE = {type(t2t_tokens)}")
321
- # # translated_data = model.generate(**t2t_tokens, tgt_lang=tgt_lang, generate_speech=False)[0].tolist()[0]
322
- # # translated_text = processor.decode(translated_data, skip_special_tokens=True)
323
- # translated_text = converter(asr_text, src_lang=src_lang, tgt_lang=tgt_lang)
324
- # print(f"TRANSLATED TEXT = {translated_text}")
325
-
326
- # # BO -> send translated_text to mongodb as caption record update based on call_id
327
- # # send_captions(clients[sid].client_id, asr_text, translated_text, call_id)
328
 
329
- # # TRANSLATED TEXT
330
- # # PM - text_output is a list with 1 string
331
- # await send_translated_text(clients[sid].client_id, asr_text, translated_text, call_id)
332
 
333
- # # # BO -> send translated_text to mongodb as caption record update based on call_id
334
- # # send_captions(clients[sid].client_id, asr_text, translated_text, call_id)
335
 
336
- # except Exception as e:
337
- # gunicorn_logger.error(f"Error in incoming_audio: {e.with_traceback()}")
338
 
339
- # def send_captions(client_id, original_text, translated_text, call_id):
340
- # # BO -> Update Call Record with Callee field based on call_id
341
- # print(f"Now updating Caption field in call record for Caller with ID: {client_id} for call: {call_id}")
342
-
343
- # data = {
344
- # "author": str(client_id),
345
- # "original_text": str(original_text),
346
- # "translated_text": str(translated_text),
347
- # "timestamp": str(datetime.now())
348
- # }
349
-
350
- # response = update_captions(get_collection_calls(), call_id, data)
351
- # return response
352
-
353
- # app.mount("/", socketio_app)
354
-
355
- # if __name__ == '__main__':
356
- # uvicorn.run("main:app", host='0.0.0.0', port=7860, log_level="debug")
357
-
358
- # # Running in Docker Container
359
- # if __name__ != "__main__":
360
- # fastapi_logger.setLevel(gunicorn_logger.level)
361
- # else:
362
- # fastapi_logger.setLevel(logging.DEBUG)
363
-
364
- from huggingface_hub import scan_cache_dir
365
-
366
- hf_cache_info = scan_cache_dir()
367
-
368
- print(hf_cache_info)
 
1
+ from operator import itemgetter
2
+ import os
3
+ from datetime import datetime
4
+ import uvicorn
5
+ from typing import Any, Optional, Tuple, Dict, TypedDict
6
+ from urllib import parse
7
+ from uuid import uuid4
8
+ import logging
9
+ from fastapi.logger import logger as fastapi_logger
10
+ import sys
11
+ # sys.path.append('/Users/benolojo/DCU/CA4/ca400_FinalYearProject/2024-ca400-olojob2-majdap2/src/backend/')
12
+
13
+ from fastapi import FastAPI
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ from fastapi import APIRouter, Body, Request, status
16
+ from pymongo import MongoClient
17
+ from dotenv import dotenv_values
18
+ from routes import router as api_router
19
+ from contextlib import asynccontextmanager
20
+ import requests
21
+
22
+ from typing import List
23
+ from datetime import date
24
+ from mongodb.operations.calls import *
25
+ from mongodb.models.calls import UserCall, UpdateCall
26
+ # from mongodb.endpoints.calls import *
27
+
28
+ from transformers import AutoProcessor, SeamlessM4Tv2Model
29
+
30
+ # from seamless_communication.inference import Translator
31
+ from Client import Client
32
+ #----------------------------------
33
+ # base seamless imports
34
+ # ---------------------------------
35
+ import numpy as np
36
+ import torch
37
+ # ---------------------------------
38
+ import socketio
39
+
40
+ ###############################################
41
+ # Configure logger
42
+
43
+ gunicorn_error_logger = logging.getLogger("gunicorn.error")
44
+ gunicorn_logger = logging.getLogger("gunicorn")
45
+ uvicorn_access_logger = logging.getLogger("uvicorn.access")
46
+
47
+ gunicorn_error_logger.propagate = True
48
+ gunicorn_logger.propagate = True
49
+ uvicorn_access_logger.propagate = True
50
+
51
+ uvicorn_access_logger.handlers = gunicorn_error_logger.handlers
52
+ fastapi_logger.handlers = gunicorn_error_logger.handlers
53
+
54
+ ###############################################
55
+
56
+ # sio is the main socket.io entrypoint
57
+ sio = socketio.AsyncServer(
58
+ async_mode="asgi",
59
+ cors_allowed_origins="*",
60
+ logger=gunicorn_logger,
61
+ engineio_logger=gunicorn_logger,
62
+ )
63
+ # sio.logger.setLevel(logging.DEBUG)
64
+ socketio_app = socketio.ASGIApp(sio)
65
+ # app.mount("/", socketio_app)
66
+
67
+ config = dotenv_values(".env")
68
+
69
+ # Read connection string from environment vars
70
+ # uri = os.environ['MONGODB_URI']
71
+
72
+ # Read connection string from .env file
73
+ uri = config['MONGODB_URI']
74
+
75
+ # Set transformers cache
76
+ # os.environ['HF_HOME'] = './.cache/'
77
+ # os.environ['SENTENCE_TRANSFORMERS_HOME'] = './.cache'
78
+
79
+ # MongoDB Connection Lifespan Events
80
+ @asynccontextmanager
81
+ async def lifespan(app: FastAPI):
82
+ # startup logic
83
+ app.mongodb_client = MongoClient(uri)
84
+ app.database = app.mongodb_client['IT-Cluster1'] #connect to interpretalk primary db
85
+ try:
86
+ app.mongodb_client.admin.command('ping')
87
+ print("MongoDB Connection Established...")
88
+ except Exception as e:
89
+ print(e)
90
 
91
+ yield
92
+
93
+ # shutdown logic
94
+ print("Closing MongoDB Connection...")
95
+ app.mongodb_client.close()
96
 
97
+ app = FastAPI(lifespan=lifespan, logger=gunicorn_logger)
 
 
98
 
99
+ # New CORS funcitonality
100
+ app.add_middleware(
101
+ CORSMiddleware,
102
+ allow_origins=["*"], # configured node app port
103
+ allow_credentials=True,
104
+ allow_methods=["*"],
105
+ allow_headers=["*"],
106
+ )
107
 
108
+ app.include_router(api_router) # include routers for user, calls and transcripts operations
 
 
 
 
 
 
 
109
 
110
+ DEBUG = True
111
 
112
+ ESCAPE_HATCH_SERVER_LOCK_RELEASE_NAME = "remove_server_lock"
113
 
114
+ TARGET_SAMPLING_RATE = 16000
115
+ MAX_BYTES_BUFFER = 480_000
116
 
117
+ print("")
118
+ print("")
119
+ print("=" * 20 + " ⭐️ Starting Server... ⭐️ " + "=" * 20)
120
 
121
+ ###############################################
122
+ # Configure socketio server
123
+ ###############################################
124
 
125
+ # TODO PM - change this to the actual path
126
+ # seamless remnant code
127
+ CLIENT_BUILD_PATH = "../streaming-react-app/dist/"
128
+ static_files = {
129
+ "/": CLIENT_BUILD_PATH,
130
+ "/assets/seamless-db6a2555.svg": {
131
+ "filename": CLIENT_BUILD_PATH + "assets/seamless-db6a2555.svg",
132
+ "content_type": "image/svg+xml",
133
+ },
134
+ }
135
+ device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
136
+ processor = AutoProcessor.from_pretrained("facebook/seamless-m4t-v2-large", force_download=True)
137
+ #cache_dir="/.cache"
138
+
139
+ # PM - hardcoding temporarily as my GPU doesnt have enough vram
140
+ # model = SeamlessM4Tv2Model.from_pretrained("facebook/seamless-m4t-v2-large").to("cpu")
141
+ model = SeamlessM4Tv2Model.from_pretrained("facebook/seamless-m4t-v2-large", force_download=True).to(device)
142
+
143
+
144
+ bytes_data = bytearray()
145
+ model_name = "seamlessM4T_v2_large"
146
+ vocoder_name = "vocoder_v2" if model_name == "seamlessM4T_v2_large" else "vocoder_36langs"
147
+
148
+ clients = {}
149
+ rooms = {}
150
+
151
+
152
+ def get_collection_users():
153
+ return app.database["user_records"]
154
+
155
+ def get_collection_calls():
156
+ # return app.database["call_records"]
157
+ return app.database["call_test"]
158
+
159
+
160
+ @app.get("/test/", response_description="Welcome User")
161
+ def test():
162
+
163
+ return {"message": "Welcome to InterpreTalk!"}
164
+
165
+
166
+ @app.post("/test_post/", response_description="List more test call records")
167
+ def test_post():
168
+ request_data = {
169
+ "call_id": "TESTID000001"
170
+ }
171
+
172
+ result = create_calls(get_collection_calls(), request_data)
173
+
174
+ # return {"message": "Welcome to InterpreTalk!"}
175
+ return result
176
+
177
+ @app.put("/test_put/", response_description="List test call records")
178
+ def test_put():
179
+
180
+ # result = list_calls(get_collection_calls(), 100)
181
+ # result = send_captions("TEST", "TEST", "TEST", "oUjUxTYTQFVVjEarIcZ0")
182
+ result = send_captions("TEST", "TEST", "TEST", "TESTID000001")
183
+
184
+ print(result)
185
+ return result
186
+
187
+
188
+ async def send_translated_text(client_id, original_text, translated_text, room_id):
189
+ print('SEND_TRANSLATED_TEXT IS WOKRING IN FASTAPI BACKEND...')
190
+ print(rooms)
191
+ print(clients)
192
+
193
+ data = {
194
+ "author": str(client_id),
195
+ "original_text": str(original_text),
196
+ "translated_text": str(translated_text),
197
+ "timestamp": str(datetime.now())
198
+ }
199
+ gunicorn_logger.info("SENDING TRANSLATED TEXT TO CLIENT")
200
+ await sio.emit("translated_text", data, room=room_id)
201
+ gunicorn_logger.info("SUCCESSFULLY SEND AUDIO TO FRONTEND")
202
+
203
+ @sio.on("connect")
204
+ async def connect(sid, environ):
205
+ print(f"πŸ“₯ [event: connected] sid={sid}")
206
+ query_params = dict(parse.parse_qsl(environ["QUERY_STRING"]))
207
+ client_id = query_params.get("client_id")
208
+ gunicorn_logger.info(f"πŸ“₯ [event: connected] sid={sid}, client_id={client_id}")
209
+ # sid = socketid, client_id = client specific ID ,always the same for same user
210
+ clients[sid] = Client(sid, client_id)
211
+ gunicorn_logger.warning(f"Client connected: {sid}")
212
+ gunicorn_logger.warning(clients)
213
+
214
+ @sio.on("disconnect")
215
+ async def disconnect(sid): # BO - also pass call id as parameter for updating MongoDB
216
+ gunicorn_logger.debug(f"πŸ“€ [event: disconnected] sid={sid}")
217
+ clients.pop(sid, None)
218
+ # BO -> Update Call record with call duration, key terms
219
+
220
+ @sio.on("target_language")
221
+ async def target_language(sid, target_lang):
222
+ gunicorn_logger.info(f"πŸ“₯ [event: target_language] sid={sid}, target_lang={target_lang}")
223
+ clients[sid].target_language = target_lang
224
+
225
+ @sio.on("call_user")
226
+ async def call_user(sid, call_id):
227
+ clients[sid].call_id = call_id
228
+ gunicorn_logger.info(f"CALL {sid}: entering room {call_id}")
229
+ rooms[call_id] = rooms.get(call_id, [])
230
+ if sid not in rooms[call_id] and len(rooms[call_id]) < 2:
231
+ rooms[call_id].append(sid)
232
+ sio.enter_room(sid, call_id)
233
+ else:
234
+ gunicorn_logger.info(f"CALL {sid}: room {call_id} is full")
235
+ # await sio.emit("room_full", room=call_id, to=sid)
236
 
237
+ # # BO - Get call id from dictionary created during socketio connection
238
+ # client_id = clients[sid].client_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
+ # gunicorn_logger.warning(f"NOW TRYING TO CREATE DB RECORD FOR Caller with ID: {client_id} for call: {call_id}")
241
+ # # # BO -> Create Call Record with Caller and call_id field (None for callee, duration, terms..)
242
+ # request_data = {
243
+ # "call_id": str(call_id),
244
+ # "caller_id": str(client_id),
245
+ # "creation_date": str(datetime.now())
246
+ # }
247
+
248
+ # response = create_calls(get_collection_calls(), request_data)
249
+ # print(response) # BO - print created db call record
250
+
251
+ @sio.on("audio_config")
252
+ async def audio_config(sid, sample_rate):
253
+ clients[sid].original_sr = sample_rate
254
+
255
+
256
+ @sio.on("answer_call")
257
+ async def answer_call(sid, call_id):
258
+
259
+ clients[sid].call_id = call_id
260
+ gunicorn_logger.info(f"ANSWER {sid}: entering room {call_id}")
261
+ rooms[call_id] = rooms.get(call_id, [])
262
+ if sid not in rooms[call_id] and len(rooms[call_id]) < 2:
263
+ rooms[call_id].append(sid)
264
+ sio.enter_room(sid, call_id)
265
+ else:
266
+ gunicorn_logger.info(f"ANSWER {sid}: room {call_id} is full")
267
+ # await sio.emit("room_full", room=call_id, to=sid)
268
+
269
+
270
+ # # BO - Get call id from dictionary created during socketio connection
271
+ # client_id = clients[sid].client_id
272
 
273
+ # # BO -> Update Call Record with Callee field based on call_id
274
+ # gunicorn_logger.warning(f"NOW UPDATING MongoDB RECORD FOR Caller with ID: {client_id} for call: {call_id}")
275
+ # # # BO -> Create Call Record with callee_id field (None for callee, duration, terms..)
276
+ # request_data = {
277
+ # "callee_id": client_id
278
+ # }
279
+
280
+ # response = update_calls(get_collection_calls(), call_id, request_data)
281
+ # print(response) # BO - print created db call record
282
 
283
 
284
+ @sio.on("incoming_audio")
285
+ async def incoming_audio(sid, data, call_id):
286
+ try:
287
+ clients[sid].add_bytes(data)
288
+
289
+ if clients[sid].get_length() >= MAX_BYTES_BUFFER:
290
+ gunicorn_logger.info('Buffer full, now outputting...')
291
+ output_path = clients[sid].output_path
292
+ vad_result, resampled_audio = clients[sid].resample_and_write_to_file()
293
+ # source lang is speakers tgt language πŸ˜ƒ
294
+ src_lang = clients[sid].target_language
295
+ if vad_result:
296
+ gunicorn_logger.info('Speech detected, now processing audio.....')
297
+ tgt_sid = next(id for id in rooms[call_id] if id != sid)
298
+ tgt_lang = clients[tgt_sid].target_language
299
+ # following example from https://github.com/facebookresearch/seamless_communication/blob/main/docs/m4t/README.md#transformers-usage
300
+ output_tokens = processor(audios=resampled_audio, src_lang=src_lang, return_tensors="pt")
301
+ model_output = model.generate(**output_tokens, tgt_lang=src_lang, generate_speech=False)[0].tolist()[0]
302
+ asr_text = processor.decode(model_output, skip_special_tokens=True)
303
+ print(f"ASR TEXT = {asr_text}")
304
+ # ASR TEXT => ORIGINAL TEXT
305
+
306
+ t2t_tokens = processor(text=asr_text, src_lang=src_lang, tgt_lang=tgt_lang, return_tensors="pt")
307
+ print(f"FIRST TYPE = {type(output_tokens)}, SECOND TYPE = {type(t2t_tokens)}")
308
+ translated_data = model.generate(**t2t_tokens, tgt_lang=tgt_lang, generate_speech=False)[0].tolist()[0]
309
+ translated_text = processor.decode(translated_data, skip_special_tokens=True)
310
+ print(f"TRANSLATED TEXT = {translated_text}")
311
+
312
+ # BO -> send translated_text to mongodb as caption record update based on call_id
313
+ # send_captions(clients[sid].client_id, asr_text, translated_text, call_id)
 
 
314
 
315
+ # TRANSLATED TEXT
316
+ # PM - text_output is a list with 1 string
317
+ await send_translated_text(clients[sid].client_id, asr_text, translated_text, call_id)
318
 
319
+ # # BO -> send translated_text to mongodb as caption record update based on call_id
320
+ # send_captions(clients[sid].client_id, asr_text, translated_text, call_id)
321
 
322
+ except Exception as e:
323
+ gunicorn_logger.error(f"Error in incoming_audio: {e.with_traceback()}")
324
 
325
+ def send_captions(client_id, original_text, translated_text, call_id):
326
+ # BO -> Update Call Record with Callee field based on call_id
327
+ print(f"Now updating Caption field in call record for Caller with ID: {client_id} for call: {call_id}")
328
+
329
+ data = {
330
+ "author": str(client_id),
331
+ "original_text": str(original_text),
332
+ "translated_text": str(translated_text),
333
+ "timestamp": str(datetime.now())
334
+ }
335
+
336
+ response = update_captions(get_collection_calls(), call_id, data)
337
+ return response
338
+
339
+ app.mount("/", socketio_app)
340
+
341
+ if __name__ == '__main__':
342
+ uvicorn.run("main:app", host='127.0.0.1', port=8080, log_level="info")
343
+
344
+ # Running in Docker Container
345
+ if __name__ != "__main__":
346
+ fastapi_logger.setLevel(gunicorn_logger.level)
347
+ else:
348
+ fastapi_logger.setLevel(logging.DEBUG)