File size: 7,920 Bytes
fd22ae3
6e771f6
fd22ae3
862d34a
6e771f6
 
57315a6
fd22ae3
b2a1255
 
fd22ae3
 
862d34a
b2a1255
 
 
 
 
 
862d34a
fd22ae3
 
 
 
 
 
 
 
 
 
 
 
 
 
862d34a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2a1255
862d34a
 
 
 
 
 
 
 
 
fd22ae3
862d34a
 
b2a1255
fd22ae3
 
 
 
 
 
 
 
 
 
b2a1255
fd22ae3
b2a1255
 
fd22ae3
 
 
 
b2a1255
 
 
fd22ae3
 
 
 
b2a1255
 
fd22ae3
 
 
 
 
 
b2a1255
fd22ae3
905bfbc
 
 
 
 
fd22ae3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
862d34a
 
 
 
fd22ae3
862d34a
fd22ae3
 
 
 
905bfbc
 
 
 
 
862d34a
fd22ae3
862d34a
fd22ae3
 
 
 
b2a1255
fd22ae3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
862d34a
fd22ae3
862d34a
fd22ae3
ac5bcfb
fd22ae3
 
862d34a
6e771f6
fd22ae3
6e771f6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import os

import gradio as gr
from openai import OpenAI


LOAD_KEYS_FROM_FILES = False

if LOAD_KEYS_FROM_FILES:
    # Load OpenAI API key
    with open('KEY_OPENAI', 'r') as file:
        os.environ['OPENAI_API_KEY'] = file.read().replace('\n', '')

    # Hugging Face API key, used for the serverless access to ASR model
    with open('KEY_HF', 'r') as file:  
        os.environ['HUGGINGFACE_API_KEY'] = file.read().replace('\n', '')


AUDIO_OUT_FILE_PREFIX = "output"   # prefixo do nome do arquivo de áudio .wav

TEMPLATE_SYSTEM_MESSAGE = """Sua função é entreter uma criança com idade entre 6 e 8 anos que adora futebol. Diretrizes para a conversa: 
- Seu nome é {NAME}
- Pergunte o nome da criança. 
- Fale sobre futebol, times, jogadores, seleções e grandes jogos. 
- Em especial, foque no futebol do Brasil, Inglaterra e Espanha. 
- {PERSONALITY}
- Fale, no máximo, três frases por mensagem. 
"""

# Mapeia a personalidade no template e na temperatura
PERSONALITIES = {
    "nova": ("Seja amigável e alegre, como uma criança. Tente iniciar novos assuntos, quando a conversa estiver repetitiva. Conte piadas de futebol, de vez em quando.", 0.8),
    "echo": ("Seja amigável e objetivo. Tente manter-se no mesmo assunto. Conte alguma curiosidade sobre um grande craque, de vez em quando.", 0.2)
}


# Função para converter o histórico de chat para o formato esperado pela API do OpenAI
def to_openai_chat_history(system_prompt, curr_message, chat_history):
    prompt = [ { 'role': 'system', 'content': system_prompt } ]
    for turn in chat_history:
        user_message, bot_message = turn
        prompt.append( {'role': 'user', 'content' : user_message} )
        prompt.append( {'role': 'assistant', 'content' : bot_message} )
    prompt.append( {'role': 'user', 'content' : curr_message } )
    return prompt


def respond(system_prompt, user_message, chat_history, temperature, voice="echo"):
        openai_history = to_openai_chat_history(system_prompt, user_message, chat_history)

        bot_response = OPENAI_CLIENT.chat.completions.create(
            messages=openai_history,
            temperature=temperature,
            model="gpt-3.5-turbo")

        assistant_msg = bot_response.choices[0].message.content

        # salva o audio 
        response = OPENAI_CLIENT.audio.speech.create(
            model="tts-1",
            voice=voice,
            input=assistant_msg
        )

        output_audio_file = f"{AUDIO_OUT_FILE_PREFIX}-{len(chat_history)+1:03}.wav"
        #response.stream_to_file(output_audio_file)
        response.write_to_file(output_audio_file)

        # adiciona ao chat, com o tipo de dado esperado pelo Gradio
        chat_history.append( (user_message, assistant_msg) )
        
        return "", chat_history, output_audio_file


def reset_and_apply(openai_key, voice):
    global OPENAI_CLIENT
    OPENAI_CLIENT = OpenAI(api_key=openai_key)
    return [("", "Olá, vamos falar de futebol?")], AUDIO_OUT_FILE_PREFIX + f"-001-{voice}.wav"

def reset_openai_client(openai_key):
    global OPENAI_CLIENT
    OPENAI_CLIENT = OpenAI(api_key=openai_key)

def on_voice_change(voice):
    persona_description, persona_temperature = PERSONALITIES[voice]
    return TEMPLATE_SYSTEM_MESSAGE.format(NAME=voice.upper(), PERSONALITY=persona_description), persona_temperature


USE_LOCAL_ASR_PIPELINE = True

# With pipeline (downloaded model)
if USE_LOCAL_ASR_PIPELINE:
    from transformers import pipeline
    import numpy as np

    global ASR_PIPELINE
    ASR_PIPELINE = pipeline(task="automatic-speech-recognition", 
                            model="openai/whisper-large-v3")
                            #model="distil-whisper/distil-small.en") # English only
else:
    import requests
    global ASR_API_URL, ASR_API_HEADERS
    
    HF_KEY = os.environ['HUGGINGFACE_API_KEY']

    # Serverless API endpoint for OpenAI's Whisper model
    ASR_API_URL = "https://api-inference.huggingface.co/models/openai/whisper-large-v3"
    ASR_API_HEADERS = {"Authorization": f"Bearer {HF_KEY}"}


def transcribe(audio_in):
    if USE_LOCAL_ASR_PIPELINE:
        # sampling rate and audio data
        #sr, y = audio_in
        #y2 = y.astype(np.float32)
        #y2 /= np.max(np.abs(y))
        #response = ASR_PIPELINE({"sampling_rate": sr, "raw": y})
        response = ASR_PIPELINE(audio_in)
    else:
        # Option2: serverless API
        with open(audio_in, "rb") as f:
            data = f.read()
        response = requests.post(ASR_API_URL, headers=ASR_API_HEADERS, data=data)
        response = response.json()
    
    #print(response)
    return response['text']


def transcribe_and_respond(audio_in, system_txtbox, user_msg_txb, *args):
    user_message = transcribe(audio_in)
    outputs = respond(system_txtbox, user_message, *args)
    return outputs


OPENAI_CLIENT = None
INITIAL_VOICE = "nova"


with gr.Blocks() as demo:
    # aqui, é resetado e instanciado o cliente
    initial_chat_history, initial_audio = reset_and_apply(os.environ['OPENAI_API_KEY'], INITIAL_VOICE)

    chatbot_area = gr.Chatbot(value=initial_chat_history)
    audio_out = gr.Audio(label="Escute a última mensagem", value=initial_audio, autoplay=True, interactive=False)
    
    user_msg_txb = gr.Textbox(label="Mensagem")
    #if USE_LOCAL_ASR_PIPELINE:
    #    audio_in = gr.Audio(label="Mensagem de Áudio", sources=['microphone'], interactive=True, type='numpy')
    #else:
    #    audio_in = gr.Audio(label="Mensagem de Áudio", sources=['microphone'], interactive=True, type='filepath')
    audio_in = gr.Audio(label="Mensagem de Áudio", sources=['microphone'], interactive=True, type='filepath')
    
    submit_btn = gr.Button("Enviar")
    
    #clear_btn = gr.ClearButton(components=[user_msg, chatbot], value="Clear console")
    reset_btn = gr.Button("Reiniciar")
    
    with gr.Accordion(label="Configurações",open=False):
        if LOAD_KEYS_FROM_FILES:
            openai_key = gr.Textbox(label="OPENAI API Key", value=os.environ['OPENAI_API_KEY'])
        else:
            openai_key = gr.Textbox(label="OPENAI API Key", placeholder="Insert your API key here")
        openai_key.change(reset_openai_client, inputs=[openai_key])

        # opções de vozes e personalidades
        voice_ddown = gr.Dropdown(label="Personalidade (muda os dois abaixo)", choices=["nova", "echo"], value=INITIAL_VOICE)

        initial_system_message, initial_temperature = on_voice_change(INITIAL_VOICE)        
        temperature_sldr = gr.Slider(label="Diversidade de respostas", minimum=0.0, maximum=1.0, value=initial_temperature, step=0.1)
        
        with gr.Accordion(label="Avançado",open=False):
            # o valor inicial é dado pela system message com o nome e personalidade dados pelos controles acima
            system_txtbox = gr.Textbox(label="System message", lines=3, value=initial_system_message)

        voice_ddown.change(on_voice_change, inputs=[voice_ddown], outputs=[system_txtbox, temperature_sldr])
        #gr.Markdown("*Clique em 'Reiniciar' para aplicar as (a maior parte das) configurações.*")

    reset_btn.click(reset_and_apply, inputs=[openai_key, voice_ddown], outputs=[chatbot_area, audio_out])

    #audio_in.stop_recording( transcribe, inputs=[audio_in], outputs=[user_msg_txb] )
    audio_in.stop_recording( transcribe_and_respond, inputs=[audio_in, system_txtbox, user_msg_txb, chatbot_area, temperature_sldr, voice_ddown], outputs=[user_msg_txb, chatbot_area, audio_out] )
    submit_btn.click(respond, inputs=[system_txtbox, user_msg_txb, chatbot_area, temperature_sldr, voice_ddown], outputs=[user_msg_txb, chatbot_area, audio_out])    # Click on the button
    user_msg_txb.submit(respond, inputs=[system_txtbox, user_msg_txb, chatbot_area, temperature_sldr, voice_ddown], outputs=[user_msg_txb, chatbot_area, audio_out]) # Press enter to submit - same effect


demo.queue().launch(share=False)