1
\$\begingroup\$

I've recently shifted from SpringAI M5 to 1.0.0 which is a major change and just wondering if there is a better way to organise SpringAI configurations. I'm using Azure OpenAI based connection along with support for embedding model.

My goal is to use multiple models from same provider but as you can see that the model is hardcoded in bean definitions.

This setup is functional but not flexible to build multi-model features and I’m looking for a more recommended way of creating chat clients and vector store. The documentation consists of examples where chat clients are configured on the go with each api call which I think is not very clean if i just have several models to pick and choose from.

Also, over the course of several milestones, the configurations are somewhat convoluted and overlapping i feel. I’m still learning the ropes of this library.

Here are my configurations:

AIConfigurations.java


import com.azure.ai.openai.OpenAIClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AccessToken;
import com.azure.core.credential.TokenCredential;
import com.azure.core.util.ClientOptions;
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;
import reactor.core.publisher.Mono;

import java.time.OffsetDateTime;
import java.util.List;

import static com.azure.ai.openai.OpenAIServiceVersion.V2023_05_15;
import static io.micrometer.observation.ObservationRegistry.NOOP;
import static java.time.Duration.ofHours;
import static org.springframework.ai.document.MetadataMode.EMBED;

/**
 * Defines Gen AI configuration
 *
 */
@Configuration
public class OpenAiConfig {

    @Value("${spring.ai.azure.openai.endpoint:}")
    private String chatModelEndpoint;

    @Value("${spring.ai.azure.openai.chat.options.deployment-name:}")
    private String chatModelDeploymentName;

    @Value("${spring.ai.azure.openai.embedding.endpoint:}")
    private String embeddingModelEndpoint;

    @Value("${spring.ai.azure.openai.embedding.options.deployment-name:}")
    private String embeddingModelDeploymentName;

    @Value("${spring.ai.azure.openai.chat.options.api-version:}")
    private String apiVersion;

    @Value("${spring.ai.azure.openai.token.lifetime:10}")
    private int tokenLifetime;

    @Value("#{'${template.file.paths}'.split(',')}")
    private List<String> templateFilePaths;

    private final AzureAuthenticationService azureAuthenticationService;

    /**
     * Constructs an OpenAiConfig instance with the provided AzureAuthenticationService.
     *
     * @param azureAuthenticationService AzureAuthenticationService to be used for fetching access tokens.
     */
    public OpenAiConfig(AzureAuthenticationService azureAuthenticationService) {
        this.azureAuthenticationService = azureAuthenticationService;
    }

    /**
     * Creates a TokenCredential bean that provides access tokens for Azure services.
     * The token is fetched from Microsoft Entra ID (formerly Azure AD) and has a configurable lifetime.
     *
     * @return TokenCredential instance that provides Azure access tokens
     */
    @Bean
    public TokenCredential tokenCredential() {
        return tokenRequestContext ->
                Mono.just(new AccessToken(azureAuthenticationService.fetchAccessToken(),
                        OffsetDateTime.now().plus(ofHours(tokenLifetime))));
    }

    /**
     * Creates an OpenAIClientBuilder configured with endpoint, credentials, and service options.
     * This builder is used to establish connections to Azure OpenAI services.
     *
     * @return OpenAIClientBuilder instance ready for creating OpenAI clients.
     */
    @Bean
    public OpenAIClientBuilder openAIClientBuilder() {
        return new OpenAIClientBuilder()
                .endpoint(chatModelEndpoint)
                .credential(tokenCredential())
                .serviceVersion(V2023_05_15)
                .clientOptions(new ClientOptions().setApplicationId("spring-ai"));
    }

    /**
     * Configures an AzureOpenAiChatModel with appropriate deployment settings and parameters.
     *
     * @param openAIClientBuilder The builder used to establish connections to Azure OpenAI.
     * @return AzureOpenAiChatModel instance with specified deployment settings
     */
    @Bean
    public AzureOpenAiChatModel azureOpenAiChatModel(OpenAIClientBuilder openAIClientBuilder) {
        return AzureOpenAiChatModel.builder()
                .openAIClientBuilder(openAIClientBuilder)
                .defaultOptions(AzureOpenAiChatOptions.builder()
                        .deploymentName(chatModelDeploymentName)
                        .build())
                .build();
    }

    /**
     * Creates a ChatClient that provides a high-level interface for interacting with Azure OpenAI.
     *
     * @param azureOpenAiChatModel the configured model to use for chat completions.
     * @return ChatClient instance ready for sending prompts and receiving completions
     */
    @Bean
    public ChatClient chatClient(AzureOpenAiChatModel azureOpenAiChatModel) {
        return ChatClient.create(azureOpenAiChatModel);
    }

    /**
     * Creates an OpenAIClient configured with endpoint, credentials, and service options.
     * This client is used for embedding operations with Azure OpenAI services.
     *
     * @return OpenAIClient instance ready for embedding operations.
     */
    @Bean
    public OpenAIClient openAIClient() {
        return new OpenAIClientBuilder()
                .endpoint(embeddingModelEndpoint)
                .credential(tokenCredential())
                .serviceVersion(V2023_05_15)
                .clientOptions(new ClientOptions().setApplicationId("spring-ai")).buildClient();
    }

    /**
     * Configures an AzureOpenAiEmbeddingModel with appropriate deployment settings and parameters.
     * This model is used for generating vector embeddings from text, which are essential for
     * document similarity search and retrieval operations.
     *
     * @param openAIClient The OpenAI client used to connect to Azure OpenAI services
     * @return AzureOpenAiEmbeddingModel instance configured for text embedding operations
     */
    @Bean
    public AzureOpenAiEmbeddingModel embeddingModel(OpenAIClient openAIClient) {
        return new AzureOpenAiEmbeddingModel(
                openAIClient,
                EMBED,
                AzureOpenAiEmbeddingOptions.builder()
                        .deploymentName(embeddingModelDeploymentName)
                        .user("user-6")
                        .build(),
                NOOP);
    }

    /**
     * Creates and configures an STGroup bean for template processing.
     *
     * @return STGroup instance initialized with the specified template file.
     */
    @Bean
    public STGroup promptTemplateGroup() {
        STGroup compositeGroup = new STGroup();
        for (String path : templateFilePaths) {
            compositeGroup.importTemplates(new STGroupFile(path.trim()));
        }
        return compositeGroup;
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ hm not sure if you better ask in stackoverflow: "I’m looking for a more recommended way of creating chat clients and vector store. " - this looks rather like implementation help than looking for a review (no offense) \$\endgroup\$ Commented Jun 18 at 8:30
  • 1
    \$\begingroup\$ I did post it in stack overflow first where it was closed within few minutes stating that this is a code review request not a question. \$\endgroup\$ Commented Jun 19 at 4:09
  • \$\begingroup\$ sorry, you're stuck between stack exchange - i wanted to add value, not making it harder for you :-D \$\endgroup\$ Commented Jun 20 at 9:20
  • \$\begingroup\$ well - maybe you can use the same technic as here (using two databases), the more detailed answer is here \$\endgroup\$ Commented Jun 23 at 6:39

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.