From 8c4a218dcf2f8d0552d43497bbf25fa340485ccf Mon Sep 17 00:00:00 2001
From: Dmitry Gruzd <dgruzd@gitlab.com>
Date: Tue, 17 Dec 2024 17:11:13 +0000
Subject: [PATCH] Apply 1 suggestion(s) to 1 file(s)

Co-authored-by: Madelein van Niekerk <mvanniekerk@gitlab.com>
---
 .gitlab/ci/gitlab-gems.gitlab-ci.yml          |   3 +
 Gemfile                                       |   3 +
 Gemfile.lock                                  |   7 +
 Gemfile.next.checksum                         |   6 +-
 Gemfile.next.lock                             |  13 +-
 gems/gitlab-active-context/.gitignore         |  11 +
 gems/gitlab-active-context/.gitlab-ci.yml     |   4 +
 gems/gitlab-active-context/.rspec             |   3 +
 gems/gitlab-active-context/.rubocop.yml       |   5 +
 gems/gitlab-active-context/Gemfile            |  16 ++
 gems/gitlab-active-context/Gemfile.lock       | 209 ++++++++++++++++++
 gems/gitlab-active-context/README.md          |  42 ++++
 gems/gitlab-active-context/Rakefile           |  12 +
 gems/gitlab-active-context/bin/console        |  11 +
 gems/gitlab-active-context/bin/setup          |   8 +
 .../gitlab-active-context.gemspec             |  30 +++
 .../lib/active_context.rb                     |  11 +
 .../lib/active_context/config.rb              |  39 ++++
 .../lib/active_context/version.rb             |   5 +
 .../spec/active_context_spec.rb               |  31 +++
 .../spec/lib/active_context/config_spec.rb    |  94 ++++++++
 .../gitlab-active-context/spec/spec_helper.rb |  16 ++
 22 files changed, 573 insertions(+), 6 deletions(-)
 create mode 100644 gems/gitlab-active-context/.gitignore
 create mode 100644 gems/gitlab-active-context/.gitlab-ci.yml
 create mode 100644 gems/gitlab-active-context/.rspec
 create mode 100644 gems/gitlab-active-context/.rubocop.yml
 create mode 100644 gems/gitlab-active-context/Gemfile
 create mode 100644 gems/gitlab-active-context/Gemfile.lock
 create mode 100644 gems/gitlab-active-context/README.md
 create mode 100644 gems/gitlab-active-context/Rakefile
 create mode 100755 gems/gitlab-active-context/bin/console
 create mode 100755 gems/gitlab-active-context/bin/setup
 create mode 100644 gems/gitlab-active-context/gitlab-active-context.gemspec
 create mode 100644 gems/gitlab-active-context/lib/active_context.rb
 create mode 100644 gems/gitlab-active-context/lib/active_context/config.rb
 create mode 100644 gems/gitlab-active-context/lib/active_context/version.rb
 create mode 100644 gems/gitlab-active-context/spec/active_context_spec.rb
 create mode 100644 gems/gitlab-active-context/spec/lib/active_context/config_spec.rb
 create mode 100644 gems/gitlab-active-context/spec/spec_helper.rb

diff --git a/.gitlab/ci/gitlab-gems.gitlab-ci.yml b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
index 6235390453b8f..f5b0b9fbe2622 100644
--- a/.gitlab/ci/gitlab-gems.gitlab-ci.yml
+++ b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
@@ -47,3 +47,6 @@ include:
   - local: .gitlab/ci/templates/gem.gitlab-ci.yml
     inputs:
       gem_name: "openbao_client"
+  - local: .gitlab/ci/templates/gem.gitlab-ci.yml
+    inputs:
+      gem_name: "gitlab-active-context"
diff --git a/Gemfile b/Gemfile
index 6220b2ece5cde..e775f3e6360de 100644
--- a/Gemfile
+++ b/Gemfile
@@ -242,6 +242,9 @@ gem 'faraday_middleware-aws-sigv4', '~> 1.0.1', feature_category: :global_search
 # Used with Elasticsearch to support http keep-alive connections
 gem 'typhoeus', '~> 1.4.0', feature_category: :global_search
 
+gem 'gitlab-active-context', path: 'gems/gitlab-active-context', require: 'active_context',
+  feature_category: :global_search
+
 # Markdown and HTML processing
 gem 'html-pipeline', '~> 2.14.3', feature_category: :markdown
 gem 'deckar01-task_list', '2.3.4', feature_category: :markdown
diff --git a/Gemfile.lock b/Gemfile.lock
index 1f664bb6a70bd..ba9c793d5686b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -23,6 +23,12 @@ PATH
     error_tracking_open_api (1.0.0)
       typhoeus (~> 1.0, >= 1.0.1)
 
+PATH
+  remote: gems/gitlab-active-context
+  specs:
+    gitlab-active-context (0.0.1)
+      zeitwerk
+
 PATH
   remote: gems/gitlab-backup-cli
   specs:
@@ -2066,6 +2072,7 @@ DEPENDENCIES
   gettext (~> 3.4, >= 3.4.9)
   gettext_i18n_rails (~> 1.13.0)
   gitaly (~> 17.5.0.pre.rc1)
+  gitlab-active-context!
   gitlab-backup-cli!
   gitlab-chronic (~> 0.10.5)
   gitlab-cloud-connector (~> 0.2.5)
diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum
index a8f7e550bd0a8..f74c00926e4c7 100644
--- a/Gemfile.next.checksum
+++ b/Gemfile.next.checksum
@@ -567,7 +567,7 @@
 {"name":"rbs","version":"3.6.1","platform":"ruby","checksum":"ed7273d018556844583d1785ac54194e67eec594d68e317d57fa90ad035532c0"},
 {"name":"rbtrace","version":"0.5.1","platform":"ruby","checksum":"e8cba64d462bfb8ba102d7be2ecaacc789247d52ac587d8003549d909cb9c5dc"},
 {"name":"rchardet","version":"1.8.0","platform":"ruby","checksum":"693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7"},
-{"name":"rdoc","version":"6.8.1","platform":"ruby","checksum":"0128002d1bfc4892bdd780940841e4ca41275f63781fd832d11bc8ba4461462c"},
+{"name":"rdoc","version":"6.9.1","platform":"ruby","checksum":"3344bf498a46b701aba70ccdd5cdfa8be37e68493984c1bf8c579f06c3442c9f"},
 {"name":"re2","version":"2.7.0","platform":"aarch64-linux","checksum":"778921298b6e8aba26a6230dd298c9b361b92e45024f81fa6aee788060fa307c"},
 {"name":"re2","version":"2.7.0","platform":"arm-linux","checksum":"d328b5286d83ae265e13b855da8e348a976f80f91b748045b52073a570577954"},
 {"name":"re2","version":"2.7.0","platform":"arm64-darwin","checksum":"7d993f27a1afac4001c539a829e2af211ced62604930c90df32a307cf74cb4a4"},
@@ -592,7 +592,7 @@
 {"name":"regexp_parser","version":"2.6.0","platform":"ruby","checksum":"f163ba463a45ca2f2730e0902f2475bb0eefcd536dfc2f900a86d1e5a7d7a556"},
 {"name":"regexp_property_values","version":"1.0.0","platform":"java","checksum":"5e26782b01241616855c4ee7bb8a62fce9387e484f2d3eaf04f2a0633708222e"},
 {"name":"regexp_property_values","version":"1.0.0","platform":"ruby","checksum":"162499dc0bba1e66d334273a059f207a61981cc8cc69d2ca743594e7886d080f"},
-{"name":"reline","version":"0.5.12","platform":"ruby","checksum":"41ab36d3fd2aaa169e99f8b82a93b9585f51130529360e24388fcccc20a055a2"},
+{"name":"reline","version":"0.6.0","platform":"ruby","checksum":"57620375dcbe56ec09bac7192bfb7460c716bbf0054dc94345ecaa5438e539d2"},
 {"name":"representable","version":"3.2.0","platform":"ruby","checksum":"cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace"},
 {"name":"request_store","version":"1.5.1","platform":"ruby","checksum":"07a204d161590789f2b1d27f9f0eadcdecd6d868cb2f03240250e1bc747df78e"},
 {"name":"responders","version":"3.0.1","platform":"ruby","checksum":"613fe28e498987f4feaa3230aa6313ca4bd5f0563a3da83511b0dd6cd8f47292"},
@@ -737,7 +737,7 @@
 {"name":"thread_safe","version":"0.3.6","platform":"ruby","checksum":"9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a"},
 {"name":"thrift","version":"0.16.0","platform":"ruby","checksum":"d023286ea89e30444c9f1c28dd76107f87d8aaf85fe1742da1d8cd3b5417dcce"},
 {"name":"tilt","version":"2.0.11","platform":"ruby","checksum":"7b180fc472cbdeb186c85d31c0f2d1e61a2c0d77e1d9fd0ca28482a9d972d6a0"},
-{"name":"timeout","version":"0.4.2","platform":"ruby","checksum":"8aca2d5ff98eb2f7a501c03f8c3622065932cc58bc58f725cd50a09e63b4cc19"},
+{"name":"timeout","version":"0.4.3","platform":"ruby","checksum":"9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e"},
 {"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"},
 {"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"},
 {"name":"toml-rb","version":"2.2.0","platform":"ruby","checksum":"a1e2c54ac3cc9d49861004f75f0648b3622ac03a76abe105358c31553227d9a6"},
diff --git a/Gemfile.next.lock b/Gemfile.next.lock
index 6df4dab08d318..4293c0f166d32 100644
--- a/Gemfile.next.lock
+++ b/Gemfile.next.lock
@@ -23,6 +23,12 @@ PATH
     error_tracking_open_api (1.0.0)
       typhoeus (~> 1.0, >= 1.0.1)
 
+PATH
+  remote: gems/gitlab-active-context
+  specs:
+    gitlab-active-context (0.0.1)
+      zeitwerk
+
 PATH
   remote: gems/gitlab-backup-cli
   specs:
@@ -1557,7 +1563,7 @@ GEM
       msgpack (>= 0.4.3)
       optimist (>= 3.0.0)
     rchardet (1.8.0)
-    rdoc (6.8.1)
+    rdoc (6.9.1)
       psych (>= 4.0.0)
     re2 (2.7.0)
       mini_portile2 (~> 2.8.5)
@@ -1587,7 +1593,7 @@ GEM
       redis (>= 4, < 6)
     regexp_parser (2.6.0)
     regexp_property_values (1.0.0)
-    reline (0.5.12)
+    reline (0.6.0)
       io-console (~> 0.5)
     representable (3.2.0)
       declarative (< 0.1.0)
@@ -1861,7 +1867,7 @@ GEM
     thread_safe (0.3.6)
     thrift (0.16.0)
     tilt (2.0.11)
-    timeout (0.4.2)
+    timeout (0.4.3)
     timfel-krb5-auth (0.8.3)
     tins (1.31.1)
       sync
@@ -2094,6 +2100,7 @@ DEPENDENCIES
   gettext (~> 3.4, >= 3.4.9)
   gettext_i18n_rails (~> 1.13.0)
   gitaly (~> 17.5.0.pre.rc1)
+  gitlab-active-context!
   gitlab-backup-cli!
   gitlab-chronic (~> 0.10.5)
   gitlab-cloud-connector (~> 0.2.5)
diff --git a/gems/gitlab-active-context/.gitignore b/gems/gitlab-active-context/.gitignore
new file mode 100644
index 0000000000000..b04a8c840df1a
--- /dev/null
+++ b/gems/gitlab-active-context/.gitignore
@@ -0,0 +1,11 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+
+# rspec failure tracking
+.rspec_status
diff --git a/gems/gitlab-active-context/.gitlab-ci.yml b/gems/gitlab-active-context/.gitlab-ci.yml
new file mode 100644
index 0000000000000..07804f168b991
--- /dev/null
+++ b/gems/gitlab-active-context/.gitlab-ci.yml
@@ -0,0 +1,4 @@
+include:
+  - local: gems/gem.gitlab-ci.yml
+    inputs:
+      gem_name: "gitlab-active-context"
diff --git a/gems/gitlab-active-context/.rspec b/gems/gitlab-active-context/.rspec
new file mode 100644
index 0000000000000..34c5164d9b56c
--- /dev/null
+++ b/gems/gitlab-active-context/.rspec
@@ -0,0 +1,3 @@
+--format documentation
+--color
+--require spec_helper
diff --git a/gems/gitlab-active-context/.rubocop.yml b/gems/gitlab-active-context/.rubocop.yml
new file mode 100644
index 0000000000000..a02c038a3e983
--- /dev/null
+++ b/gems/gitlab-active-context/.rubocop.yml
@@ -0,0 +1,5 @@
+inherit_from:
+  - ../config/rubocop.yml
+
+Gemfile/MissingFeatureCategory:
+  Enabled: false
\ No newline at end of file
diff --git a/gems/gitlab-active-context/Gemfile b/gems/gitlab-active-context/Gemfile
new file mode 100644
index 0000000000000..ea2cd03ed92af
--- /dev/null
+++ b/gems/gitlab-active-context/Gemfile
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in active_context.gemspec
+gemspec
+
+gem "rake", "~> 13.0"
+gem "activesupport"
+
+group :development, :test do
+  gem "rspec", "~> 3.0"
+  gem "byebug"
+  gem "rubocop"
+  gem "rubocop-rspec"
+end
diff --git a/gems/gitlab-active-context/Gemfile.lock b/gems/gitlab-active-context/Gemfile.lock
new file mode 100644
index 0000000000000..f20e0bf5e27c8
--- /dev/null
+++ b/gems/gitlab-active-context/Gemfile.lock
@@ -0,0 +1,209 @@
+PATH
+  remote: .
+  specs:
+    gitlab-active-context (0.0.1)
+      zeitwerk
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    actionpack (8.0.0.1)
+      actionview (= 8.0.0.1)
+      activesupport (= 8.0.0.1)
+      nokogiri (>= 1.8.5)
+      rack (>= 2.2.4)
+      rack-session (>= 1.0.1)
+      rack-test (>= 0.6.3)
+      rails-dom-testing (~> 2.2)
+      rails-html-sanitizer (~> 1.6)
+      useragent (~> 0.16)
+    actionview (8.0.0.1)
+      activesupport (= 8.0.0.1)
+      builder (~> 3.1)
+      erubi (~> 1.11)
+      rails-dom-testing (~> 2.2)
+      rails-html-sanitizer (~> 1.6)
+    activesupport (8.0.0.1)
+      base64
+      benchmark (>= 0.3)
+      bigdecimal
+      concurrent-ruby (~> 1.0, >= 1.3.1)
+      connection_pool (>= 2.2.5)
+      drb
+      i18n (>= 1.6, < 2)
+      logger (>= 1.4.2)
+      minitest (>= 5.1)
+      securerandom (>= 0.3)
+      tzinfo (~> 2.0, >= 2.0.5)
+      uri (>= 0.13.1)
+    addressable (2.8.7)
+      public_suffix (>= 2.0.2, < 7.0)
+    ast (2.4.2)
+    base64 (0.2.0)
+    benchmark (0.4.0)
+    bigdecimal (3.1.8)
+    builder (3.3.0)
+    byebug (11.1.3)
+    concurrent-ruby (1.3.4)
+    connection_pool (2.4.1)
+    crack (1.0.0)
+      bigdecimal
+      rexml
+    crass (1.0.6)
+    date (3.4.1)
+    diff-lcs (1.5.1)
+    drb (2.2.1)
+    erubi (1.13.0)
+    gitlab-styles (13.0.2)
+      rubocop (~> 1.68.0)
+      rubocop-capybara (~> 2.21.0)
+      rubocop-factory_bot (~> 2.26.1)
+      rubocop-graphql (~> 1.5.4)
+      rubocop-performance (~> 1.21.1)
+      rubocop-rails (~> 2.26.0)
+      rubocop-rspec (~> 3.0.4)
+      rubocop-rspec_rails (~> 2.30.0)
+    hashdiff (1.1.2)
+    i18n (1.14.6)
+      concurrent-ruby (~> 1.0)
+    io-console (0.8.0)
+    irb (1.14.1)
+      rdoc (>= 4.0.0)
+      reline (>= 0.4.2)
+    json (2.9.0)
+    language_server-protocol (3.17.0.3)
+    logger (1.6.2)
+    loofah (2.23.1)
+      crass (~> 1.0.2)
+      nokogiri (>= 1.12.0)
+    mini_portile2 (2.8.8)
+    minitest (5.25.4)
+    nokogiri (1.17.1)
+      mini_portile2 (~> 2.8.2)
+      racc (~> 1.4)
+    nokogiri (1.17.1-arm64-darwin)
+      racc (~> 1.4)
+    parallel (1.26.3)
+    parser (3.3.6.0)
+      ast (~> 2.4.1)
+      racc
+    psych (5.2.1)
+      date
+      stringio
+    public_suffix (6.0.1)
+    racc (1.8.1)
+    rack (3.1.8)
+    rack-session (2.0.0)
+      rack (>= 3.0.0)
+    rack-test (2.1.0)
+      rack (>= 1.3)
+    rackup (2.2.1)
+      rack (>= 3)
+    rails-dom-testing (2.2.0)
+      activesupport (>= 5.0.0)
+      minitest
+      nokogiri (>= 1.6)
+    rails-html-sanitizer (1.6.1)
+      loofah (~> 2.21)
+      nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
+    railties (8.0.0.1)
+      actionpack (= 8.0.0.1)
+      activesupport (= 8.0.0.1)
+      irb (~> 1.13)
+      rackup (>= 1.0.0)
+      rake (>= 12.2)
+      thor (~> 1.0, >= 1.2.2)
+      zeitwerk (~> 2.6)
+    rainbow (3.1.1)
+    rake (13.2.1)
+    rdoc (6.8.1)
+      psych (>= 4.0.0)
+    regexp_parser (2.9.3)
+    reline (0.5.12)
+      io-console (~> 0.5)
+    rexml (3.3.9)
+    rspec (3.13.0)
+      rspec-core (~> 3.13.0)
+      rspec-expectations (~> 3.13.0)
+      rspec-mocks (~> 3.13.0)
+    rspec-core (3.13.2)
+      rspec-support (~> 3.13.0)
+    rspec-expectations (3.13.3)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.13.0)
+    rspec-mocks (3.13.2)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.13.0)
+    rspec-rails (7.1.0)
+      actionpack (>= 7.0)
+      activesupport (>= 7.0)
+      railties (>= 7.0)
+      rspec-core (~> 3.13)
+      rspec-expectations (~> 3.13)
+      rspec-mocks (~> 3.13)
+      rspec-support (~> 3.13)
+    rspec-support (3.13.2)
+    rubocop (1.68.0)
+      json (~> 2.3)
+      language_server-protocol (>= 3.17.0)
+      parallel (~> 1.10)
+      parser (>= 3.3.0.2)
+      rainbow (>= 2.2.2, < 4.0)
+      regexp_parser (>= 2.4, < 3.0)
+      rubocop-ast (>= 1.32.2, < 2.0)
+      ruby-progressbar (~> 1.7)
+      unicode-display_width (>= 2.4.0, < 3.0)
+    rubocop-ast (1.36.2)
+      parser (>= 3.3.1.0)
+    rubocop-capybara (2.21.0)
+      rubocop (~> 1.41)
+    rubocop-factory_bot (2.26.1)
+      rubocop (~> 1.61)
+    rubocop-graphql (1.5.4)
+      rubocop (>= 1.50, < 2)
+    rubocop-performance (1.21.1)
+      rubocop (>= 1.48.1, < 2.0)
+      rubocop-ast (>= 1.31.1, < 2.0)
+    rubocop-rails (2.26.2)
+      activesupport (>= 4.2.0)
+      rack (>= 1.1)
+      rubocop (>= 1.52.0, < 2.0)
+      rubocop-ast (>= 1.31.1, < 2.0)
+    rubocop-rspec (3.0.5)
+      rubocop (~> 1.61)
+    rubocop-rspec_rails (2.30.0)
+      rubocop (~> 1.61)
+      rubocop-rspec (~> 3, >= 3.0.1)
+    ruby-progressbar (1.13.0)
+    securerandom (0.4.0)
+    stringio (3.1.2)
+    thor (1.3.2)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    unicode-display_width (2.6.0)
+    uri (1.0.2)
+    useragent (0.16.11)
+    webmock (3.24.0)
+      addressable (>= 2.8.0)
+      crack (>= 0.3.2)
+      hashdiff (>= 0.4.0, < 2.0.0)
+    zeitwerk (2.7.1)
+
+PLATFORMS
+  arm64-darwin
+  ruby
+
+DEPENDENCIES
+  activesupport
+  byebug
+  gitlab-active-context!
+  gitlab-styles
+  rake (~> 13.0)
+  rspec (~> 3.0)
+  rspec-rails
+  rubocop
+  rubocop-rspec
+  webmock
+
+BUNDLED WITH
+   2.5.23
diff --git a/gems/gitlab-active-context/README.md b/gems/gitlab-active-context/README.md
new file mode 100644
index 0000000000000..9a08af1c81ba5
--- /dev/null
+++ b/gems/gitlab-active-context/README.md
@@ -0,0 +1,42 @@
+# GitLab Active Context
+
+`ActiveContext` is a gem used for interfacing with vector stores like Elasticsearch, OpenSearch and Postgres with PGVector for storing and querying vectors.
+
+## Development
+
+After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
+
+## Installation
+
+TODO
+
+## Usage
+
+### Configuration
+
+Add an initializer with the following options:
+
+1. `enabled`: `true|false`. Defaults to `false`
+1. `databases`: Hash containing database configuration options
+1. `logger`: Logger. Defaults to `Logger.new($stdout)`
+
+For example:
+
+```ruby
+ActiveContext.configure do |config|
+  config.enabled = true
+  config.logger = ::Gitlab::Elasticsearch::Logger.build
+
+  config.databases = {
+    es1: {
+      adapter: 'elasticsearch',
+      prefix: 'gitlab',
+      options: ::Gitlab::CurrentSettings.elasticsearch_config
+    }
+  }
+end
+```
+
+## Contributing
+
+TODO
diff --git a/gems/gitlab-active-context/Rakefile b/gems/gitlab-active-context/Rakefile
new file mode 100644
index 0000000000000..cca7175449300
--- /dev/null
+++ b/gems/gitlab-active-context/Rakefile
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require "bundler/gem_tasks"
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new(:spec)
+
+require "rubocop/rake_task"
+
+RuboCop::RakeTask.new
+
+task default: %i[spec rubocop]
diff --git a/gems/gitlab-active-context/bin/console b/gems/gitlab-active-context/bin/console
new file mode 100755
index 0000000000000..03b917ae71f49
--- /dev/null
+++ b/gems/gitlab-active-context/bin/console
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require "bundler/setup"
+require "active_context"
+
+# You can add fixtures and/or initialization code here to make experimenting
+# with your gem easier. You can also use a different console, if you like.
+
+require "irb"
+IRB.start(__FILE__)
diff --git a/gems/gitlab-active-context/bin/setup b/gems/gitlab-active-context/bin/setup
new file mode 100755
index 0000000000000..dce67d860af47
--- /dev/null
+++ b/gems/gitlab-active-context/bin/setup
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+IFS=$'\n\t'
+set -vx
+
+bundle install
+
+# Do any other automated setup that you need to do here
diff --git a/gems/gitlab-active-context/gitlab-active-context.gemspec b/gems/gitlab-active-context/gitlab-active-context.gemspec
new file mode 100644
index 0000000000000..fe9c5bb7f7c3b
--- /dev/null
+++ b/gems/gitlab-active-context/gitlab-active-context.gemspec
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require_relative "lib/active_context/version"
+
+Gem::Specification.new do |spec|
+  spec.name = "gitlab-active-context"
+  spec.version = ActiveContext::VERSION
+  spec.authors = ["GitLab"]
+  spec.email = ["gitlab_rubygems@gitlab.com"]
+
+  spec.summary = "Abstraction for indexing and searching vectors"
+  spec.description = "Abstraction for indexing and searching vectors"
+  spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/gitlab-active-context"
+  spec.license = "MIT"
+  spec.required_ruby_version = ">= 3.1.0"
+
+  spec.metadata["homepage_uri"] = spec.homepage
+
+  spec.files = Dir['lib/**/*.rb']
+  spec.require_paths = ["lib"]
+
+  spec.add_dependency 'zeitwerk'
+
+  spec.add_development_dependency 'gitlab-styles'
+  spec.add_development_dependency 'rspec-rails'
+  spec.add_development_dependency 'rubocop-rspec'
+  spec.add_development_dependency 'webmock'
+
+  spec.metadata["rubygems_mfa_required"] = "true"
+end
diff --git a/gems/gitlab-active-context/lib/active_context.rb b/gems/gitlab-active-context/lib/active_context.rb
new file mode 100644
index 0000000000000..f27e283de1903
--- /dev/null
+++ b/gems/gitlab-active-context/lib/active_context.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require "zeitwerk"
+loader = Zeitwerk::Loader.for_gem
+loader.setup
+
+module ActiveContext
+  def self.configure(...)
+    ActiveContext::Config.configure(...)
+  end
+end
diff --git a/gems/gitlab-active-context/lib/active_context/config.rb b/gems/gitlab-active-context/lib/active_context/config.rb
new file mode 100644
index 0000000000000..60f8e1399e89e
--- /dev/null
+++ b/gems/gitlab-active-context/lib/active_context/config.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module ActiveContext
+  class Config
+    CONFIG = Struct.new(:enabled, :databases, :logger)
+
+    class << self
+      def configure(&block)
+        @instance = new(block)
+      end
+
+      def config
+        @instance&.config || {}
+      end
+
+      def enabled?
+        config.enabled || false
+      end
+
+      def databases
+        config.databases || {}
+      end
+
+      def logger
+        config.logger || Logger.new($stdout)
+      end
+    end
+
+    def initialize(config_block)
+      @config_block = config_block
+    end
+
+    def config
+      struct = CONFIG.new
+      @config_block.call(struct)
+      struct
+    end
+  end
+end
diff --git a/gems/gitlab-active-context/lib/active_context/version.rb b/gems/gitlab-active-context/lib/active_context/version.rb
new file mode 100644
index 0000000000000..a0d650521e46f
--- /dev/null
+++ b/gems/gitlab-active-context/lib/active_context/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module ActiveContext
+  VERSION = "0.0.1"
+end
diff --git a/gems/gitlab-active-context/spec/active_context_spec.rb b/gems/gitlab-active-context/spec/active_context_spec.rb
new file mode 100644
index 0000000000000..6b9bc738671b2
--- /dev/null
+++ b/gems/gitlab-active-context/spec/active_context_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+RSpec.describe ActiveContext do
+  it "has a version number" do
+    expect(ActiveContext::VERSION).not_to be_nil
+  end
+
+  describe '.configure' do
+    let(:elastic) do
+      {
+        es1: {
+          adapter: 'elasticsearch',
+          prefix: 'gitlab',
+          options: { elastisearch_url: 'http://localhost:9200' }
+        }
+      }
+    end
+
+    it 'creates a new instance with the provided configuration block' do
+      ActiveContext.configure do |config|
+        config.enabled = true
+        config.databases = elastic
+        config.logger = ::Logger.new(nil)
+      end
+
+      expect(ActiveContext::Config.enabled?).to be true
+      expect(ActiveContext::Config.databases).to eq(elastic)
+      expect(ActiveContext::Config.logger).to be_a(::Logger)
+    end
+  end
+end
diff --git a/gems/gitlab-active-context/spec/lib/active_context/config_spec.rb b/gems/gitlab-active-context/spec/lib/active_context/config_spec.rb
new file mode 100644
index 0000000000000..d689a0d60fdf8
--- /dev/null
+++ b/gems/gitlab-active-context/spec/lib/active_context/config_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+RSpec.describe ActiveContext::Config do
+  let(:logger) { ::Logger.new(nil) }
+  let(:elastic) do
+    {
+      es1: {
+        adapter: 'elasticsearch',
+        prefix: 'gitlab',
+        options: { elastisearch_url: 'http://localhost:9200' }
+      }
+    }
+  end
+
+  before do
+    described_class.configure do |config|
+      config.enabled = nil
+    end
+  end
+
+  describe '.configure' do
+    it 'creates a new instance with the provided configuration block' do
+      described_class.configure do |config|
+        config.enabled = true
+        config.databases = elastic
+        config.logger = logger
+      end
+
+      expect(described_class.enabled?).to be true
+      expect(described_class.databases).to eq(elastic)
+      expect(described_class.logger).to eq(logger)
+    end
+  end
+
+  describe '.enabled?' do
+    context 'when enabled is not set' do
+      it 'returns false' do
+        expect(described_class.enabled?).to be false
+      end
+    end
+
+    context 'when enabled is set to true' do
+      before do
+        described_class.configure do |config|
+          config.enabled = true
+        end
+      end
+
+      it 'returns true' do
+        expect(described_class.enabled?).to be true
+      end
+    end
+  end
+
+  describe '.databases' do
+    context 'when databases are not set' do
+      it 'returns an empty hash' do
+        expect(described_class.databases).to eq({})
+      end
+    end
+
+    context 'when databases are set' do
+      before do
+        described_class.configure do |config|
+          config.databases = elastic
+        end
+      end
+
+      it 'returns the configured databases' do
+        expect(described_class.databases).to eq(elastic)
+      end
+    end
+  end
+
+  describe '.logger' do
+    context 'when logger is not set' do
+      it 'returns a default stdout logger' do
+        expect(described_class.logger).to be_a(Logger)
+      end
+    end
+
+    context 'when logger is set' do
+      before do
+        described_class.configure do |config|
+          config.logger = logger
+        end
+      end
+
+      it 'returns the configured logger' do
+        expect(described_class.logger).to eq(logger)
+      end
+    end
+  end
+end
diff --git a/gems/gitlab-active-context/spec/spec_helper.rb b/gems/gitlab-active-context/spec/spec_helper.rb
new file mode 100644
index 0000000000000..d32874b9f34be
--- /dev/null
+++ b/gems/gitlab-active-context/spec/spec_helper.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require "active_context"
+require 'logger'
+
+RSpec.configure do |config|
+  # Enable flags like --only-failures and --next-failure
+  config.example_status_persistence_file_path = ".rspec_status"
+
+  # Disable RSpec exposing methods globally on `Module` and `main`
+  config.disable_monkey_patching!
+
+  config.expect_with :rspec do |c|
+    c.syntax = :expect
+  end
+end
-- 
GitLab