Files
Aakash BakhleandGitHub 83dda621c5 feat: default new accounts to captain v2 (#14917)
# Pull Request Template

## Description

defaults new accounts to captain v2

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.

locally and specs

## 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
- [x] Any dependent changes have been merged and published in downstream
modules
2026-07-07 16:21:51 +05:30

227 lines
6.6 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Concerns::Agentable do
let(:dummy_class) do
Class.new do
include Concerns::Agentable
attr_reader :account
attr_accessor :temperature
def initialize(name: 'Test Agent', temperature: 0.8, account: nil)
@name = name
@temperature = temperature
@account = account
end
def self.name
'DummyClass'
end
private
def agent_name
@name
end
def prompt_context
{ base_key: 'base_value' }
end
end
end
let(:account) { create(:account) }
let(:dummy_instance) { dummy_class.new(account: account) }
let(:mock_agents_agent) { instance_double(Agents::Agent) }
before do
InstallationConfig.where(name: 'CAPTAIN_OPEN_AI_MODEL').destroy_all
allow(Agents::Agent).to receive(:new).and_return(mock_agents_agent)
allow(Captain::PromptRenderer).to receive(:render).and_return('rendered_template')
end
describe '#agent' do
it 'creates an Agents::Agent with correct parameters' do
expect(Agents::Agent).to receive(:new).with(
name: 'Test Agent',
instructions: instance_of(Proc),
tools: [],
model: Llm::Models.default_model_for('assistant'),
temperature: 0.8,
response_schema: Captain::ResponseSchema
)
dummy_instance.agent
end
it 'uses default temperature when temperature is nil' do
dummy_instance.temperature = nil
expect(Agents::Agent).to receive(:new).with(
hash_including(temperature: 0.5)
)
dummy_instance.agent
end
it 'converts temperature to float' do
dummy_instance.temperature = '0.5'
expect(Agents::Agent).to receive(:new).with(
hash_including(temperature: 0.5)
)
dummy_instance.agent
end
end
describe '#agent_instructions' do
it 'calls Captain::PromptRenderer with base context' do
expect(Captain::PromptRenderer).to receive(:render).with(
'dummy_class',
hash_including(base_key: 'base_value')
)
dummy_instance.agent_instructions
end
it 'merges context state when provided' do
context_double = instance_double(Agents::RunContext,
context: {
state: {
assistant_config: { 'feature_contact_attributes' => true },
conversation: { id: 123 },
contact: { name: 'John' }
}
})
expected_context = {
base_key: 'base_value',
conversation: { id: 123 },
contact: { name: 'John' },
campaign: {}
}
expect(Captain::PromptRenderer).to receive(:render).with(
'dummy_class',
hash_including(expected_context)
)
dummy_instance.agent_instructions(context_double)
end
it 'merges campaign data from context state' do
context_double = instance_double(Agents::RunContext,
context: {
state: {
conversation: { id: 123 },
contact: { name: 'John' },
campaign: { id: 10, title: 'Summer Sale', message: 'Check it out' }
}
})
expect(Captain::PromptRenderer).to receive(:render).with(
'dummy_class',
hash_including(
campaign: { id: 10, title: 'Summer Sale', message: 'Check it out' }
)
)
dummy_instance.agent_instructions(context_double)
end
it 'handles context without state' do
context_double = instance_double(Agents::RunContext, context: {})
expect(Captain::PromptRenderer).to receive(:render).with(
'dummy_class',
hash_including(
base_key: 'base_value',
conversation: {},
contact: nil,
campaign: {}
)
)
dummy_instance.agent_instructions(context_double)
end
end
describe '#template_name' do
it 'returns underscored class name' do
expect(dummy_instance.send(:template_name)).to eq('dummy_class')
end
end
describe '#agent_tools' do
it 'returns empty array by default' do
expect(dummy_instance.send(:agent_tools)).to eq([])
end
end
describe '#agent_model' do
it 'returns the assistant feature default model' do
expect(dummy_instance.send(:agent_model)).to eq(Llm::Models.default_model_for('assistant'))
end
it 'returns account override model when present' do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_MODEL', value: 'gpt-4.1-nano')
account.update!(captain_models: { 'assistant' => 'gpt-5.2' })
expect(dummy_instance.send(:agent_model)).to eq('gpt-5.2')
end
it 'returns the installation model when account override is absent' do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_MODEL', value: 'gpt-4.1-nano')
expect(dummy_instance.send(:agent_model)).to eq('gpt-4.1-nano')
end
it 'returns the Captain V2 default when Captain V2 is enabled' do
create(:installation_config, name: 'CAPTAIN_OPEN_AI_MODEL', value: 'gpt-4.1-nano')
account.enable_features!('captain_integration_v2')
expect(dummy_instance.send(:agent_model)).to eq('gpt-5.2')
expect(account.reload.captain_models).to be_nil
end
it 'returns the assistant feature default model when account is nil' do
agent = dummy_class.new(account: nil)
expect(agent.send(:agent_model)).to eq(Llm::Models.default_model_for('assistant'))
end
end
describe '#agent_response_schema' do
it 'returns Captain::ResponseSchema' do
expect(dummy_instance.send(:agent_response_schema)).to eq(Captain::ResponseSchema)
end
end
describe 'required methods' do
let(:incomplete_class) do
Class.new do
include Concerns::Agentable
end
end
let(:incomplete_instance) { incomplete_class.new }
describe '#agent_name' do
it 'raises NotImplementedError when not implemented' do
expect { incomplete_instance.send(:agent_name) }
.to raise_error(NotImplementedError, /must implement agent_name/)
end
end
describe '#prompt_context' do
it 'raises NotImplementedError when not implemented' do
expect { incomplete_instance.send(:prompt_context) }
.to raise_error(NotImplementedError, /must implement prompt_context/)
end
end
end
end