File size: 7,723 Bytes
b650615
 
 
 
 
 
 
 
 
d1f824d
 
 
 
b650615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ad621f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441df3d
b650615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ad621f
b650615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ad621f
b650615
6ad621f
441df3d
6ad621f
441df3d
6ad621f
b650615
 
 
 
 
 
6ad621f
b650615
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ad621f
 
 
5f1f1e4
 
 
 
 
b650615
 
 
441df3d
b650615
 
 
 
 
 
 
 
 
 
 
6ad621f
b650615
 
 
 
 
6ad621f
b650615
 
 
 
 
d1f824d
6f79cc4
 
6ad621f
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import os
import sys
import tempfile
import time
import urllib.parse
import webbrowser
import gradio as gr
from dotenv import load_dotenv

# global variables
SERVER_NAME = "0.0.0.0"
SERVER_PORT = 7860

# Patch para compatibilidade com Python 3.13+
if sys.version_info >= (3, 13):
    import types
    sys.modules['aifc'] = types.ModuleType('aifc')
    sys.modules['audioop'] = types.ModuleType('audioop')

# Lazy imports para o Whisper e gTTS
whisper_model = None

def get_whisper_model():
    global whisper_model
    if whisper_model is None:
        import whisper
        print("Carregando modelo Whisper (Local e Gratuito)...")
        whisper_model = whisper.load_model("base")
    return whisper_model

def get_glm_response(text):    
    max_retries = 3
    retry_delay = 2 # segundos
    
    for attempt in range(max_retries):
        try:
            from openai import OpenAI
            
            hf_token = os.getenv("HF_TOKEN")
            if not hf_token or hf_token == "seu_token_hf_aqui":
                return None
                
            client = OpenAI(
                base_url="https://router.huggingface.co/v1",
                api_key=hf_token,
                timeout=30.0 # Timeout de 30 segundos
            )
            
            response = client.chat.completions.create(
                model="zai-org/GLM-4.7-Flash",
                messages=[
                    {"role": "system", "content": "Você é um assistente virtual útil e conciso. Responda em português."},
                    {"role": "user", "content": text}
                ]
            )
            return response.choices[0].message.content
        except Exception as e:
            error_str = str(e)
            print(f"Tentativa {attempt + 1} falhou: {error_str}")
            
            # Se for erro de timeout ou gateway (504/502/503), tenta novamente
            if any(code in error_str for code in ["504", "502", "503", "timeout"]) and attempt < max_retries - 1:
                time.sleep(retry_delay * (attempt + 1))
                continue
                
            # Limpa o erro se for HTML bruto (Hugging Face costuma retornar HTML em erros de gateway)
            if "<!DOCTYPE html>" in error_str or "<html>" in error_str:
                if "504" in error_str:
                    return "Erro: O servidor da IA demorou muito para responder (Timeout). Por favor, tente novamente em instantes."
                return "Erro: O servidor da IA está temporariamente indisponível. Tente novamente mais tarde."
                
            return f"Erro na IA: {error_str}"
    return "Erro: Não foi possível obter resposta da IA após várias tentativas."

def try_local_commands(text):
    s = (text or "").lower()
    if "wikipedia" in s:
        query = s.replace("wikipedia", "").replace("pesquisar", "").strip()
        if not query:
            return "O que devo pesquisar na Wikipedia?"
        url = "https://pt.wikipedia.org/wiki/Special:Search?search=" + urllib.parse.quote_plus(query)
        webbrowser.open(url)
        return f"Pesquisando '{query}' na Wikipedia."
        
    if "youtube" in s or "vídeo" in s or "video" in s:
        query = s.replace("youtube", "").replace("vídeo", "").replace("video", "").replace("pesquisar", "").strip()
        if not query:
            return "O que devo pesquisar no YouTube?"
        url = "https://www.youtube.com/results?search_query=" + urllib.parse.quote_plus(query)
        webbrowser.open(url)
        return f"Pesquisando '{query}' no YouTube."
        
    if "farmácia" in s or "farmacia" in s:
        webbrowser.open("https://www.google.com/maps/search/farmacia+perto+de+mim")
        return "Abrindo mapa de farmácias próximas."

    return None

def text_to_speech(text):
    try:
        from gtts import gTTS
        tts = gTTS(text=text, lang='pt')
        temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3")
        tts.save(temp_file.name)
        return temp_file.name
    except Exception as e:
        print(f"Erro TTS: {e}")
        return None

def process_interaction(audio_path, text_input, history):
    # Inicializar histórico se for None
    if history is None:
        history = []
        
    # Determinar a entrada (áudio ou texto)
    input_text = ""
    
    try:
        if audio_path:
            print(f"Processando áudio de: {audio_path}")
            model = get_whisper_model()
            result = model.transcribe(audio_path, language="pt", fp16=False)
            input_text = result["text"].strip()
            print(f"Transcrição Whisper: {input_text}")
        elif text_input:
            input_text = text_input
            print(f"Entrada de texto: {input_text}")
        
        if not input_text:
            return history, "", gr.update()

        # Processar comando local primeiro
        response_text = try_local_commands(input_text)
        
        # Se não for comando local, tentar IA GLM
        if response_text is None:
            response_text = get_glm_response(input_text)
            
            # Se a IA não estiver configurada (sem token), apenas confirma o que ouviu
            if response_text is None:
                response_text = f"Você disse: {input_text}. (Comando não reconhecido e IA não configurada)"
        
        print(f"Resposta: {response_text}")

        # Gerar áudio
        audio_response = text_to_speech(response_text)
        
        # Atualizar histórico
        history.append({"role": "user", "content": input_text})
        history.append({"role": "assistant", "content": response_text})
        
        return history, "", audio_response if audio_response else gr.update()

    except Exception as e:
        error_msg = f"Erro no processamento: {str(e)}"
        print(error_msg)
        history.append({"role": "user", "content": input_text if input_text else "???"})
        history.append({"role": "assistant", "content": error_msg})
        return history, "", gr.update()

def main():
    load_dotenv()
    
    with gr.Blocks(title="Assistente Virtual") as demo:
        gr.Markdown("# 🤖 Assistente Virtual com IA")
        gr.Markdown("Este assistente usa **OpenAI Whisper** para voz e **GLM-4.7-Flash** para inteligência via Hugging Face.")
        
        gr.Markdown("### 🎤 Comandos Disponíveis:")
        gr.Markdown("- 'Pesquisar Wikipedia sobre [assunto]'")
        gr.Markdown("- 'Abrir YouTube' ou 'Vídeo de [assunto]'")
        gr.Markdown("- 'Farmácia próxima'")
        
        with gr.Row():
            with gr.Column(scale=2):
                chatbot = gr.Chatbot(label="Conversa")
                audio_output = gr.Audio(label="Resposta em Áudio", autoplay=True)
            
            with gr.Column(scale=1):
                audio_input = gr.Audio(label="Fale aqui", type="filepath")
                text_input = gr.Textbox(label="Ou digite aqui", placeholder="Ex: Pesquisar Wikipedia sobre Python")
                btn_send = gr.Button("Enviar", variant="primary")
                btn_clear = gr.Button("Limpar Conversa")

        # Eventos
        btn_send.click(
            process_interaction, 
            inputs=[audio_input, text_input, chatbot], 
            outputs=[chatbot, text_input, audio_output]
        )
        
        text_input.submit(
            process_interaction, 
            inputs=[audio_input, text_input, chatbot], 
            outputs=[chatbot, text_input, audio_output]
        )

        btn_clear.click(lambda: ([], "", gr.update(value=None)), None, [chatbot, text_input, audio_output])

    demo.launch(server_name=SERVER_NAME, server_port=SERVER_PORT, share=True)

if __name__ == "__main__":
    main()