diff --git a/ee/app/services/analytics/ai_analytics/ai_user_metrics_service.rb b/ee/app/services/analytics/ai_analytics/ai_user_metrics_service.rb index 5c30214a204cd80fb6ed51aecbfba57b7af87f79..cafd119a3644b1a9214c226bd6d468faee5c2ed0 100644 --- a/ee/app/services/analytics/ai_analytics/ai_user_metrics_service.rb +++ b/ee/app/services/analytics/ai_analytics/ai_user_metrics_service.rb @@ -138,7 +138,8 @@ def event_name_from_id(event_id) end def namespace_filtering_enabled? - # for old Duo Chat we don't use namespace filtering for now. See https://gitlab.com/gitlab-org/gitlab/-/issues/578538 + return Feature.enabled?(:use_duo_chat_namespace_path_filter, namespace) if feature.to_sym == :chat + Feature.enabled?(:use_ai_events_namespace_path_filter, namespace) end diff --git a/ee/app/services/analytics/ai_analytics/duo_chat_usage_service.rb b/ee/app/services/analytics/ai_analytics/duo_chat_usage_service.rb index 2105a0bfa4bd9dcf7e2743a4780c75b88e612d8f..b74d1c26f3d550731ccdb47f4e97f98920f716a5 100644 --- a/ee/app/services/analytics/ai_analytics/duo_chat_usage_service.rb +++ b/ee/app/services/analytics/ai_analytics/duo_chat_usage_service.rb @@ -60,7 +60,7 @@ class DuoChatUsageService def filter_by_namespace_path_enabled? # for Duo Chat we don't want to use the namespace filter. See https://gitlab.com/gitlab-org/gitlab/-/issues/578538 - false + Feature.enabled?(:use_duo_chat_namespace_path_filter, namespace) end end end diff --git a/ee/config/feature_flags/wip/use_duo_chat_namespace_path_filter.yml b/ee/config/feature_flags/wip/use_duo_chat_namespace_path_filter.yml new file mode 100644 index 0000000000000000000000000000000000000000..fb1b53381eb04d128923d624afa323bd9a0bd809 --- /dev/null +++ b/ee/config/feature_flags/wip/use_duo_chat_namespace_path_filter.yml @@ -0,0 +1,10 @@ +--- +name: use_duo_chat_namespace_path_filter +description: Toggle namespace filtering for AI Duo Chat events +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/582944 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/216709 +rollout_issue_url: +milestone: '18.8' +group: group::optimize +type: wip +default_enabled: false diff --git a/ee/spec/services/analytics/ai_analytics/ai_user_metrics_service_spec.rb b/ee/spec/services/analytics/ai_analytics/ai_user_metrics_service_spec.rb index 9634a15cd744a8d46d5efa0481a5bc255661010d..6912dd679890e18572e10e923f34b505eacad937 100644 --- a/ee/spec/services/analytics/ai_analytics/ai_user_metrics_service_spec.rb +++ b/ee/spec/services/analytics/ai_analytics/ai_user_metrics_service_spec.rb @@ -241,7 +241,7 @@ context 'when namespace filtering is disabled' do before do - stub_feature_flags(use_ai_events_namespace_path_filter: false) + stub_feature_flags(use_duo_chat_namespace_path_filter: false) end it 'returns chat metrics across all namespaces' do @@ -264,6 +264,59 @@ end end + context 'with non-chat data' do + let(:feature) { :code_review } + let_it_be(:other_namespace) { create(:project).reload.project_namespace } + + before do + clickhouse_fixture(:ai_usage_events_daily, [ + { user_id: user1.id, namespace_path: container.traversal_path, event: 10, + date: (to - 3.days).to_date, occurrences: 2 }, # encounter_duo_code_review_error_during_review + { user_id: user1.id, namespace_path: other_namespace.traversal_path, event: 11, + date: (to - 3.days).to_date, occurrences: 1 }, # find_no_issues_duo_code_review_after_review + { user_id: user2.id, namespace_path: container.traversal_path, event: 12, + date: (to - 2.days).to_date, occurrences: 1 } # find_nothing_to_review_duo_code_review_on_mr + ]) + end + + context 'when namespace filtering is disabled' do + before do + stub_feature_flags(use_ai_events_namespace_path_filter: false) + end + + it 'returns code review metrics across all namespaces' do + expect(service_response).to be_success + expect(service_response.payload).to match({ + user1.id => a_hash_including( + total_events_count: 3, + encounter_duo_code_review_error_during_review_event_count: 2, + find_no_issues_duo_code_review_after_review_event_count: 1 + ), + user2.id => a_hash_including( + total_events_count: 1, + find_nothing_to_review_duo_code_review_on_mr_event_count: 1 + ) + }) + end + end + + context 'when namespace filtering is enabled' do + it 'returns code review metrics only within the specified namespace' do + expect(service_response).to be_success + expect(service_response.payload).to match({ + user1.id => a_hash_including( + total_events_count: 2, + encounter_duo_code_review_error_during_review_event_count: 2 + ), + user2.id => a_hash_including( + total_events_count: 1, + find_nothing_to_review_duo_code_review_on_mr_event_count: 1 + ) + }) + end + end + end + context 'when ClickHouse returns unknown event IDs' do before do clickhouse_fixture(:ai_usage_events_daily, [ diff --git a/ee/spec/services/analytics/ai_analytics/duo_chat_usage_service_spec.rb b/ee/spec/services/analytics/ai_analytics/duo_chat_usage_service_spec.rb index 257bfd05c3ef0ef5f89ad5c2db92ddd802ee9ef1..ec65945af14b690f828aa5dd9483d6263620fbb0 100644 --- a/ee/spec/services/analytics/ai_analytics/duo_chat_usage_service_spec.rb +++ b/ee/spec/services/analytics/ai_analytics/duo_chat_usage_service_spec.rb @@ -14,7 +14,8 @@ let_it_be(:user1) { create(:user, developer_of: group) } let_it_be(:user2) { create(:user, developer_of: subgroup) } let_it_be(:user3) { create(:user, developer_of: group) } - let_it_be(:stranger_user) { create(:user) } + let_it_be(:not_contributor_1) { create(:user) } + let_it_be(:not_contributor_2) { create(:user) } let(:current_user) { user1 } let(:from) { Time.current } @@ -90,9 +91,12 @@ { user_id: user1.id, namespace_path: group.traversal_path, event: 6, timestamp: to - 4.days }, { user_id: user2.id, namespace_path: project_namespace.traversal_path, event: 6, timestamp: to - 2.days }, { user_id: user2.id, namespace_path: project_namespace.traversal_path, event: 6, timestamp: to - 2.days }, - # Matches namespace path filter but shouldn't be included since it's duo chat - { user_id: stranger_user.id, namespace_path: project_namespace.traversal_path, event: 6, + # User is not contributor, included only when use_duo_chat_namespace_path_filter flag is enabled + { user_id: not_contributor_1.id, namespace_path: project_namespace.traversal_path, event: 6, timestamp: to - 2.days }, + { user_id: not_contributor_2.id, namespace_path: group.traversal_path, event: 6, + timestamp: to - 1.day }, + # Not Duo Chat event { user_id: user2.id, namespace_path: project_namespace.traversal_path, event: 1, timestamp: to - 2.days }, # out of timeframe { user_id: user3.id, namespace_path: project_namespace.traversal_path, event: 6, timestamp: to + 2.days }, @@ -117,7 +121,7 @@ end end - context 'when use_ai_events_namespace_path_filter feature flag is disabled' do + context 'when use_duo_chat_namespace_path_filter feature flag is disabled' do let(:expected_results) do { contributors_count: 3, @@ -126,7 +130,7 @@ end before do - stub_feature_flags(use_ai_events_namespace_path_filter: false) + stub_feature_flags(use_duo_chat_namespace_path_filter: false) end context 'for group' do @@ -150,14 +154,18 @@ end end - context 'when use_ai_events_namespace_path_filter feature flag is enabled' do + context 'when use_duo_chat_namespace_path_filter feature flag is enabled' do + before do + stub_feature_flags(use_duo_chat_namespace_path_filter: true) + end + context 'for group' do let_it_be(:container) { group } let(:expected_results) do { - contributors_count: 3, - duo_chat_contributors_count: 2 + contributors_count: 4, + duo_chat_contributors_count: 3 } end