feat: Add the support for images in Captain (#11850)
This commit is contained in:
committed by
GitHub
parent
802f0694ed
commit
5b9f997fa0
@@ -12,9 +12,16 @@ class Captain::Llm::AssistantChatService < Llm::BaseOpenAiService
|
||||
register_tools
|
||||
end
|
||||
|
||||
def generate_response(input, previous_messages = [], role = 'user')
|
||||
@messages += previous_messages
|
||||
@messages << { role: role, content: input } if input.present?
|
||||
# additional_message: A single message (String) from the user that should be appended to the chat.
|
||||
# It can be an empty String or nil when you only want to supply historical messages.
|
||||
# message_history: An Array of already formatted messages that provide the previous context.
|
||||
# role: The role for the additional_message (defaults to `user`).
|
||||
#
|
||||
# NOTE: Parameters are provided as keyword arguments to improve clarity and avoid relying on
|
||||
# positional ordering.
|
||||
def generate_response(additional_message: nil, message_history: [], role: 'user')
|
||||
@messages += message_history
|
||||
@messages << { role: role, content: additional_message } if additional_message.present?
|
||||
request_chat_completion
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
class Captain::OpenAiMessageBuilderService
|
||||
pattr_initialize [:message!]
|
||||
|
||||
def generate_content
|
||||
parts = []
|
||||
parts << text_part(@message.content) if @message.content.present?
|
||||
parts.concat(attachment_parts(@message.attachments)) if @message.attachments.any?
|
||||
|
||||
return 'Message without content' if parts.blank?
|
||||
return parts.first[:text] if parts.one? && parts.first[:type] == 'text'
|
||||
|
||||
parts
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def text_part(text)
|
||||
{ type: 'text', text: text }
|
||||
end
|
||||
|
||||
def image_part(image_url)
|
||||
{ type: 'image_url', image_url: { url: image_url } }
|
||||
end
|
||||
|
||||
def attachment_parts(attachments)
|
||||
image_attachments = attachments.where(file_type: :image)
|
||||
image_content = image_parts(image_attachments)
|
||||
|
||||
transcription = extract_audio_transcriptions(attachments)
|
||||
transcription_part = text_part(transcription) if transcription.present?
|
||||
|
||||
attachment_part = text_part('User has shared an attachment') if attachments.where.not(file_type: %i[image audio]).exists?
|
||||
|
||||
[image_content, transcription_part, attachment_part].flatten.compact
|
||||
end
|
||||
|
||||
def image_parts(image_attachments)
|
||||
image_attachments.each_with_object([]) do |attachment, parts|
|
||||
url = get_attachment_url(attachment)
|
||||
parts << image_part(url) if url.present?
|
||||
end
|
||||
end
|
||||
|
||||
def get_attachment_url(attachment)
|
||||
return attachment.download_url if attachment.download_url.present?
|
||||
return attachment.external_url if attachment.external_url.present?
|
||||
|
||||
attachment.file.attached? ? attachment.file_url : nil
|
||||
end
|
||||
|
||||
def extract_audio_transcriptions(attachments)
|
||||
audio_attachments = attachments.where(file_type: :audio)
|
||||
return '' if audio_attachments.blank?
|
||||
|
||||
audio_attachments.map do |attachment|
|
||||
result = Messages::AudioTranscriptionService.new(attachment).perform
|
||||
result[:success] ? result[:transcriptions] : ''
|
||||
end.join
|
||||
end
|
||||
end
|
||||
@@ -1,13 +1,34 @@
|
||||
module Enterprise::MessageTemplates::HookExecutionService
|
||||
MAX_ATTACHMENT_WAIT_SECONDS = 4
|
||||
|
||||
def trigger_templates
|
||||
super
|
||||
return unless should_process_captain_response?
|
||||
return perform_handoff unless inbox.captain_active?
|
||||
|
||||
Captain::Conversation::ResponseBuilderJob.perform_later(
|
||||
conversation,
|
||||
conversation.inbox.captain_assistant
|
||||
)
|
||||
schedule_captain_response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def schedule_captain_response
|
||||
job_args = [conversation, conversation.inbox.captain_assistant]
|
||||
|
||||
if message.attachments.blank?
|
||||
Captain::Conversation::ResponseBuilderJob.perform_later(*job_args)
|
||||
else
|
||||
wait_time = calculate_attachment_wait_time
|
||||
Captain::Conversation::ResponseBuilderJob.set(wait: wait_time).perform_later(*job_args)
|
||||
end
|
||||
end
|
||||
|
||||
def calculate_attachment_wait_time
|
||||
attachment_count = message.attachments.size
|
||||
base_wait = 1.second
|
||||
|
||||
# Wait longer for more attachments or larger files
|
||||
additional_wait = [attachment_count * 1, MAX_ATTACHMENT_WAIT_SECONDS].min.seconds
|
||||
base_wait + additional_wait
|
||||
end
|
||||
|
||||
def should_process_captain_response?
|
||||
|
||||
Reference in New Issue
Block a user