Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe573f77b6 | ||
|
|
bd98421e57 | ||
|
|
9bba75f4b6 | ||
|
|
097c7215ab | ||
|
|
b898cd3738 |
@@ -24,7 +24,8 @@ module Concerns::Agentable
|
||||
current_time: format_current_time(state[:timezone]),
|
||||
conversation: state[:conversation] || {},
|
||||
contact: config['feature_contact_attributes'].present? ? state[:contact] : nil,
|
||||
campaign: state[:campaign] || {}
|
||||
campaign: state[:campaign] || {},
|
||||
message_length_limit: state[:message_length_limit]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ class Captain::Assistant::AgentRunnerService
|
||||
def generate_response(message_history: [])
|
||||
message_to_process, context = run_payload(message_history)
|
||||
@last_run_result = runner.run(message_to_process, context: context, max_turns: 10)
|
||||
record_turn_start(@last_run_result)
|
||||
@last_run_result = rewrite_oversized_response(@last_run_result) if response_too_long?(@last_run_result)
|
||||
|
||||
raise "Captain response exceeds the channel limit of #{message_length_limit} characters" if response_too_long?(@last_run_result)
|
||||
|
||||
process_agent_result(@last_run_result)
|
||||
rescue StandardError => e
|
||||
@@ -93,6 +97,34 @@ class Captain::Assistant::AgentRunnerService
|
||||
response
|
||||
end
|
||||
|
||||
def rewrite_oversized_response(result)
|
||||
runner.run(
|
||||
"Your previous response was #{response_text(result).length} characters, but this channel allows a maximum of " \
|
||||
"#{message_length_limit} characters. Rewrite it within the limit while preserving the essential information. " \
|
||||
'Do not call tools or perform new actions.',
|
||||
context: result.context,
|
||||
max_turns: 10
|
||||
)
|
||||
end
|
||||
|
||||
def record_turn_start(result)
|
||||
history = Array(result.context&.dig(:conversation_history))
|
||||
turn_start_index = history.rindex { |message| message[:role].to_s == 'user' }
|
||||
result.context[:captain_v2_turn_start_index] = turn_start_index if turn_start_index
|
||||
end
|
||||
|
||||
def response_too_long?(result)
|
||||
message_length_limit && response_text(result).length > message_length_limit
|
||||
end
|
||||
|
||||
def response_text(result)
|
||||
extract_text_from_content(result.output).to_s
|
||||
end
|
||||
|
||||
def message_length_limit
|
||||
@message_length_limit ||= Captain::MessageLengthLimit.for(@conversation)
|
||||
end
|
||||
|
||||
def error_response(error_message)
|
||||
{
|
||||
'response' => 'conversation_handoff',
|
||||
|
||||
@@ -31,6 +31,7 @@ module Captain::Assistant::RunnerStateHelper
|
||||
def build_conversation_state(state)
|
||||
state[:conversation] = slice_attrs(@conversation, CONVERSATION_STATE_ATTRIBUTES)
|
||||
state[:channel_type] = @conversation.inbox&.channel_type
|
||||
state[:message_length_limit] = Captain::MessageLengthLimit.for(@conversation)
|
||||
state[:contact] = slice_attrs(@conversation.contact, CONTACT_STATE_ATTRIBUTES) if @conversation.contact
|
||||
state[:campaign] = slice_attrs(@conversation.campaign, CAMPAIGN_STATE_ATTRIBUTES) if @conversation.campaign
|
||||
state[:contact_inbox] = slice_attrs(@conversation.contact_inbox, CONTACT_INBOX_STATE_ATTRIBUTES) if @conversation.contact_inbox
|
||||
|
||||
@@ -74,8 +74,8 @@ class Captain::Assistant::SessionCaptureService
|
||||
# (assistant replies, tool calls/results, handoff hops).
|
||||
def current_turn_history
|
||||
history = Array(context[:conversation_history])
|
||||
last_user_index = history.rindex { |message| message[:role].to_s == 'user' }
|
||||
current_turn = last_user_index ? history[last_user_index..] : history
|
||||
turn_start_index = context[:captain_v2_turn_start_index] || history.rindex { |message| message[:role].to_s == 'user' } || 0
|
||||
current_turn = history[turn_start_index..]
|
||||
|
||||
current_turn.map do |message|
|
||||
content = message[:content]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
class Captain::MessageLengthLimit
|
||||
DEFAULT = 10_000
|
||||
INSTAGRAM_DIRECT_MESSAGE = 'instagram_direct_message'.freeze
|
||||
CHANNEL_LIMITS = {
|
||||
'Channel::FacebookPage' => 2_000,
|
||||
'Channel::Instagram' => 1_000,
|
||||
'Channel::Line' => 2_000,
|
||||
'Channel::Sms' => 320,
|
||||
'Channel::Telegram' => 4_096,
|
||||
'Channel::Tiktok' => 6_000,
|
||||
'Channel::Whatsapp' => 4_096
|
||||
}.freeze
|
||||
TWILIO_LIMITS = {
|
||||
'sms' => 320,
|
||||
'whatsapp' => 1_600
|
||||
}.freeze
|
||||
|
||||
def self.for(conversation)
|
||||
return unless conversation
|
||||
|
||||
inbox = conversation.inbox
|
||||
return CHANNEL_LIMITS.fetch('Channel::Instagram') if conversation.additional_attributes['type'] == INSTAGRAM_DIRECT_MESSAGE
|
||||
return TWILIO_LIMITS.fetch(inbox.channel.medium) if inbox.twilio?
|
||||
|
||||
CHANNEL_LIMITS.fetch(inbox.channel_type, DEFAULT)
|
||||
end
|
||||
end
|
||||
@@ -50,6 +50,11 @@ Always respect these boundaries:
|
||||
|
||||
When a Response Guideline or Guardrail explicitly requires transfer for a matched condition, follow it instead of the generic consent-first handoff defaults below.
|
||||
|
||||
{% if message_length_limit -%}
|
||||
# Channel Requirements
|
||||
Keep your response at or under {{ message_length_limit }} characters so it can be delivered through this channel.
|
||||
{% endif -%}
|
||||
|
||||
# Decision Framework
|
||||
|
||||
## 1. Analyze the Request
|
||||
|
||||
@@ -47,6 +47,11 @@ Always respect these boundaries:
|
||||
{% endfor %}
|
||||
{% endif -%}
|
||||
|
||||
{% if message_length_limit -%}
|
||||
# Channel Requirements
|
||||
Keep your response at or under {{ message_length_limit }} characters so it can be delivered through this channel.
|
||||
{% endif -%}
|
||||
|
||||
{% if tools.size > 0 -%}
|
||||
# Available Tools
|
||||
You have access to these tools:
|
||||
|
||||
Reference in New Issue
Block a user