-
Notifications
You must be signed in to change notification settings - Fork 15.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
It's impossible to use and **async** ChatMessageHistory with langchain-core. #22021
Comments
The solution is to use SQLChatMessageHistory(
session_id=session_id,
connection_string=history_url, # Open a new connection each time
)
``̀ |
I assume this is a work-around around one of the issues? I think you identified a number of separate issues that need to be addressed? Is that correct? |
Yes. This is a work-around around one of the issues in PostgresChatMessageHistory() (see here. |
I'm trying to re-implement the An implementation workaround is to use this: def add_messages(self, messages: Sequence[BaseMessage]) -> None:
# The methode RunnableWithMessageHistory._exit_history() call
# add_message method by mistake and not aadd_message.
# See https://github.com/langchain-ai/langchain/discussions/22022
if self.async_mode:
loop = asyncio.get_event_loop()
loop.run_until_complete(self.aadd_message(smessages))
else:
with self.session_maker() as session:
for message in messages:
session.add(self.converter.to_sql_model(message, self.session_id))
session.commit() I'll propose a PR for this, improving the interface for accepting an ( I'm going to use an approach similar to the PR I'm proposing to finally make langchain use resilient. @baskaryan I'd like you to take the time to validate this PR, which prohibits me from publishing an article explaining how to make langchain use resilient. |
is this resolved? |
this bug is non-existent on local dev, but on prod it does. this is a newly-known issue on Langchain GitHub which can be accessed here: [langchain-ai/langchain#22021]
…22065) # package community: Fix SQLChatMessageHistory ## Description Here is a rewrite of `SQLChatMessageHistory` to properly implement the asynchronous approach. The code circumvents [issue 22021](#22021) by accepting a synchronous call to `def add_messages()` in an asynchronous scenario. This bypasses the bug. For the same reasons as in [PR 22](langchain-ai/langchain-postgres#32) of `langchain-postgres`, we use a lazy strategy for table creation. Indeed, the promise of the constructor cannot be fulfilled without this. It is not possible to invoke a synchronous call in a constructor. We compensate for this by waiting for the next asynchronous method call to create the table. The goal of the `PostgresChatMessageHistory` class (in `langchain-postgres`) is, among other things, to be able to recycle database connections. The implementation of the class is problematic, as we have demonstrated in [issue 22021](#22021). Our new implementation of `SQLChatMessageHistory` achieves this by using a singleton of type (`Async`)`Engine` for the database connection. The connection pool is managed by this singleton, and the code is then reentrant. We also accept the type `str` (optionally complemented by `async_mode`. I know you don't like this much, but it's the only way to allow an asynchronous connection string). In order to unify the different classes handling database connections, we have renamed `connection_string` to `connection`, and `Session` to `session_maker`. Now, a single transaction is used to add a list of messages. Thus, a crash during this write operation will not leave the database in an unstable state with a partially added message list. This makes the code resilient. We believe that the `PostgresChatMessageHistory` class is no longer necessary and can be replaced by: ``` PostgresChatMessageHistory = SQLChatMessageHistory ``` This also fixes the bug. ## Issue - [issue 22021](#22021) - Bug in _exit_history() - Bugs in PostgresChatMessageHistory and sync usage - Bugs in PostgresChatMessageHistory and async usage - [issue 36](langchain-ai/langchain-postgres#36) ## Twitter handle: pprados ## Tests - libs/community/tests/unit_tests/chat_message_histories/test_sql.py (add async test) @baskaryan, @eyurtsev or @hwchase17 can you check this PR ? And, I've been waiting a long time for validation from other PRs. Can you take a look? - [PR 32](langchain-ai/langchain-postgres#32) - [PR 15575](#15575) - [PR 13200](#13200) --------- Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
My PR fixed this bug. |
…22065) # package community: Fix SQLChatMessageHistory ## Description Here is a rewrite of `SQLChatMessageHistory` to properly implement the asynchronous approach. The code circumvents [issue 22021](#22021) by accepting a synchronous call to `def add_messages()` in an asynchronous scenario. This bypasses the bug. For the same reasons as in [PR 22](langchain-ai/langchain-postgres#32) of `langchain-postgres`, we use a lazy strategy for table creation. Indeed, the promise of the constructor cannot be fulfilled without this. It is not possible to invoke a synchronous call in a constructor. We compensate for this by waiting for the next asynchronous method call to create the table. The goal of the `PostgresChatMessageHistory` class (in `langchain-postgres`) is, among other things, to be able to recycle database connections. The implementation of the class is problematic, as we have demonstrated in [issue 22021](#22021). Our new implementation of `SQLChatMessageHistory` achieves this by using a singleton of type (`Async`)`Engine` for the database connection. The connection pool is managed by this singleton, and the code is then reentrant. We also accept the type `str` (optionally complemented by `async_mode`. I know you don't like this much, but it's the only way to allow an asynchronous connection string). In order to unify the different classes handling database connections, we have renamed `connection_string` to `connection`, and `Session` to `session_maker`. Now, a single transaction is used to add a list of messages. Thus, a crash during this write operation will not leave the database in an unstable state with a partially added message list. This makes the code resilient. We believe that the `PostgresChatMessageHistory` class is no longer necessary and can be replaced by: ``` PostgresChatMessageHistory = SQLChatMessageHistory ``` This also fixes the bug. ## Issue - [issue 22021](#22021) - Bug in _exit_history() - Bugs in PostgresChatMessageHistory and sync usage - Bugs in PostgresChatMessageHistory and async usage - [issue 36](langchain-ai/langchain-postgres#36) ## Twitter handle: pprados ## Tests - libs/community/tests/unit_tests/chat_message_histories/test_sql.py (add async test) @baskaryan, @eyurtsev or @hwchase17 can you check this PR ? And, I've been waiting a long time for validation from other PRs. Can you take a look? - [PR 32](langchain-ai/langchain-postgres#32) - [PR 15575](#15575) - [PR 13200](#13200) --------- Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
…22933) - **Description:** When use RunnableWithMessageHistory/SQLChatMessageHistory in async mode, we'll get the following error: ``` Error in RootListenersTracer.on_chain_end callback: RuntimeError("There is no current event loop in thread 'asyncio_3'.") ``` which throwed by https://github.com/langchain-ai/langchain/blob/ddfbca38dfa22954eaeda38614c6e1ec0cdecaa9/libs/community/langchain_community/chat_message_histories/sql.py#L259. and no message history will be add to database. In this patch, a new _aexit_history function which will'be called in async mode is added, and in turn aadd_messages will be called. In this patch, we use `afunc` attribute of a Runnable to check if the end listener should be run in async mode or not. - **Issue:** #22021, #22022 - **Dependencies:** N/A
@pprados do you have sample code of how to use it correctly with async with your fix? |
Checked other resources
Example Code
Error Message and Stack Trace (if applicable)
Error in RootListenersTracer.on_chain_end callback: ValueError('Please initialize the PostgresChatMessageHistory with a sync connection or use the aadd_messages method instead.')
Description
It's impossible to use and async ChatMessageHistory with langchain-core.
The
ChatMessageHistory
class is synchronous and doesn't have an async counterpart.This is a problem because the
RunnableWithMessageHistory
class requires aChatMessageHistory
object to be passed to it. This means that it's impossible to use an async ChatMessageHistory with langchain-core.I can't find any example of how to use it. I will try to create an example of how to use
PostgresChatMessageHistory
with async mode.There are many problems:
_exit_history()
PostgresChatMessageHistory
and sync usagePostgresChatMessageHistory
and async usageBug in
_exit_history()
In
RunnableWithMessageHistory
, the_exit_history()
is called because the chain has| runnable.with_listeners(on_end=self._exit_history)
. This method is not async and it will raise an error. This method calladd_messages()
and notawait aadd_messages()
.Result
Bugs in
PostgresChatMessageHistory
and sync usageIn
PostgresChatMessageHistory
, the design is problematic.Langchain, with LCEL, is declarative programming. You have to declare a chain in global variables, then invoke them when necessary. This is how langserv is able to publish interfaces with
add_route()
.For optimization reasons,
PostgresChatMessageHistory
wishes to recycle connections. The class provides a constructor which accepts async_connection
parameter. However, it is not possible to have a global connection, in order to reuse it when implementingget_session_history()
.A connection is not reentrant! You can't use the same connection in multiple threads. But, the design of langchain-postgres is to have a global connection. This is a problem.
The alternative is to create a new connection each time you need to access the database.
Then, why accept only a connection and not an engine? The engine is a connection pool.
Bugs in
PostgresChatMessageHistory
and async usageIf we ignore the problem mentioned above with
_exit_history()
, there are even more difficulties. It's not easy to initialize a global async connection. Because it's must be initialized in an async function.And, it's not possible to call
init_async_connection()
inget_session_history()
.get_session_history()
is not async. It's a problem.It is therefore currently impossible to implement session history correctly in asynchronous mode.
Either you use a global connection, but that's not possible, or you open the connection in ̀get_session_history()`, but that's impossible.
The only solution is to completely break the use of LCEL, by building the chain just after the connection is opened. It's still very strange. To publish it with langserv, you need to use a
RunnableLambda
.It's a very strange way to use langchain.
But a good use of langchain in a website consists precisely in using only asynchronous approaches. This must include history management.
System Info
langchain==0.1.20
langchain-community==0.0.38
langchain-core==0.2.1
langchain-openai==0.1.7
langchain_postgres==0.0.6
langchain-rag==0.1.46
langchain-text-splitters==0.0.1
The text was updated successfully, but these errors were encountered: