Back to all functions

OpenAI Assistants API

Built by community member w.williams, this function uses the OpenAI assistants API with threads to send and retrieve messages. This function will create a new thread if no existing thread exists or add messages to an existing threadID if provided.

Created By
W. Williams
Voiceflow
download-icon
INPUT VARIABLES
{
threadID
}
{
input
}
{
assistantId
}
{
openaiAPIKey
}
{
}
{
}
share-icon
OUTPUT VARIABLES
{
answer
}
{
threadID
}
{
error
}
{
}
{
}
{
}
paths-icon
PATHS
{
default
}
{
error
}
{
}
{
}
{
}
{
}

Function Walkthrough

Function Code Snippet

 
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
  }
}
copy-icon

Have something to share?

Share your creation with over 250,000 other global Voiceflow users.

ghraphic