Skip to content

.Net: First round of MEVD DI changes #10960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

roji
Copy link
Member

@roji roji commented Mar 13, 2025

  • Align to the .NET (and MEAI) standard of having AddQdrant() (no service key) and AddKeyedQdrant() (non-optional service key).
  • Rename serviceId to the more standard serviceKey naming, and change its type from string to object.
  • Add ServiceLifetime parameters to all methods.
  • Obsolete the KernelBuilderExtensions, in preparation for #10855

Contributes to #10549

/cc @westey-m @adamsitnik @dmytrostruk

@roji roji requested a review from a team as a code owner March 13, 2025 16:07
@markwallace-microsoft markwallace-microsoft added .NET Issue or Pull requests regarding .NET code kernel Issues or pull requests impacting the core kernel memory labels Mar 13, 2025
@github-actions github-actions bot changed the title First round of MEVD DI changes .Net: First round of MEVD DI changes Mar 13, 2025
Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code LGTM, but IMO if we want to introduce any breaking changes we should introduce more changes (remove most of the overloads, accept factory methods that take ServiceProvider as an argument). PTAL at my comments and let me know what is your opinion.

/// <param name="serviceId">An optional service id to use as the service key.</param>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to which the vector store should be added.</param>
/// <param name="options">Options to further configure the <see cref="IVectorStore"/>.</param>
/// <param name="lifetime">The service lifetime for the store. Defaults to <see cref="ServiceLifetime.Transient"/>.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Transient is the default? I would hope Azure AI Search SDK client to be the same as other Azure SDK clients:

https://devblogs.microsoft.com/azure-sdk/lifetime-management-and-thread-safety-guarantees-of-azure-sdk-net-clients/#client-lifetime

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree - this is specifically the thing I want to change in the follow-up to this PR, just didn't want to put everything in at once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I agree - see #10549 (comment) and below for the discussion. I'm mainly saving this for a later PR as it's a more important behavioral change, and I didn't want to do it all in one PR.

/// <param name="lifetime">The service lifetime for the store. Defaults to <see cref="ServiceLifetime.Singleton"/>.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddKeyedAzureAISearchVectorStore(
this IServiceCollection serviceCollection,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is the objectKey nullable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be, thanks.

Uri endpoint,
AzureKeyCredential credential,
AzureAISearchVectorStoreOptions? options = default,
ServiceLifetime lifetime = ServiceLifetime.Transient)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that all these overloads already existed, but I just want to share my experience from HealthChecks project. You can read Xabaril/AspNetCore.Diagnostics.HealthChecks#2040, but to tell the long story short:

  • all Azure SDK clients should be registered as singletons by default (they are thread safe)
  • users may need to access IServiceProvider when creating the option bags (to for example read sth from the config system)
  • each client may provide various ctors that accept various arguments. To simplify the API we can just allow the users to provide a factory method that returns the client rather than having a dedicated overload for each client ctor (like here with Url, TokenCredential etc)

Having said that, the API shape could be following:

public static IServiceCollection AddAzureAISearchVectorStore(
    this IServiceCollection serviceCollection,
    Func<IServiceProvider, SearchIndexClient>? clientFactory = default, // when not provided, we just resolve it from the DI
    Func<IServiceProvider, AzureAISearchVectorStoreOptions>? optionsFactory = default,
    ServiceLifetime lifetime = ServiceLifetime.Singleton)
    
public static IServiceCollection AddKeyedAzureAISearchVectorStore(
    this IServiceCollection serviceCollection,
    object serviceKey // mandatory to provide,
    Func<IServiceProvider, SearchIndexClient>? clientFactory = default, // when not provided, we just resolve it from the DI
    Func<IServiceProvider, AzureAISearchVectorStoreOptions>? optionsFactory = default,
    ServiceLifetime lifetime = ServiceLifetime.Singleton)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all Azure SDK clients should be registered as singletons by default (they are thread safe)

Yep, see above.

users may need to access IServiceProvider when creating the option bags (to for example read sth from the config system)

Good point - I overlooked that, thanks! I'm thinking to add an optional Func<IServiceProvider, SearchIndexClient> parameter to the existing APIs; if it's provided, it's used, otherwise we attempt to resolve the SearchIndexClient from DI. How does that sound?

each client may provide various ctors that accept various arguments. To simplify the API we can just allow the users to provide a factory method that returns the client rather than having a dedicated overload for each client ctor (like here with Url, TokenCredential etc)

Sure, though I think that it's a bit nicer for the easy/default path to just have such sugar on it, so a single UseAzureAISearchVectorStore() call is enough to set everything up. Of course we shouldn't go overboard with it and add many of these.

string collectionName,
Uri endpoint,
TokenCredential tokenCredential,
AzureAISearchVectorStoreRecordCollectionOptions<TRecord>? options = default,
string? serviceId = default)
ServiceLifetime lifetime = ServiceLifetime.Singleton)
=> AddKeyedAzureAISearchVectorStoreRecordCollection(serviceCollection, serviceKey: null, collectionName, endpoint, tokenCredential, options, lifetime);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is my understanding correct that it's going to work only for a single collection? As soon as the user tries to register two, we are going to fail?

I wonder if we should simply always use Keyed DI for collections, were the key would be the collection name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea here is that the single-collection scenario is common enough for this to make sense as dedicated sugar... Users do have AddKeyedAzureAISearchVectorStoreRecordCollection it they want to work with multiple collections. Does that make sense?

@westey-m westey-m deleted the branch microsoft:feature-vector-data-preb2 April 29, 2025 15:43
@westey-m westey-m closed this Apr 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kernel Issues or pull requests impacting the core kernel memory .NET Issue or Pull requests regarding .NET code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants