import os import io import sys import asyncio import random import logging import requests import chromadb import gradio as gr import pytesseract import edge_tts from PIL import Image from pdf2image import convert_from_path from pathlib import Path # ========================= # CONFIGURAÇÕES E ESTILO # ========================= sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") GROQ_API_KEY = os.getenv("GROQ_API_KEY") GROQ_URL = "https://api.groq.com/openai/v1/chat/completions" CHROMA_PATH = Path("./chroma_arabic_tutor") DOCS_PATH = Path("./docs_arabic") DOCS_PATH.mkdir(parents=True, exist_ok=True) # CSS para organizar o layout e destacar o Árabe custom_css = """ .gradio-container { background-color: #f8fafc; } .arabic-display { font-size: 32px !important; line-height: 1.6; direction: rtl; text-align: right; background: #fff; padding: 15px; border-radius: 8px; border: 1px solid #e2e8f0; } .explanation-box { margin-bottom: 20px; padding: 10px; border-left: 4px solid #0369a1; } """ # ========================= # AUTO-INDEXAÇÃO (RAG) # ========================= chroma_client = chromadb.PersistentClient(path=str(CHROMA_PATH)) collection = chroma_client.get_or_create_collection(name="arabic_lessons") def auto_indexar(): if collection.count() > 0: return f"✅ Biblioteca Ativa: {collection.count()} trechos." pdfs = list(DOCS_PATH.glob("*.pdf")) if not pdfs: return "⚠️ Pasta 'docs_arabic' vazia." for pdf in pdfs: try: images = convert_from_path(pdf, last_page=20) for i, image in enumerate(images): text = pytesseract.image_to_string(image, lang='ara+eng') if text and len(text.strip()) > 50: chunks = [text[j:j+1000] for j in range(0, len(text), 800)] ids = [f"{pdf.name}_p{i}_{k}" for k in range(len(chunks))] collection.add(ids=ids, documents=chunks) except Exception as e: print(f"Erro OCR: {e}") return f"✅ Pronta: {collection.count()} trechos." status_inicial = auto_indexar() # ========================= # MOTOR DE ÁUDIO PREMIUM (EDGE-TTS) # ========================= async def gerar_audio_premium(texto): # Selecionamos uma voz árabe natural (Fatima ou Hamed) voice = "ar-SA-ZariyahNeural" output_path = "pronuncia_premium.mp3" communicate = edge_tts.Communicate(texto, voice) await communicate.save(output_path) return output_path # ========================= # MOTOR DO TUTOR # ========================= def tutor_engine(mensagem, historico): if not GROQ_API_KEY: return historico + [["Erro", "Configure a GROQ_API_KEY."]], "", None contexto = "" if collection.count() > 0: busca = collection.query(query_texts=[mensagem], n_results=3) contexto = "\n".join(busca['documents'][0]) # Prompt instruindo organização visual clara prompt_sistema = ( "Você é Al-Mu'allim. Responda com organização visual impecável em Markdown.\n" "Use títulos (###), listas e negrito. Separe a parte árabe da explicação.\n" "IMPORTANTE: Não retorne códigos JSON, colchetes ou termos como {'text': }.\n" f"CONTEXTO TÉCNICO:\n{contexto}" ) api_msgs = [{"role": "system", "content": prompt_sistema}] for u, b in historico: api_msgs.append({"role": "user", "content": u}) api_msgs.append({"role": "assistant", "content": b}) api_msgs.append({"role": "user", "content": mensagem}) try: r = requests.post(GROQ_URL, headers={"Authorization": f"Bearer {GROQ_API_KEY}"}, json={"model": "llama-3.1-8b-instant", "messages": api_msgs, "temperature": 0.3}) resposta = r.json()['choices'][0]['message']['content'] # Limpeza de caracteres indesejados caso a IA os gere resposta = resposta.replace("{'text':", "").replace("}", "").strip() except: resposta = "Houve um erro na comunicação." # Gera o áudio de alta qualidade de forma assíncrona audio_path = asyncio.run(gerar_audio_premium(resposta)) historico.append((mensagem, resposta)) return historico, "", audio_path # ========================= # INTERFACE ORGANIZADA # ========================= with gr.Blocks(css=custom_css) as demo: gr.Markdown("## 🌙 Al-Mu'allim: Tutor de Árabe Premium") with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot(label="Aula e Explicações", height=500) with gr.Row(): msg = gr.Textbox(placeholder="Sua pergunta aqui...", scale=4, show_label=False) btn = gr.Button("Enviar", variant="primary", scale=1) with gr.Column(scale=1): gr.Markdown("### 🔊 Pronúncia Nativa") audio_out = gr.Audio(label="Clique para ouvir", autoplay=True) gr.Markdown("---") gr.Markdown(f"**Status da Biblioteca:**\n{status_inicial}") btn.click(tutor_engine, [msg, chatbot], [chatbot, msg, audio_out]) msg.submit(tutor_engine, [msg, chatbot], [chatbot, msg, audio_out]) if __name__ == "__main__": demo.launch()