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:
|
Rails/HelperInstanceVariable:
|
||||||
Exclude:
|
Exclude:
|
||||||
- enterprise/app/helpers/captain/chat_helper.rb
|
- enterprise/app/helpers/captain/chat_helper.rb
|
||||||
|
- enterprise/app/helpers/captain/loggable.rb
|
||||||
|
|
||||||
Rails/ApplicationController:
|
Rails/ApplicationController:
|
||||||
Exclude:
|
Exclude:
|
||||||
@@ -336,4 +337,4 @@ FactoryBot/RedundantFactoryOption:
|
|||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
FactoryBot/FactoryAssociationWithStrategy:
|
FactoryBot/FactoryAssociationWithStrategy:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
module Captain::ChatHelper
|
module Captain::ChatHelper
|
||||||
|
include Captain::Loggable
|
||||||
|
|
||||||
def request_chat_completion
|
def request_chat_completion
|
||||||
log_chat_completion_request
|
log_chat_completion_request
|
||||||
|
|
||||||
response = @client.chat(
|
tools = @tool_registry&.registered_tools || []
|
||||||
parameters: {
|
temperature = @assistant&.config&.[]('temperature').to_f || 1
|
||||||
model: @model,
|
|
||||||
messages: @messages,
|
log_captain_request(tools, temperature)
|
||||||
tools: @tool_registry&.registered_tools || [],
|
start_time = Time.current
|
||||||
response_format: { type: 'json_object' },
|
response = @client.chat(parameters: chat_parameters(tools, temperature))
|
||||||
temperature: @assistant&.config&.[]('temperature').to_f || 1
|
duration_ms = ((Time.current - start_time) * 1000).round
|
||||||
}
|
log_captain_response(response, duration_ms)
|
||||||
)
|
|
||||||
|
|
||||||
handle_response(response)
|
handle_response(response)
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
|
log_captain_error(e)
|
||||||
Rails.logger.error "#{self.class.name} Assistant: #{@assistant.id}, Error in chat completion: #{e}"
|
Rails.logger.error "#{self.class.name} Assistant: #{@assistant.id}, Error in chat completion: #{e}"
|
||||||
raise e
|
raise e
|
||||||
end
|
end
|
||||||
@@ -61,6 +63,7 @@ module Captain::ChatHelper
|
|||||||
'assistant_thinking'
|
'assistant_thinking'
|
||||||
)
|
)
|
||||||
result = @tool_registry.send(function_name, arguments)
|
result = @tool_registry.send(function_name, arguments)
|
||||||
|
log_captain_tool_call(function_name, arguments, result)
|
||||||
persist_message(
|
persist_message(
|
||||||
{
|
{
|
||||||
content: I18n.t('captain.copilot.completed_tool_call', function_name: function_name),
|
content: I18n.t('captain.copilot.completed_tool_call', function_name: function_name),
|
||||||
@@ -94,8 +97,17 @@ module Captain::ChatHelper
|
|||||||
def log_chat_completion_request
|
def log_chat_completion_request
|
||||||
Rails.logger.info(
|
Rails.logger.info(
|
||||||
"#{self.class.name} Assistant: #{@assistant.id}, Requesting chat completion
|
"#{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
|
end
|
||||||
|
|
||||||
|
def chat_parameters(tools, temperature)
|
||||||
|
{
|
||||||
|
model: @model,
|
||||||
|
messages: @messages,
|
||||||
|
tools: tools,
|
||||||
|
response_format: { type: 'json_object' },
|
||||||
|
temperature: temperature
|
||||||
|
}
|
||||||
|
end
|
||||||
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
|
delegate :account, :inbox, to: :@conversation
|
||||||
|
|
||||||
def generate_and_process_response
|
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
|
message_history: collect_previous_messages
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ require 'openai'
|
|||||||
class Captain::Llm::AssistantChatService < Llm::BaseOpenAiService
|
class Captain::Llm::AssistantChatService < Llm::BaseOpenAiService
|
||||||
include Captain::ChatHelper
|
include Captain::ChatHelper
|
||||||
|
|
||||||
def initialize(assistant: nil)
|
def initialize(assistant: nil, conversation: nil)
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@assistant = assistant
|
@assistant = assistant
|
||||||
|
@conversation = conversation
|
||||||
@messages = [system_message]
|
@messages = [system_message]
|
||||||
@response = ''
|
@response = ''
|
||||||
register_tools
|
register_tools
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
class Llm::BaseOpenAiService
|
class Llm::BaseOpenAiService
|
||||||
DEFAULT_MODEL = 'gpt-4o-mini'.freeze
|
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
|
def initialize
|
||||||
@client = OpenAI::Client.new(
|
@client = OpenAI::Client.new(
|
||||||
access_token: InstallationConfig.find_by!(name: 'CAPTAIN_OPEN_AI_API_KEY').value,
|
access_token: InstallationConfig.find_by!(name: 'CAPTAIN_OPEN_AI_API_KEY').value,
|
||||||
@@ -15,7 +21,8 @@ class Llm::BaseOpenAiService
|
|||||||
private
|
private
|
||||||
|
|
||||||
def uri_base
|
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
|
end
|
||||||
|
|
||||||
def setup_model
|
def setup_model
|
||||||
|
|||||||
Reference in New Issue
Block a user