10

I'm creating a conversation like so:

llm = ChatOpenAI(temperature=0, openai_api_key=OPENAI_API_KEY, model_name=OPENAI_DEFAULT_MODEL)
conversation = ConversationChain(llm=llm, memory=ConversationBufferMemory())

But what I really want is to be able to save and load that ConversationBufferMemory() so that it's persistent between sessions. There doesn't seem to be any obvious tutorials for this but I noticed "Pydantic" so I tried to do this:

saved_dict = conversation.memory.chat_memory.dict()
cm = ChatMessageHistory(**saved_dict) # or cm = ChatMessageHistory.parse_obj(saved_dict)

But this fails:

ValidationError: 6 validation errors for ChatMessageHistory
messages -> 0
  Can't instantiate abstract class BaseMessage with abstract method type (type=type_error)

Thoughts? I'd love links to any sort of guide, repo, reference, etc.

Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112
  • 1
    The problem I see is that the `dict()` output doesn't include class names so trying to reload it means that it can't see whether it's a `HumanMessage` or `AIMessage` so it just uses `BaseMessage` and fails – Neil C. Obremski Apr 08 '23 at 14:09

2 Answers2

7

Rather than mess around too much with LangChain/Pydantic serialization issues, I decided to just use Pickle the whole thing and that worked fine:

pickled_str = pickle.dumps(conversation.memory)
conversation2 = ConversationChain(llm=llm, memory=pickle.loads(pickled_str)
Neil C. Obremski
  • 18,696
  • 24
  • 83
  • 112
  • Thanks for the tip. it works fine in interactive Python shell but when I save the commands to a .py file and run it no such luck. the file saves but with just the memory format no history of chat. agent_chain = initialize_agent(tools, llm, agent="conversational-react-description", memory=memory) print(agent_chain.run(input='how many cars do i have?')) filesv = open('Chat_mem_001.pkl', 'wb') pickle.dump(agent_chain.memory, filesv) filesv.close() .... Any ideas why works fine in shell but not .py file run? – Shane Apr 25 '23 at 14:41
7

I just did something similar, hopefully this will be helpful. On a high level:

  1. use ConversationBufferMemory as the memory to pass to the Chain initialization
llm = ChatOpenAI(temperature=0, model_name='gpt-3.5-turbo-0301')
original_chain = ConversationChain(
    llm=llm,
    verbose=True,
    memory=ConversationBufferMemory()
)
original_chain.run('what do you know about Python in less than 10 words')
  1. extract messages from memory in the form of List[langchain.schema.HumanMessage|AIMessage] (not serializable)
extracted_messages = original_chain.memory.chat_memory.messages
  1. transform the extracted message to serializable native Python objects
ingest_to_db = messages_to_dict(extracted_messages)
  1. perform db operations to write to and read from database of your choice, I'll just use json.dumps and json.loads to illustrate
retrieve_from_db = json.loads(json.dumps(ingest_to_db))
  1. transform the retrieved serialized object back to List[langchain.schema.HumanMessage|AIMessage]
retrieved_messages = messages_from_dict(retrieve_from_db)
  1. construct ChatMessageHistory from the messages
retrieved_chat_history = ChatMessageHistory(messages=retrieved_messages)
  1. construct ConversationBufferMemory from ChatMessageHistory
retrieved_memory = ConversationBufferMemory(chat_memory=retrieved_chat_history)
  1. pass memory back to the newly initiated Chain
reloaded_chain = ConversationChain(
    llm=llm,
    verbose=True,
    memory=retrieved_memory
)

You can find the full code snippet at this GitHub Link

Shum
  • 344
  • 2
  • 7