Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c205e3b1ab | ||
|
|
b57f4f8a1c | ||
|
|
16033f5d11 | ||
|
|
802431f2f2 | ||
|
|
781fafd560 | ||
|
|
c28cc1748e | ||
|
|
f7e01e771a | ||
|
|
375f6cba47 | ||
|
|
3a07f675e6 |
+2
-1
@@ -87,6 +87,7 @@ Metrics/ModuleLength:
|
||||
Rails/HelperInstanceVariable:
|
||||
Exclude:
|
||||
- enterprise/app/helpers/captain/chat_helper.rb
|
||||
- enterprise/app/helpers/captain/loggable.rb
|
||||
|
||||
Rails/ApplicationController:
|
||||
Exclude:
|
||||
@@ -336,4 +337,4 @@ FactoryBot/RedundantFactoryOption:
|
||||
Enabled: false
|
||||
|
||||
FactoryBot/FactoryAssociationWithStrategy:
|
||||
Enabled: false
|
||||
Enabled: false
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
module Captain::ChatHelper
|
||||
include Captain::Loggable
|
||||
|
||||
def request_chat_completion
|
||||
log_chat_completion_request
|
||||
|
||||
response = @client.chat(
|
||||
parameters: {
|
||||
model: @model,
|
||||
messages: @messages,
|
||||
tools: @tool_registry&.registered_tools || [],
|
||||
response_format: { type: 'json_object' },
|
||||
temperature: @assistant&.config&.[]('temperature').to_f || 1
|
||||
}
|
||||
)
|
||||
tools = @tool_registry&.registered_tools || []
|
||||
temperature = @assistant&.config&.[]('temperature').to_f || 1
|
||||
|
||||
log_captain_request(tools, temperature)
|
||||
start_time = Time.current
|
||||
response = @client.chat(parameters: chat_parameters(tools, temperature))
|
||||
duration_ms = ((Time.current - start_time) * 1000).round
|
||||
log_captain_response(response, duration_ms)
|
||||
|
||||
handle_response(response)
|
||||
rescue StandardError => e
|
||||
log_captain_error(e)
|
||||
Rails.logger.error "#{self.class.name} Assistant: #{@assistant.id}, Error in chat completion: #{e}"
|
||||
raise e
|
||||
end
|
||||
@@ -61,6 +63,7 @@ module Captain::ChatHelper
|
||||
'assistant_thinking'
|
||||
)
|
||||
result = @tool_registry.send(function_name, arguments)
|
||||
log_captain_tool_call(function_name, arguments, result)
|
||||
persist_message(
|
||||
{
|
||||
content: I18n.t('captain.copilot.completed_tool_call', function_name: function_name),
|
||||
@@ -94,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
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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.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,
|
||||
last_message: @messages.last
|
||||
})
|
||||
end
|
||||
|
||||
def log_captain_response(response, duration_ms)
|
||||
log_captain_activity(Llm::BaseOpenAiService::RESPONSE, {
|
||||
conversation_id: @conversation&.id,
|
||||
duration_ms: duration_ms,
|
||||
response: response.dig('choices', 0, 'message'),
|
||||
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
|
||||
@@ -26,7 +26,7 @@ class Captain::Conversation::ResponseBuilderJob < ApplicationJob
|
||||
delegate :account, :inbox, to: :@conversation
|
||||
|
||||
def generate_and_process_response
|
||||
@response = Captain::Llm::AssistantChatService.new(assistant: @assistant).generate_response(
|
||||
@response = Captain::Llm::AssistantChatService.new(assistant: @assistant, conversation: @conversation).generate_response(
|
||||
message_history: collect_previous_messages
|
||||
)
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ require 'openai'
|
||||
class Captain::Llm::AssistantChatService < Llm::BaseOpenAiService
|
||||
include Captain::ChatHelper
|
||||
|
||||
def initialize(assistant: nil)
|
||||
def initialize(assistant: nil, conversation: nil)
|
||||
super()
|
||||
|
||||
@assistant = assistant
|
||||
@conversation = conversation
|
||||
@messages = [system_message]
|
||||
@response = ''
|
||||
register_tools
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
class Llm::BaseOpenAiService
|
||||
DEFAULT_MODEL = 'gpt-4o-mini'.freeze
|
||||
|
||||
# Captain logging event types
|
||||
REQUEST = 'REQUEST'.freeze
|
||||
RESPONSE = 'RESPONSE'.freeze
|
||||
TOOL_CALL = 'TOOL_CALL'.freeze
|
||||
ERROR = 'ERROR'.freeze
|
||||
|
||||
def initialize
|
||||
@client = OpenAI::Client.new(
|
||||
access_token: InstallationConfig.find_by!(name: 'CAPTAIN_OPEN_AI_API_KEY').value,
|
||||
@@ -15,7 +21,8 @@ class Llm::BaseOpenAiService
|
||||
private
|
||||
|
||||
def uri_base
|
||||
InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value || 'https://api.openai.com/'
|
||||
endpoint = InstallationConfig.find_by(name: 'CAPTAIN_OPEN_AI_ENDPOINT')&.value
|
||||
endpoint.presence || 'https://api.openai.com/'
|
||||
end
|
||||
|
||||
def setup_model
|
||||
|
||||
Reference in New Issue
Block a user