OvidijusParsiunas / deep-chat

Fully customizable AI chatbot component for your website
https://deepchat.dev
MIT License
1.27k stars 175 forks source link

persist messages and api key using localStorage/IndexedDB #89

Closed bruffridge closed 5 months ago

bruffridge commented 5 months ago

Is it possible to persist the messages and OpenAI API key in the browser's localStorage or IndexedDB? I'm using a simple Svelte implementation of <deep-chat> with npm deep-chat-dev package with OpenAI Assistants directConnection.

Great work on this btw! I've tested out function calling, code interpreter, and file upload and retrieval all work out of the box!

OvidijusParsiunas commented 5 months ago

Hi @bruffridge. Great to hear you had a good experience when working with Deep Chat.

Yes, it is possible to store the OpenAI key, messages and anything else in the localStorage, it is actually how we store data in our playground. The strategy to do this will depend on your overall app implementation, but by referring to our playground code, you can store data like so:

localStorage.setItem('deep-chat-data', JSON.stringify({apiKey: '', initialMessages: []}));

Playground code

Then assign it to a default config if localStorage.getItem('deep-chat-data') already exists: Object.assign(config, JSON.parse(localStorage.getItem('deep-chat-data')));

Playground code

And apply it to Deep Chat:

<deep-chat directConnection={openAI: {key: config.apiKey}} initialMessages={config.initialMessages}></deep-chat>

Playground code

The above implementation is just pseudo-code and will have to be tailored to your implementation, but hopefully it helps.

I have personally never used indexDB, but this sort of use-case can be done with any storage system.

Hopefully the code above helps you get started.

Let me know if you need any help. Thanks!

bruffridge commented 5 months ago

Thanks! I'll give this a try. I'll have to figure out how to update localStorage every time new messages are added to the conversation. It looks like initialMessages are just for informational messages, and aren't sent to the API as part of the message conversation.

bruffridge commented 5 months ago

Is it possible to expose a new method or event so we can get access to the key after it is set in the UI? I'd like to use the built-in key setting UI rather than rolling my own just to get access to the key.

OvidijusParsiunas commented 5 months ago

In regards to the persisting messages in localStorage - you can use the onNewMessage event and store new messages into an array that would be stored inside localStorage. This is very context dependent, hence there are countless different ways you can approach it, e.g. update localStorage every time onNewMessage is triggered or collect all messages into an array and store it in local storage before the browser closes via window.addEventListener('beforeunload', ...) (this is how the playground does it). I recommend trying out different approaches to see which one works for you.

In regards to getting access to the key, if you have set it as a property using directConnection, you can simply access it by deepChatRef.directConnection which will return the object including the key. If the user has inserted the key via the built in key insertion interface, you can get access to the key via deepChatRef._activeService object which contains a key property. To note, I plan to revamp that interface in the future, hence this is the best it can do at the moment.

bruffridge commented 5 months ago

This is what I needed thanks! In case it helps someone, _activeService isn't available right away, but it is available when the onComponentRender event occurs, which is probably where I'll add the logic to save the key to localStorage.

deepChatRef.onComponentRender = () => {
  console.log(deepChatRef._activeService.key);
};
OvidijusParsiunas commented 5 months ago

Yep you are correct. All property values should be accessed after the component has rendered.

To note, the onComponentRender event occurs every time the component is re-drawn which includes the first time it is rendered on the screen and when the user submits the key. Hence, be careful which render events you use as they can be triggered multiple times throughout the component lifecycle and the key may not be present in the initial ones.

OvidijusParsiunas commented 5 months ago

Since the original question has been cleared, I will be closing this issue. If you have any further questions you can still comment here, however if you need other help - you are more than welcome to create a new issue. Thankyou!