fix(captain/hermes-builder): callback resolve session via last_session

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) <noreply@anthropic.com>
This commit is contained in:
Rodribm10 2026-05-01 22:53:43 -03:00
parent f362949579
commit 5f6aed05c9
3 changed files with 18 additions and 26 deletions

View File

@ -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-<account>-<user>
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

View File

@ -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).'

View File

@ -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