Back to all functions

OpenAI Assistants API

Functions
1

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

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
No items found.