Skip to content

Commit

Permalink
Added functionality to get routes when user asks
Browse files Browse the repository at this point in the history
  • Loading branch information
mayurvir committed Apr 8, 2024
1 parent 680a335 commit cdf7725
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 6 deletions.
1 change: 1 addition & 0 deletions config/openai.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"SUPPORTED_ACTIONS": [
{ "action": "get_routes", "description": "If the user has requested for routes for a travel plan between two places or asked to plan a trip." },
{ "action": "search", "description": "If the user clearly indicates to perform a search for a specific product. Sample instructions : 'find a hotel', 'find an ev charger', 'find tickets'" },
{ "action": "select", "description": "If the user likes or selects any item, this action should be used. This action can only be called if a search has been called before." },
{ "action": "init", "description": "If the user wants to place an order after search and select and has shared the billing details. This action can only be called if a select has been called before." },
Expand Down
18 changes: 16 additions & 2 deletions controllers/Bot.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import AI from '../services/AI.js'
import DBService from '../services/DBService.js'
import logger from '../utils/logger.js'
import { v4 as uuidv4 } from 'uuid'
import MapsService from '../services/MapService.js'
const mapService = new MapsService()

const actionsService = new ActionsService()
const db = new DBService();
Expand Down Expand Up @@ -78,7 +80,7 @@ async function process_text(req, res) {
// inputs
let message = req.body.Body
const sender = req.body.From
const format = req.headers['content-type'] || 'text/xml';
const format = (req?.headers && req.headers['content-type']) || 'text/xml';
const raw_yn = req.body.raw_yn || false;

let response= {
Expand All @@ -95,7 +97,9 @@ async function process_text(req, res) {
formatted: []
},
bookings: [],
active_transaction: null
active_transaction: null,
routes:[],
selected_route:null
}

// Update lat, long
Expand Down Expand Up @@ -168,6 +172,16 @@ async function process_text(req, res) {
session = EMPTY_SESSION;
response.formatted = 'Session & profile cleared! You can start a new session now.';
}
else if(ai.action?.action === 'get_routes'){
const routes = await mapService.generate_routes(message, session.text);
const formatting_response = await ai.format_response(routes.data?.routes_formatted || routes.errors, [...session.actions.formatted, { role: 'user', content: message },...session.text]);
response.formatted = formatting_response.message;
session.routes = routes.data?.routes || session.routes;
logger.info(`AI response: ${response.formatted}`);

session.text.push({ role: 'user', content: message });
session.text.push({ role: 'assistant', content: response.formatted });
}
else if(ai.action?.action == null) {

// get ai response
Expand Down
5 changes: 3 additions & 2 deletions services/AI.js
Original file line number Diff line number Diff line change
Expand Up @@ -549,10 +549,11 @@ class AI {
return bookings_updated;
}

async get_details_by_description(message, desired_output){
async get_details_by_description(message, context=[], desired_output){

const openai_messages = [
{ role: 'system', content: `Your job is to analyse the given user input and extract details in the json format given : ${JSON.stringify(desired_output)}` },
{ role: 'system', content: `Your job is to analyse the given user input and extract details in the json format given : ${desired_output}` },
...context,
{ role: 'user', content: message }
]

Expand Down
53 changes: 53 additions & 0 deletions services/MapService.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {Client} from "@googlemaps/google-maps-services-js";
import logger from '../utils/logger.js'
import AI from './AI.js'
const ai = new AI();


class MapsService {
Expand Down Expand Up @@ -53,6 +55,57 @@ class MapsService {

return encodeURIComponent(color);
}

async generate_routes(message, context=[]) {
let response = {
status:false,
data: {},
errors: []
};

// identify source and destination
const format = {
'source': 'SOURCE_LOCATION',
'destination': 'DESTINATION_LOCATION'
}

const details = await ai.get_details_by_description(message, context, JSON.stringify(format));
logger.info(JSON.stringify(details, null, 2));
if(!details.source || !details.destination) {
if (!details.source ) {
response.errors.push("Can you please specify the source location?");
}
if (!details.destination) {
response.errors.push("Can you please specify the destination location?");
}
}
else{
// Get gps for source and destination
const source_gps = await this.lookupGps(details.source);
const destination_gps = await this.lookupGps(details.destination);

if(!source_gps || !destination_gps) {
if(!source_gps) {
response.errors.push("Can you please specify the source location?");
}
if(!destination_gps) {
response.errors.push("Can you please specify the destination location?");
}
}
else{
// generate routes
response.data.routes = await this.getRoutes(source_gps, destination_gps);
response.data.routes_formatted = {
"description": `these are the various routes that you can take. Which one would you like to select:`,
"routes": response.data.routes.map((route, index) => `Route ${index+1}: ${route.summary}`)
}
response.status = true;
}
}

logger.info(`Generated routes response : ${JSON.stringify(response, null, 2)}`);
return response;
}
}

export default MapsService;
49 changes: 47 additions & 2 deletions tests/unit/controllers/bot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const expect = chai.expect
const mapService = new MapsService()
const ai = new AI();
const actionsService = new ActionService()
import request from 'supertest'
import app from '../../../server.js'

describe.only('Test cases for AI', () => {
it('Should return message with location polygon', async () => {
Expand Down Expand Up @@ -68,7 +70,7 @@ describe.only('Test cases for Google maps', () => {
'destination': 'DESTINATION_LOCATION'
}

const details = await ai.get_details_by_description(ask, format);
const details = await ai.get_details_by_description(ask, [], JSON.stringify(format));
expect(details).to.have.property('source');
expect(details).to.have.property('destination');

Expand Down Expand Up @@ -150,6 +152,49 @@ describe.only('Test cases for Google maps', () => {
logger.info(`route_image: ${route_image}`);
expect(routes).to.be.an('array');
})
})

it('It should share a set of routes when given an instruction.', async () => {
const ask = "Can you plan a trip from Denver to Yellowstone national park?";

// generate routes
const routesResponse = await mapService.generate_routes(ask);
expect(routesResponse).to.have.property('status');
expect(routesResponse).to.have.property('data');
expect(routesResponse.status).to.be.true;
expect(routesResponse.data.routes).to.be.an('array').that.is.not.empty;
})

it('It should ask for details when given an incomplete instruction.', async () => {
const ask = "Can you plan a trip to Yellowstone national park?";

// generate routes
const routesResponse = await mapService.generate_routes(ask);
expect(routesResponse).to.have.property('status');
expect(routesResponse).to.have.property('errors');
expect(routesResponse.status).to.be.false;
expect(routesResponse.errors).to.be.an('array').that.is.not.empty;
})

it('Should share routes when asked to share routes.', async () => {
const ask = "Can you get routes from Denver to Yellowstone national park?";
const response = await request(app).post('/webhook').send({
"From": process.env.TEST_RECEPIENT_NUMBER,
"Body": ask
})
logger.info(JSON.stringify(response.text, null, 2));
expect(response.status).to.be.eq(200)

})

it('Should come back asking for more details.', async () => {
const ask = "Can you get routes to Yellowstone national park?";
const response = await request(app).post('/webhook').send({
"From": process.env.TEST_RECEPIENT_NUMBER,
"Body": ask
})
logger.info(response.text);
expect(response.status).to.be.eq(200)

})
})

0 comments on commit cdf7725

Please sign in to comment.