Checked other resources
Example Code
import { ChatOpenAI } from "@langchain/openai";
const chunk = (delta, finish = null) =>
`data: ${JSON.stringify({
id: "c",
object: "chat.completion.chunk",
created: 1,
model: "m",
choices: [{ index: 0, delta, finish_reason: finish }],
})}\n\n`;
// A server that sends role: "" instead of "assistant".
const sse =
chunk({
role: "",
tool_calls: [
{
index: 0,
id: "call_1",
type: "function",
function: { name: "get_weather", arguments: '{"city":"BJ"}' },
},
],
}) +
chunk({}, "tool_calls") +
"data: [DONE]\n\n";
const model = new ChatOpenAI({
model: "m",
apiKey: "x",
configuration: {
baseURL: "https://example.invalid/v1",
fetch: async () =>
new Response(sse, {
status: 200,
headers: { "content-type": "text/event-stream" },
}),
},
});
for await (const c of await model.stream("hi")) {
console.log(c.tool_call_chunks);
}
Prints undefined for every chunk. Changing role: "" to role: "assistant" in the same script prints the expected tool_call_chunk.
Error Message and Stack Trace (if applicable)
No error is raised. The tool call is dropped silently.
Description
ChatOpenAI loses streamed tool calls when a server sends role: "" instead of "assistant". convertCompletionsDeltaToBaseMessageChunk resolves the role with delta.role ?? defaultRole, and ?? only falls back on null/undefined, so an empty string reaches the final branch and the delta is converted to a ChatMessageChunk rather than an AIMessageChunk. Any tool_calls on that delta are discarded and tool_call_chunks is undefined. Plain text still streams normally, which makes this easy to miss: the response looks healthy and only tool calls disappear.
Empty delta.role is something OpenAI-compatible servers do in practice — anomalyco/opencode#28427 reports the same responses from the consumer side. #11157 fixes the closely related case where role is omitted entirely, but keeps ??, so with that PR applied the empty-string variant still fails. Coalescing with || instead covers both, and explicit "tool" / "system" / "user" roles keep working since those are truthy.
System Info
@langchain/openai 1.5.8
@langchain/core 1.2.9
node v20.19.5
linux x64
Checked other resources
Example Code
Prints
undefinedfor every chunk. Changingrole: ""torole: "assistant"in the same script prints the expectedtool_call_chunk.Error Message and Stack Trace (if applicable)
No error is raised. The tool call is dropped silently.
Description
ChatOpenAIloses streamed tool calls when a server sendsrole: ""instead of"assistant".convertCompletionsDeltaToBaseMessageChunkresolves the role withdelta.role ?? defaultRole, and??only falls back onnull/undefined, so an empty string reaches the final branch and the delta is converted to aChatMessageChunkrather than anAIMessageChunk. Anytool_callson that delta are discarded andtool_call_chunksisundefined. Plain text still streams normally, which makes this easy to miss: the response looks healthy and only tool calls disappear.Empty
delta.roleis something OpenAI-compatible servers do in practice — anomalyco/opencode#28427 reports the same responses from the consumer side. #11157 fixes the closely related case whereroleis omitted entirely, but keeps??, so with that PR applied the empty-string variant still fails. Coalescing with||instead covers both, and explicit"tool"/"system"/"user"roles keep working since those are truthy.System Info