Add "Search with Gemini" button and results to feature library modal
What does this MR do and why?
Adds the explicit "Search with Gemini" (Tier 3) frontend to the "More Features" feature library modal (feature_library_modal.vue):
- Shows a "Search with Gemini" button, gated on an injected availability signal (
geminiSearchAvailable+ a resolvedresourceId), once a search query settles. - On click, calls the dedicated
ai_searchendpoint and shows a "Searching with Gemini …" loading state. - Renders returned features in a "Suggested by Gemini" section reusing the existing
feature-library-itemgrid, deduped against Tier 1/2 results. - Adds a Gemini-specific empty state ("Gemini couldn't find a matching feature …") and a Hide control; hiding falls back to the original empty state.
- Wires real availability via
sidebars_helperdelegating toFeatureMatchService#ai_search_available?(single source of truth); adds thesearch_with_gemini_in_feature_library_modalinternal event. - Treats 404 (feature disabled) and 429 (rate limited) as expected, no Sentry noise.
Tier 1/2 results still render instantly; Gemini is explicit-trigger so AI latency never blocks core search.
References
Refs https://gitlab.com/gitlab-org/gitlab/-/work_items/603100 Part of epic https://gitlab.com/groups/gitlab-org/-/epics/22330
This is a stacked MR. It targets 603091-feature-library-ai-search-endpoint (the AI search endpoint, !245775 (merged)) rather than master, and will auto-retarget to master once that MR merges. Review this MR's diff on top of the endpoint branch.
Screenshots or screen recordings
Screenshot in dark mode:
How to set up and validate locally
-
Restart GDK with SaaS simulation enabled (Gemini availability is SaaS-gated):
GITLAB_SIMULATE_SAAS=1 gdk restartThen enable both feature flags for your user:
Feature.enable(:feature_library_modal, User.find_by_username('<your-username>')) Feature.enable(:feature_discovery_gemini_search, User.find_by_username('<your-username>')) -
Make your namespace a trial (Gemini availability requires
resource.root_ancestor.trial_active?):Group.find_by(id: <Group ID>).gitlab_subscription.update(trial: true, trial_starts_on: 20.days.ago, trial_ends_on: 10.days.from_now) -
The AI Gateway isn't wired up locally, so temporarily stub the Tier 3 response. Apply the patch below to
ee/app/services/ee/onboarding/feature_library/feature_match_service.rb, then restart Rails. Do not commit this stub. To exercise the Gemini empty state, change the stubbed return to[].Local AI search stub patch
diff --git a/ee/app/services/ee/onboarding/feature_library/feature_match_service.rb b/ee/app/services/ee/onboarding/feature_library/feature_match_service.rb --- a/ee/app/services/ee/onboarding/feature_library/feature_match_service.rb +++ b/ee/app/services/ee/onboarding/feature_library/feature_match_service.rb @@ -34,6 +34,13 @@ def ai_gateway_ids(normalized_query) ).execute return [] if catalogue.empty? + # TEMP LOCAL STUB -- do NOT commit. Mimics the AI Gateway locally by + # returning a few real catalogue ids. The sleep lets the + # "Searching with Gemini …" loading state show. To test the Gemini + # empty state, replace the return below with `return []`. + sleep 1.5 + return catalogue.first(4).pluck(:id) # rubocop:disable CodeReuse/ActiveRecord, Database/AvoidUsingPluckWithoutLimit -- catalogue is a plain Array + completion(catalogue, normalized_query).execute # rubocop:disable Lint/UnreachableCode -- temp stub end -
Open the "More Features" modal from the super sidebar; type a query (2+ chars).
-
Once results settle, click "Search with Gemini"; observe the loading state, then the "Suggested by Gemini" tiles.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
