app/models/ruby_conversations/concerns/conversation_chat.rb
# frozen_string_literal: true module RubyConversations module Concerns # Handles chat-related functionality for Conversation module ConversationChat extend ActiveSupport::Concern attr_accessor :tools_optional def tools_required? !tools_optional end def call_llm(system_message: nil, &block) validate_conversation_state chat_messages = generate_chat_response(system_message, &block) validate_tool_calls(chat_messages) if tools_required? store_and_update_conversation(chat_messages) end alias execute call_llm def validate_tool_calls(chat_messages) return unless tools_configured? return if tool_calls?(chat_messages) last_message = chat_messages.last&.content error_message = "No tool call found for tool(s): '#{tool_names}'. Please check the prompt." raise RubyConversations::ToolCallValidationError.new( error_message, last_message: last_message, tool_names: tool_names ) end def tools_configured? tool.present? || tools&.present? end def tool_names tool&.name || tools&.map(&:name) end def tool_calls?(chat_messages) chat_messages.any? { |message| message.tool_calls.present? } end def llm model_identifier end private def validate_conversation_state message = messages.last raise ArgumentError, 'Conversation must have at least one message to execute' unless message end def generate_chat_response(system_message, &) setup_llm_chat(system_message: system_message) chat.ask(messages.last.request, &) chat.messages end def store_and_update_conversation(chat_messages) update_last_message_response(messages.last, chat_messages) RubyConversations.client.store_conversation_or_message(self, messages.last) { response: chat_messages, conversation: self, content: chat_messages.last.content } end def setup_llm_chat(system_message: nil) configure_llm_credentials configure_tools chat.with_instructions(system_message) if system_message.present? end def update_last_message_response(message, chat_messages) message.response = chat_messages.map(&:to_h).to_json end def configure_tools chat.with_tool(tool) if tool.present? tools&.each { |tool| chat.with_tool(tool) } end end end end