microsoft / teams-ai

SDK focused on building AI based applications and extensions for Microsoft Teams and other Bot Framework channels
MIT License
363 stars 156 forks source link

Builder pattern to create Application instance #615

Closed singhk97 closed 7 months ago

singhk97 commented 10 months ago

The Application class is big, and connects multiple big components, like the Turn state, AI module, the Activity handlers, and Storage. There are many different configurations you can use to create this class. Right now, to create such an instance you simply populate a single ApplicationOptions object and pass it to the class. This lacks the necessary readability to understand the different components that are being registered to the application object.

The ApplicationBuilder class provides a clear public interface to simplify the creation of an Application instance, allowing end users to build the application step by step. This improves readability and makes the configuration process less overwhelming.

before:

const app = new Application<TState>({
    storage,
    adapter,
    botAppId,
    ai: {
        planner,
        promptManager,
        prompt: 'chatGPT'
    }
    removeRecipientMention: true,
    startTypingTimer: true,
    longRunningMessages: true,
});

after:

const app = new ApplicationBuilder<TState>()
    .configureOptions({
        removeRecipientMention: true,
        startTypingTimer: true,
    })
    .withStorage(storage)
    .withAI({
        planner,
        promptManager,
        prompt: 'chatGPT'
    })
    .withLongRunningMessages(adapter, botAppId)
    .build();

Here's the ApplicationBuilder public interface for the JS SDK:

/**
 * A builder class for creating an Application instance.
 * @template TState Optional. Type of the turn state. This allows for strongly typed access to the turn state.
 */
export class ApplicationBuilder<TState extends TurnState = DefaultTurnState> {
    /**
     * Creates a new ApplicationBuilder instance.
     */
    public constructor();

    /**
     * Configures the storage system to use for storing the bot's state.
     * @param {Storage} storage The storage system to use.
     * @returns {this} The ApplicationBuilder instance.
     */
    public withStorage(storage: Storage): this;

    /**
     * Configures the AI system to use for processing incoming messages.
     * @param {AIOptions<TState>} aiOptions The options for the AI system.
     * @returns {this} The ApplicationBuilder instance.
     */
    public withAI(aiOptions: AIOptions<TState>): this;

    /**
     * Configures the turn state manager to use for managing the bot's turn state.
     * @param {TurnStateManager<TState>} turnStateManager The turn state manager to use.
     * @returns {this} The ApplicationBuilder instance.
     */
    public withTurnStateManager(turnStateManager: TurnStateManager<TState>): this;

    /**
     * Configures the application to use long running messages.
     * @param {BotAdapter} adapter The instance to use for routing incoming requests.
     * @param {string} botAppId The Microsoft App ID for the bot.
     */
    public withLongRunningMessages(adapter: BotAdapter, botAppId: string): this;

    /**
     * Configures additional options for the Application instance.
     * @param {Partial<ApplicationOptions<TState>>} options The options to configure.
     * @returns {this} The ApplicationBuilder instance.
     */
    public configureOptions(options: Partial<ApplicationOptions<TState>>): this;

    /**
     * Builds and returns a new Application instance.
     * @returns {Application<TState>} The Application instance.
     */
    public build(): Application<TState>;
}
The following Adjunct Changes in this drop down is not going to implement anymore. Since this will create big disparity with Python SDK, which won't be implementing the builder pattern

Adjunct Changes

- Refactored `ApplicationOptions` class to only include configurations and moved the major components to the `Application` class constructor. The idea is that an options class should only have knobs and not major components of the library. ```ts /** * Options for the Application class. * @template TState Type of the turn state. */ export interface ApplicationOptions { /** * Optional. Options used to customize the processing of Adaptive Card requests. */ adaptiveCards?: AdaptiveCardsOptions; /** * Optional. Options used to customize the processing of task module requests. */ taskModules?: TaskModulesOptions; /** * Optional. If true, the bot will automatically remove mentions of the bot's name from incoming * messages. Defaults to true. */ removeRecipientMention: boolean; /** * Optional. If true, the bot will automatically start a typing timer when messages are received. * This allows the bot to automatically indicate that it's received the message and is processing * the request. Defaults to true. */ startTypingTimer: boolean; /** * Optional. If true, the bot supports long running messages that can take longer then the 10 - 15 * second timeout imposed by most channels. Defaults to false. * @summary * This works by immediately converting the incoming request to a proactive conversation. Care should * be used for bots that operate in a shared hosting environment. The incoming request is immediately * completed and many shared hosting environments will mark the bot's process as idle and shut it down. */ longRunningMessages: boolean; } ```
### Tasks
- [ ] https://github.com/microsoft/teams-ai/issues/639
- [ ] https://github.com/microsoft/teams-ai/issues/652
- [ ] https://github.com/microsoft/teams-ai/issues/710
singhk97 commented 10 months ago

Should add configureTaskModule and configureAdaptiveCards, to configure these options

corinagum commented 9 months ago

The idea is that an options class should only have knobs and not major components of the library.

@singhk97 Not sure I understand this. The builder is setting up the app which is making use of the entire library. We want knobs in the sense that a lot of these features will exist regardless of user setup -- we don't want to NOT enable Adaptive Cards just because they didn't provide the explicit call to set it up.