diff --git a/config/feature_flags/gitlab_com_derisk/use_sonnet_35.yml b/config/feature_flags/gitlab_com_derisk/use_sonnet_35.yml new file mode 100644 index 0000000000000000000000000000000000000000..37f58bae80b6b878e86b5c634e32799b4a854ea7 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/use_sonnet_35.yml @@ -0,0 +1,9 @@ +--- +name: use_sonnet_35 +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/468334 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/157696 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/469499 +milestone: '17.2' +group: group::ai framework +type: gitlab_com_derisk +default_enabled: false diff --git a/ee/lib/gitlab/llm/chain/requests/ai_gateway.rb b/ee/lib/gitlab/llm/chain/requests/ai_gateway.rb index b64e5f336d64b93df35753ec36b7fa66c9f027e1..492eda1c8d96bfe5d2fdacafb1caea08aba295e1 100644 --- a/ee/lib/gitlab/llm/chain/requests/ai_gateway.rb +++ b/ee/lib/gitlab/llm/chain/requests/ai_gateway.rb @@ -70,7 +70,7 @@ def default_options def model(options) return options[:model] if options[:model].present? - CLAUDE_3_SONNET + Feature.enabled?(:use_sonnet_35, user) ? CLAUDE_3_5_SONNET : CLAUDE_3_SONNET end def provider(options) diff --git a/ee/lib/gitlab/llm/concerns/available_models.rb b/ee/lib/gitlab/llm/concerns/available_models.rb index 9be8117711612b3c8d587b3035dfaf9d5da93d63..4c533dd381731b9d6e0d1f523df95893d2896b54 100644 --- a/ee/lib/gitlab/llm/concerns/available_models.rb +++ b/ee/lib/gitlab/llm/concerns/available_models.rb @@ -4,6 +4,7 @@ module Gitlab module Llm module Concerns module AvailableModels + CLAUDE_3_5_SONNET = 'claude-3-5-sonnet-20240620' CLAUDE_3_SONNET = 'claude-3-sonnet-20240229' CLAUDE_3_HAIKU = 'claude-3-haiku-20240307' CLAUDE_2_1 = 'claude-2.1' @@ -13,7 +14,8 @@ module AvailableModels VERTEX_MODEL_CODE = 'code-bison' VERTEX_MODEL_CODECHAT = 'codechat-bison' VERTEX_MODEL_TEXT = 'text-bison' - ANTHROPIC_MODELS = [CLAUDE_2_1, CLAUDE_3_SONNET, CLAUDE_3_HAIKU, DEFAULT_INSTANT_MODEL].freeze + ANTHROPIC_MODELS = [CLAUDE_2_1, CLAUDE_3_SONNET, CLAUDE_3_5_SONNET, CLAUDE_3_HAIKU, + DEFAULT_INSTANT_MODEL].freeze VERTEX_MODELS = [VERTEX_MODEL_CHAT, VERTEX_MODEL_CODECHAT, VERTEX_MODEL_CODE, VERTEX_MODEL_TEXT].freeze AVAILABLE_MODELS = { diff --git a/ee/spec/lib/gitlab/llm/chain/requests/ai_gateway_spec.rb b/ee/spec/lib/gitlab/llm/chain/requests/ai_gateway_spec.rb index c414e1d75c179a49d051b29f9a891ef31ac372e0..0d828899388e43bb614cdc964e628c6307f176ef 100644 --- a/ee/spec/lib/gitlab/llm/chain/requests/ai_gateway_spec.rb +++ b/ee/spec/lib/gitlab/llm/chain/requests/ai_gateway_spec.rb @@ -74,6 +74,7 @@ before do allow(Gitlab::Llm::Logger).to receive(:build).and_return(logger) allow(instance).to receive(:ai_client).and_return(ai_client) + stub_feature_flags(use_sonnet_35: false) end shared_examples 'performing request to the AI Gateway' do @@ -137,6 +138,53 @@ end end + context 'when using Sonnet 3.5 model' do + let(:expected_model) { described_class::CLAUDE_3_5_SONNET } + + before do + stub_feature_flags(use_sonnet_35: true) + end + + it 'calls the AI Gateway streaming endpoint and yields response without stripping it' do + expect(ai_client).to receive(:stream).with(endpoint: endpoint, body: body).and_yield(response) + .and_return(response) + + expect { |b| instance.request(prompt, &b) }.to yield_with_args(response) + end + + it_behaves_like 'performing request to the AI Gateway' + + it_behaves_like 'tracks events for AI requests', 4, 2, klass: 'Gitlab::Llm::Anthropic::Client' do + before do + allow(ai_client).to receive(:stream).with(endpoint: endpoint, body: body).and_return(response) + end + end + + context 'when additional params are passed in as options' do + let(:options) do + { temperature: 1, stop_sequences: %W[\n\Foo Bar:], max_tokens_to_sample: 1024, disallowed_param: 1, topP: 1 } + end + + let(:params) do + { + max_tokens_to_sample: 1024, + stop_sequences: ["\n\Foo", "Bar:"], + temperature: 1 + } + end + + it_behaves_like 'performing request to the AI Gateway' + end + + context 'when unit primitive is passed' do + let(:endpoint) { "#{described_class::BASE_ENDPOINT}/test" } + + subject(:request) { instance.request(prompt, unit_primitive: :test) } + + it_behaves_like 'performing request to the AI Gateway' + end + end + context 'when invalid model is passed' do let(:model) { 'test' }