I am using Langchain with Redis as the persistence layer. It works, but kind of—I have a strange behavior which is as follows:
I send a message, and it always responds to the previous prompt’s message.
I don’t know what’s wrong; I followed the official documentation and also looked at other code, and it seems correct.
See below:
$ curl -XPOST -H "session-id: 123" -d '{"message": "what is the capital of united states?"}' http://localhost:8000
{"message":"Of course! How can I assist you today?"}
Again, diferent prompt, now the previus and correct answer:
$ curl -XPOST -H "session-id: 123" -d '{"message": "hello?"}' http://localhost:8000
{"message":"The capital of the United States is Washington, D.C."}%
Code
import os
from typing import Any
import orjson
from langchain.globals import set_debug
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_core.messages import SystemMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts import MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_openai import ChatOpenAI
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from tenacity import retry
from tenacity import stop_after_attempt
set_debug(True)
llm = ChatOpenAI(
model="gpt-4o",
temperature=0,
openai_api_key=os.environ["OPENAI_APIKEY"],
)
prompt = ChatPromptTemplate.from_messages(
[
SystemMessage(content="You are a helpful assistant."),
MessagesPlaceholder(variable_name="history"),
# HumanMessage(content="{question}"),
]
)
chain = prompt | llm
chain_with_history = RunnableWithMessageHistory(
chain,
lambda session_id: RedisChatMessageHistory(session_id, url=os.environ["REDIS_DSN"]),
input_messages_key="question",
history_messages_key="history",
)
class OrjsonResponse(JSONResponse):
def render(self, content: Any) -> bytes:
return orjson.dumps(content)
@retry(stop=stop_after_attempt(3))
async def echo(request: Request):
data = await request.json()
output = chain_with_history.invoke(
{"question": data["message"]},
config={"configurable": {"session_id": request.headers["session-id"]}},
)
return OrjsonResponse({"message": output.content})
app = Starlette(
routes=[
Route("/", echo, methods=["POST"]),
],
middleware=[
Middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["POST"])
],
)