`ActiveContext::Task` is a framework for managing long-running, asynchronous operations within the ActiveContext system. It provides a structured way to define, execute, and track complex workflows that may involve multiple sequential or dependent steps.
This is designed to handle operations like embedding model switching, embeddings field creation and backfill, and metadata synchronization that require careful orchestration and error handling.
## Components
### Task Base Class (`ActiveContext::Task[1.0]`)
The abstract base class for all task implementations and defines the interface that all subclasses must follow.
The concrete class implementations define the actual logic of the task.
**Batched Tasks:**
Tasks can be marked as `batched!` to indicate they perform work in batches and may need multiple executions to complete. For batched tasks, `completed?` must be implemented to determine when all work is done.
**Example Task class implementations**
-`Ai::ActiveContext::Tasks::BackfillEmbeddings`: Generates embeddings for existing documents using a new embedding model.
-`Ai::ActiveContext::Tasks::SyncFeatureSettings`: Synchronizes feature settings with the embedding model configuration.
### Task Model (`Ai::ActiveContext::Task`)
The ActiveRecord model that persists task information.
**Database Schema:**
-`connection_id` - Foreign key to the vector store connection
-`depends_on_id` - Foreign key to the preceding task (for [task chains](#task-chains))
-`name` - The task class name
-`status` - Current execution status (`pending`, `in_progress`, `completed`, `failed`)
-`params` - JSON parameters passed to the task
-`retries_left` - Number of retry attempts remaining (default: 3)
-`started_at` - When execution began
-`completed_at` - When execution finished
-`error_message` - Error details if the task failed
The AI Context Abstraction Layer is a critical component that provides a unified interface for semantic search across different vector stores (Elasticsearch, OpenSearch, PostgreSQL with pgvector). This layer needs to be:
1. Reusable across multiple GitLab services
2. Maintainable and testable in isolation
3. Clearly separated from GitLab-specific implementations
4. Extensible for future vector store backends
## Decision
We have decided to:
1. Implement the ActiveContext Abstraction Layer as a Ruby gem located in `gems/gitlab-active-context` within the GitLab Rails repository
2. Name the gem `ActiveContext`, thus all gem classes are namespaced under `ActiveContext::`
3. GitLab-specific implementations should be namespaced under `Ai::ActiveContext` (for example, `Ai::ActiveContext::Collections::Code`, `Ai::ActiveContext::References::Code`)
The ActiveContext framework needs to support long-running, asynchronous operations that may span multiple steps and require careful orchestration.
For example, switching from one embedding model to another requires:
1. Creating a new vector field
1. Backfilling embeddings for all documents
1. Updating collection metadata
1. Syncing feature settings
1. Cleaning up old fields
### Challenges with Existing Approaches
**Simple Sidekiq Workers:**
- No built-in support for task dependencies
- Difficult to handle cascading failures
- No visibility into multi-step workflows
- Hard to retry individual steps in a chain
**Orchestration Services:**
- Complex state management
- Difficult to recover from crashes
- No persistence of workflow state
- Hard to debug long-running operations
## Decision
We have decided to introduce the [**ActiveContext Task Framework**](../active_context_tasks.md) for managing long-running, asynchronous operations. It provides a structured way to define, execute, and track complex workflows that may involve multiple sequential or dependent steps.
- Model selection by the user cannot be supported, making it a blocker for Self-Managed instances with Duo Self-hosted, where the user must select their own models
- Adding new models requires updating the hard-coded version hashes and doing a manual backfill process, creating maintenance burden
### Existing Model Configuration feature
There is already an existing [Model Configuration admin page](https://docs.gitlab.com/administration/gitlab_duo/model_selection/) for other AI features. However, this cannot support the backfill process needed when switching embedding models.
## Decision
We have decided to redesign the embedding models to support flexible model selection with one-click model switching that triggers a backfill process in the background.
The main concepts of the redesign are:
-**Model Metadata** - embedding model configuration is persisted in `Ai::ActiveContext::Collection` record `metadata` rather than hard-coded
-**3 model configurations per Collection**
-`current_indexing_embedding_model`: The model currently used for indexing content
-`next_indexing_embedding_model`: The model queued to replace the current model (used when switching to a new model)
-`search_embedding_model`: The model used for query embeddings during search. This matches the `current_indexing_embedding_model`
-**Asynchronous switching process** - Model switching triggers an asynchronous process that leverages the [Active Context Tasks framework](../active_context_tasks.md)
-**Indirect syncing with AI Feature Settings** - A Collection's `current_indexing_embedding_model` will be synced with the `Ai::FeatureSetting` records used by other AI features
For further details, please see the [Embedding Models design document](../embedding_models.md).