internal, internal/frontend: add vector-search experiment flag

Add the vector-search experiment flag to internal/experiment.go and
gate query vector embedding generation in fetchSearchPage behind
experiment.IsActive(ctx, internal.ExperimentVectorSearch).

Change-Id: If0efb59aa05bbc1150e8970ff76f0928db704d7a
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/806461
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Auto-Submit: Ethan Lee <ethanalee@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
kokoro-CI: kokoro <noreply+kokoro@google.com>
diff --git a/internal/experiment.go b/internal/experiment.go
index b4d29ce..22ba1f5 100644
--- a/internal/experiment.go
+++ b/internal/experiment.go
@@ -7,12 +7,14 @@
 
 const (
 	ExperimentEnableStdFrontendFetch = "enable-std-frontend-fetch"
+	ExperimentVectorSearch           = "vector-search"
 )
 
 // Experiments represents all of the active experiments in the codebase and
 // a description of each experiment.
 var Experiments = map[string]string{
 	ExperimentEnableStdFrontendFetch: "Enable frontend fetching for module std.",
+	ExperimentVectorSearch:           "Enable hybrid vector search for package queries.",
 }
 
 // Experiment holds data associated with an experimental feature for frontend
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index 6a1d7ba..8e3069d 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -21,6 +21,7 @@
 	"golang.org/x/mod/semver"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/derrors"
+	"golang.org/x/pkgsite/internal/experiment"
 	pagepkg "golang.org/x/pkgsite/internal/frontend/page"
 	"golang.org/x/pkgsite/internal/frontend/serrors"
 	"golang.org/x/pkgsite/internal/frontend/versions"
@@ -247,7 +248,7 @@
 	maxResultCount := maxSearchOffset + pageParams.limit
 
 	var vec []float32
-	if embeddingsClient != nil && !searchSymbols && strings.TrimSpace(cq) != "" {
+	if embeddingsClient != nil && !searchSymbols && strings.TrimSpace(cq) != "" && experiment.IsActive(ctx, internal.ExperimentVectorSearch) {
 		embedCtx, cancel := context.WithTimeout(ctx, searchEmbeddingTimeout)
 		defer cancel()
 		vecs, err := embeddingsClient.GenerateEmbeddings(embedCtx, []string{cq}, "RETRIEVAL_QUERY")
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index 8a01a42..fd92188 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -19,6 +19,7 @@
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/google/safehtml"
 	"golang.org/x/pkgsite/internal"
+	"golang.org/x/pkgsite/internal/experiment"
 	"golang.org/x/pkgsite/internal/fetchdatasource"
 	"golang.org/x/pkgsite/internal/frontend/page"
 	"golang.org/x/pkgsite/internal/frontend/serrors"
@@ -874,7 +875,7 @@
 }
 
 func TestFetchSearchPageWithVector(t *testing.T) {
-	ctx := context.Background()
+	ctx := experiment.NewContext(context.Background(), internal.ExperimentVectorSearch)
 
 	tests := []struct {
 		name       string