Spaces:
Sleeping
Sleeping
import gradio as gr | |
from transformers import AutoTokenizer, AutoModelForQuestionAnswering | |
import torch | |
import logging | |
import warnings | |
from typing import List, Tuple, Dict, Optional | |
import random | |
from datetime import datetime | |
from dataclasses import dataclass | |
from enum import Enum | |
from difflib import SequenceMatcher | |
import numpy as np | |
import re | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
warnings.filterwarnings('ignore') | |
class ThemeType(Enum): | |
MARRIAGE = "casamento" | |
FAMILY = "familia" | |
SPIRITUAL = "vida_espiritual" | |
WORK = "trabalho" | |
RELATIONSHIPS = "relacionamentos" | |
GENERAL = "geral" | |
class BiblicalExample: | |
question: str | |
passage: str | |
text: str | |
base_response: str | |
application: str | |
sentiment: str | |
keywords: List[str] = None | |
theme: str = "" | |
def __post_init__(self): | |
if self.keywords is None: | |
self.keywords = self.extract_keywords() | |
def extract_keywords(self) -> List[str]: | |
"""Extract meaningful keywords from question and response.""" | |
text = f"{self.question} {self.base_response}" | |
words = text.lower().split() | |
stop_words = {'a', 'o', 'e', 'de', 'do', 'da', 'em', 'para', 'com', 'um', 'uma'} | |
keywords = [word for word in words if len(word) > 3 and word not in stop_words] | |
return list(set(keywords)) | |
class TextProcessor: | |
def normalize_text(text: str) -> str: | |
text = re.sub(r'[^\w\s]', ' ', text) | |
text = ' '.join(text.lower().split()) | |
return text | |
def extract_main_concepts(text: str) -> List[str]: | |
stop_words = {'a', 'o', 'e', 'de', 'do', 'da', 'em', 'para', 'com', 'um', 'uma', 'que', 'como'} | |
words = TextProcessor.normalize_text(text).split() | |
concepts = [word for word in words if word not in stop_words and len(word) > 3] | |
return list(set(concepts)) | |
class ResponseValidator: | |
def __init__(self): | |
self.text_processor = TextProcessor() | |
self.similarity_threshold = 0.3 | |
self.keyword_threshold = 0.2 | |
self.concept_match_threshold = 0.25 | |
def calculate_text_similarity(self, text1: str, text2: str) -> float: | |
normalized_text1 = self.text_processor.normalize_text(text1) | |
normalized_text2 = self.text_processor.normalize_text(text2) | |
return SequenceMatcher(None, normalized_text1, normalized_text2).ratio() | |
def calculate_keyword_similarity(self, text1: str, text2: str) -> float: | |
keywords1 = set(self.text_processor.extract_main_concepts(text1)) | |
keywords2 = set(self.text_processor.extract_main_concepts(text2)) | |
if not keywords1 or not keywords2: | |
return 0.0 | |
return len(keywords1.intersection(keywords2)) / len(keywords1.union(keywords2)) | |
def calculate_concept_similarity(self, question: str, example: BiblicalExample) -> float: | |
question_concepts = set(self.text_processor.extract_main_concepts(question)) | |
example_concepts = set(example.keywords) | |
if not question_concepts or not example_concepts: | |
return 0.0 | |
return len(question_concepts.intersection(example_concepts)) / len(question_concepts.union(example_concepts)) | |
def validate_response(self, question: str, example: BiblicalExample) -> Tuple[bool, Dict[str, float]]: | |
text_similarity = self.calculate_text_similarity(question, example.question) | |
keyword_similarity = self.calculate_keyword_similarity(question, example.question) | |
concept_similarity = self.calculate_concept_similarity(question, example) | |
weights = {'text': 0.4, 'keyword': 0.3, 'concept': 0.3} | |
total_score = ( | |
text_similarity * weights['text'] + | |
keyword_similarity * weights['keyword'] + | |
concept_similarity * weights['concept'] | |
) | |
metrics = { | |
'text_similarity': round(text_similarity, 3), | |
'keyword_similarity': round(keyword_similarity, 3), | |
'concept_similarity': round(concept_similarity, 3), | |
'total_score': round(total_score, 3) | |
} | |
is_valid = ( | |
text_similarity >= self.similarity_threshold or | |
keyword_similarity >= self.keyword_threshold or | |
concept_similarity >= self.concept_match_threshold | |
) | |
return is_valid, metrics | |
class SapienciaBiblica: | |
def __init__(self): | |
logger.info("Inicializando SapiênciaBíblica...") | |
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
self.model_name = "pierreguillou/bert-base-cased-squad-v1.1-portuguese" | |
self.session_history = [] | |
self.biblical_examples = self.get_default_examples_dict() | |
self.validator = ResponseValidator() | |
self.setup_model() | |
def get_default_examples_dict(self) -> Dict[str, List[BiblicalExample]]: | |
"""Return default biblical examples for each theme.""" | |
return { | |
"casamento": [ | |
BiblicalExample( | |
question="Como melhorar a comunicação no casamento?", | |
passage="Efésios 4:29", | |
text="Não saia da vossa boca nenhuma palavra torpe, mas só a que for boa para promover a edificação, para que dê graça aos que a ouvem.", | |
base_response="A comunicação efetiva no casamento requer sabedoria, paciência e amor. A Bíblia nos ensina a usar palavras que edificam e não destroem.", | |
application="Pratique escuta ativa, escolha momentos adequados para conversas importantes, e sempre fale com amor e respeito.", | |
sentiment="supportive", | |
theme="casamento" | |
), | |
BiblicalExample( | |
question="Como resolver conflitos conjugais?", | |
passage="Efésios 4:26-27", | |
text="Não se ponha o sol sobre a vossa ira. Não deis lugar ao diabo.", | |
base_response="Os conflitos devem ser resolvidos rapidamente, com amor e sabedoria, não permitindo que a raiva permaneça.", | |
application="Resolva os conflitos no mesmo dia, pratique o perdão e busque entender o ponto de vista do cônjuge.", | |
sentiment="instructive", | |
theme="casamento" | |
) | |
], | |
"familia": [ | |
BiblicalExample( | |
question="Como educar filhos biblicamente?", | |
passage="Provérbios 22:6", | |
text="Instrui o menino no caminho em que deve andar, e até quando envelhecer não se desviará dele.", | |
base_response="A educação dos filhos deve ser baseada em princípios bíblicos, com amor e disciplina consistente.", | |
application="Estabeleça rotinas de devocional em família, seja exemplo de caráter e aplique disciplina com amor.", | |
sentiment="instructive", | |
theme="familia" | |
) | |
], | |
"vida_espiritual": [ | |
BiblicalExample( | |
question="Como desenvolver uma vida de oração?", | |
passage="1 Tessalonicenses 5:17", | |
text="Orai sem cessar.", | |
base_response="A vida de oração se desenvolve através da prática constante e sincera.", | |
application="Reserve um tempo diário para oração, mantenha um diário de oração e pratique a gratidão.", | |
sentiment="spiritual", | |
theme="vida_espiritual" | |
) | |
], | |
"trabalho": [ | |
BiblicalExample( | |
question="Como ter integridade no trabalho?", | |
passage="Colossenses 3:23", | |
text="E tudo quanto fizerdes, fazei-o de coração, como ao Senhor.", | |
base_response="A integridade no trabalho significa fazer o melhor possível, como se trabalhássemos diretamente para Deus.", | |
application="Seja honesto, pontual e dedicado em suas responsabilidades profissionais.", | |
sentiment="professional", | |
theme="trabalho" | |
) | |
], | |
"relacionamentos": [ | |
BiblicalExample( | |
question="Como construir amizades verdadeiras?", | |
passage="Provérbios 17:17", | |
text="Em todo tempo ama o amigo, e na angústia nasce o irmão.", | |
base_response="Amizades verdadeiras são construídas com amor, lealdade e presença constante.", | |
application="Seja presente, demonstre interesse genuíno e apoie seus amigos nos momentos difíceis.", | |
sentiment="friendly", | |
theme="relacionamentos" | |
) | |
], | |
"geral": [ | |
BiblicalExample( | |
question="Como encontrar paz em tempos difíceis?", | |
passage="João 14:27", | |
text="Deixo-vos a paz, a minha paz vos dou; não vo-la dou como o mundo a dá. Não se turbe o vosso coração, nem se atemorize.", | |
base_response="A verdadeira paz vem de Deus e permanece mesmo em meio às tribulações.", | |
application="Mantenha seu foco em Deus, pratique a gratidão e confie em Suas promessas.", | |
sentiment="comforting", | |
theme="geral" | |
), | |
BiblicalExample( | |
question="Como tomar decisões sábias?", | |
passage="Tiago 1:5", | |
text="E, se algum de vós tem falta de sabedoria, peça-a a Deus, que a todos dá liberalmente.", | |
base_response="A sabedoria verdadeira vem de Deus e está disponível para todos que a pedem com fé.", | |
application="Ore pedindo sabedoria, busque conselho de pessoas piedosas e avalie as decisões à luz da Palavra.", | |
sentiment="instructive", | |
theme="geral" | |
) | |
] | |
} | |
def setup_model(self): | |
try: | |
self.tokenizer = AutoTokenizer.from_pretrained(self.model_name) | |
self.model = AutoModelForQuestionAnswering.from_pretrained(self.model_name) | |
self.model.to(self.device) | |
logger.info(f"Modelo carregado com sucesso no dispositivo: {self.device}") | |
except Exception as e: | |
logger.error(f"Erro ao carregar modelo: {str(e)}") | |
raise | |
def get_unique_response(self, question: str, theme: str = None) -> Tuple[str, Dict, str]: | |
"""Generate a unique response for each question.""" | |
if not question.strip(): | |
return "Por favor, faça uma pergunta específica.", {}, self.format_history() | |
if not theme or theme not in self.biblical_examples: | |
theme = self.find_best_theme(question) | |
try: | |
# Encontrar exemplo base mais relevante | |
best_example, validation_metrics = self.find_best_example(question, theme) | |
if not best_example: | |
return self.generate_fallback_response(question, theme), { | |
"theme": theme, | |
"status": "no_matching_example" | |
}, self.format_history() | |
# Gerar resposta personalizada usando BERT | |
context = f"{best_example.text} {best_example.application}" | |
inputs = self.tokenizer( | |
question, | |
context, | |
return_tensors="pt", | |
max_length=512, | |
truncation=True, | |
padding=True | |
).to(self.device) | |
# Obter resposta do modelo | |
with torch.no_grad(): | |
outputs = self.model(**inputs) | |
start_scores = outputs.start_logits | |
end_scores = outputs.end_logits | |
start_idx = torch.argmax(start_scores) | |
end_idx = torch.argmax(end_scores) | |
# Extrair resposta gerada | |
answer_tokens = inputs["input_ids"][0][start_idx:end_idx + 1] | |
generated_answer = self.tokenizer.decode(answer_tokens) | |
# Gerar reflexão específica | |
reflection = self.generate_specific_reflection(question, best_example) | |
# Formatar resposta final | |
sentiment = self.analyze_sentiment(question) | |
final_response = self.format_customized_response( | |
question=question, | |
generated_answer=generated_answer, | |
reflection=reflection, | |
example=best_example, | |
sentiment=sentiment | |
) | |
# Preparar metadata | |
metadata = self.create_metadata(best_example, theme, validation_metrics) | |
metadata.update({ | |
"response_type": "generated", | |
"generation_success": True | |
}) | |
# Salvar no histórico | |
history = self.save_to_history(question, theme, final_response, metadata) | |
return final_response, metadata, history | |
except Exception as e: | |
logger.error(f"Erro na geração de resposta: {str(e)}") | |
return self.generate_fallback_response(question, theme), { | |
"theme": theme, | |
"status": "generation_error" | |
}, self.format_history() | |
def format_customized_response(self, question: str, generated_answer: str, reflection: str, | |
example: BiblicalExample, sentiment: str) -> str: | |
"""Format a unique response with generated content and biblical guidance.""" | |
intro = { | |
'positive': "Que bom que você está buscando orientação! ", | |
'negative': "Entendo seu momento e estou aqui para ajudar. ", | |
'neutral': "Agradeço sua busca por sabedoria. " | |
} | |
# Limpar e validar a resposta gerada | |
generated_answer = generated_answer.strip() | |
if len(generated_answer) < 10: # Resposta muito curta, usar base | |
generated_answer = example.base_response | |
return f""" | |
🌟 Orientação Personalizada: | |
{intro[sentiment]}{generated_answer} | |
📖 Passagem Bíblica: | |
{example.passage}: {example.text} | |
✨ Aplicação Prática: | |
{example.application} | |
💭 Reflexão Específica para Sua Situação: | |
{reflection} | |
🙏 Observação: | |
Esta orientação é baseada em princípios bíblicos. Para questões específicas, | |
considere consultar sua liderança espiritual local. | |
""" | |
def generate_specific_reflection(self, question: str, example: BiblicalExample) -> str: | |
"""Generate a specific reflection based on the question and biblical context.""" | |
try: | |
context = f"{question} {example.text} {example.application}" | |
inputs = self.tokenizer( | |
"Como aplicar este princípio bíblico nesta situação específica?", | |
context, | |
return_tensors="pt", | |
max_length=512, | |
truncation=True | |
).to(self.device) | |
with torch.no_grad(): | |
outputs = self.model(**inputs) | |
start_idx = torch.argmax(outputs.start_logits) | |
end_idx = torch.argmax(outputs.end_logits) | |
tokens = self.tokenizer.convert_ids_to_tokens(inputs["input_ids"][0][start_idx:end_idx+1]) | |
reflection = self.tokenizer.convert_tokens_to_string(tokens) | |
if len(reflection.strip()) < 10: | |
return "Aplique estes princípios bíblicos em sua situação específica, buscando sabedoria em oração." | |
return reflection.strip() | |
except Exception as e: | |
logger.error(f"Erro ao gerar reflexão: {str(e)}") | |
return "Reflita sobre como aplicar estes princípios em sua vida, buscando a direção de Deus." | |
def find_best_theme(self, question: str) -> str: | |
"""Find the most relevant theme for the question.""" | |
question = question.lower() | |
theme_keywords = { | |
"casamento": ["casamento", "cônjuge", "esposa", "marido", "casal"], | |
"familia": ["família", "filhos", "pais", "criação", "lar"], | |
"vida_espiritual": ["oração", "jejum", "adoração", "espiritual", "fé"], | |
"trabalho": ["trabalho", "emprego", "carreira", "profissão"], | |
"relacionamentos": ["amizade", "relacionamento", "conflito", "perdão"], | |
} | |
max_matches = 0 | |
best_theme = "geral" | |
# Analisar similaridade com cada tema | |
for theme, keywords in theme_keywords.items(): | |
matches = sum(1 for keyword in keywords if keyword in question) | |
similarity_score = self.validator.calculate_text_similarity( | |
question, | |
' '.join(keywords) | |
) | |
total_score = matches + (similarity_score * 2) | |
if total_score > max_matches: | |
max_matches = total_score | |
best_theme = theme | |
return best_theme | |
def find_best_example(self, question: str, theme: str) -> Tuple[Optional[BiblicalExample], Dict[str, float]]: | |
"""Find the most relevant example and validation metrics.""" | |
examples = self.biblical_examples.get(theme, self.biblical_examples["geral"]) | |
best_score = 0 | |
best_example = None | |
best_metrics = {} | |
for example in examples: | |
is_valid, metrics = self.validator.validate_response(question, example) | |
if is_valid and metrics['total_score'] > best_score: | |
best_score = metrics['total_score'] | |
best_example = example | |
best_metrics = metrics | |
return best_example, best_metrics | |
def analyze_sentiment(self, text: str) -> str: | |
"""Analyze the sentiment of the input text.""" | |
positive_words = {'alegria', 'esperança', 'paz', 'amor', 'gratidão', 'feliz', 'bem'} | |
negative_words = {'tristeza', 'medo', 'ansiedade', 'preocupação', 'angústia', 'mal', 'dor'} | |
text_words = set(text.lower().split()) | |
pos_count = len(text_words.intersection(positive_words)) | |
neg_count = len(text_words.intersection(negative_words)) | |
return 'positive' if pos_count > neg_count else 'negative' if neg_count > pos_count else 'neutral' | |
def generate_fallback_response(self, question: str, theme: str) -> str: | |
"""Generate a thoughtful fallback response.""" | |
theme_verses = { | |
"casamento": ("Efésios 5:25", "Maridos, amai vossas mulheres, como também Cristo amou a igreja..."), | |
"familia": ("Salmos 127:3", "Eis que os filhos são herança do Senhor..."), | |
"vida_espiritual": ("Filipenses 4:6", "Não andeis ansiosos por coisa alguma..."), | |
"trabalho": ("Colossenses 3:23", "E tudo quanto fizerdes, fazei-o de coração..."), | |
"relacionamentos": ("João 13:34", "Um novo mandamento vos dou: Que vos ameis uns aos outros..."), | |
"geral": ("Provérbios 3:5-6", "Confia no Senhor de todo o teu coração...") | |
} | |
verse = theme_verses.get(theme, theme_verses["geral"]) | |
return f""" | |
🤔 Compreendo sua busca por orientação sobre {theme}... | |
Para melhor atendê-lo, considere reformular sua pergunta de forma mais específica, | |
mencionando detalhes da situação que você gostaria de abordar. | |
📖 Enquanto isso, reflita sobre esta passagem: | |
{verse[0]}: "{verse[1]}" | |
🙏 Lembre-se: | |
"Porque o SENHOR dá a sabedoria, e da sua boca vem o conhecimento e o entendimento." | |
- Provérbios 2:6 | |
""" | |
def create_metadata(self, example: BiblicalExample, theme: str, validation_metrics: Dict[str, float]) -> Dict: | |
"""Create comprehensive metadata for the response.""" | |
return { | |
"theme": theme, | |
"passage": example.passage, | |
"application": example.application, | |
"sentiment": example.sentiment, | |
"validation_metrics": validation_metrics, | |
"keywords_matched": example.keywords, | |
"response_quality": "high" if validation_metrics['total_score'] > 0.6 else "medium" if validation_metrics['total_score'] > 0.4 else "low" | |
} | |
def save_to_history(self, question: str, theme: str, response: str, metadata: Dict) -> str: | |
"""Save interaction to history and return formatted history.""" | |
self.session_history.append({ | |
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
"theme": theme, | |
"question": question, | |
"response": response, | |
"metadata": metadata | |
}) | |
return self.format_history() | |
def format_history(self) -> str: | |
"""Format the session history for display.""" | |
if not self.session_history: | |
return "Nenhuma consulta realizada ainda." | |
history_text = "📚 Histórico de Consultas:\n\n" | |
for entry in reversed(self.session_history[-5:]): # Show last 5 entries | |
history_text += f"🕒 {entry['timestamp']}\n" | |
history_text += f"📌 Tema: {entry['theme']}\n" | |
history_text += f"❓ Pergunta: {entry['question']}\n" | |
if 'validation_metrics' in entry['metadata']: | |
history_text += f"📊 Relevância: {entry['metadata']['validation_metrics']['total_score']:.2f}\n" | |
history_text += "─" * 40 + "\n" | |
return history_text | |
def get_verse_of_day(self) -> str: | |
"""Get a random verse of the day.""" | |
verses = [ | |
("João 3:16", "Porque Deus amou o mundo de tal maneira que deu o seu Filho unigênito, para que todo aquele que nele crê não pereça, mas tenha a vida eterna."), | |
("Salmos 23:1", "O Senhor é meu pastor e nada me faltará."), | |
("Filipenses 4:13", "Posso todas as coisas em Cristo que me fortalece."), | |
("Jeremias 29:11", "Porque eu bem sei os pensamentos que tenho a vosso respeito, diz o Senhor; pensamentos de paz, e não de mal, para vos dar o fim que esperais."), | |
("Provérbios 3:5-6", "Confia no Senhor de todo o teu coração, e não te estribes no teu próprio entendimento. Reconhece-o em todos os teus caminhos, e ele endireitará as tuas veredas.") | |
] | |
verse = random.choice(verses) | |
return f"📖 Versículo do Dia:\n{verse[0]}\n\n{verse[1]}" | |
def get_daily_prayer_focus(self) -> str: | |
"""Get a random prayer focus for the day.""" | |
focuses = [ | |
("Gratidão e Louvor", "Agradecer a Deus por suas bênçãos e manifestar louvor"), | |
("Família e Relacionamentos", "Orar pela harmonia e proteção familiar"), | |
("Sabedoria e Direção", "Buscar orientação divina para decisões"), | |
("Paz e Serenidade", "Cultivar a paz interior e confiar em Deus"), | |
("Cura e Restauração", "Buscar a cura emocional e espiritual") | |
] | |
focus = random.choice(focuses) | |
return f"🙏 Foco de Oração:\n{focus[0]}\n{focus[1]}" | |
def get_examples_for_interface(self) -> List[List[str]]: | |
"""Get formatted examples for the Gradio interface.""" | |
examples = [] | |
for theme in self.biblical_examples: | |
for example in self.biblical_examples[theme]: | |
examples.append([theme, example.question]) | |
return examples | |
def create_interface(): | |
counselor = SapienciaBiblica() | |
with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
gr.Markdown(""" | |
# 🕊️ SapiênciaBíblica | |
### Orientação Divina para Vida Moderna | |
""") | |
with gr.Row(): | |
with gr.Column(): | |
verse_of_day = gr.Textbox( | |
label="📖 Versículo do Dia", | |
value=counselor.get_verse_of_day(), | |
lines=4, | |
interactive=False | |
) | |
with gr.Column(): | |
prayer_focus = gr.Textbox( | |
label="🙏 Foco de Oração", | |
value=counselor.get_daily_prayer_focus(), | |
lines=4, | |
interactive=False | |
) | |
with gr.Row(): | |
with gr.Column(): | |
theme = gr.Dropdown( | |
choices=[t.value for t in ThemeType], | |
label="🎯 Tema (Opcional)", | |
value="geral", | |
info="Selecione um tema ou deixe em automático" | |
) | |
question = gr.Textbox( | |
label="❓ Sua Pergunta", | |
placeholder="Digite sua pergunta sobre qualquer tema bíblico...", | |
lines=3 | |
) | |
submit_btn = gr.Button("🙏 Buscar Orientação", variant="primary") | |
with gr.Column(): | |
answer_output = gr.Textbox( | |
label="✨ Orientação", | |
lines=12 | |
) | |
with gr.Accordion("📚 Detalhes"): | |
metadata_output = gr.JSON( | |
label="📋 Informações Detalhadas" | |
) | |
history_output = gr.Textbox( | |
label="📚 Histórico", | |
lines=10, | |
interactive=False | |
) | |
gr.Examples( | |
examples=counselor.get_examples_for_interface(), | |
inputs=[theme, question], | |
outputs=[answer_output, metadata_output, history_output], | |
fn=counselor.get_unique_response, | |
label="📝 Exemplos de Perguntas", | |
examples_per_page=5 | |
) | |
submit_btn.click( | |
fn=counselor.get_unique_response, | |
inputs=[question, theme], | |
outputs=[answer_output, metadata_output, history_output] | |
) | |
return demo | |
if __name__ == "__main__": | |
try: | |
logger.info("Iniciando SapiênciaBíblica...") | |
demo = create_interface() | |
demo.launch( | |
server_name="0.0.0.0", | |
share=True, | |
show_error=True, | |
server_port=7860 | |
) | |
except Exception as e: | |
logger.error(f"Erro ao iniciar aplicação: {str(e)}") | |
raise |