brave / brave-browser

Brave browser for Android, iOS, Linux, macOS, Windows.
https://brave.com
Mozilla Public License 2.0
17.53k stars 2.27k forks source link

AI Chat service refactor to support multiple-conversation, persistence, and standalone page #31623

Open petemill opened 1 year ago

petemill commented 1 year ago

As of now, most AI Chat functionality is handled by a TabHelper, reflecting the single use-case focused around an active Tab, via the sidebar. Those conversations are not persisted and not accessible outside of the Sidebar for the specific Tab.

A refactor will support:


AIChatService (Profile KeyedService)

Manages conversations for the profile. Stores all conversations in database and manages retrieval.

AssociatedContentDelegate (on desktop implemented by AIChatTabHelper)

Supplements a conversation with page content

ai_chat::Conversation

In-memory store for a conversation and its data. Instances are owned by AIChatService. \ Builds prompts for new messages based on conversation history and metadata.

ai_chat::ConversationEntry

struct

ai_chat::engine_consuming

A set of functions which build and use various prompts based on model and data, makes model decisions, and sends requests to either local LLM or remote API. Might use other files for engine/model-specific constants and algorithms


UI Flow

The mojom between WebUI C++ and WebUI JS will change to provide functions for starting new conversations, getting the conversations list metadata, and providing the entries and other state properties of the active conversation. It will also provide events for both when the active conversation state changes and when the list of all conversations’ metadata changes.

Sidebar

The WebUI c++ knows it is open as a sidebar and has a target Tab WebContents. The UI can then be provided with a specific Conversation to be open at via AIChatTabHelper::GetAssociatedConversation, which will return an existing or new Conversation. The UI will also allow a different conversation to be chosen and made active.

Standalone

The page will open with no conversation selected. A list of all conversations will be displayed as well as a prompt to start a new conversation. Any conversation can be chosen and made active, and new conversations can be created.

Incognito

Incognito and regular profiles will be treated as independent. Incognito version will not use any persistence.

Android

The same WebUI will be used on Android


Storage

We will use SQLite to store all conversations from the regular profile. No conversations from incognito profile will be persisted.

yrliou commented 6 months ago

Just a quick note, I don't think we should use web_contents directly in Conversation(Driver), we shouldn't use something from content layer here, instead, I think we might want a delegate maybe named PageContextDelegate which AIChatTabHelper would implement.

yrliou commented 6 months ago

Things currently owned by conversation_driver, like ai_chatmetrics(not owned instance), credentialmanager, feedbackapi, should be owned by AIChatService instead if it doesn't need to be one instance for each conversation. Needed things can be passed in the service factory. And Conversation(Driver) can have a raw_ptr if needed for those which will be passed when AIChatService create the conversation.

We can also create and own instances of EngineConsumerClaude, EngineConsumerLlama in AIChatService, and take model parameter for functions which submit queries.

We should also remove the inheritance between tab_helper and conversation_driver, creation of tab_helper will need less parameters since most of things are needed by conversation_driver and not the tab_helper.

petemill commented 6 months ago

Things currently owned by conversation_driver, like ai_chatmetrics(not owned instance), credentialmanager, feedbackapi, should be owned by AIChatService instead

Absolutely

We should also remove the inheritance between tab_helper and conversation_driver, creation of tab_helper will need less parameters since most of things are needed by conversation_driver and not the tab_helper.

Yes that was the plan I had too. I thought it was explicitly mentioned above but maybe an edit wasn't submitted a while ago, or maybe it was in a DM conversation with someone when iOS was started and ConversationDriver was extracted from the original AIChatTabHelper, but either way the above was actually designed before ConversationDriver subclassing AIChatTabHelper was implemented for iOS. So the implication was that Conversation and the AIChatTabHelper were separate classes with an optional association. Content association should be via delegate so that standalone conversations will be nicer.