Hey all,
I’m working on an assistant I’m expecting to call my functions. I was previously using this before streaming, and now trying to integrate it.
My current source code looks like this, with all of the possible event outputs from the AssistantStream printed for the sake of testing
async function runAssistant(threadId, assistantId, req, res) {
let textOut = "";
let toolChangedLast = false;
let functionName = "";
let toolCallArgs = "";
let toolCallId = "";
const run = client.beta.threads.runs.createAndStream(threadId, {
assistant_id: assistantId,
instructions: "...",
});
run.on('textCreated', (text) => console.log(`textCreated: ${JSON.stringify(text)}`))
.on('textDelta', (textDelta, snapshot) => {
textOut += textDelta.value;
console.log(textOut);
toolChangedLast = false;
})
.on('toolCallCreated', (toolCall) => {
console.log("TOOL CALL CREATED", JSON.stringify(toolCall));
// reset tool call
functionName = "";
toolCallArgs = "";
toolCallId = "";
toolCallId = toolCall.id;
functionName = toolCall.function.name;
toolChangedLast = true;
})
.on('toolCallDelta', async (toolCallDelta, snapshot) => {
console.log("TOOL CALL DELTA", JSON.stringify(toolCallDelta));
toolChangedLast = true;
toolCallArgs += toolCallDelta.function.arguments;
const completeArgs = checkToolCallArgsComplete(toolCallArgs);
if (completeArgs !== null) {
const output = [my source code tool call];
let runs = await client.beta.threads.runs.list(threadId);
const curr_run = runs.data.find(run => run.status === "requires_action");
if (curr_run) {
let submittedToolOutputs = await client.beta.threads.runs.submitToolOutputs(
threadId,
curr_run.id,
{
tool_outputs: [
{
tool_call_id: toolCallId,
output: JSON.stringify(output.data)
}
]
}
);
} else {
console.log("NO RUN FOUND");
console.log(`runs are ${JSON.stringify(runs)}`);
}
}
})
.on('messageCreated', (message) => {
console.log("messageCreated:", JSON.stringify(message));
})
.on('messageDelta', (messageDelta, snapshot) => {
console.log("messageDelta:", JSON.stringify(messageDelta));
})
.on('messageDone', (message) => {
console.log("messageDone:", JSON.stringify(message));
})
.on('runStepCreated', (runStep) => {
console.log("runStepCreated:", JSON.stringify(runStep));
})
.on('runStepDelta', (delta, snapshot) => {
console.log("runStepDelta:", JSON.stringify(delta));
})
.on('runStepDone', (runStep, snapshot) => {
console.log("runStepDone:", JSON.stringify(runStep));
})
.on('toolCallDone', (toolCall) => {
console.log("toolCallDone:", JSON.stringify(toolCall));
toolChangedLast = true;
})
.on('textCreated', (content) => {
console.log("textCreated:", JSON.stringify(content));
})
.on('textDelta', (delta, snapshot) => {
console.log("textDelta:", JSON.stringify(delta));
toolChangedLast = false;
})
.on('textDone', (content, snapshot) => {
console.log("textDone:", JSON.stringify(content));
toolChangedLast = false;
})
.on('imageFileDone', (content, snapshot) => {
console.log("imageFileDone:", JSON.stringify(content));
})
.on('end', async () => {
console.log("end event");
res.end(); // Close the request
});
}
When I ran this, I noticed that submitting tool output would succeed, but the streaming thread would then send an end event. If I triggered another run after the stream was ended, the text generation steps would proceed as I would’ve expected them to after tool generation was received.
...sequence of tool call delta steps, ultimately resulting in tool submission....
2024-03-14 23:28:00 toolCallDone: {"index":0,"id":"call_873ulzpT4aR3GBZFxdBuaYI2","type":"function","function":{"name":"myTool","arguments":"","output":null}}
2024-03-14 23:28:00 end event
2024-03-14 23:28:00 Connection closed
Perhaps I’m submitting the tool input the wrong way for the streaming paradigm, but the docs don’t suggest an alternative. Any insight here would be appreciated!