diff --git a/enterprise/app/helpers/captain/chat_helper.rb b/enterprise/app/helpers/captain/chat_helper.rb index 8892573d9..d09ce5f96 100644 --- a/enterprise/app/helpers/captain/chat_helper.rb +++ b/enterprise/app/helpers/captain/chat_helper.rb @@ -1,53 +1,21 @@ module Captain::ChatHelper + include Captain::Loggable + def request_chat_completion log_chat_completion_request tools = @tool_registry&.registered_tools || [] temperature = @assistant&.config&.[]('temperature').to_f || 1 - # Log request details - log_captain_activity(Llm::BaseOpenAiService::REQUEST, { - conversation_id: @conversation&.id, - assistant_id: @assistant&.id, - assistant_name: @assistant&.name, - model: @model, - temperature: temperature, - messages_count: @messages.size, - tools_count: tools.size, - last_message: @messages.last - }) - + log_captain_request(tools, temperature) start_time = Time.current - response = @client.chat( - parameters: { - model: @model, - messages: @messages, - tools: tools, - response_format: { type: 'json_object' }, - temperature: temperature - } - ) + response = @client.chat(parameters: chat_parameters(tools, temperature)) duration_ms = ((Time.current - start_time) * 1000).round - - # Log response details - log_captain_activity(Llm::BaseOpenAiService::RESPONSE, { - conversation_id: @conversation&.id, - duration_ms: duration_ms, - response: response.dig('choices', 0) - }) + log_captain_response(response, duration_ms) handle_response(response) rescue StandardError => e - log_captain_activity(Llm::BaseOpenAiService::ERROR, { - conversation_id: @conversation&.id, - error_class: e.class.name, - error_message: e.message, - error_backtrace: e.backtrace&.first(5), - context: { - model: @model, - assistant_id: @assistant&.id - } - }) + log_captain_error(e) Rails.logger.error "#{self.class.name} Assistant: #{@assistant.id}, Error in chat completion: #{e}" raise e end @@ -95,15 +63,7 @@ module Captain::ChatHelper 'assistant_thinking' ) result = @tool_registry.send(function_name, arguments) - - # Log tool call details - log_captain_activity(Llm::BaseOpenAiService::TOOL_CALL, { - conversation_id: @conversation&.id, - tool_name: function_name, - arguments: arguments, - result: result.to_s.truncate(1000) - }) - + log_captain_tool_call(function_name, arguments, result) persist_message( { content: I18n.t('captain.copilot.completed_tool_call', function_name: function_name), @@ -137,8 +97,17 @@ module Captain::ChatHelper def log_chat_completion_request Rails.logger.info( "#{self.class.name} Assistant: #{@assistant.id}, Requesting chat completion - for messages #{@messages} with #{@tool_registry&.registered_tools&.length || 0} tools - " + for messages #{@messages} with #{@tool_registry&.registered_tools&.length || 0} tools" ) end + + def chat_parameters(tools, temperature) + { + model: @model, + messages: @messages, + tools: tools, + response_format: { type: 'json_object' }, + temperature: temperature + } + end end diff --git a/enterprise/app/helpers/captain/loggable.rb b/enterprise/app/helpers/captain/loggable.rb new file mode 100644 index 000000000..fbd996c41 --- /dev/null +++ b/enterprise/app/helpers/captain/loggable.rb @@ -0,0 +1,55 @@ +module Captain::Loggable + private + + def log_captain_activity(event_type, log_data = {}) + return if ENV['CAPTAIN_ENHANCED_LOGGING'].blank? + + conversation_id = log_data[:conversation_id] + prefix = conversation_id ? "[##{conversation_id}]" : '' + + # Add timestamp to all log data + log_data[:timestamp] = Time.current.iso8601 + + Rails.logger.info "[CAPTAIN_DEBUG] #{prefix} [#{event_type}] #{JSON.pretty_generate(log_data)}" + end + + def log_captain_request(tools, temperature) + log_captain_activity(Llm::BaseOpenAiService::REQUEST, { + conversation_id: @conversation&.id, + assistant_id: @assistant&.id, + model: @model, + temperature: temperature, + messages_count: @messages.size, + tools_count: tools.size, + messages: @messages, + tools: tools.map { |tool| tool.dig(:function, :name) } + }) + end + + def log_captain_response(response, duration_ms) + log_captain_activity(Llm::BaseOpenAiService::RESPONSE, { + conversation_id: @conversation&.id, + duration_ms: duration_ms, + response: response, + usage: response['usage'] + }) + end + + def log_captain_error(error) + log_captain_activity(Llm::BaseOpenAiService::ERROR, { + conversation_id: @conversation&.id, + error_class: error.class.name, + error_message: error.message, + context: { model: @model, assistant_id: @assistant&.id } + }) + end + + def log_captain_tool_call(function_name, arguments, result) + log_captain_activity(Llm::BaseOpenAiService::TOOL_CALL, { + conversation_id: @conversation&.id, + tool_name: function_name, + arguments: arguments, + result: result.to_s.truncate(1000) + }) + end +end diff --git a/enterprise/app/services/llm/base_open_ai_service.rb b/enterprise/app/services/llm/base_open_ai_service.rb index ed0e32b5d..a36a5b6de 100644 --- a/enterprise/app/services/llm/base_open_ai_service.rb +++ b/enterprise/app/services/llm/base_open_ai_service.rb @@ -14,7 +14,6 @@ class Llm::BaseOpenAiService log_errors: Rails.env.development? ) setup_model - @enhanced_logging_enabled = ENV['CAPTAIN_ENHANCED_LOGGING'].present? rescue StandardError => e raise "Failed to initialize OpenAI client: #{e.message}" end @@ -29,16 +28,4 @@ class Llm::BaseOpenAiService config_value = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_MODEL')&.value @model = (config_value.presence || DEFAULT_MODEL) end - - def log_captain_activity(event_type, log_data = {}) - return unless @enhanced_logging_enabled - - conversation_id = log_data[:conversation_id] - prefix = conversation_id ? "[##{conversation_id}]" : '' - - # Add timestamp to all log data - log_data[:timestamp] = Time.current.iso8601 - - Rails.logger.info "[CAPTAIN_DEBUG] #{prefix} [#{event_type}] #{JSON.pretty_generate(log_data)}" - end end