fix(captain): converte markdown bold pra formato WhatsApp no callback Hermes

Hermes (e LLMs default em geral) emitem **negrito** no formato markdown
padrão. WhatsApp usa formato próprio: *negrito* (single asterisk). Sem
conversão, o cliente vê asteriscos literais no WhatsApp, parecendo bug.

Defesa em camadas:
1. SOUL.md da Valentina foi atualizado com regra explícita de formato
   WhatsApp (single asterisk pra bold, underscore pra itálico, etc).
2. Este controller faz normalização defensiva no callback recebido do
   Hermes: regex `**texto**` -> `*texto*` antes de criar a mensagem
   outgoing. Não afeta o resto do conteúdo.

normalize_for_whatsapp() é trivialmente reversível e idempotente
(executar 2x é igual a 1x).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rodribm10 2026-05-01 15:24:57 -03:00
parent d781f4a048
commit cd519a73c4

View File

@ -21,16 +21,13 @@ class Webhooks::Captain::HermesCallbackController < ApplicationController
before_action :fetch_inbox
def process_payload
content = params[:content].to_s.strip
content = extract_content
return head :bad_request if content.blank?
conversation = recent_conversation_for(@inbox)
return log_no_conversation_and_ack if conversation.blank?
Rails.logger.info(
"[Hermes::Callback] reply received for conv #{conversation.display_id} (#{content.length} chars)"
)
log_reply(conversation, content)
create_outgoing_message(conversation, content)
head :ok
rescue StandardError => e
@ -73,6 +70,26 @@ class Webhooks::Captain::HermesCallbackController < ApplicationController
head :ok
end
def extract_content
normalize_for_whatsapp(params[:content].to_s.strip)
end
# Converte markdown padrão (que LLMs default usam) pra formato WhatsApp:
# **negrito** -> *negrito*
# WhatsApp usa single asterisk pra bold; double asterisk aparece literal
# pro cliente, parecendo bug. Defesa caso o SOUL.md não convença o LLM.
def normalize_for_whatsapp(content)
return content if content.blank?
content.gsub(/\*\*([^*\n]+?)\*\*/, '*\1*')
end
def log_reply(conversation, content)
Rails.logger.info(
"[Hermes::Callback] reply received for conv #{conversation.display_id} (#{content.length} chars)"
)
end
def create_outgoing_message(conversation, content)
assistant = conversation.inbox.captain_assistant
sender = assistant.presence || User.find_by(id: conversation.assignee_id)