mirror of
https://github.com/langchain-ai/langserve.git
synced 2026-07-18 19:04:27 -04:00
stream API returns entire answer after a while, instead of actually streaming the answer for ConversationalRetrievalChain
#38
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @tigerinus on GitHub (Nov 13, 2023).
Originally assigned to: @eyurtsev on GitHub.
Hello,
I built a simple langchain app using
ConversationalRetrievalChainandlangserve.It is working great for its
invokeAPI. However when it comes tostreamAPI, it returns entire answer after a while, instead of actually streaming the answer.Here is the
langservepart:Please help. Thanks!
@eyurtsev commented on GitHub (Nov 13, 2023):
@tigerinus could you confirm that this is a langserve issue rather than langchain issue? i..e,
@tigerinus commented on GitHub (Nov 13, 2023):
Yes, it works. It prints the answer token by token.
@eyurtsev commented on GitHub (Nov 14, 2023):
Could you check what happens if you remove the middleware for gzip compression?
@tigerinus commented on GitHub (Nov 14, 2023):
That didn't help.
How exactly should I call the
/streamendpoint?Does it work elsewhere?
@eyurtsev commented on GitHub (Nov 14, 2023):
Using the RemoteClient. The RemoteClient should be pointing at the path of the runnable (not the
/streamendpoint)See example here in the middle that streams: https://github.com/langchain-ai/langserve/blob/main/examples/llm/client.ipynb
The server should stream if the underlying runnable used by the server can stream. You can also try
astream_logfrom the RemoteClient@tigerinus commented on GitHub (Nov 14, 2023):
I will try that notebook.
However our frontend is written in VueJS. Would be nice to have an example code in JS.
If we call
/streamdirectly we see the answer returned in whole chunk@tigerinus commented on GitHub (Nov 15, 2023):
@eyurtsev I have no idea how to get the notebook example working for my app:
I tried
But the backend always say something like:
@eyurtsev commented on GitHub (Nov 15, 2023):
for js: Have you tried the js remote runnable client? https://api.js.langchain.com/classes/runnables_remote.RemoteRunnable.html
absent any bugs, it should be a drop in replacement -- so you could launch the server code provided in the example, make confirm the python client code works, and then replace with js version
For your code snippet could you include both server and client code?
In the example I referenced the remote runnables take a path to the mounted location of the runnable:
@gabegaz commented on GitHub (Nov 15, 2023):
I also encountered the same issue
@eyurtsev commented on GitHub (Nov 15, 2023):
@gabegaz do you have a snippet of both client and server code you're using? If i have full code, I can try to recreate locally
@gabegaz commented on GitHub (Nov 15, 2023):
Here is my server side code snippet:
`from langchain.llms import LlamaCpp
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from fastapi import FastAPI
from langserve import add_routes
n_gpu_layers = 1
n_batch = 512
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])
llm = LlamaCpp(
model_path=mistral+"mistral-7b-instruct-v0.1.Q5_K_M.gguf",
n_gpu_layers=n_gpu_layers,
n_batch=n_batch,
n_ctx=2048,
f16_kv=True,
callback_manager=callback_manager,
verbose=True,
)
app = FastAPI(
title="LangChain Server",
version="1.0",
description="A simple api server using Langchain's Runnable interfaces",
)
add_routes(
app,
llm,
path="/datalem",
)
if name == "main":
import uvicorn
uvicorn.run(app, host="localhost", port=8000)`
And here is the client code:
`from langchain.prompts.chat import ChatPromptTemplate
from langserve import RemoteRunnable
from .models import Chat
from django.http import JsonResponse
from django.utils import timezone
from django.shortcuts import render
datalem = RemoteRunnable("http://localhost:5000/datalem/")
def chatbot_langserve(request):
chats = Chat.objects.filter(user=request.user.id)
My problem is on the client side. How can I get response from this print() function?
# response =datalem.invoke(prompt) for chunk in datalem.stream(prompt): print(chunk.content, end="", flush=True)@eyurtsev commented on GitHub (Nov 16, 2023):
@tigerinus , @gabegaz Thanks for the code. The underlying chain (
ConversationalRetrievalChain) does not support streaming, so this is not a langserve issue.TLDR:
Use this for conversational retriever: https://python.langchain.com/docs/expression_language/cookbook/retrieval#conversational-retrieval-chain
How to debug whether streaming is supported
The best way to debug is use the server chain (without any server in the middle) and confirm whether it streams or not.
In this case, you can test in the following manner:
You'll see that the underlying chain only has one chunk! Sot it definitely does not stream.
If you create it using LCEL, confirm first that streaming works server side and then hook it into langserve
@eyurtsev commented on GitHub (Nov 16, 2023):
Updated the example: https://github.com/langchain-ai/langserve/pull/238
See this code: https://github.com/langchain-ai/langserve/blob/main/examples/conversational_retrieval_chain/server.py
and corresponding client: https://github.com/langchain-ai/langserve/blob/main/examples/conversational_retrieval_chain/client.ipynb
@tigerinus commented on GitHub (Nov 16, 2023):
It is by design or a bug that ConversationalRetrievalChain doesn't support streaming?
@eyurtsev commented on GitHub (Nov 16, 2023):
Neither/both?
ConversationalRetrievalChain is an old style langchain chain that doesn't fully utilize LCEL.
A lot of the older objects do not support all the capabilities that LCEL offers (e.g., native async, optimized batching, stream, stream log etc.)
We're not deprecating the old style objects since users depend on them, but we're also not enhancing them in many cases since it's hard and also doesn't really make sense since LCEL a lot more flexibility to customize the behavior properly for the given application domain.
@rickknowles-cognitant commented on GitHub (Jan 4, 2024):
Thanks @eyurtsev - the previous reply was super helpful to understand the nature of the problem I've been seeing (which is pretty similar to the OP's problem).
My question is then: is there something we can do as a workaround ? It appears that Chainlit has done something async using a callback handler that is effectively granting them the ability to do it.
(image taken from https://docs.chainlit.io/api-reference/integrations/langchain )
The source for the handler appears here: https://github.com/Chainlit/chainlit/blob/main/backend/chainlit/langchain/callbacks.py
it's then invoked in chainlit message calls as:
From what I can see in the source (although I'm not sure if I'm reading it right) they are using a combination of a Tracer subclass and consuming the entire thing to pull out the element they want ?
Is this an advisable approach ? Or is there something else you'd recommend to people who wanted to get to this sort of functionality in a langserve environment ?
Thanks in advance,
Rick
@mhadi4194 commented on GitHub (Feb 11, 2024):
@gabegaz As you mentioned, the problem seems to be on client side. As client receives the tokens, it is supposed to render the html or update the user interface (which is typically done in other languages such as js).
see this example that I got from googling: "how to render streaming response in python on client side?"
instead of this:
execute something like this code on client side: