feat: Route reply box LLM tasks (2/6) (#14840)

# Pull Request Template

## Description

Routes OSS reply-box and small Captain tasks through feature-specific
LLM model resolution. Rewrite, reply suggestion, summary, follow-up, and
CSAT utility analysis now resolve through the `editor` feature; label
suggestion resolves through `label_suggestion`. Existing credentials,
account OpenAI hook behavior, and instrumentation event names remain
unchanged.

Linear: https://linear.app/chatwoot/issue/CW-7425/test-new-models
Depends on #14839

## Type of change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update

## How Has This Been Tested?

- `bundle exec rspec spec/lib/captain/base_task_service_spec.rb
spec/lib/captain/rewrite_service_spec.rb
spec/lib/captain/reply_suggestion_service_spec.rb
spec/lib/captain/summary_service_spec.rb
spec/lib/captain/label_suggestion_service_spec.rb
spec/lib/captain/csat_utility_analysis_service_spec.rb
spec/lib/captain/follow_up_service_spec.rb` passed with 81 examples, 0
failures.
- `bundle exec rubocop lib/captain/base_task_service.rb
lib/captain/rewrite_service.rb lib/captain/reply_suggestion_service.rb
lib/captain/summary_service.rb lib/captain/label_suggestion_service.rb
lib/captain/csat_utility_analysis_service.rb
lib/captain/follow_up_service.rb
enterprise/lib/enterprise/captain/reply_suggestion_service.rb
spec/lib/captain/base_task_service_spec.rb
spec/lib/captain/rewrite_service_spec.rb
spec/lib/captain/reply_suggestion_service_spec.rb
spec/lib/captain/summary_service_spec.rb
spec/lib/captain/label_suggestion_service_spec.rb
spec/lib/captain/csat_utility_analysis_service_spec.rb
spec/lib/captain/follow_up_service_spec.rb` passed with no offenses.
- `git diff --check` passed.

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Sony Mathew
2026-06-25 17:01:05 +05:30
committed by GitHub
parent 8b977b35a8
commit 91d8a4e2a3
15 changed files with 47 additions and 11 deletions
+8 -1
View File
@@ -37,12 +37,13 @@ class Captain::BaseTaskService
"#{endpoint}/v1"
end
def make_api_call(model:, messages:, schema: nil, tools: [])
def make_api_call(messages:, model: nil, feature: nil, schema: nil, tools: [])
# Community edition prerequisite checks
# Enterprise module handles these with more specific error messages (cloud vs self-hosted)
return { error: I18n.t('captain.disabled'), error_code: 403 } unless captain_tasks_enabled?
return { error: I18n.t('captain.api_key_missing'), error_code: 401 } unless api_key_configured?
model = resolved_model(model: model, feature: feature)
instrumentation_params = build_instrumentation_params(model, messages)
instrumentation_method = tools.any? ? :instrument_tool_session : :instrument_llm_call
@@ -55,6 +56,12 @@ class Captain::BaseTaskService
response.merge(follow_up_context: build_follow_up_context(messages, response))
end
def resolved_model(model:, feature:)
return model if feature.blank?
Llm::FeatureRouter.resolve(feature: feature, account: account)[:model]
end
def execute_ruby_llm_request(model:, messages:, schema: nil, tools: [])
credential = llm_credential
+1 -1
View File
@@ -3,7 +3,7 @@ class Captain::CsatUtilityAnalysisService < Captain::BaseTaskService
def perform
api_response = make_api_call(
model: GPT_MODEL,
feature: 'editor',
messages: [
{ role: 'system', content: system_prompt },
{ role: 'user', content: message }
+1 -1
View File
@@ -33,7 +33,7 @@ class Captain::FollowUpService < Captain::BaseTaskService
{ role: 'user', content: user_message }
]
response = make_api_call(model: GPT_MODEL, messages: messages)
response = make_api_call(feature: 'editor', messages: messages)
return response if response[:error]
response.merge(follow_up_context: update_follow_up_context(user_message, response[:message]))
+1 -1
View File
@@ -12,7 +12,7 @@ class Captain::LabelSuggestionService < Captain::BaseTaskService
# Make API call
response = make_api_call(
model: GPT_MODEL, # TODO: Use separate model for label suggestion
feature: 'label_suggestion',
messages: [
{ role: 'system', content: prompt_from_file('label_suggestion') },
{ role: 'user', content: content }
+1 -1
View File
@@ -3,7 +3,7 @@ class Captain::ReplySuggestionService < Captain::BaseTaskService
def perform
make_api_call(
model: GPT_MODEL,
feature: 'editor',
messages: [
{ role: 'system', content: system_prompt },
{ role: 'user', content: formatted_conversation }
+1 -1
View File
@@ -36,7 +36,7 @@ class Captain::RewriteService < Captain::BaseTaskService
def call_llm_with_prompt(system_content, user_content = content)
make_api_call(
model: GPT_MODEL,
feature: 'editor',
messages: [
{ role: 'system', content: system_content },
{ role: 'user', content: user_content }
+1 -1
View File
@@ -3,7 +3,7 @@ class Captain::SummaryService < Captain::BaseTaskService
def perform
make_api_call(
model: GPT_MODEL,
feature: 'editor',
messages: [
{ role: 'system', content: system_prompt },
{ role: 'user', content: conversation.to_llm_text(include_contact_details: false) }