Initialize conversation chain memory's input key from the conversation chain's input key
Fixes: #1800
This PR suggests that the memory of the conversation chain inherits the conversation chains input_key.
My understanding is that currently Conversation chain's memory does not inherit the conversation chains input_key and we try to deduce it with get_prompt_input_key assuming it there are only memory, input and stop variables.
I've tested the PR with the following code:
# HO.
from langchain.chat_models import ChatOpenAI
from langchain.prompts import (
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
ChatPromptTemplate,
MessagesPlaceholder
)
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
system_msg_template = SystemMessagePromptTemplate.from_template(template="You are a translator helping me in translating from {input_language} to {output_language}. " +
"Please translate the messages I type.")
human_msg_template = HumanMessagePromptTemplate.from_template(template="{input}")
prompt_template = ChatPromptTemplate.from_messages([system_msg_template, MessagesPlaceholder(variable_name="history"), human_msg_template])
mem = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(memory=mem, prompt=prompt_template, llm=ChatOpenAI(temperature=0.0), verbose=True)
print(conversation.predict(input="Hello, how are you?", input_language="English", output_language="French"))
print(conversation.predict(input="I want to go to the cinema.", input_language="English", output_language="German"))
Please note the input_language and the output_language template variables given in the system prompt. A sample execution yields:
> Entering new ConversationChain chain...
Prompt after formatting:
System: You are a translator helping me in translating from English to French. Please translate the messages I type.
Human: Hello, how are you?
> Finished chain.
Bonjour, comment vas-tu ?
> Entering new ConversationChain chain...
Prompt after formatting:
System: You are a translator helping me in translating from English to German. Please translate the messages I type.
Human: Hello, how are you?
AI: Bonjour, comment vas-tu ?
Human: I want to go to the cinema.
> Finished chain.
Ich möchte ins Kino gehen.
I've also tested the quickstart example:
# HO.
from langchain.prompts import (
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate
)
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know."),
MessagesPlaceholder(variable_name="history"),
HumanMessagePromptTemplate.from_template("{input}")
])
llm = ChatOpenAI(temperature=0)
memory = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(memory=memory, prompt=prompt, llm=llm)
print(conversation.predict(input="Hi there!"))
# -> 'Hello! How can I assist you today?'
print(conversation.predict(input="I'm doing well! Just having a conversation with an AI."))
# -> "That sounds like fun! I'm happy to chat with you. Is there anything specific you'd like to talk about?"
print(conversation.predict(input="Tell me about yourself."))
# -> "Sure! I am an AI language model created by OpenAI. I was trained on a large dataset of text from the internet, which allows me to understand and generate human-like language. I can answer questions, provide information, and even have conversations like this one. Is there anything else you'd like to know about me?"
Sample execution:
Hello! How may I assist you today?
I'm happy to chat with you! Is there anything specific you'd like to talk about?
Sure thing! I am an artificial intelligence designed to answer questions and provide assistance to humans. I am programmed with a vast amount of knowledge on various topics and I use natural language processing and machine learning to understand and generate responses. I don't have a physical presence but I exist solely in the digital world. My primary goal is to help and make people's lives easier!
@hwchase17 Hey, any idea when you could approve this fix? Currently this bug affects get-started examples from the documentation. Thank you!
@hwchase17 I'd appreciate this PR being merged as well. I'm wanting to do something similar to the sample @ulucinar posted earlier
@hwchase17 any idea on approving this?
hm not sure how i feel about the specific example you provided, in which system prompt changes every call. historical messages only make sense in the context that they were called in, no? for example what is value of including french translation in buffer once the prompt has changed to refer to german. aside from potentially not being useful in prompt, the real problem is if you tried to reconstruct this conversation from the memory it wouldn't make any sense (you wouldn't be able to tell why each successive input led to an output in a different language).
but i guess you could potentially want to have multiple input vars even just in the human message (you could reimplement the above to have a generic system message and then each human message specifies what language to translate to). but in that case i think the more generic fix is giving BaseChatMemory some way to reconstruct the full user message from multiple input keys. could do that by adding an optional user_message_prompt template to BaseChatMemory, and if there are multiple non-memory input keys and a user_message_prompt is provided then they're all piped into the prompt.
@hwchase17 thoughts?
sorry for missing this, agree we need to fix this
if set(expected_keys).difference(set(prompt_variables)): should be changed, we need to do this to unlock using prompts with more than one input variable. we could also do some logic to dynamically determine the input keys based on the prompt - memory variables (lets keep backwards compatibility tho)
as for setting the memory key... lets still keep that explicit for now. eg they can set mem = ConversationBufferMemory(return_messages=True, input_key="input") ideally we flag this on init of this class
can this be merged?
I agree that we need to make our code more flexible to support prompts with multiple input variables. Here's a plan to proceed:
For the comparison of expected_keys and prompt_variables, modify it to check if all expected_keys are in prompt_variables, but still allow prompt_variables to contain additional keys. This should make the code more flexible and compatible with prompts having multiple input variables.
Here's how this could be implemented:
if not set(expected_keys).issubset(set(prompt_variables)):
Regarding the setting of the memory key, I understand that we should keep this explicit for now. Users will continue to set the memory key when they're creating an instance of the class. We should also add a check in the initialisation of the class to ensure that a memory key is provided, and if not, a warning will be raised to inform the user to set one.
I believe these changes will address the issues you've highlighted and make the ConversationBufferMemory more robust and flexible. Let's get these adjustments in and update the PR.
Please let me know if you have any further suggestions or concerns.
@hwchase17 @ulucinar
Can this be merged? @hwchase17
I too wish to have this functionality merged.
You should fix this or I'll get fired.
If you're serious just manually update the file. I did it today and it works.
sorry for missing this, agree we need to fix this
if set(expected_keys).difference(set(prompt_variables)):should be changed, we need to do this to unlock using prompts with more than one input variable. we could also do some logic to dynamically determine the input keys based on the prompt - memory variables (lets keep backwards compatibility tho)as for setting the memory key... lets still keep that explicit for now. eg they can set
mem = ConversationBufferMemory(return_messages=True, input_key="input")ideally we flag this on init of this class
I dont think this was ever fixed
Can this be merged? @hwchase17 ? or by when can we expect this PR will be merged ?
@ulucinar Hi , could you, please, resolve the merging issues and address the last comments (if needed)? After that ping me and I push this PR for the review. Thanks!
If this PR is not needed anymore, could you, please, let me know?
Closing due to inactivity and library being a different directory structure now, and feel free to reopen against the current state of the repo!