LangChainAdapter DataStreamProtocol changes not parsing to Messages properly
Description
Using vercel-ai-sdk with a custom backend api/chat endpoint implemented in python. I am successful streaming responses back to the Next.js implementation that is using useChat(…) to call the backend. The backend is using LangGraph agents and I have AIMessageChunks responses that when passed through LangChainAdapter.toDataStreamResponse look like:
2:[{"content":""}]
2:[{"content":"Sure"}]
2:[{"content":","}]
2:[{"content":" I"}]
2:[{"content":" can"}]
2:[{"content":" help"}]
2:[{"content":" you"}]
2:[{"content":" manage"}]
2:[{"content":" the"}]
2:[{"content":" repository"}]
2:[{"content":"."}]
However, when trying to render them in pages.tsx using the messages from useChat, they are being received as "{..., content: "2:[...]"}". When I manually send other types of data stream responses (3:{...},e:{...}, 9:{...} , they properly populate the messages with content or parts, but this Data Part 2:[...] does not.
Code example
page.tsx
const {
messages,
...
} = useChat({
body: {
chatId,
},
onError: (error) => {...},
});
return (
...
{messages.map((message, index) => (
<PreviewMessage
key={message.id}
chatId={chatId}
message={message} // THIS MESSAGE DOES NOT HAVE THE CONTENT
isLoading={isLoading && messages.length - 1 === index}
/>
))}
...
)
route.tsx (api/chat)
export async function POST(req: Request) {
const { messages, chatId } = await req.json();
const lastMessage = messages[messages.length - 1];
try {
const stream = createDataStream({
execute: async (dataStream) => {
await stream_text(chatId, lastMessage.content, dataStream);
},
onError: error => `Custom error: ${error.message}`,
});
return LangChainAdapter.toDataStreamResponse(stream);
} catch (error) {...}
}
AI provider
ChatOpenAI
Additional context
No response
@sicter are you using streamProtocol: "data", in your useChat?
Here is my code that works:
const chat = useChat({
api: props.endpoint,
onResponse(response) {
const sourcesHeader = response.headers.get("x-sources");
const sources = sourcesHeader
? JSON.parse(Buffer.from(sourcesHeader, "base64").toString("utf8"))
: [];
const messageIndexHeader = response.headers.get("x-message-index");
if (sources.length && messageIndexHeader !== null) {
setSourcesForMessages({
...sourcesForMessages,
[messageIndexHeader]: sources,
});
}
},
streamProtocol: "data",
onFinish: () => {
chat.setData([]);
},
onError: (e) => console.log(e),
});
Hey @jayadehart , thanks for the suggestion. I wasnt originally, but both before and after adding it I'm still getting the same result: other data stream protocols work if I send them manually, but what seems to be happening is:
- LangChainAdapter seems to be sending everything as
2:[{...}] - the Messages content is a string that just contains the output
2:[{...}incontent
e.x
{messages.map((message, index) => (
<div>{JSON.stringify(message)}</div>
))}
results in my chat showing:
{"id":"qSuSpgfoqPOJD7Qa","createdAt":"2025-04-10T21:40:06.213Z","role":"assistant","content":"2:[[]]\n2:[[{\"id\":\"toolu_01QGhDAfs4RjQTiwfGRm8U3y\",\"input\":{},\"name\":\"code\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\"\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\"{\\\"prefix\\\"\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\": \\\"To create\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\" a cha\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\"t model\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\" in\",\"type\":\"tool_use\",\"index\":0}]]\n2:[[{\"partial_json\":\" L\",\"type\":\"tool_use\",\"index\":0}]]\n2:
If there's more detail I can provide that would help to debug, I'd be happy to share more
@sicter I'm not an expert at all the underlying processing that is happening with useChat and such, but if you want to look at a working example of converting langchain stream output to a data stream you can check out my repo:
https://github.com/jayadehart/js-melee-rag/blob/main/app/api/chat/retrieval/route.ts
@jayadehart Thanks so much, a reference would be super helpful. But I don't have access to your repo, can you share it with me?
@sicter my bad, didn't realize I had made it private. It should be public now!
@jayadehart maybe the link is wrong? I'm still getting a 404
@sicter try now
@jayadehart got it- thanks!