Built-in Services

Overridable built-in services

Overview

To provide greater flexibility and avoid the over-engineering often associated with framework libraries, along with the confusing and conflicting typical WithThis(), AddThat() chaining methods, Wisej.AI employs overridable services for higher-level functionalities. This design allows developers to easily customize these functionalities to suit their specific needs.

Existing Services

Here are the services utilized by various Wisej.AI components, along with their default implementations:

Service
Implementations
Used by

DefaultOCRService

SmartObjectAdapter

SmartDataEntryAdapter

DefaultLoggerService

Everywhere

RecursiveCharacterTextSplitterService

SmartHub

DefaultTokenizerService

DatabaseTools

WebSearchTools

DocumentSearchTools

DefaultHttpClientService

SmartHttpEndpoint

PineconeEmbeddingStorageService

AzureAISearchEmbeddingStorageService

ChromaEmbeddingStorageService

HuggingFaceEmbeddingGenerationService

WebSearchTools

BingWebSearchService

GoogleWebSearchService

BraveWebSearchService

WebSearchTools

DefaultSessionTrimmingService

SmartSession

DefaultRerankingService

DocumentTools DocumentSearchTools

DefaultDocumentConversionService

SmartHub

SmartReportAdapter

WebSearchTools

MemoryEmbeddingStorageService

FileSystemEmbeddingStorageService

ChromaEmbeddingStorageService

PineconeEmbeddingStorageService

AzureAISearchEmbeddingStorageService

SmartHub

SmartDocumentAdapter DocumentTools

DocumentSearchTools

DefaultEmbeddingGenerationService

SmartHub DocumentTools

DocumentSearchTools

Replacing Services

To replace any of the built-in services, simply implement the corresponding interface and register your service. The optimal place to perform this registration is in the static constructor of the Program class or module in VB.NET.

Alternatively, you can extend any of the built-in service implementations by overriding the virtual properties and methods. In this case, you still need to create your own service class, but instead of implementing the interface from scratch, you extend the existing service implementation and override only the parts you wish to modify.

For instance, if you want to use Aspose PDF for converting PDF documents to text while retaining the default IDocumentConversionService, you can follow this approach:

public class AsposeDocumentConversionService 
	: DefaultDocumentConversionService
{
  static AsposeDocumentConversionService()
  {
    new Aspose.Pdf.License()
      .SetLicense(
        File.OpenRead(Application.MapPath("Aspose.Pdf.lic")));
  }
  
  public string[] Convert(
          Stream stream, 
          string fileType, Metadata metadata = null)
  {
    if (fileType == "pdf")
      return ConvertPdfToText(stream, metadata);

    return base.Convert(stream, fileType);
  }
}

When the default implementation doesn't offer virtual methods, you can still leverage it by maintaining an instance of the default implementation within your service implementation. Here's how you can do it:

public class AsposeDocumentConversionService 
	: IDocumentConversionService
{
  private IDocumentConversionService _baseService;
  
  static AsposeDocumentConversionService()
  {
    new Aspose.Pdf.License()
      .SetLicense(
        File.OpenRead(Application.MapPath("Aspose.Pdf.lic")));
  }
  
  public AsposeDocumentConversionService()
  {
    _baseService = new DefaultDocumentConversionService();
  }
  
  public string[] Convert(
          Stream stream, 
          string fileType, Metadata metadata = null)
  {
    if (fileType == "pdf")
      return ConvertPdfToText(stream, metadata);

    return _baseService.Convert(stream, fileType);
  }
}

In place of invoking the base or MyBase methods, utilize the _baseService instance.

Services Lifetime

You are not required to consistently use the same service once you decide to register your own replacements. Wisej.AI registers the following services with a Transient lifetime:

IOCRService, IHttpClientService, ITextSplitterService, IDocumentConversionService

This means that each time a service is requested, the consumer receives a new instance of the implementation. This approach allows for flexibility and adaptability, enabling you to customize and replace services as needed without being tied to a single instance.

For example, the SmartHub component requests all the services it utilizes at the time of its creation, as do all the adapters. If you wish to use different services for each instance, ensure that you register the new services before the SmartHub or the adapters are instantiated. This allows you to customize the behavior of each component by providing them with the specific services they require, ensuring that they operate with the desired functionality.

The following services are registered with a Shared lifetime, meaning that only a single instance of each service is created and shared across the entire application:

ILoggerService, ITokenizerService, IEmbeddingStorageService, IEmbeddingGenerationService, ISessionTrimmingService.

When registering your own services, you have the flexibility to change the lifetime of any service.

Last updated