From bc9b79df103e6decbee0daa9328f2149b865ab9b Mon Sep 17 00:00:00 2001
From: Tetiana Chupryna <tchupryna@gitlab.com>
Date: Wed, 5 Feb 2025 11:09:53 +0000
Subject: [PATCH] Send current page params as set of params

We are going to deprecate current page prompts and move them
to AI Gateway
---
 .../models/ai/ai_resource/base_ai_resource.rb | 11 +++
 ee/app/models/ai/ai_resource/ci/build.rb      |  6 ++
 .../current_page_context_prompt_in_aigw.yml   |  9 +++
 ee/lib/gitlab/duo/chat/react_executor.rb      |  8 +++
 ee/lib/gitlab/llm/chain/gitlab_context.rb     |  2 +-
 .../gitlab/duo/chat/react_executor_spec.rb    | 67 +++++++++++++++++--
 .../ai/ai_resource/base_ai_resource_spec.rb   |  7 ++
 .../models/ai/ai_resource/ci/build_spec.rb    |  6 ++
 8 files changed, 110 insertions(+), 6 deletions(-)
 create mode 100644 ee/config/feature_flags/wip/current_page_context_prompt_in_aigw.yml

diff --git a/ee/app/models/ai/ai_resource/base_ai_resource.rb b/ee/app/models/ai/ai_resource/base_ai_resource.rb
index 6935c700493b3..09baa3a911d9d 100644
--- a/ee/app/models/ai/ai_resource/base_ai_resource.rb
+++ b/ee/app/models/ai/ai_resource/base_ai_resource.rb
@@ -13,6 +13,17 @@ def initialize(user, resource)
       def serialize_for_ai(_content_limit:)
         raise NotImplementedError
       end
+
+      def current_page_params
+        {
+          type: current_page_type,
+          title: resource.title
+        }
+      end
+
+      def current_page_type
+        raise NotImplementedError
+      end
     end
   end
 end
diff --git a/ee/app/models/ai/ai_resource/ci/build.rb b/ee/app/models/ai/ai_resource/ci/build.rb
index c4006103c59ab..2f61cc223411e 100644
--- a/ee/app/models/ai/ai_resource/ci/build.rb
+++ b/ee/app/models/ai/ai_resource/ci/build.rb
@@ -25,6 +25,12 @@ def current_page_short_description
           The user is currently on a page that displays a ci build which the user might refer to, for example, as 'current', 'this' or 'that'.
           SENTENCE
         end
+
+        def current_page_params
+          {
+            type: current_page_type
+          }
+        end
       end
     end
   end
diff --git a/ee/config/feature_flags/wip/current_page_context_prompt_in_aigw.yml b/ee/config/feature_flags/wip/current_page_context_prompt_in_aigw.yml
new file mode 100644
index 0000000000000..8929ee377ed47
--- /dev/null
+++ b/ee/config/feature_flags/wip/current_page_context_prompt_in_aigw.yml
@@ -0,0 +1,9 @@
+---
+name: current_page_context_prompt_in_aigw
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/508317
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/177622
+rollout_issue_url:
+milestone: '17.9'
+group: group::duo chat
+type: wip
+default_enabled: false
diff --git a/ee/lib/gitlab/duo/chat/react_executor.rb b/ee/lib/gitlab/duo/chat/react_executor.rb
index 446c95c038c8a..bb309c38ca76d 100644
--- a/ee/lib/gitlab/duo/chat/react_executor.rb
+++ b/ee/lib/gitlab/duo/chat/react_executor.rb
@@ -313,6 +313,7 @@ def conversation
         end
 
         def current_resource_params
+          return current_page_params if Feature.enabled?(:current_page_context_prompt_in_aigw, context.current_user)
           return unless current_resource_type
 
           {
@@ -335,6 +336,13 @@ def current_resource_content
         end
         strong_memoize_attr :current_resource_content
 
+        def current_page_params
+          context.current_page_params
+        rescue ArgumentError
+          nil
+        end
+        strong_memoize_attr :current_page_params
+
         def current_file_params
           return unless current_selection || current_blob
 
diff --git a/ee/lib/gitlab/llm/chain/gitlab_context.rb b/ee/lib/gitlab/llm/chain/gitlab_context.rb
index e42a2ccaf4adf..9d20aad78648a 100644
--- a/ee/lib/gitlab/llm/chain/gitlab_context.rb
+++ b/ee/lib/gitlab/llm/chain/gitlab_context.rb
@@ -13,7 +13,7 @@ class GitlabContext
 
         attr_reader :project
 
-        delegate :current_page_type, :current_page_short_description,
+        delegate :current_page_type, :current_page_short_description, :current_page_params,
           to: :authorized_resource, allow_nil: true
 
         # rubocop:disable Metrics/ParameterLists -- we probably need to rethink this initializer
diff --git a/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb b/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
index 6876fa755d64e..37388dd499d08 100644
--- a/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
+++ b/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
@@ -53,6 +53,8 @@
     end
 
     let(:issue_resource) { Ai::AiResource::Issue.new(user, resource) }
+    let(:issue_page_params) { { type: issue_resource.current_page_type, title: resource.title } }
+
     let(:answer_chunk) { create(:final_answer_chunk, chunk: "Ans") }
 
     let(:step_params) do
@@ -60,10 +62,7 @@
         messages: [{
           role: "user",
           content: user_input,
-          context: {
-            type: issue_resource.current_page_type,
-            content: issue_resource.current_page_short_description
-          },
+          context: issue_page_params,
           current_file: nil,
           additional_context: context.additional_context
         }],
@@ -367,7 +366,7 @@ def expect_sli_error(failed)
     context "when error event received and it's prompt length error" do
       let(:message) do
         <<~MESSAGE
-        Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'prompt is too long: 200082 tokens > 199999 maximum'}}
+          Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'prompt is too long: 200082 tokens > 199999 maximum'}}
         MESSAGE
       end
 
@@ -435,6 +434,29 @@ def expect_sli_error(failed)
       end
     end
 
+    context "when there is no resource" do
+      let(:context) do
+        Gitlab::Llm::Chain::GitlabContext.new(
+          current_user: user,
+          container: nil,
+          resource: nil,
+          ai_request: nil
+        )
+      end
+
+      it "sends request without context" do
+        params = step_params
+        params[:messages].first[:context] = nil
+
+        expect_next_instance_of(Gitlab::Duo::Chat::StepExecutor) do |react_agent|
+          expect(react_agent).to receive(:step).with(hash_including(params))
+            .and_yield(action_event).and_return([action_event])
+        end
+
+        agent.execute
+      end
+    end
+
     context "when code is selected" do
       let(:selected_text) { 'code selection' }
       let(:current_file) do
@@ -485,6 +507,41 @@ def expect_sli_error(failed)
       end
     end
 
+    context "when current page is included in context" do
+      it "pass current page params" do
+        params = step_params
+        params[:messages].first[:context] = issue_page_params
+
+        expect_next_instance_of(Gitlab::Duo::Chat::StepExecutor) do |react_agent|
+          expect(react_agent).to receive(:step).with(params)
+            .and_yield(action_event).and_return([action_event])
+        end
+
+        agent.execute
+      end
+
+      context "with feature flag disabled" do
+        before do
+          stub_feature_flags(current_page_context_prompt_in_aigw: false)
+        end
+
+        it "pass current page description" do
+          params = step_params
+          params[:messages].first[:context] = {
+            type: issue_resource.current_page_type,
+            content: issue_resource.current_page_short_description
+          }
+
+          expect_next_instance_of(Gitlab::Duo::Chat::StepExecutor) do |react_agent|
+            expect(react_agent).to receive(:step).with(params)
+                                                 .and_yield(action_event).and_return([action_event])
+          end
+
+          agent.execute
+        end
+      end
+    end
+
     context 'when Duo chat is self-hosted' do
       let_it_be(:self_hosted_model) { create(:ai_self_hosted_model, api_token: 'test_token') }
       let_it_be(:ai_feature) { create(:ai_feature_setting, self_hosted_model: self_hosted_model, feature: :duo_chat) }
diff --git a/ee/spec/models/ai/ai_resource/base_ai_resource_spec.rb b/ee/spec/models/ai/ai_resource/base_ai_resource_spec.rb
index c140ce3d0e620..fa2fb3029212a 100644
--- a/ee/spec/models/ai/ai_resource/base_ai_resource_spec.rb
+++ b/ee/spec/models/ai/ai_resource/base_ai_resource_spec.rb
@@ -9,4 +9,11 @@
         .to raise_error(NotImplementedError)
     end
   end
+
+  describe '#current_page_params' do
+    it 'returns params to construct prompt' do
+      expect { described_class.new(nil, nil).current_page_params }
+        .to raise_error(NotImplementedError)
+    end
+  end
 end
diff --git a/ee/spec/models/ai/ai_resource/ci/build_spec.rb b/ee/spec/models/ai/ai_resource/ci/build_spec.rb
index eccfe2219d54a..ab4e9c520baef 100644
--- a/ee/spec/models/ai/ai_resource/ci/build_spec.rb
+++ b/ee/spec/models/ai/ai_resource/ci/build_spec.rb
@@ -37,4 +37,10 @@
         .to include("The user is currently on a page that displays a ci build")
     end
   end
+
+  describe '#current_page_params' do
+    it 'returns params to construct prompt' do
+      expect(wrapped_build.current_page_params.keys).to eq([:type])
+    end
+  end
 end
-- 
GitLab