fix(captain): respect channel message limits

This commit is contained in:
aakashb95
2026-07-17 16:54:27 +05:30
parent 259187e1fd
commit b898cd3738
6 changed files with 61 additions and 1 deletions
+2 -1
View File
@@ -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,9 @@ 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)
@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 +96,28 @@ 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 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&.inbox)
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.inbox)
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
@@ -0,0 +1,23 @@
class Captain::MessageLengthLimit
DEFAULT = 10_000
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(inbox)
return unless inbox
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: