gorkemgoknar commited on
Commit
148edac
1 Parent(s): 4e91928

Add application file

Browse files
Files changed (1) hide show
  1. app.py +408 -0
app.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ctransformers import AutoModelForCausalLM
2
+ import re, requests, json
3
+ import gradio as gr
4
+ import random
5
+ import torch
6
+ from itertools import chain
7
+ import asyncio
8
+
9
+ from transformers import (
10
+ StoppingCriteriaList,
11
+ MaxLengthCriteria,
12
+ )
13
+
14
+ # Created by
15
+ # https://huggingface.co/gorkemgoknar
16
+
17
+ #Coqui V1 api render voice, you can also use XTTS
18
+ COQUI_URL="https://app.coqui.ai/api/v2/samples"
19
+ ### Warning each sample will consume your credits
20
+ COQUI_TOKEN="YOUR_COQUI_TOKEN_ON_YOUR_ACCOUNT"
21
+
22
+ MAX_NEW_TOKENS = 30
23
+ GPU_LAYERS = 0
24
+ CPU_THREADS = 2
25
+ STOP_LIST=["###","##"]
26
+
27
+ stopping_criteria = StoppingCriteriaList([MaxLengthCriteria(max_length=64)])
28
+
29
+ llm = AutoModelForCausalLM.from_pretrained("gorkemgoknar/llama2-7f-moviechatbot-ggml-q4",
30
+ model_type='llama',
31
+ gpu_layers=GPU_LAYERS,
32
+ max_new_tokens=MAX_NEW_TOKENS,
33
+ stop=STOP_LIST,
34
+ threads=CPU_THREADS)
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+ ##########################################
43
+ #You can use coqui.ai api to generate audio
44
+ #first you need to create clone voice for characters
45
+
46
+ voices = {}
47
+ voices["Gerald"]="VOICE_ID_OF_GERALD"
48
+ voices["Vader"]="VOICE_ID"
49
+ voices["Batman"]="VOICE_ID"
50
+ voices["Gandalf"]="VOICE_ID"
51
+ voices["Morpheus"]="VOICE_ID"
52
+ voices["Neo"]="VOICE_ID"
53
+ voices["Ig-11"]="VOICE_ID"
54
+ voices["Tony Stark"]="VOICE_ID"
55
+ voices["Kirk"]="VOICE_ID"
56
+ voices["Spock"]="VOICE_ID"
57
+
58
+ def get_audio_url(text,character):
59
+ url = COQUI_URL
60
+
61
+ # voice id of "Baldur Sanjin" from buildin coqui.ai speakers
62
+ # more via https://docs.coqui.ai/reference/speakers_retrieve
63
+ payload = {
64
+ "voice_id": voices[character], ## Voice id in form of (this is dummy) "a399c204-7040-4f1d-bb92-5223fa1aeceb"
65
+ "text": f"{text}",
66
+ "emotion": "Neutral", ## You can set Angry, Surprise etc on V1 api.. XTTS auto understands it
67
+ "speed": 1,
68
+ }
69
+ headers = {
70
+ "accept": "application/json",
71
+ "content-type": "application/json",
72
+ "authorization": f"Bearer {COQUI_TOKEN}"
73
+ }
74
+
75
+ response = requests.post(url, json=payload, headers=headers)
76
+ res = json.loads(response.text)
77
+ #print(res)
78
+ return res["audio_url"]
79
+
80
+
81
+ def get_response_cpp(prompt):
82
+
83
+ response_text= llm(prompt)
84
+
85
+ return response_text
86
+
87
+ def build_question(character,question,context=None, answer=None,history=None , use_history=False, modify_history=True,human_character=None,add_answer_to_history=True):
88
+ # THIS MODEL (gorkemgoknar/llama2-7f-moviechatbot-ggml-q4) is specifically fined tuned by
89
+ # ### Context: {context}### History: {history}### {human_character}: {question}### {character}: {answer}
90
+ # Where History contains all previous lines talked by characters in order
91
+ # Context is actually arbitrary it shows something characters can start talking upon
92
+
93
+ if context is None:
94
+ context= "movie"
95
+
96
+ #if human_character is None:
97
+ # human_character=""
98
+ #else:
99
+ # human_character="#"+"I am " + human_character +"#"
100
+
101
+ if use_history:
102
+ if history is None:
103
+ if answer is None:
104
+ history=""
105
+ else:
106
+ history=answer
107
+ else:
108
+ if modify_history:
109
+ if answer is None:
110
+ history=history
111
+ else:
112
+ if add_answer_to_history:
113
+ history=history +"#" + answer
114
+ else:
115
+ history=history
116
+ else:
117
+ history=history
118
+
119
+ if human_character is None:
120
+ prompt = f"### Context: {context}### History: {history}### Human: {question}### {character}:"
121
+ else:
122
+ prompt = f"### Context: {context}### History: {history}### {human_character}: {question}### {character}:"
123
+
124
+
125
+ else:
126
+ if human_character is None:
127
+ prompt = f"### Context: {context}### Human: {question}### {character}:"
128
+ else:
129
+ prompt = f"### Context: {context}### {human_character}: {question}### {character}:"
130
+ return prompt,history
131
+
132
+
133
+
134
+ def get_answer_from_response(text,character):
135
+
136
+ # on HF it has same text plus additional
137
+ #response= text.split(f"### {character}:")[1]
138
+ # on cpp it continues
139
+ response= text
140
+ # get only first line of response
141
+ response= response.split("###")[0]
142
+ response= response.split("#")[0]
143
+ # Weirdly llama2 7f creates some German or Polski on the end... need to crop them
144
+ response= response.split("Unterscheidung")[0] # weird, german seperators on output
145
+ response= response.split("Hinweis")[0] # weird, german seperators on output
146
+ response= response.split("sierp ")[0] # weird, sierp
147
+ response= response.split("sierpni ")[0] # weird, sierp
148
+ response= response.split("sierpien")[0] # weird, sierp
149
+ response= response.split("\n")[0] # cut at end of line
150
+ response= re.split("sierp.+\d+", response)[0] # comes as sierpina 2018 something something
151
+
152
+ response= response.split(":")[0]
153
+ return response
154
+
155
+ def run_chatter(num_repeat=2, character="kirk",human_character="Mr. Sulu",context="Captain Kirk from U.S.S. Enterprise",
156
+ initial_question="There is a ship approaching captain!",
157
+ withaudio=False,
158
+ history=None,
159
+ add_answer_to_history=True,
160
+ answer=None,
161
+ debug_print=False,
162
+ use_cpu=False):
163
+
164
+ question=initial_question
165
+
166
+ dialogue=""
167
+
168
+ if debug_print:
169
+ print("**** START Dialogue ****")
170
+ print("Input History:",history)
171
+
172
+ audio_urls=[]
173
+
174
+ for i in range(num_repeat):
175
+ if question is not None:
176
+ question=question.strip()
177
+ if answer is not None:
178
+ answer=answer.strip()
179
+
180
+ prompt,history= build_question(character,question,context=context,history=history,answer=answer,human_character=human_character,use_history=True,add_answer_to_history=add_answer_to_history)
181
+ print("PROMPT:",prompt)
182
+
183
+ response= get_response_cpp(prompt)
184
+ print("RESPONSE:",response)
185
+ answer = get_answer_from_response(response,character).strip()
186
+
187
+ if withaudio:
188
+ answer_audio_url = get_audio_url(answer)
189
+ audio_urls.append(answer_audio_url)
190
+ if debug_print:
191
+ print("\nAct:",i+1)
192
+
193
+ dialogue = dialogue + f"{human_character}: {question}" + "\n"
194
+ if debug_print:
195
+ print(f"{human_character}:",question)
196
+ print(f"{character}:",answer)
197
+
198
+ dialogue = dialogue + f"{character}: {answer}" + "\n"
199
+
200
+ if question is not None:
201
+ question=question.strip()
202
+ if answer is not None:
203
+ answer=answer.strip()
204
+
205
+ prompt,history= build_question(human_character,answer,context=context,history=history,answer=question,human_character=character,use_history=True,add_answer_to_history=add_answer_to_history)
206
+ print("PROMPT:",prompt)
207
+
208
+ response= get_response_cpp(prompt)
209
+ print("RESPONSE:",response)
210
+ resp_answer = get_answer_from_response(response,human_character)
211
+
212
+ if withaudio:
213
+ #
214
+ response_audio_url = get_audio_url(resp_answer)
215
+ audio_urls.append(response_audio_url)
216
+
217
+ if debug_print:
218
+ print(f"{human_character}:",resp_answer)
219
+
220
+ question = resp_answer
221
+
222
+
223
+ if debug_print:
224
+ print("Final History:",history)
225
+ print("**** END Dialogue ****")
226
+ if withaudio:
227
+ return dialogue,question,answer,history,audio_urls
228
+ else:
229
+ return dialogue,question,answer,history
230
+
231
+
232
+ ######################
233
+ # GRADIO PART
234
+ ######################
235
+
236
+
237
+ # to close on Jupyter remote
238
+ #if("interface" in vars()):
239
+ # print("Closing existing interface")
240
+ # interface.close()
241
+
242
+
243
+ css="""
244
+ .chatbox {display:flex;flex-direction:column}
245
+ .user_msg, .resp_msg {padding:4px;margin-bottom:4px;border-radius:4px;width:80%}
246
+ .user_msg {background-color:cornflowerblue;color:white;align-self:start}
247
+ .resp_msg {background-color:lightgray;align-self:self-end}
248
+ .audio {background-color:cornflowerblue;color:white;align-self:start;height:5em}
249
+
250
+ """
251
+
252
+ WITH_AUDIO=False
253
+
254
+ async def add_text(char1,char2,runs,context,initial_question,history):
255
+ print(f"{char1} talks to {char2}")
256
+
257
+ history = None
258
+ last_question=None
259
+ # todo build a context from dropdown
260
+ returned_history = ""
261
+ for i in range(int(runs)):
262
+ print("char1:",char1," :", initial_question)
263
+ returned_history += char1 + " : " + initial_question + "\n"
264
+
265
+ dialogue,last_question,last_answer,history = run_chatter(num_repeat=1,
266
+ character=char2,
267
+ human_character=char1,
268
+ context=context,
269
+ initial_question=initial_question,
270
+ withaudio=False,
271
+ history=history,
272
+ answer=last_question,
273
+ debug_print=False,
274
+ add_answer_to_history=False
275
+ )
276
+
277
+ print("char2:",char2," :", last_answer)
278
+ returned_history += char2 + " : " + last_answer + "\n"
279
+ # add last answer to history
280
+ history = history + "#" +initial_question + "#"+ last_answer
281
+
282
+ if WITH_AUDIO:
283
+ char1_audio_url= get_audio_url(initial_question,char1)
284
+
285
+ audios = (
286
+ gr.Audio.update() ,
287
+ gr.Audio.update() ,
288
+ gr.Audio.update() ,
289
+ gr.Audio.update() ,
290
+ gr.Audio.update() ,
291
+ gr.Audio.update() ,
292
+ gr.Audio.update() ,
293
+ gr.Audio.update()
294
+ )
295
+
296
+ char2_audio_url= get_audio_url(last_answer,char2)
297
+ audios = list(audios)
298
+ #should now do a loop
299
+
300
+
301
+
302
+ audios[i*2] = gr.Audio.update(char1_audio_url, visible=True,label=str(i*2 )+"_"+char1)
303
+ audios[i*2 + 1] = gr.Audio.update(char2_audio_url, visible=True,label=str(i*2 + 1)+"_"+char2)
304
+
305
+ audios = tuple(audios)
306
+
307
+ #This needs to be last before yield
308
+ initial_question=last_question
309
+
310
+ yield gr.update(value=initial_question, interactive=True),returned_history, *audios
311
+ else:
312
+ #This needs to be last before yield
313
+ initial_question=last_question
314
+ yield gr.update(value=initial_question, interactive=True),returned_history
315
+
316
+
317
+
318
+
319
+ history=None
320
+ #some selected ones are in for demo use
321
+ CHARACTER_1_CHOICES = ["Gandalf","Gerald", "Morpheus", "Neo","Kirk","Spock","Vader", "Ig-11","Tony Stark","Batman"]
322
+ CHARACTER_2_CHOICES = ["Gandalf","Gerald", "Morpheus", "Neo","Kirk","Spock","Vader", "Ig-11","Tony Stark","Batman"]
323
+ CONTEXT_CHOICES = ["talks friendly",
324
+ "insults",
325
+ "diss in rap",
326
+ "on a cruise ship going to Mars from Earth",
327
+ "blames on something",
328
+ "tries to save the world",
329
+ "talks agressively",
330
+ "argues over if a movie is good"]
331
+
332
+ EXAMPLE_INITIALS=["I challenge you to battle of words!",
333
+ "how much would a woodchuck chuck if a woodchuck could chuck wood?",
334
+ "The world is changing.","What do you think about AI?","Are you real?","I went to the supermarket yesterday.", "Who are you?", "I am richer than you!"]
335
+
336
+ RUN_COUNT = [2,3,4]
337
+
338
+ title = "Metayazar - Movie Chatbot Llama Finetuned"
339
+ description = "Chat with your favorite movie characters. Finetuned with Llama2 "
340
+ article = "<p style='text-align: center'><a href='https://www.linkedin.com/pulse/ai-goes-job-interview-g%C3%B6rkem-g%C3%B6knar/' target='_blank'>AI Goes to Job Interview</a> | <a href='https://www.metayazar.com/' target='_blank'>Metayazar AI Writer</a> |<a href='https://www.linkedin.com/in/goknar/' target='_blank'>Görkem Göknar</a></p>"
341
+
342
+ history = {"character": "None", "message_history" : [] }
343
+
344
+
345
+
346
+ def change_run_count(run_count):
347
+ print("update run count:",run_count)
348
+ visible_audios=[False,False,False,False,False,False,False,False]
349
+ run_count=int(run_count)
350
+ for i in range(run_count*2-1):
351
+ if i>=len(visible_audios):
352
+ break
353
+ visible_audios[i] = False # Set true to become visible upon change
354
+
355
+ return_list=[]
356
+ #Max audio 8
357
+ for i in range(8):
358
+ return_list.append( gr.Audio.update( visible=visible_audios[i]) )
359
+
360
+ return return_list
361
+
362
+
363
+ with gr.Blocks(css=css) as interface:
364
+
365
+ with gr.Row():
366
+ drop_char1 = gr.components.Dropdown(CHARACTER_1_CHOICES,label="Character 1",value=CHARACTER_1_CHOICES[0])
367
+ drop_char2 = gr.components.Dropdown(CHARACTER_2_CHOICES,label="Character 2",value=CHARACTER_2_CHOICES[1])
368
+ run_count = gr.components.Dropdown(RUN_COUNT,label="Line count per character",value="2")
369
+ context_choice = gr.components.Dropdown(CONTEXT_CHOICES, label="Context",value=CONTEXT_CHOICES[0])
370
+ with gr.Row():
371
+ txt = gr.Textbox(
372
+ show_label=False,
373
+ placeholder="Enter text and press enter, or upload an image",
374
+ value=EXAMPLE_INITIALS[0],elem_classes="user_msg"
375
+ )
376
+ submit_btn = gr.Button(value="Submit")
377
+ examples = gr.Examples(examples=EXAMPLE_INITIALS,
378
+ inputs=[txt])
379
+ with gr.Row():
380
+ with gr.Column():
381
+ history = gr.Textbox(lines=25,
382
+ show_label=True,
383
+ label="History",
384
+ placeholder="History",
385
+ ).style(height=50)
386
+ if WITH_AUDIO:
387
+ with gr.Column():
388
+ audio1 = gr.Audio(elem_id="audio1",elem_classes="audio",autoplay=False,visible=False)
389
+ audio2 = gr.Audio(elem_id="audio2",elem_classes="audio",autoplay=False,visible=False)
390
+ audio3 = gr.Audio(elem_id="audio3",elem_classes="audio",autoplay=False,visible=False)
391
+ audio4 = gr.Audio(elem_id="audio4",elem_classes="audio",autoplay=False,visible=False)
392
+ audio5 = gr.Audio(elem_id="audio5",elem_classes="audio",autoplay=False,visible=False)
393
+ audio6 = gr.Audio(elem_id="audio6",elem_classes="audio",autoplay=False,visible=False)
394
+ audio7 = gr.Audio(elem_id="audio7",elem_classes="audio",autoplay=False,visible=False)
395
+ audio8 = gr.Audio(elem_id="audio8",elem_classes="audio",autoplay=False,visible=False)
396
+
397
+ if WITH_AUDIO:
398
+ run_count.change(change_run_count,[run_count],[audio1,audio2,audio3,audio4,audio5,audio6,audio7,audio8])
399
+ submit_btn.click(add_text, [drop_char1, drop_char2,run_count, context_choice, txt,history], [txt,history,audio1,audio2,audio3,audio4,audio5,audio6,audio7,audio8], api_name="chat")
400
+ else:
401
+ # no audio returned
402
+ submit_btn.click(add_text, [drop_char1, drop_char2,run_count, context_choice, txt,history], [txt,history], api_name="chat")
403
+
404
+
405
+
406
+ interface.queue(concurrency_count=20).launch(server_name="192.168.1.44", server_port=8880, share=True)
407
+
408
+