class Api::V1::Accounts::Captain::AssistantsController < Api::V1::Accounts::BaseController before_action :current_account before_action -> { check_authorization(Captain::Assistant) } before_action :set_assistant, only: [:show, :update, :destroy, :playground] def index @assistants = account_assistants.ordered end def show; end def create @assistant = account_assistants.create!(assistant_params) end def update payload = assistant_params overrides = system_prompt_action_overrides if overrides.present? payload[:config] ||= {} payload[:config].merge!(overrides) end @assistant.update!(payload) end def destroy @assistant.destroy head :no_content end def playground content = params[:message_content] || params.dig(:assistant, :message_content) history = params[:message_history] || params.dig(:assistant, :message_history) || [] history = history.map { |m| { role: m[:role] || m['role'], content: m[:content] || m['content'] } } if captain_v2_enabled? # For V2, we only pass the history. The current message is already in history from frontend # or should be treated as the last turn. response = Captain::Assistant::AgentRunnerService.new(assistant: @assistant).generate_response( message_history: history ) else # V1 Engine (Single Agent) response = Captain::Llm::AssistantChatService.new(assistant: @assistant).generate_response( additional_message: content, message_history: history ) end render json: response rescue StandardError => e Rails.logger.error "Playground Error: #{e.message}" render json: { response: "Erro técnico: #{e.message}", reasoning: e.backtrace.first }, status: :internal_server_error end def tools assistant = Captain::Assistant.new(account: Current.account) @tools = assistant.available_agent_tools end private def set_assistant @assistant = account_assistants.find(params[:id]) end def account_assistants @account_assistants ||= Captain::Assistant.for_account(Current.account.id) end def assistant_payload params[:assistant].presence || params end def assistant_params permitted = assistant_payload.permit(:name, :description, :llm_provider, :llm_model, :api_key, config: [ :product_name, :role_name, :feature_faq, :feature_memory, :feature_citation, :welcome_message, :handoff_message, :resolution_message, :instructions, :temperature, :playbook, :distance_threshold, :max_rag_results, :system_prompt, :handoff_on_sentiment, { system_prompt_blocks: [:key, :title, :content, :order] } ]) # Handle array parameters separately to allow partial updates permitted[:response_guidelines] = assistant_payload[:response_guidelines] if assistant_payload.key?(:response_guidelines) permitted[:guardrails] = assistant_payload[:guardrails] if assistant_payload.key?(:guardrails) permitted end def system_prompt_action_overrides action = assistant_payload[:system_prompt_action].to_s return {} if action.blank? config = @assistant.config || {} versions = Array(config['system_prompt_versions']) blocks = assistant_payload.dig(:config, :system_prompt_blocks) case action when 'save_version' return {} if blocks.blank? versions << { 'blocks' => blocks, 'saved_at' => Time.zone.now.to_i, 'saved_by_id' => Current.user&.id } { 'system_prompt_versions' => versions.last(10) } when 'revert_last' last = versions.pop return {} if last.blank? { 'system_prompt_blocks' => last['blocks'], 'system_prompt_versions' => versions.last(10) } when 'restore_default' { 'system_prompt' => nil, 'system_prompt_blocks' => nil } else {} end end def playground_params params.require(:assistant).permit(:message_content, message_history: [:role, :content]) end def message_history (playground_params[:message_history] || []).map { |message| { role: message[:role], content: message[:content] } } end def captain_v2_enabled? Current.account.feature_enabled?('captain_integration_v2') end end