Hello,
Does one know how we are supposed to respond to function calls while streaming using the Responses API?
When I try creating a response with streaming on and the conversation ID, passing the function call ID, I get this error back:
invalid_request_error - 400 Another process is currently operating on this conversation. Please retry in a few seconds.
Also, omitting the “conversation” param gives another error saying that the function call output doesn’t match a function call.
Any official documentation for it?
works fine in realtime… are you calling the function on the .delta event or waiting for .done?
Is there something broken with this approach below?
@mcfinley does it work for you using Response along with a Conversation?
What this approach basically does (apart from processing text outputs):
- Create a first Response with user input and streaming on.
- Process function call when a function call event (done) is received.
- Create a Response for the function call output with streaming on (and the function call ID).
But #3 fails saying the conversation is locked.
Here is the code:
const bodyWithoutInput: ResponseCreateParamsBase = {
conversation: conversation.openAiId,
metadata,
model: "gpt-5-2025-08-07",
store: true,
truncation: "disabled",
user: message.author.userId,
tools: this.getTools(),
};
try {
let outputText = "";
const stream = await this.openAIService.client.responses.create({
...bodyWithoutInput,
stream: true,
tool_choice: this.getToolChoice(someData),
input, // Processed earlier.
});
for await (const streamEvent of stream) {
switch (streamEvent.type) {
// Handle response created.
case "response.created": {
// Update PENDING assistant message status to DRAFT.
break;
}
// Handle tool calls.
case "response.output_item.done": {
if (streamEvent.item.type !== "function_call") break;
const res = await this.processFunctionCall(someData, streamEvent.item);
someData = res.someData;
try {
await this.openAIService.client.responses.create({
...bodyWithoutInput,
conversation: conversation.openAiId,
stream: true,
tool_choice: this.getToolChoice(someData),
input: res.input, // Input includes `call_id`, `output` (string) and `type` with a "function_call_output" value.
});
} catch (error) {
// Update assistant message status to ERROR.
await this.conversationItemService.update(
assistantMessage,
{ status: ConversationItemStatusEnum.ERROR },
someData
);
}
break;
}
// Handle text deltas.
case "response.output_text.delta": {
break;
}
// Handle text done.
case "response.output_text.done": {
// Update assistant message status to COMPLETED.
break;
}
case "response.failed": {
// Update assistant message status to ERROR.
break;
}
}
}
} catch (error) {
// Update assistant message status to ERROR.
}
Looks like you’re INSIDE the for loop processing the stream from the outer call to .create when you make another call? Why not queue up the tool response and call .create() for the tool response AFTER you’re done processing the stream?
My case is very different so I’m not sure how much applies. I’m calling the tool when I get response.function_call_arguments.done, and I’m in the realtime API so there is no specific config to stream… its always streaming.
Indeed processing the stream fully and queuing up the function calls does the job, thank you!
The code below does the job:
let functionCalls: ResponseFunctionToolCall[] = [];
do {
// Process text messages.
({ assistantMessage, functionCalls } =
await this.streamResponse(input, {
assistantMessage,
conversation,
toolChoice: this.getToolChoice(someData),
tools,
user,
}));
// Process function calls.
input = [];
for (const functionCall of functionCalls) {
const res = await this.processFunctionCall(tarotReading, functionCall);
someData = res.someData;
input = [...input, ...res.input];
}
} while (functionCalls.length);
nice! for what its worth, I have been able to make openai calls inside callbacks like this when I use a new copy of the client. so tools CAN make openai calls but they have to be on a different client object, which would not suit your use case because you needed to put the tool answer into the existing conversation.