dlflannery commited on
Commit
f83538e
·
verified ·
1 Parent(s): 5cc17f2

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +654 -0
app.py ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import openai
4
+ from openai import OpenAI
5
+ from dotenv import load_dotenv
6
+ from pathlib import Path
7
+ from time import sleep
8
+ import audioread
9
+ import queue
10
+ import threading
11
+ from glob import glob
12
+ # import copy
13
+ import json
14
+ # from io import BytesIO
15
+ # from pydantic import BaseModel
16
+ # import pprint
17
+ # import pandas as pd
18
+ from datetime import datetime, timedelta
19
+ # import pytz
20
+ # import math
21
+ # import numpy as np
22
+ import sqlite3
23
+ import struct
24
+ import re
25
+
26
+ load_dotenv(override=True)
27
+ key = os.getenv('OPENAI_API_KEY')
28
+ users = os.getenv('LOGNAME')
29
+ unames = users.split(',')
30
+ pwds = os.getenv('PASSWORD')
31
+ pwdList = pwds.split(',')
32
+
33
+ site = os.getenv('SITE')
34
+ if site == 'local':
35
+ dp = Path('./data')
36
+ dp.mkdir(exist_ok=True)
37
+ dataDir = './data/'
38
+ else:
39
+ dp = Path('/data')
40
+ dp.mkdir(exist_ok=True)
41
+ dataDir = '/data/'
42
+
43
+
44
+ speak_file = dataDir + "speek.wav"
45
+
46
+ # client = OpenAI(api_key = key)
47
+
48
+ #digits = ['zero: ','one: ','two: ','three: ','four: ','five: ','six: ','seven: ','eight: ','nine: ']
49
+
50
+ abbrevs = {'St. ' : 'Saint ', 'Mr. ': 'mister ', 'Mrs. ':'mussus ', 'Mr. ':'mister ', 'Ms. ':'mizz '}
51
+
52
+ # def load_db(dropDown):
53
+ # db_paths = glob(dataDir + '*.db')
54
+ # db_list = []
55
+ # for path in db_paths:
56
+ # db_list.append(os.path.basename)
57
+ # return gr.Dropdown(choices=db_list, value=db_list[0])
58
+
59
+ def set_db(value):
60
+ return value
61
+
62
+ def remove_times(txt):
63
+ pattern = '\s\[\d+\]\s'
64
+ rv = re.sub(pattern, '', txt)
65
+ return rv
66
+
67
+ def seek_hms(seek_ms):
68
+ seek_ms /= 1000;
69
+ hrs = int(seek_ms / 3600)
70
+ mins = int((seek_ms - hrs * 3600) / 60)
71
+ secs = int(seek_ms - hrs * 3600 - mins * 60)
72
+ return f'{hrs}h{mins}m{secs}s'
73
+
74
+ def do_search(prompt, db_name):
75
+ if not os.path.exists(dataDir + db_name):
76
+ return []
77
+ embeddings = get_db_embeddings(db_name)
78
+ (prompt_embed, status) = get_prompt_embedding(prompt)
79
+ dot_products = []
80
+ for (name, text, time, yt_id, db_embed) in embeddings:
81
+ dp = dot_product(prompt_embed, db_embed)
82
+ dot_products.append((name, text, time, yt_id, dp) )
83
+ sorted_dots = sorted(dot_products, key=lambda x: x[4])[-10:]
84
+ sorted_dots.reverse()
85
+ return sorted_dots
86
+
87
+ def dot_product(v1, v2):
88
+ # v1n = np.array(v1)
89
+ # v2n = np.array(v2)
90
+ # dotp = float(np.dot(v1n, v2n))
91
+ dotp = 0.0
92
+ for i in range(len(v1)):
93
+ dotp += v1[i]*v2[i]
94
+ return dotp
95
+
96
+ def get_db_embeddings(db_name):
97
+ # times = []
98
+ # names = []
99
+ # texts = []
100
+ # yt_ids = []
101
+ embeds = []
102
+ conn = sqlite3.connect(dataDir + db_name)
103
+ cur = conn.cursor()
104
+ result = cur.execute('SELECT * from Embeds')
105
+ unpacker = struct.Struct('<f')
106
+ for row in result.fetchall():
107
+ time = row[1]
108
+ name = row[2]
109
+ text = row[3]
110
+ yt_id = row[4]
111
+ embed = row[5]
112
+ x = []
113
+ row_embed = []
114
+ for i in range(1536):
115
+ j = 4*i
116
+ x=bytes(embed[j:j+4])
117
+ val = unpacker.unpack(x)[0]
118
+ row_embed.append(val)
119
+ embeds.append( (name, text, time, yt_id, row_embed) )
120
+ conn.close()
121
+ return (embeds)
122
+
123
+
124
+ def get_prompt_embedding(txt):
125
+ response = Client().embeddings.create(
126
+ input=txt,
127
+ model="text-embedding-3-small"
128
+ )
129
+ embedding = response.data[0].embedding
130
+ l = len(embedding)
131
+ return (embedding, 'ok')
132
+
133
+ def write_db_file(fpath):
134
+ try:
135
+ with open(fpath, 'rb') as fp:
136
+ data = fp.read()
137
+ fname = os.path.basename(fpath)
138
+ except:
139
+ return 'Unable to load database, could not read selected file'
140
+ try:
141
+ with open(dataDir + fname, 'wb') as fp:
142
+ fp.write(data)
143
+ except:
144
+ return 'Unable to load database, could not write data'
145
+ try:
146
+ os.remove(fpath)
147
+ except:
148
+ return "Database loaded, but error deleting temp file"
149
+ return 'Database loaded'
150
+
151
+
152
+
153
+ def Client():
154
+ return OpenAI(api_key = key)
155
+
156
+
157
+ def md(txt):
158
+ # if 'DOCTYPE' in txt:
159
+ # return str(txt.replace('GPT','<br>GPT'))
160
+ # else:
161
+ return str(txt).replace('```', ' ').replace(' ', '&nbsp;&nbsp;').replace(' ', '&nbsp;&nbsp;').replace(' ', '&nbsp;&nbsp;').replace('\n','<br>')
162
+ # return txt
163
+
164
+
165
+
166
+ def genUsageStats(do_reset=False):
167
+ result = []
168
+ ttotal4o_in = 0
169
+ ttotal4o_out = 0
170
+ ttotal4mini_in = 0
171
+ ttotal4mini_out = 0
172
+ totalAudio = 0
173
+ totalSpeech = 0
174
+ totalImages = 0
175
+ totalHdImages = 0
176
+ if do_reset:
177
+ dudPath = dataDir + '_speech.txt'
178
+ if os.path.exists(dudPath):
179
+ os.remove(dudPath)
180
+ for user in unames:
181
+ tokens4o_in = 0
182
+ tokens4o_out = 0
183
+ tokens4mini_in = 0
184
+ tokens4mini_out = 0
185
+ fp = dataDir + user + '_log.txt'
186
+ if os.path.exists(fp):
187
+ accessOk = False
188
+ for i in range(3):
189
+ try:
190
+ with open(fp) as f:
191
+ dataList = f.readlines()
192
+ if do_reset:
193
+ os.remove(fp)
194
+ else:
195
+ for line in dataList:
196
+ (u, t) = line.split(':')
197
+ (t, m) = t.split('-')
198
+ (tin, tout) = t.split('/')
199
+ incount = int(tin)
200
+ outcount = int(tout)
201
+ if 'mini' in m:
202
+ tokens4mini_in += incount
203
+ tokens4mini_out += outcount
204
+ ttotal4mini_in += incount
205
+ ttotal4mini_out += outcount
206
+ else:
207
+ tokens4o_in += incount
208
+ tokens4o_out += outcount
209
+ ttotal4o_in += incount
210
+ ttotal4o_out += outcount
211
+ accessOk = True
212
+ break
213
+ except:
214
+ sleep(3)
215
+ if not accessOk:
216
+ return f'File access failed reading stats for user: {user}'
217
+ userAudio = 0
218
+ fp = dataDir + user + '_audio.txt'
219
+ if os.path.exists(fp):
220
+ accessOk = False
221
+ for i in range(3):
222
+ try:
223
+ with open(fp) as f:
224
+ dataList = f.readlines()
225
+ if do_reset:
226
+ os.remove(fp)
227
+ else:
228
+ for line in dataList:
229
+ (dud, len) = line.split(':')
230
+ userAudio += int(len)
231
+ totalAudio += int(userAudio)
232
+ accessOk = True
233
+ break
234
+ except:
235
+ sleep(3)
236
+ if not accessOk:
237
+ return f'File access failed reading audio stats for user: {user}'
238
+ userSpeech = 0
239
+ fp = dataDir + user + '_speech.txt'
240
+ if os.path.exists(fp):
241
+ accessOk = False
242
+ for i in range(3):
243
+ try:
244
+ with open(fp) as f:
245
+ dataList = f.readlines()
246
+ if do_reset:
247
+ os.remove(fp)
248
+ else:
249
+ for line in dataList:
250
+ (dud, len) = line.split(':')
251
+ userSpeech += int(len)
252
+ totalSpeech += int(userSpeech)
253
+ accessOk = True
254
+ break
255
+ except:
256
+ sleep(3)
257
+ if not accessOk:
258
+ return f'File access failed reading speech stats for user: {user}'
259
+ user_images = 0
260
+ user_hd_images = 0
261
+ result.append([user, f'{tokens4mini_in}/{tokens4mini_out}', f'{tokens4o_in}/{tokens4o_out}', f'audio:{userAudio}',f'speech:{userSpeech}', f'images:{user_images}/{user_hd_images}'])
262
+ result.append(['totals', f'{ttotal4mini_in}/{ttotal4mini_out}', f'{ttotal4o_in}/{ttotal4o_out}', f'audio:{totalAudio}',f'speech:{totalSpeech}', f'images:{totalImages}/{totalHdImages}'])
263
+ return result
264
+
265
+ def new_conversation(user):
266
+ clean_up(user) # .wav files
267
+ return [None, [], gr.Markdown(value='', label='Dialog', container=True), '']
268
+
269
+ def updatePassword(user, pwd):
270
+ password = pwd.lower().strip()
271
+ if user == unames[0] and password == pwdList[0]:
272
+ return [password, "*********", gr.Button(visible=True, value='Upload Database')]
273
+ else:
274
+ return [password, "*********", gr.Button(visible=False, value='Upload Database')]
275
+
276
+
277
+ def chat(prompt, user_window, pwd_window, past, response, gptModel, clip_text, db_name):
278
+ user_window = user_window.lower().strip()
279
+ isBoss = False
280
+ clip_txt = clip_text
281
+ if not response:
282
+ response = ''
283
+ else:
284
+ loc = response.find('Following are Clips')
285
+ if loc > -1:
286
+ response = response[:loc]
287
+ plot = gr.LinePlot(visible=False)
288
+ # plot = gr.Plot(visible=False)
289
+ if user_window == unames[0] and pwd_window == pwdList[0]:
290
+ isBoss = True
291
+ if prompt.startswith('dot'):
292
+ results = do_search(prompt[4:], selected_db)
293
+ txt = ''
294
+ for (name, text, time, yt_id, dp) in results:
295
+ yt_id = yt_id.replace('"','')
296
+ seek_HMS = seek_hms(time)
297
+ seek_colons = seek_HMS.replace('h',' : ').replace('m',' : ').replace('s','')
298
+ yt_url = f'https://www.youtube.com/watch?v={yt_id}#t={seek_HMS}'
299
+ txt += md(f'\n\n{name}\nAt seek time: {seek_colons}\nYouTube Link: [URL]({yt_url}\n\n{remove_times(text)}\n================')
300
+ return [past, txt, None, gptModel,clip_text]
301
+ if prompt.startswith('embed'):
302
+ (embed, status) = get_prompt_embedding(prompt)
303
+ return [past, str(status), None, gptModel,clip_text]
304
+ if prompt.startswith('allembeds'):
305
+ get_db_embeddings()
306
+ return [past, 'ok', None, gptModel,clip_text]
307
+ if prompt == 'stats':
308
+ response = genUsageStats()
309
+ return [past, str(response), None, gptModel,clip_text]
310
+ if prompt == 'reset':
311
+ response = genUsageStats(True)
312
+ return [past, md(response), None, gptModel,clip_text]
313
+ if prompt.startswith('gpt4'):
314
+ gptModel = 'gpt-4o-2024-08-06'
315
+ prompt = prompt[5:]
316
+ if prompt.startswith("clean"):
317
+ user = prompt[6:]
318
+ response = f'cleaned all .wav and .b64 files for {user}'
319
+ final_clean_up(user, True)
320
+ return [past, response, None, gptModel,clip_text]
321
+ if prompt.startswith('files'):
322
+ (log_cnt, wav_cnt, other_cnt, others, log_list) = list_permanent_files()
323
+ response = f'{log_cnt} log files\n{wav_cnt} .wav files\n{other_cnt} Other files:\n{others}\nlogs: {str(log_list)}'
324
+ return [past, response, None, gptModel,clip_text]
325
+ if user_window in unames and pwd_window == pwdList[unames.index(user_window)]:
326
+ chatType = 'normal'
327
+ prompt = prompt.strip()
328
+ finish_reason = 'ok'
329
+ rag_txt = ''
330
+ prompt_bare = prompt
331
+ first_time = False
332
+ if len(past) == 0:
333
+ first_time = True
334
+ results = do_search(prompt, db_name)
335
+ clip_txt = '\n=================\n\nFollowing are Clips and YouTube Links based on your initial query:\n=================\n'
336
+ if len(results) == 0:
337
+ clip_txt += '\nSorry, the database you selected appears to be missing\n'
338
+ for (name, text, time, yt_id, dp) in results:
339
+ yt_id = yt_id.replace('"','')
340
+ seek_HMS = seek_hms(time)
341
+ seek_colons = seek_HMS.replace('h',' : ').replace('m',' : ').replace('s','')
342
+ pure_text = remove_times(text)
343
+ yt_url = f'https://www.youtube.com/watch?v={yt_id}#t={seek_HMS}'
344
+ clip_txt += md(f'\n\n{name}\nAt seek time: {seek_colons}\nYouTube Link: [URL]({yt_url}\n\n{pure_text}\n================')
345
+ rag_txt += pure_text
346
+ prompt = rag_txt + '.\n ' + prompt + '\nGive higher priority to the information just provided.'
347
+ past.append({"role":"user", "content":prompt})
348
+ completion = Client().chat.completions.create(model=gptModel, messages=past)
349
+ reporting_model = gptModel
350
+ reply = completion.choices[0].message.content
351
+ tokens_in = completion.usage.prompt_tokens
352
+ tokens_out = completion.usage.completion_tokens
353
+ tokens = completion.usage.total_tokens
354
+ response += "\n\n***YOU***: " + prompt_bare + "\n\n***GPT***: " + reply.replace('```','\n\n```\n\n')
355
+ if len(clip_txt) > 0:
356
+ response += md(clip_txt)
357
+
358
+ if isBoss:
359
+ response += md(f"\n\n{reporting_model}: tokens in/out = {tokens_in}/{tokens_out}\n")
360
+ if finish_reason != 'ok':
361
+ response += md(f"\n{finish_reason}\n")
362
+ if tokens > 40000:
363
+ response += "\n\nTHIS DIALOG IS GETTING TOO LONG. PLEASE RESTART CONVERSATION SOON."
364
+ past.append({"role":"assistant", "content": reply})
365
+ accessOk = False
366
+ for i in range(3):
367
+ try:
368
+ dataFile = new_func(user_window)
369
+ with open(dataFile, 'a') as f:
370
+ m = '4o'
371
+ if 'mini' in reporting_model:
372
+ m = '4omini'
373
+ f.write(f'{user_window}:{tokens_in}/{tokens_out}-{m}\n')
374
+ accessOk = True
375
+ break
376
+ except Exception as e:
377
+ sleep(3)
378
+ if not accessOk:
379
+ response += f"\nDATA LOG FAILED, path = {dataFile}"
380
+ return [past, response , None, gptModel,clip_txt]
381
+ else:
382
+ return [[], "User name and/or password are incorrect", prompt, gptModel,clip_txt]
383
+
384
+ def new_func(user):
385
+ dataFile = dataDir + user + '_log.txt'
386
+ return dataFile
387
+
388
+ def transcribe(user, pwd, fpath):
389
+ user = user.lower().strip()
390
+ pwd = pwd.lower().strip()
391
+ if not (user in unames and pwd in pwdList):
392
+ return 'Bad credentials'
393
+ with audioread.audio_open(fpath) as audio:
394
+ duration = int(audio.duration)
395
+ if duration > 0:
396
+ with open(dataDir + user + '_audio.txt','a') as f:
397
+ f.write(f'audio:{str(duration)}\n')
398
+ with open(fpath,'rb') as audio_file:
399
+ transcript = Client().audio.transcriptions.create(
400
+ model='whisper-1', file = audio_file ,response_format = 'text' )
401
+ reply = transcript
402
+ return str(reply)
403
+
404
+ def pause_message():
405
+ return "Audio input is paused. Resume or Stop as desired"
406
+
407
+ def update_user(user_win):
408
+ user_win = user_win.lower().strip()
409
+ user = 'unknown'
410
+ for s in unames:
411
+ if user_win == s:
412
+ user = s
413
+ break
414
+ return [user, user]
415
+
416
+ def speech_worker(chunks=[],q=[]):
417
+ for chunk in chunks:
418
+ fpath = q.pop(0)
419
+ response = Client().audio.speech.create(model="tts-1", voice="fable", input=chunk, speed=0.85, response_format='wav')
420
+ with open(fpath, 'wb') as fp:
421
+ fp.write(response.content)
422
+
423
+ def gen_speech_file_names(user, cnt):
424
+ rv = []
425
+ for i in range(0, cnt):
426
+ rv.append(dataDir + f'{user}_speech{i}.wav')
427
+ return rv
428
+
429
+ def final_clean_up(user, do_b64 = False):
430
+ user = user.strip().lower()
431
+ if user == 'kill':
432
+ flist = glob(dataDir + '*')
433
+ elif user == 'all':
434
+ flist = glob(dataDir + '*_speech*.wav')
435
+ else:
436
+ flist = glob(dataDir + f'{user}_speech*.wav')
437
+ for fpath in flist:
438
+ try:
439
+ os.remove(fpath)
440
+ except:
441
+ continue
442
+
443
+ def list_permanent_files():
444
+ flist = os.listdir(dataDir)
445
+ others = []
446
+ log_cnt = 0
447
+ wav_cnt = 0
448
+ other_cnt = 0
449
+ list_logs = []
450
+ for fpath in flist:
451
+ if fpath.endswith('.txt'):
452
+ log_cnt += 1
453
+ list_logs.append(fpath)
454
+ elif fpath.endswith('.wav'):
455
+ wav_cnt += 1
456
+ else:
457
+ others.append(fpath)
458
+ other_cnt = len(others)
459
+ if log_cnt > 5:
460
+ list_logs = []
461
+ return (str(log_cnt), str(wav_cnt), str(other_cnt), str(others), list_logs)
462
+
463
+
464
+ def show_help():
465
+ txt = '''
466
+ 1. Gemeral:
467
+ 1.1 Login with user name and password (not case-sensitive)
468
+ 1.2 Type prompts (questions, instructions) into "Prompt or Question" window (OR) you can speak prompts by
469
+ tapping the audio "Record" button, saying your prompt, then tapping the "Stop" button.
470
+ Your prompt will appear in the Prompt window, and you can edit it there if needed.
471
+ 1.3 Text in the "Dialog" window can be spoken by tapping the "Speak Dialog" button.
472
+ 2. Chat:
473
+ 2.1 Enter prompt and tap the "Submit Prompt/Question" button. The responses appear in the Dialog window.
474
+ 2.2 Enter follow-up questions in the Prompt window either by typing or speaking. Tap the voice
475
+ entry "Reset Voice Entry" button to enable additional voice entry. Then tap "Submit Prompt/Question".
476
+ 2.3 If topic changes or when done chatting, tap the "Restart Conversation" button.
477
+ Hints:
478
+ 1. Better chat results are obtained by including detailed descriptions and instructions
479
+ in the prompt.
480
+ 2. Always tap "Restart Conversation" before changing chat topics.
481
+ 3. Audio input and output functions depend on the hardware capability of your device.
482
+ 4. "Speak Dialog" will voice whatever is currently in the Dialog window. You can repeat it and you
483
+ can edit what's to be spoken. Except: In a chat conversation, spoken dialog will only include
484
+ the latest prompt/response ("YOU:/GPT:") sequence.'''
485
+ return str(txt).replace('```', ' ').replace(' ', '&nbsp;&nbsp;').replace(' ', '&nbsp;&nbsp;').replace(' ', '&nbsp;&nbsp;').replace('\n','<br>')
486
+
487
+ def upload_db_file(visibility):
488
+ viz = not visibility
489
+ return [viz, gr.File(visible=viz, type="filepath", interactive=True, label='Upload Database')]
490
+
491
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
492
+ history = gr.State([])
493
+ password = gr.State("")
494
+ user = gr.State("unknown")
495
+ model = gr.State("gpt-4o-mini")
496
+ clip_text = gr.State("")
497
+ file_browser_visibility = gr.State(False)
498
+ q = gr.State([])
499
+ qsave = gr.State([])
500
+ selected_db = gr.State('GoodNews.db')
501
+
502
+ def clean_up(user):
503
+ flist = glob(dataDir + f'{user}_speech*.wav')
504
+ for fpath in flist:
505
+ try:
506
+ os.remove(fpath)
507
+ except:
508
+ continue
509
+
510
+ def initial_audio_output(txt, user):
511
+ global digits
512
+ global abbrevs
513
+ if not user in unames:
514
+ return [gr.Audio(sources=None), []]
515
+ clean_up(user)
516
+ q = []
517
+ if len(txt.strip()) < 5:
518
+ return ['None', q]
519
+ try:
520
+ loc = txt.rindex('YOU:')
521
+ txt = txt[loc:]
522
+ except:
523
+ pass
524
+ for s,x in abbrevs.items():
525
+ txt = txt.replace(s, x)
526
+ words_in = txt.replace('**', '').replace('&nbsp;','').split('<br>')
527
+ words_out = []
528
+ for s in words_in:
529
+ s = s.lstrip('- *@#$%^&_=+-')
530
+ if len(s) > 0:
531
+ loc = s.find(' ')
532
+ if loc > 1:
533
+ val = s[0:loc]
534
+ isnum = val.replace('.','0').isdecimal()
535
+ if isnum:
536
+ if val.endswith('.'):
537
+ val = val[:-1].replace('.',' point ') + '., '
538
+ else:
539
+ val = val.replace('.', ' point ') + ', '
540
+ s = 'num'+ val + s[loc:]
541
+ words_out.append(s)
542
+ chunklist = []
543
+ for chunk in words_out:
544
+ if chunk.strip() == '':
545
+ continue
546
+ isnumbered = chunk.startswith('num')
547
+ number = ''
548
+ loc = 0
549
+ if isnumbered:
550
+ chunk = chunk[3:]
551
+ loc = chunk.index(',')
552
+ number = chunk[0:loc]
553
+ chunk = chunk[loc:]
554
+ locs = []
555
+ for i in range(1,len(chunk)-1):
556
+ (a, b, c) = chunk[i-1:i+2]
557
+ if a.isdecimal() and b == '.' and c.isdecimal():
558
+ locs.append(i)
559
+ for i in locs:
560
+ chunk = chunk[:i] + ' point ' + chunk[i+1:]
561
+ if len(chunk) > 50:
562
+ finechunks = chunk.split('.')
563
+ for fchunk in finechunks:
564
+ if isnumbered:
565
+ fchunk = number + fchunk
566
+ isnumbered = False
567
+ if len(fchunk) > 0:
568
+ if fchunk != '"':
569
+ chunklist.append(fchunk)
570
+ else:
571
+ line = number + chunk
572
+ if line != '"':
573
+ chunklist.append(line)
574
+ total_speech = 0
575
+ for chunk in chunklist:
576
+ total_speech += len(chunk)
577
+ with open(dataDir + user + '_speech.txt','a') as f:
578
+ f.write(f'speech:{str(total_speech)}\n')
579
+ chunk = chunklist[0]
580
+ if chunk.strip() == '':
581
+ return gr.Audio(sources=None)
582
+ fname_list = gen_speech_file_names(user, len(chunklist))
583
+ q = fname_list.copy()
584
+ qsave = fname_list.copy()
585
+ fname = q.pop(0)
586
+ if len(chunklist) > 0:
587
+ threading.Thread(target=speech_worker, daemon=True, args=(chunklist[1:],fname_list[1:])).start()
588
+ response = Client().audio.speech.create(model="tts-1", voice="fable", input=chunk, speed=0.85, response_format='wav')
589
+ with open(fname, 'wb') as fp:
590
+ fp.write(response.content)
591
+ return [fname, q]
592
+
593
+ def gen_output_audio(q, user):
594
+ try:
595
+ fname = q.pop(0)
596
+ except:
597
+ final_clean_up(user)
598
+ return [None, gr.Audio(sources=None)]
599
+ if not os.path.exists(fname):
600
+ sleep(3)
601
+ if not os.path.exists(fname):
602
+ response = Client().audio.speech.create(model="tts-1", voice="fable",
603
+ input='Sorry, text-to-speech is responding too slow right now', speed=0.85, response_format='wav')
604
+ with open(fname, 'wb') as fp:
605
+ fp.write(response.content)
606
+ q = []
607
+ return [fname, q]
608
+
609
+
610
+ gr.Markdown('# MTOI Search')
611
+ gr.Markdown('Enter user name & password. Tap "Help & Hints" button for more instructions.')
612
+ with gr.Row():
613
+ user_window = gr.Textbox(label = "User Name")
614
+ user_window.blur(fn=update_user, inputs=user_window, outputs=[user, user_window])
615
+ pwd_window = gr.Textbox(label = "Password")
616
+ help_button = gr.Button(value='Help & Hints')
617
+ with gr.Row():
618
+ audio_widget = gr.Audio(type='filepath', format='wav',waveform_options=gr.WaveformOptions(
619
+ show_recording_waveform=True), sources=['microphone'], scale = 3, label="Prompt/Question Voice Entry", max_length=120)
620
+ reset_button = gr.ClearButton(value="Reset Voice Entry", scale=1) #new_func1()
621
+ with gr.Row():
622
+ clear_button = gr.Button(value="Restart Conversation")
623
+ db_chooser = gr.Dropdown(type="value", label='Choose Database', show_label=True,
624
+ choices=['GoodNews.db', 'passover.db', 'MTOI5.db'], interactive=True)
625
+ button_upload_db = gr.Button(value='Upload Database', visible=False)
626
+ speak_output = gr.Button(value="Speak Dialog", visible=True)
627
+ submit_button = gr.Button(value="Submit Prompt/Question")
628
+ prompt_window = gr.Textbox(label = "Prompt or Question")
629
+ gr.Markdown('### **Dialog:**')
630
+ #output_window = gr.Text(container=True, label='Dialog')
631
+ output_window = gr.Markdown(container=True)
632
+ with gr.Row():
633
+ db_file = gr.File(visible=False, type="filepath", interactive=True, label='Upload Database')
634
+ pwd_window.blur(updatePassword, inputs = [user_window, pwd_window], outputs = [password, pwd_window, button_upload_db])
635
+ submit_button.click(chat,
636
+ inputs=[prompt_window, user_window, password, history, output_window, model, clip_text, selected_db],
637
+ outputs=[history, output_window, prompt_window, model, clip_text])
638
+ clear_button.click(fn=new_conversation, inputs=user_window, outputs=[prompt_window, history, output_window, clip_text])
639
+ audio_widget.stop_recording(fn=transcribe, inputs=[user_window, password, audio_widget],
640
+ outputs=[prompt_window])
641
+ audio_widget.pause_recording(fn=pause_message, outputs=[prompt_window])
642
+ reset_button.add(audio_widget)
643
+ audio_out = gr.Audio(autoplay=True, visible=False)
644
+ audio_out.stop(fn=gen_output_audio, inputs=[q, user_window], outputs = [audio_out, q])
645
+ speak_output.click(fn=initial_audio_output, inputs=[output_window, user_window], outputs=[audio_out, q])
646
+ help_button.click(fn=show_help, outputs=output_window)
647
+ button_upload_db.click(fn=upload_db_file,inputs = [file_browser_visibility],
648
+ outputs = [file_browser_visibility, db_file])
649
+ db_file.upload(fn=write_db_file, inputs=[db_file], outputs=[output_window])
650
+ db_chooser.input(fn=set_db,inputs= [db_chooser], outputs= [selected_db])
651
+ # demo.load(fn=load_db,inputs=[db_chooser],outputs=[db_chooser])
652
+
653
+ demo.launch(share=True, allowed_paths=[dataDir], ssr_mode=False)
654
+