Personal finance AI (v1) #2022
Reference in New Issue
Block a user
Delete Branch "zachgoll/ai-improvements"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
This PR is a continuation of #1985 and finalizes the "V1" of the personal finance AI chat feature.
Domain overview
Chat- has many messages, has one "assistant"Chat::Debuggable- defines "debug mode", where the chat will persist verbose debug messages to help better understand the path it took to get to its final responseMessage- a message can be a "user message", "assistant message", or "developer message" (uses STI)ToolCall- belongs to aMessageand is a "subroutine" a message uses to augment its response. Tool calls are only relevant forassistantmessages and are optional.Assistant- owned byChat, this represents a generic "LLM assistant" that can perform chat completionsAssistant::Providedis responsible for finding the correct provider for a given response (i.e. the user can select a model to use for each message)Assistant::Functions- the assistant comes with a library of pre-defined "functions" that the LLM provider can call to augment chat responses. AnAssistant::Functions::ConcreteFunctionmust provide aname,description,parametersschema, andcall(params = {})method. These are passed to and executed by the Provider.Provider::ConcreteLLMProvider(e.g.Provider::OpenAI)Assistant::Provideabledefines the interface that all LLM providers must implement to provide completions for the assistantSwappable LLM Providers
This first version of the chat implements a single
Provider::OpenAI, which implements to theAssistant::Provideableinterface.Provider responsibilities
Each "LLM Provider" is responsible for:
Assistant::Provideable::ChatResponsefor theAssistantto useConcrete LLM implementations
To introduce a new LLM, simply implement the interface:
This way,
Assistantcan easily choose different models for each chat message:Turbo frames, streams, and broadcasts
The AI chat feature is entirely contained within the global sidebar, and uses Turbo frames to load the various resource views for the
Chatresource (i.e.new,show,index).In
application.html.erblayout, thechat_view_path(@chat)helper is used to determine which resource view should currently show in the chat sidebar:@chatis set and it is a persisted record, the sidebar loads theshowpath@chatis set and its a new record, the sidebar loadsnew@chatisnil, showindexChat state
There are two important concepts for managing the sidebar chat state:
@chatcontroller instance variableThe
@chatvariable is set via inheritance inApplicationControllerandChatsController. In other words, the sidebar defaults to showing the "last viewed chat" unless told otherwise by a more specific action in the inheritance hierarchy:Broadcasts and "thinking"
Assistant responses run in background jobs and therefore require a "thinking" indicator. The base
Messagemodel implements bothcreateandupdatecallbacks, which both broadcast the changes to the Chat ifbroadcast?returns true on the specific type.showaction can have a?thinking=trueparam to trigger the AI "thinking" message. TheAssistantResponseJobis then responsible for removing that message when the response is complete (otherwise, it is just removed on the next page refresh since the param is passed in a turbo frame). SeeChatsController#createandMessagesController#createwhere weredirect_to chat_path(@chat, thinking: true)to immediately show the "thinking" message.Hey I'd love to use this and try it out! I'm self hosting now, is it possible to expose an env var to change the openai servers to an internal one? I'm specifically hosting openwebui and it should (theoretically) just be a drop in replacement.