iachat/enterprise/app/models/concerns/agentable.rb
Rodribm10 7b009cf47f feat(lifecycle): inject concierge context into Captain orchestrator prompt
Adds concierge.* and reservation.* Liquid variables to agent_instructions
so Sofia's orchestrator_prompt receives unit persona/knowledge/variables
and reservation data resolved from conversation.custom_attributes.current_unit_id.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 09:25:16 -03:00

122 lines
3.3 KiB
Ruby

module Concerns::Agentable
extend ActiveSupport::Concern
def agent
Agents::Agent.new(
name: agent_name,
instructions: ->(context) { agent_instructions(context) },
tools: agent_tools,
model: agent_model,
temperature: temperature.to_f || 0.7,
response_schema: agent_response_schema
)
end
def agent_instructions(context = nil)
enhanced_context = prompt_context
if context
state = context.context[:state] || {}
conversation_data = state[:conversation] || {}
contact_data = state[:contact] || {}
enhanced_context = enhanced_context.merge(
conversation: conversation_data,
contact: contact_data,
concierge: resolve_concierge_context(conversation_data),
reservation: resolve_reservation_context(conversation_data)
)
end
ctx = enhanced_context.with_indifferent_access
custom = orchestrator_prompt_override
if custom.present?
Captain::PromptRenderer.render_string(custom, ctx)
else
Captain::PromptRenderer.render(template_name, ctx)
end
end
def default_orchestrator_prompt
Captain::PromptRenderer.read_template(template_name)
end
private
def agent_name
raise NotImplementedError, "#{self.class} must implement agent_name"
end
def orchestrator_prompt_override
respond_to?(:orchestrator_prompt) ? orchestrator_prompt.presence : nil
end
def template_name
self.class.name.demodulize.underscore
end
def agent_tools
[] # Default implementation, override if needed
end
def agent_model
InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value.presence || LlmConstants::DEFAULT_MODEL
end
def agent_response_schema
Captain::ResponseSchema
end
def prompt_context
raise NotImplementedError, "#{self.class} must implement prompt_context"
end
def resolve_concierge_context(conversation_data)
unit = resolve_current_unit(conversation_data)
return default_concierge_context if unit.blank?
{
'persona_name' => unit.concierge_persona_name,
'unit_name' => unit.name.to_s,
'knowledge' => unit.concierge_knowledge,
'variables' => unit.concierge_variables
}
end
def resolve_reservation_context(conversation_data)
conv = lookup_conversation(conversation_data)
return {} if conv.blank?
reservation = Captain::Reservation
.where(account_id: conv.account_id, contact_id: conv.contact_id)
.order(created_at: :desc)
.first
return {} if reservation.blank?
Captain::Lifecycle::ContextBuilder.build(reservation).fetch('reservation', {})
end
def resolve_current_unit(conversation_data)
conv = lookup_conversation(conversation_data)
return nil if conv.blank?
unit_id = conv.custom_attributes.to_h['current_unit_id']
return nil if unit_id.blank?
Captain::Unit.find_by(id: unit_id)
end
def lookup_conversation(conversation_data)
return nil if conversation_data.blank?
id = conversation_data.is_a?(Hash) ? (conversation_data[:id] || conversation_data['id']) : nil
return nil if id.blank?
::Conversation.find_by(id: id)
end
def default_concierge_context
{ 'persona_name' => 'Sofia', 'unit_name' => '', 'knowledge' => '', 'variables' => {} }
end
end