From 5f6aed05c9c823621beb841caf55f259894a0a73 Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Fri, 1 May 2026 22:53:43 -0300 Subject: [PATCH] fix(captain/hermes-builder): callback resolve session via last_session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: Hermes nao propaga chat_id no metadata do callback. Callback controller nao conseguia resolver qual session armazenar a resposta. WARN "no session_key resolvable — ignorando" descartava todas as respostas do Construtor. Fix: HermesBuilder::Storage.remember_last_session() grava ultima session por account quando admin chama /start ou /create. Callback le essa key via last_session_for(account_id). MVP-safe pra 1 admin por conta. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hermes_builder_callback_controller.rb | 31 +++---------------- .../captain/hermes_builder_controller.rb | 2 ++ .../app/services/hermes_builder/storage.rb | 11 +++++++ 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/app/controllers/webhooks/captain/hermes_builder_callback_controller.rb b/app/controllers/webhooks/captain/hermes_builder_callback_controller.rb index 362ff2297..901cb31f0 100644 --- a/app/controllers/webhooks/captain/hermes_builder_callback_controller.rb +++ b/app/controllers/webhooks/captain/hermes_builder_callback_controller.rb @@ -30,35 +30,14 @@ class Webhooks::Captain::HermesBuilderCallbackController < ApplicationController private - # Estratégia: usar o session_id do metadata (Hermes propaga o chat_id). - # Fallback: account_id da query string + último user que mandou msg - # (raro, mas evita perder resposta). + # Hermes nao propaga chat_id no metadata da resposta de callback, entao + # usamos a ultima sessao ativa do account (gravada por + # HermesBuilder::Storage.remember_last_session no /start e /create). + # MVP-safe pra 1 admin por vez por conta. def resolve_session_key - chat_id = params[:metadata]&.[](:chat_id) || params.dig(:metadata, 'chat_id') - if chat_id.is_a?(String) && chat_id.include?('builder-') - # Formato: webhook:construtor-admin:session:builder-- - session_id = chat_id.split(':').last - return "hermes_builder:#{session_id}" if session_id.start_with?('builder-') - end - account_id = params[:account_id] return nil if account_id.blank? - # Fallback: pega últimas 5 sessões do account, retorna a mais recente - # com mensagens. Aceitável pra MVP com 1 admin testando por vez. - recent_session_key_for(account_id) - end - - def recent_session_key_for(account_id) - return nil unless Rails.cache.respond_to?(:redis) - - pattern = "hermes_builder:builder-#{account_id}-*" - keys = Rails.cache.redis.with { |c| c.keys(pattern) } - return nil if keys.blank? - - keys.first.sub(/^.*?(hermes_builder:.*)$/, '\1') - rescue StandardError => e - Rails.logger.warn("[HermesBuilder::Callback] recent_session_key fallback failed: #{e.class} - #{e.message}") - nil + HermesBuilder::Storage.last_session_for(account_id) end end diff --git a/enterprise/app/controllers/api/v1/accounts/captain/hermes_builder_controller.rb b/enterprise/app/controllers/api/v1/accounts/captain/hermes_builder_controller.rb index f2aac82ea..25c2e5ba1 100644 --- a/enterprise/app/controllers/api/v1/accounts/captain/hermes_builder_controller.rb +++ b/enterprise/app/controllers/api/v1/accounts/captain/hermes_builder_controller.rb @@ -21,6 +21,7 @@ class Api::V1::Accounts::Captain::HermesBuilderController < Api::V1::Accounts::B return render json: { error: 'Texto vazio' }, status: :bad_request if text.blank? HermesBuilder::Storage.append(session_key, role: 'user', content: text) + HermesBuilder::Storage.remember_last_session(Current.account.id, session_key) HermesBuilder::Dispatcher.send_to_construtor(session_id: session_id, message: text) render json: { ok: true, session_id: session_id }, status: :accepted @@ -34,6 +35,7 @@ class Api::V1::Accounts::Captain::HermesBuilderController < Api::V1::Accounts::B # quando admin clica "Iniciar" — sem ter que digitar primeira msg. def start HermesBuilder::Storage.clear(session_key) + HermesBuilder::Storage.remember_last_session(Current.account.id, session_key) HermesBuilder::Dispatcher.send_to_construtor( session_id: session_id, message: '__START__ Inicie o fluxo de criação de novo agente Hermes. Comece pela primeira pergunta do Bloco 1 (nome do agente).' diff --git a/enterprise/app/services/hermes_builder/storage.rb b/enterprise/app/services/hermes_builder/storage.rb index 1275fa3b7..945dbbbf9 100644 --- a/enterprise/app/services/hermes_builder/storage.rb +++ b/enterprise/app/services/hermes_builder/storage.rb @@ -29,4 +29,15 @@ module HermesBuilder::Storage def clear(session_key) Rails.cache.delete(session_key) end + + # Última sessão ativa por account — usada pelo callback do Hermes pra + # roteamento (Hermes nao propaga chat_id no metadata da resposta). + # Aceitavel pra MVP com 1 admin por vez por conta. + def remember_last_session(account_id, session_key) + Rails.cache.write("hermes_builder:last_session:account:#{account_id}", session_key, expires_in: TTL) + end + + def last_session_for(account_id) + Rails.cache.read("hermes_builder:last_session:account:#{account_id}") + end end