4 Commits
2.1 ... 2.2

Author SHA1 Message Date
Fábio Cavalcanti
69cb3b1965 personalização de comportamento e mensagens de transcrição 2025-01-07 11:36:39 -03:00
Fábio Cavalcanti
ec65839beb correção na lógica de resumos 2025-01-07 10:15:56 -03:00
Fábio Cavalcanti
abc4c4298a romeno no readme 2024-12-19 18:08:40 -03:00
Fábio Cavalcanti
161251a403 adicionado Romeno as linguagens novas 2024-12-19 18:05:59 -03:00
5 changed files with 347 additions and 145 deletions

39
main.py
View File

@@ -108,21 +108,42 @@ async def transcreve_audios(request: Request):
"source": audio_source "source": audio_source
}) })
# Carregar configurações de formatação
output_mode = get_config("output_mode", "both")
summary_header = get_config("summary_header", "🤖 *Resumo do áudio:*")
transcription_header = get_config("transcription_header", "🔊 *Transcrição do áudio:*")
character_limit = int(get_config("character_limit", "500"))
# Transcrever áudio # Transcrever áudio
storage.add_log("INFO", "Iniciando transcrição") storage.add_log("INFO", "Iniciando transcrição")
transcription_text, _ = await transcribe_audio(audio_source) transcription_text, _ = await transcribe_audio(audio_source)
# Resumir se necessário # Determinar se precisa de resumo baseado no modo de saída
summary_text = None
if output_mode in ["both", "summary_only"] or (
output_mode == "smart" and len(transcription_text) > character_limit
):
summary_text = await summarize_text_if_needed(transcription_text) summary_text = await summarize_text_if_needed(transcription_text)
# Formatar mensagem # Construir mensagem baseada no modo de saída
summary_message = ( message_parts = []
f"🤖 *Resumo do áudio:*\n\n"
f"{summary_text}\n\n" if output_mode == "smart":
f"🔊 *Transcrição do áudio:*\n\n" if len(transcription_text) > character_limit:
f"{transcription_text}\n\n" message_parts.append(f"{summary_header}\n\n{summary_text}")
f"{dynamic_settings['BUSINESS_MESSAGE']}" else:
) message_parts.append(f"{transcription_header}\n\n{transcription_text}")
else:
if output_mode in ["both", "summary_only"] and summary_text:
message_parts.append(f"{summary_header}\n\n{summary_text}")
if output_mode in ["both", "transcription_only"]:
message_parts.append(f"{transcription_header}\n\n{transcription_text}")
# Adicionar mensagem de negócio
message_parts.append(dynamic_settings['BUSINESS_MESSAGE'])
# Juntar todas as partes da mensagem
summary_message = "\n\n".join(message_parts)
# Enviar resposta # Enviar resposta
await send_message_to_whatsapp( await send_message_to_whatsapp(

View File

@@ -342,14 +342,76 @@ def manage_blocks():
else: else:
st.info("Nenhum usuário bloqueado.") st.info("Nenhum usuário bloqueado.")
# manager.py - Adicionar na seção de configurações
def message_settings_section():
st.subheader("📝 Configurações de Mensagem")
# Carregar configurações atuais
message_settings = storage.get_message_settings()
# Headers personalizados
col1, col2 = st.columns(2)
with col1:
summary_header = st.text_input(
"Cabeçalho do Resumo",
value=message_settings["summary_header"],
help="Formato do cabeçalho para o resumo do áudio"
)
with col2:
transcription_header = st.text_input(
"Cabeçalho da Transcrição",
value=message_settings["transcription_header"],
help="Formato do cabeçalho para a transcrição do áudio"
)
# Modo de saída
output_mode = st.selectbox(
"Modo de Saída",
options=["both", "summary_only", "transcription_only", "smart"],
format_func=lambda x: {
"both": "Resumo e Transcrição",
"summary_only": "Apenas Resumo",
"transcription_only": "Apenas Transcrição",
"smart": "Modo Inteligente (baseado no tamanho)"
}[x],
value=message_settings["output_mode"]
)
# Configuração do limite de caracteres (visível apenas no modo inteligente)
if output_mode == "smart":
character_limit = st.number_input(
"Limite de Caracteres para Modo Inteligente",
min_value=100,
max_value=5000,
value=int(message_settings["character_limit"]),
help="Se a transcrição exceder este limite, será enviado apenas o resumo"
)
else:
character_limit = message_settings["character_limit"]
# Botão de salvar
if st.button("💾 Salvar Configurações de Mensagem"):
try:
new_settings = {
"summary_header": summary_header,
"transcription_header": transcription_header,
"output_mode": output_mode,
"character_limit": character_limit
}
storage.save_message_settings(new_settings)
st.success("Configurações de mensagem salvas com sucesso!")
except Exception as e:
st.error(f"Erro ao salvar configurações: {str(e)}")
def manage_settings(): def manage_settings():
st.title("⚙️ Configurações") st.title("⚙️ Configurações")
st.subheader("Configurações do Sistema")
# Seção de chaves GROQ com sistema de rodízio # Criar tabs para melhor organização
st.subheader("🔑 Gerenciamento de Chaves GROQ") tab1, tab2, tab3 = st.tabs(["🔑 Chaves API", "🌐 Configurações Gerais", "📝 Formatação de Mensagens"])
# Campo para chave principal (mantendo compatibilidade) with tab1:
st.subheader("Gerenciamento de Chaves GROQ")
# Campo para gerenciamento de chaves GROQ
main_key = st.text_input( main_key = st.text_input(
"GROQ API Key Principal", "GROQ API Key Principal",
value=st.session_state.settings["GROQ_API_KEY"], value=st.session_state.settings["GROQ_API_KEY"],
@@ -396,10 +458,10 @@ def manage_settings():
st.error("Chave inválida! A chave deve começar com 'gsk_'") st.error("Chave inválida! A chave deve começar com 'gsk_'")
else: else:
st.warning("Por favor, insira uma chave válida") st.warning("Por favor, insira uma chave válida")
pass
# Outras configurações do sistema with tab2:
st.markdown("---") st.subheader("Configurações do Sistema")
st.subheader("Outras Configurações")
# Business Message # Business Message
st.text_input( st.text_input(
@@ -424,9 +486,9 @@ def manage_settings():
key="process_self_messages" key="process_self_messages"
) )
# Nova seção de configuração de idioma # Configuração de idioma
st.markdown("---") st.markdown("---")
st.subheader("🌐 Configuração de Idioma") st.subheader("🌐 Idioma")
# Dicionário de idiomas em português # Dicionário de idiomas em português
IDIOMAS = { IDIOMAS = {
@@ -439,6 +501,7 @@ def manage_settings():
"ja": "Japonês", "ja": "Japonês",
"ko": "Coreano", "ko": "Coreano",
"zh": "Chinês", "zh": "Chinês",
"ro": "Romeno",
"ru": "Russo", "ru": "Russo",
"ar": "Árabe", "ar": "Árabe",
"hi": "Hindi", "hi": "Hindi",
@@ -459,13 +522,71 @@ def manage_settings():
help="Selecione o idioma para transcrição dos áudios e geração dos resumos", help="Selecione o idioma para transcrição dos áudios e geração dos resumos",
key="transcription_language" key="transcription_language"
) )
pass
# Botão de salvar com feedback visual with tab3:
st.subheader("Formatação de Mensagens")
# Headers personalizados
col1, col2 = st.columns(2)
with col1:
summary_header = st.text_input(
"Cabeçalho do Resumo",
value=get_from_redis("summary_header", "🤖 *Resumo do áudio:*"),
key="summary_header",
help="Formato do cabeçalho para o resumo do áudio"
)
with col2:
transcription_header = st.text_input(
"Cabeçalho da Transcrição",
value=get_from_redis("transcription_header", "🔊 *Transcrição do áudio:*"),
key="transcription_header",
help="Formato do cabeçalho para a transcrição do áudio"
)
# Modo de saída - Corrigido para usar index
output_modes = ["both", "summary_only", "transcription_only", "smart"]
output_mode_labels = {
"both": "Resumo e Transcrição",
"summary_only": "Apenas Resumo",
"transcription_only": "Apenas Transcrição",
"smart": "Modo Inteligente (baseado no tamanho)"
}
current_mode = get_from_redis("output_mode", "both")
mode_index = output_modes.index(current_mode) if current_mode in output_modes else 0
output_mode = st.selectbox(
"Modo de Saída",
options=output_modes,
format_func=lambda x: output_mode_labels[x],
index=mode_index,
key="output_mode",
help="Selecione como deseja que as mensagens sejam enviadas"
)
if output_mode == "smart":
character_limit = st.number_input(
"Limite de Caracteres para Modo Inteligente",
min_value=100,
max_value=5000,
value=int(get_from_redis("character_limit", "500")),
help="Se a transcrição exceder este limite, será enviado apenas o resumo"
)
# Botão de salvar unificado
if st.button("💾 Salvar Todas as Configurações"): if st.button("💾 Salvar Todas as Configurações"):
try: try:
# Salvar configurações principais # Salvar configurações existentes
save_settings() save_settings()
# Salvar novas configurações de mensagem
save_to_redis("summary_header", summary_header)
save_to_redis("transcription_header", transcription_header)
save_to_redis("output_mode", output_mode)
if output_mode == "smart":
save_to_redis("character_limit", str(character_limit))
# Se há uma chave principal, adicionar ao sistema de rodízio # Se há uma chave principal, adicionar ao sistema de rodízio
if main_key and main_key.startswith("gsk_"): if main_key and main_key.startswith("gsk_"):
storage.add_groq_key(main_key) storage.add_groq_key(main_key)
@@ -475,10 +596,11 @@ def manage_settings():
st.success("✅ Todas as configurações foram salvas com sucesso!") st.success("✅ Todas as configurações foram salvas com sucesso!")
# Mostrar resumo das chaves ativas e idioma selecionado # Mostrar resumo
total_keys = len(storage.get_groq_keys()) total_keys = len(storage.get_groq_keys())
st.info(f"""Sistema configurado com {total_keys} chave(s) GROQ no rodízio st.info(f"""Sistema configurado com {total_keys} chave(s) GROQ no rodízio
Idioma definido: {IDIOMAS[selected_language]}""") Idioma definido: {IDIOMAS[selected_language]}
Modo de saída: {output_mode_labels[output_mode]}""")
except Exception as e: except Exception as e:
st.error(f"Erro ao salvar configurações: {str(e)}") st.error(f"Erro ao salvar configurações: {str(e)}")

View File

@@ -241,16 +241,22 @@ O TranscreveZAP agora suporta transcrição e resumo em múltiplos idiomas. Na s
3. A mudança de idioma é aplicada instantaneamente após salvar 3. A mudança de idioma é aplicada instantaneamente após salvar
Idiomas suportados: Idiomas suportados:
- 🇧🇷 Português (padrão) - 🇩🇪 Alemão
- 🇺🇸 Inglês - 🇸🇦 Árabe
- 🇨🇳 Chinês
- 🇰🇷 Coreano
- 🇪🇸 Espanhol - 🇪🇸 Espanhol
- 🇫🇷 Francês - 🇫🇷 Francês
- 🇩🇪 Alemão - 🇮🇳 Hindi
- 🇳🇱 Holandês
- 🇬🇧 Inglês
- 🇮🇹 Italiano - 🇮🇹 Italiano
- 🇯🇵 Japonês - 🇯🇵 Japonês
- 🇰🇷 Coreano - 🇵🇱 Polonês
- 🇨🇳 Chinês - 🇧🇷 Português (padrão)
- 🇷🇴 Romeno
- 🇷🇺 Russo - 🇷🇺 Russo
- 🇹🇷 Turco
## 🔄 Sistema de Rodízio de Chaves GROQ ## 🔄 Sistema de Rodízio de Chaves GROQ
O TranscreveZAP agora suporta múltiplas chaves GROQ com sistema de rodízio automático para melhor distribuição de carga e redundância. O TranscreveZAP agora suporta múltiplas chaves GROQ com sistema de rodízio automático para melhor distribuição de carga e redundância.

View File

@@ -118,6 +118,13 @@ async def summarize_text_if_needed(text):
请仅以摘要的形式回答,就好像是你在发送这条消息。 请仅以摘要的形式回答,就好像是你在发送这条消息。
不要问候,也不要在摘要前后写任何内容,只需用一句简短的话总结音频中所说的内容。 不要问候,也不要在摘要前后写任何内容,只需用一句简短的话总结音频中所说的内容。
""", """,
"ro": """
Înțelege contextul acestui audio și creează un rezumat foarte concis despre ce este vorba.
Acest audio a fost trimis prin WhatsApp, de cineva, către Fabio.
Scrie DOAR rezumatul audio-ului ca și cum tu ai trimite acest mesaj.
Nu saluta, nu scrie nimic înainte sau după rezumat, răspunde doar cu un rezumat concis despre ce s-a spus în audio.
""",
"ru": """ "ru": """
Поймите контекст этого аудио и сделайте очень краткое резюме, о чем идет речь. Поймите контекст этого аудио и сделайте очень краткое резюме, о чем идет речь.
Это аудио было отправлено через WhatsApp кем-то Фабио. Это аудио было отправлено через WhatsApp кем-то Фабио.
@@ -164,11 +171,21 @@ async def summarize_text_if_needed(text):
raise raise
async def transcribe_audio(audio_source, apikey=None): async def transcribe_audio(audio_source, apikey=None):
"""Transcreve áudio usando a API GROQ com sistema de rodízio de chaves""" """
Transcreve áudio usando a API GROQ com sistema de rodízio de chaves.
Args:
audio_source: Caminho do arquivo de áudio ou URL
apikey: Chave da API opcional para download de áudio
Returns:
tuple: (texto_transcrito, False)
"""
storage.add_log("INFO", "Iniciando processo de transcrição") storage.add_log("INFO", "Iniciando processo de transcrição")
url = "https://api.groq.com/openai/v1/audio/transcriptions" url = "https://api.groq.com/openai/v1/audio/transcriptions"
groq_key = await get_groq_key() groq_key = await get_groq_key()
groq_headers = {"Authorization": f"Bearer {groq_key}"} groq_headers = {"Authorization": f"Bearer {groq_key}"}
# Obter idioma configurado # Obter idioma configurado
language = redis_client.get("TRANSCRIPTION_LANGUAGE") or "pt" language = redis_client.get("TRANSCRIPTION_LANGUAGE") or "pt"
storage.add_log("DEBUG", "Idioma configurado para transcrição", { storage.add_log("DEBUG", "Idioma configurado para transcrição", {
@@ -212,20 +229,12 @@ async def transcribe_audio(audio_source, apikey=None):
async with session.post(url, headers=groq_headers, data=data) as response: async with session.post(url, headers=groq_headers, data=data) as response:
if response.status == 200: if response.status == 200:
result = await response.json() result = await response.json()
message = result.get("text", "") transcription = result.get("text", "")
storage.add_log("INFO", "Transcrição concluída com sucesso", { storage.add_log("INFO", "Transcrição concluída com sucesso", {
"text_length": len(message) "text_length": len(transcription)
}) })
is_summary = False return transcription, False
if len(message) > 1000:
storage.add_log("DEBUG", "Texto longo detectado, iniciando resumo", {
"text_length": len(message)
})
is_summary = True
message = await summarize_text_if_needed(message)
return message, is_summary
else: else:
error_text = await response.text() error_text = await response.text()
storage.add_log("ERROR", "Erro na transcrição", { storage.add_log("ERROR", "Erro na transcrição", {
@@ -244,7 +253,6 @@ async def transcribe_audio(audio_source, apikey=None):
# Limpar arquivos temporários # Limpar arquivos temporários
if isinstance(audio_source, str) and os.path.exists(audio_source): if isinstance(audio_source, str) and os.path.exists(audio_source):
os.unlink(audio_source) os.unlink(audio_source)
async def send_message_to_whatsapp(server_url, instance, apikey, message, remote_jid, message_id): async def send_message_to_whatsapp(server_url, instance, apikey, message, remote_jid, message_id):
"""Envia mensagem via WhatsApp""" """Envia mensagem via WhatsApp"""
storage.add_log("DEBUG", "Preparando envio de mensagem", { storage.add_log("DEBUG", "Preparando envio de mensagem", {
@@ -348,3 +356,34 @@ async def get_audio_base64(server_url, instance, apikey, message_id):
"message_id": message_id "message_id": message_id
}) })
raise raise
async def format_message(transcription_text, summary_text=None):
"""Formata a mensagem baseado nas configurações."""
settings = storage.get_message_settings()
message_parts = []
# Determinar modo de saída
output_mode = settings["output_mode"]
char_limit = int(settings["character_limit"])
if output_mode == "smart":
# Modo inteligente baseado no tamanho
if len(transcription_text) > char_limit:
if summary_text:
message_parts.append(f"{settings['summary_header']}\n\n{summary_text}")
else:
message_parts.append(f"{settings['transcription_header']}\n\n{transcription_text}")
elif output_mode == "summary_only":
if summary_text:
message_parts.append(f"{settings['summary_header']}\n\n{summary_text}")
elif output_mode == "transcription_only":
message_parts.append(f"{settings['transcription_header']}\n\n{transcription_text}")
else: # both
if summary_text:
message_parts.append(f"{settings['summary_header']}\n\n{summary_text}")
message_parts.append(f"{settings['transcription_header']}\n\n{transcription_text}")
# Adicionar mensagem de negócio
message_parts.append(dynamic_settings['BUSINESS_MESSAGE'])
return "\n\n".join(message_parts)

View File

@@ -200,3 +200,17 @@ class StorageHandler:
self.redis.set(self._get_redis_key("groq_key_counter"), str(next_counter)) self.redis.set(self._get_redis_key("groq_key_counter"), str(next_counter))
return keys[counter % len(keys)] return keys[counter % len(keys)]
def get_message_settings(self):
"""Obtém as configurações de mensagens."""
return {
"summary_header": self.redis.get(self._get_redis_key("summary_header")) or "🤖 *Resumo do áudio:*",
"transcription_header": self.redis.get(self._get_redis_key("transcription_header")) or "🔊 *Transcrição do áudio:*",
"output_mode": self.redis.get(self._get_redis_key("output_mode")) or "both",
"character_limit": int(self.redis.get(self._get_redis_key("character_limit")) or "500"),
}
def save_message_settings(self, settings: dict):
"""Salva as configurações de mensagens."""
for key, value in settings.items():
self.redis.set(self._get_redis_key(key), str(value))