Enhance a Geo App with PubNub & ChatGPT / OpenAI

pubnub share

Geolocation applications are increasingly becoming essential in our daily lives. From food delivery apps to ride-hailing services, real-time geolocation is a crucial feature that enhances user experience. However, as services become more easily accessible and apps easier to find, you’ll need to implement additional features to maintain an edge in the competitive market. One way to do this is to improve your user experience with Generative Artificial Intelligence (AI). Generative AI has exploded in popularity, where natural language processing applications such as Microsoft’s Bing AI (which uses OpenAI’s GPT model), Google Bard, and Amazon Codewhisperer are used in a variety of use cases, including blockchain, devops, e-commerce, and even robotics.

In this blog post, you’ll learn how to easily integrate arguably the most popular Generative AI application OpenAI’s ChatGPT into an existing open-source Geolocation web app with PubNub to provide details about your current location.

Prerequisites

Although not required to integrate ChatGPT into a Geolocation application, it would be best to follow how to architect Geolocation applications, where you’ll learn about the fundamentals of real-time geolocation services for developers and what to consider while building geolocation features.

You can also learn how this Geolocation application was built by following our tutorial. This geolocation app allows participants to locate and interact with each other, based on PubNub's Javascript SDK for web development. PubNub powers the following functionality in the application:

For this tutorial, you’ll need the following requirements: 

Integration of OpenAI's ChatGPT

ChatGPT, developed by OpenAI, is an advanced language model that uses machine learning to generate human-like text. It can be used to build chatbots, generate creative content, and more. In this context, we will be using ChatGPT to provide interactive location-based information, enhancing the overall user experience of our geolocation application.

PubNub now integrates with OpenAI GPT to enhance real-time communication and messaging experiences. With this integration, you can easily host a serverless environment that allows you to intercept messages published in your app, process this information with OpenAI’s platform, and then forward these responses back to your applications with PubNub. Once you’ve set up a PubNub account, it takes less than five minutes to host this integration by following the Setup section in the integration documentation. You can think of this integration as a quick and easy plugin for your PubNub applications.

We will need to modify the code in the function to suit our specific needs. You’ll be modifying the instructions that detail the task that ChatGPT needs to complete, as well as publishing the response from ChatGPT back to the application. For this application, ChatGPT will return three interesting or historical facts about the provided coordinates sent from the Geolocation app. 

Replace the following code in your Function:

const pubnub = require('pubnub');

// 
// What task should the GPT Model complete?
// 
const INSTRUCTIONS = "Provide three interesting or historical facts about the location in the text. You must abide by the following rules at all times: 1) If the provided coordinates do not match an exact location, please use the nearest city when providing the facts. 2) Keep each fact short and one sentence long. 3) Do not provide the answers in a list format. 4) Do not repeat the coordinates back, start with the city. 5) If the coordinates don't correspond to any known information, state 'DNE' exactly.";
                  // "Rewrite text replacing harmful text with positive text.";
                  // "Remove names, emails, phone numbers, credit card numbers and locations from the text.";
                  // "List the questions asked in the text.";
                  // "Is this message text harmful?";
const MODEL="gpt-3.5-turbo";

//
// API Key For OpenAI
// **Add your API Key to MY SECRETS (Left Panel)**
//
let OPENAI_API_KEY = null;
function getOpenaiApiKey() {
    // Use cached key
    if (OPENAI_API_KEY) {
        return new Promise(resolve => resolve(OPENAI_API_KEY));
    }
    // Fetch key from vault
    return vault.get("OPENAI_API_KEY").then(apikey => {
        OPENAI_API_KEY = apikey;
        return new Promise(resolve => resolve(OPENAI_API_KEY));
    });
}

//
// Import Modules
//
const xhr = require('xhr');
const vault = require('vault');

// 
// Main
// 
export default (request) => {
    if (request.message.sender != "ChatGPT")
    {
        console.log("Request:", request);

        // Input data for the GPT Model
        let message = request.message.text != undefined || request.message.text != null ? request.message.text : request.message;
        const inboundChannel = request.channels[0];

        // Ask LLM AI Model to make perform INSTRUCTION
        return getOpenaiApiKey().then(apiKey => {
            return openAI(message, apiKey).then(aiResponse => {
                console.log("OPENAI TASK RESULT:", aiResponse);
                console.log("Inbound channel:",inboundChannel );
                pubnub.publish({
                    channel: inboundChannel,
                    message: {
                        content: {
                            type: "text",
                            text: aiResponse
                        },
                        sender: "ChatGPT",
                    },
                });
                return request.ok()
            });
        });
    }
    return request.ok();
};

//
// API Call to OpenAI asking the AI to run functions if it thinks it needs to
//
function openAI(text, apiKey) {
    const url = 'https://api.openai.com/v1/chat/completions';
    const msgs = [{"role": "system", "content": INSTRUCTIONS, "role": "user", "content": `text: ${text} \n${INSTRUCTIONS}`}];
    const http_options = {
        'method': 'POST',
        'headers': {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${apiKey}`,
         },
         'body': JSON.stringify({
            "model": "gpt-3.5-turbo",
            "messages": msgs,
            "temperature": 0.7,
            "max_tokens": 100,  //  I am only asking for simple responses
            "top_p": 1,
            "frequency_penalty": 0,
            "presence_penalty": 0
        }),
        timeout: 9500,  //  PubNub functions (by default) will timeout after 10s.  This limit can be raised if needed
        retries: 0
    };

    // Send API Request to OpenAI GPT Model
    return xhr.fetch(url, http_options)
        .then((resp) => JSON.parse(resp.body).choices[0].message.content)
        .catch((err) => new Promise((_, reject) => reject(err)));  
}

Set up the channel name to be chatgpt.facts.*, which is a wildcard subscribe to listen for any messages of that type. You can learn more here. Be sure to press the Save button to save your changes and press the Restart Module button (or stop/start the module) to deploy the new changes. 

Setting up PubNub and OpenAI's ChatGPT on Your Geolocation App

Once you’ve set up the serverless OpenAI GPT integration with PubNub, you’re almost ready to begin viewing the ChatGPT integration in the Geolocation Demo! You’ll need to replace a few of your API keys in the files and understand how the geolocation app is receiving the location information.

In index.html, right below the map-canvas-div, there is a space that displays the location information returned from ChatGPT. Be sure to replace the API key for the Google Maps API source towards the bottom of the file in the key parameter.

The entirety of the HTML and CSS code can be found on GitHub.

Let’s view the logic of the application in js/app.js to receive these location updates for the backend. You’ll need to begin by replacing your PubNub publish and subscribe keys you obtained earlier when initializing the PubNub object:

var pubnub = new PubNub({
  publishKey:   'YOUR_PUBLISH_KEY',
  subscribeKey: 'YOUR_SUBSCRIBE_KEY',
  uuid: UUID
});

The channel of the ChatGPT connection, which is the connection to transmit data between the user and the PubNub platform, is specifically designated to publish and subscribe to an exact channel, so only that user can send/receive location information from ChatGPT that is specific to them.

var chatGPTChannel = `chatgpt.facts.${UUID}`;
var chatGPTResponseArea = document.getElementById("location-details-text");
var currentPosition = {
    lat: null,
    long: null,
    city: null
};

The channel is added when subscribing to the PubNub channels:

pubnub.subscribe({
    channels: [GEOchannel, GEOchannel+'.greet', chatGPTChannel], 
    withPresence: true
});

The showPosition function is called whenever the user’s location has been updated. You’ll publish the updated user coordinates to be caught by the OpenAI GPT integration hosted via PubNub Functions, but only under a few conditions. In sendMessageChatGPT, the new updated coordinates must be from a different city and be further than two miles from the original location. This DISTANCE_BETWEEN_CITIES_MILES const can be adjusted as you see fit, but defaults to 2.

async function showPosition(position) {
   if ( document.getElementById('locationshareswitch').checked === false ) { 
    //Set the channel members
    var uuids = [
        UUID,
        { id: UUID, custom: {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
            username: username,
            greeting: greeting
        }},
    ]; 
    setChannelMembers(GEOchannel, uuids);
    var message = { 
        uuid:UUID, 
        username:username, 
        greeting: greeting, 
        lat:position.coords.latitude, 
        lng:position.coords.longitude
    };

    //Update with coordinates,
    publishMessage(GEOchannel, message);
    // For future feature: playback of user history
    publishMessage(GEOchannel+"."+UUID, message);

    //Determine if Publish Message to ChatGPT to evaluate with facts
    sendMessageChatGPT(position.coords.latitude, position.coords.longitude);
   }
}

// Evaluates the coordinates to determine city and send to ChatGPT to return facts if a new city.
function sendMessageChatGPT(latitude, longitude) {
    var geocoder = new google.maps.Geocoder;
    var latlng = {lat: parseFloat(latitude), lng: parseFloat(longitude)};
    var city = null;

    //Determine if the provided coordinates have a matching city. If they do, send to ChatGPT.
    geocoder.geocode({'location': latlng}, function(results, status) {
        if (status === 'OK') {
            if (results[0]) {
                for (var i = 0; i < results[0].address_components.length; i++) {
                    if (results[0].address_components[i].types.indexOf('locality') !== -1) {
                        city = results[0].address_components[i].long_name;
                        break;
                    }
                }

                //If there is no set position yet, force the distance check to pass.
                var distance = (currentPosition.lat === null || currentPosition.long === null) ? DISTANCE_BETWEEN_CITIES_MILES + 1 : getDistanceMiles(currentPosition.lat, currentPosition.long, latitude, longitude);
                
                //If the coordinates match a valid city and is a new city, along with being more than a set amount of miles inbetween, send to ChatGPT to evaluate.
                if(city != null && city != currentPosition.city && distance > DISTANCE_BETWEEN_CITIES_MILES) {
                    currentPosition.city = city;
                    currentPosition.lat = latitude;
                    currentPosition.long = longitude;      
                    //Get interesting facts about the coordinates from ChatGPT
                    publishMessage(chatGPTChannel, `(${latitude},${longitude})`);    
                }                     
            } 
            //Didn't find a matching City. Update the field appropriately.
            else if (currentPosition.city == "") { 
                chatGPTResponseArea.value = "Couldn't find a matching city based on your coordinates...";
            }
        } else {
            console.log('Geocoder failed due to: ' + status);
        }
    });
}

// AI-Generated Code
// Tool: ChatGPT v4
function getDistanceMiles(originalLat, originalLng, newLat, newLng) {
    var originalLatLng = new google.maps.LatLng(originalLat, originalLng);
    var newLatLng = new google.maps.LatLng(newLat, newLng);
    var distanceInMeters = google.maps.geometry.spherical.computeDistanceBetween(originalLatLng, newLatLng);
    // Convert distance to miles
    return distanceInMeters / 1609.34;
}

Finally, a listener is necessary to handle the response from ChatGPT. In the redraw function, which is called whenever a new message is received on the PubNub platform, a condition is added to listen for updates from the PubNub Function. The function setInterval is used to simulate a typing effect, mimicking ChatGPT’s responses. The condition is added at the end:

else if(payload.channel == chatGPTChannel && (payload.message.sender != undefined && payload.message.sender != pubnub.uuid)) { //Display response from ChatGPT, not from ourselves.
        chatGPTResponseArea.value = '';
        var speed = 50;  
        let i = 0;
        let intervalId = setInterval(() => {
            if (i < payload.message.content.text.length) {
                chatGPTResponseArea.value += payload.message.content.text[i];
                i++;
            } else {
                clearInterval(intervalId);
            }
        }, speed);
    }

If you’d like to see the application in its entirety, you can view the application on GitHub.

You’re now ready to see the location information received from ChatGPT! Open the index.html file in your browser (double-click the file in explorer) and allow the app to use your current location. Once your location has been registered in the map, you should see the location section be updated with historical/interesting facts regarding the city that is closest to your location.

What's Next

You’ve learned how you can easily enhance a geolocation application with PubNub and OpenAI's ChatGPT. Not only does it provide a better user experience by providing interesting information about locations, but it gives your app a competitive edge by incorporating Generative AI. 

You can use what you’ve learned in this tutorial to enhance this application using PubNub’s JavaScript SDK and OpenAI’s API Reference documentation for these additional features. Don’t worry if your application is in a different programming language: PubNub offers 25+ SDKs in both web and mobile apps with React, Python, Android Kotlin, iOS Swift, Java, and more for application development.

If you would like to learn more about how to get started integrating PubNub and OpenAI’s ChatGPT into your own application, take a look at our growing collection of updates, demos, tutorials, and documentation.

If you would like more personal assistance, please reach out to us. We’ll walk you through the best way to add PubNub to your application.