OpenAI Parallel Function Calling + Node.js
Published: 2024-01-28 14:56:13

**OpenAI Parallel Function Calling + Node.js**

import OpenAI from "openai";
import yahooFinance from 'yahoo-finance2';
import fetch from 'node-fetch';

const openai = new OpenAI();

async function getLatestCompanyNews(company_name) {
    const GNEWS_API_KEY = process.env.GNEWS_API_KEY;
    const url = `https://gnews.io/api/v4/search?q=${company_name}&token=${GNEWS_API_KEY}&lang=en&sortBy=publishedAt`;
    try {
        const response = await fetch(url);
        const data = await response.json();
        return data.articles;
    } catch (error) {
        console.error(`Error fetching news for ${company_name}: ${error}`);
        return [];
    }
}

async function usdToGbp(usd_amount) {
    const url = "https://api.exchangerate-api.com/v4/latest/USD";
    try {
        const response = await fetch(url);
        const data = await response.json();
        const gbp_rate = data.rates['GBP'];
        return usd_amount * gbp_rate;
    } catch (error) {
        console.error(`Error converting USD to GBP: ${error}`);
        return `Error: ${error}`;
    }
}

async function getStockPrice(symbol) {
    try {
        const quote = await yahooFinance.quote(symbol);
        return { 
            symbol: symbol, 
            price: quote.regularMarketPrice, 
            currency: quote.currency 
        };
    } catch (error) {
        console.error(`Error fetching stock price for ${symbol}: ${error}`);
        throw error;
    }
}

const tools = [
    {
        "type": "function",
        "function": {
            "name": "getStockPrice",
            "description": "Get the current stock price of a company using its stock symbol",
            "parameters": {
                "type": "object",
                "properties": {
                    "symbol": {
                        "type": "string",
                        "description": "Stock symbol (e.g., 'AAPL' for Apple)"
                    }
                },
                "required": ["symbol"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "getLatestCompanyNews",
            "description": "Get the latest news about a company",
            "parameters": {
                "type": "object",
                "properties": {
                    "company_name": {
                        "type": "string",
                        "description": "Company name (e.g., 'Apple')"
                    }
                },
                "required": ["company_name"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "usdToGbp",
            "description": "Convert USD to GBP",
            "parameters": {
                "type": "object",
                "properties": {
                    "usd_amount": {
                        "type": "number",
                        "description": "Amount in USD"
                    }
                },
                "required": ["usd_amount"]
            }
        }
    },
];

// Step 1: Define your assistant 
const assistant = await openai.beta.assistants.create({
    name: "Data Analyst",
    instructions:
        "You are a Data Analyst",
    tools: tools,
    model: "gpt-4-1106-preview",
});

// Step 2: Creating a thread and sending a message
const thread = await openai.beta.threads.create();

// Step 3: Create a message
const message = await openai.beta.threads.messages.create(thread.id, {
    role: "user",
    content: "Can you please provide me stock price, " + 
             "stock price in GBP, " +
             "and the latest company news of Apple?"
});

// Step 4: Create a run with custom instructions
const run = await openai.beta.threads.runs.create(thread.id, {
    assistant_id: assistant.id,
    instructions: "Please address the user as Mervin Praison.",    
});

// Function to check run status and print messages
const checkStatusAndPrintMessages = async (threadId, runId) => {
    let runStatus = await openai.beta.threads.runs.retrieve(threadId, runId);
    console.log(runStatus)    
    if(runStatus.status === "completed"){
        let messages = await openai.beta.threads.messages.list(threadId);
        messages.data.forEach((msg) => {
            const role = msg.role;
            const content = msg.content[0].text.value; 
            console.log(
                `${role.charAt(0).toUpperCase() + role.slice(1)}: ${content}`
            );
        });
        console.log("Run is completed.");
        clearInterval(intervalId);
    } else if (runStatus.status === 'requires_action') {
        console.log("Requires action");
        const requiredActions = runStatus.required_action.submit_tool_outputs.tool_calls;
        console.log(requiredActions);
        
        // Dispatch table
        const dispatchTable = {
            "getStockPrice": getStockPrice,
            "getLatestCompanyNews": getLatestCompanyNews,
            "usdToGbp": usdToGbp
        };

        let toolsOutput = [];
    
        for (const action of requiredActions) {
            const funcName = action.function.name;
            const functionArguments = JSON.parse(action.function.arguments);

            if (dispatchTable[funcName]) {
                try {
                    const output = await dispatchTable[funcName](...Object.values(functionArguments));
                    toolsOutput.push({ tool_call_id: action.id, output: JSON.stringify(output) });
                } catch (error) {
                    console.log(`Error executing function ${funcName}: ${error}`);
                }
            } else {
                console.log("Function not found");
            }
        }

        await openai.beta.threads.runs.submitToolOutputs(threadId, runId, { tool_outputs: toolsOutput });
    } else {
        console.log("Run is not completed yet.");
    }  
};

const intervalId = setInterval(() => {
    checkStatusAndPrintMessages(thread.id, run.id)
}, 10000);

package.json

{


  "dependencies": {
    "node-fetch": "^3.3.2",
    "openai": "^4.16.1",
    "yahoo-finance2": "^2.8.1"
  },
  "type": "module"
}