export default async function main(args) {
// Destructure input variables from arguments
const { inputVars } = args
// Trim whitespace from all input variables
for (const key in inputVars) {
if (inputVars.hasOwnProperty(key)) {
inputVars[key] = inputVars[key].trim();
}
}
// Initialize variables for next path, output variables, and trace logs
let nextPath = 'default'
const outputVars = {}
const trace = []
// Set default values for output variables
outputVars.answer = ''
outputVars.error = ''
// Define a delay function to pause execution for a specified duration
function delay(ms) {
return new Promise((resolve) => {
const startTime = Date.now()
let currentTime = Date.now()
while (currentTime - startTime < ms) currentTime = Date.now()
resolve()
});
}
try {
let runResponse = {}
// If threadId does not match a specific pattern, create a new thread
if ( ! inputVars.threadId.match(/^thread_/) ) {
runResponse = await fetch('https://api.openai.com/v1/threads/runs', {
method: 'POST',
headers: {
'Authorization': `Bearer ${inputVars.openaiApiKey}`,
'Content-Type': 'application/json',
'OpenAI-Beta': 'assistants=v1'
},
body: JSON.stringify({
'assistant_id': inputVars.assistantId,
"thread": {
'messages': [
{
'role': 'user',
'content': inputVars.input
}
]
}
})
})
} else {
// If threadId matches the pattern, post a new message to the existing thread
const messageResponse = await fetch(`https://api.openai.com/v1/threads/${inputVars.threadId}/messages`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${inputVars.openaiApiKey}`,
'Content-Type': 'application/json',
'OpenAI-Beta': 'assistants=v1'
},
body: JSON.stringify({
'role': 'user',
'content': inputVars.input
})
})
if ( ! messageResponse.ok ) throw new Error(`OpenAI Error (Message): ${messageResponse.status}`)
// Run the thread after posting the new message
runResponse = await fetch(`https://api.openai.com/v1/threads/${inputVars.threadId}/runs`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${inputVars.openaiApiKey}`,
'Content-Type': 'application/json',
'OpenAI-Beta': 'assistants=v1'
},
body: JSON.stringify({
'assistant_id': inputVars.assistantId,
})
});
}
if ( ! runResponse.ok ) throw new Error(`OpenAI Error (Run): ${runResponse.status}`)
const run = await runResponse.json
// Save threadId to output variables
outputVars.threadId = run.thread_id
let check = 1
// Poll for status updates, with a maximum of 30 checks
while ( true ) {
await delay(1000)
const statusResponse = await fetch(`https://api.openai.com/v1/threads/${run.thread_id}/runs/${run.id}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${inputVars.openaiApiKey}`,
'OpenAI-Beta': 'assistants=v1'
}
})
if ( statusResponse.ok ) {
const status = await statusResponse.json
if ( ! ['queued', 'in_progress'].includes(status?.status || 'queued') || check > 30 ) break
}
check++
}
// Retrieve the message response from the thread
const messageResponse = await fetch(`https://api.openai.com/v1/threads/${run.thread_id}/messages`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${inputVars.openaiApiKey}`,
'OpenAI-Beta': 'assistants=v1'
}
})
if ( ! messageResponse.ok ) throw new Error(`OpenAI Error (Messages): ${messageResponse.status}`)
const message = await messageResponse.json
if ( ! message.data ) throw new Error('No answer could be located.')
// Set the answer in output variables
outputVars.answer = message.data[0].content[0].text.value
// Extract user questions from the messages and log them
const questions = message.data
.filter(item => item.role === 'user')
.map(item => {
const date = new Date(item.created_at * 1000).toISOString()
return {
content: item.content[0].text.value,
created_at: date
}
})
trace.push( {
type: 'debug',
payload: {
message: JSON.stringify({
assistantId: inputVars.assistantId,
threadId: inputVars.threadId,
questions
})
}
} )
} catch(error) {
// If an error occurs, set the next path to 'error' and log the error message
nextPath = 'error'
outputVars.error = error.message
}
// Return the next path, output variables, and trace logs
return {
next: { path: nextPath },
outputVars: outputVars,
trace: trace
}
}