Function Instruction Type | Yext Hitchhikers Platform
Function steps leverage the Yext Functions framework to give Yext Chat access to user-defined serverless Typescript functions. This allows the bot to perform complex tasks like:
- Transforming, reading, and writing data to external APIs
- Using open source libraries to manipulate or analyze data
- Performing complex business logic
The function has access to the full history of the conversation, including any collected data, as well as any additional context data you’ve provided.
Additionally, the return value of the function is saved into the queryResult
, so the bot can use it to answer questions.
Let’s walk through an example.
Example
In this example, we’ll show a function that uses OpenWeatherMap to fetch the weather in a specific city.
To use a function in an instruction, simply specify the pluginId
(a plugin is a collection of functions) and the functionName
.
{
"$id": "example",
"$schema": "https://schema.yext.com/config/chat/chat-bot/v1",
"name": "Example",
"initialMessage": "Hello! How are you doing?",
"goals": {
"GET-WEATHER": {
"goal": "Get the weather in a city.",
"examples": [
"How's the weather in San Francisco?",
"What's the weather in Los Angeles?",
"What's the weather in San Diego?"
],
"instructions": [
{
"collect": {
"instruction": "Ask the user what city they're interested in",
"fields": [
{
"fieldType": "STRING",
"id": "city",
"optional": false
}
]
}
},
{
"function": {
"functionName": "getWeather",
"pluginId": "weatherPlugin"
}
},
{
"reply": {
"instruction": "Based on the response, tell them the weather in that city. You must ALWAYS respond in Farenheit. You may convert the temperature from Kelvin/Celsius to Farenheit if needed.",
"mode": "DIRECT_ANSWER"
}
}
]
}
}
}
So what should the function actually look like? Yext functions receive a payload object with the following keys:
messages
- the message historynotes
- the “notes” stored in the conversation, which includes thequeryResult
from previous messages, thepredictedGoal
, and morecontext
- any additional metadata passed to the Chat API, which might include details about the user or other contextual information that might influence the chat bot
(In other words, the Yext function has access to the exact same data that the Chat API has access to.)
Therefore a function to fetch data based on the “city” collected from the user might look like this:
export const getWeather = async ({ messages, notes }) => {
const city = notes.collectedData.city;
if (!city) {
throw new Error("No city provided".)
}
const params = {
city,
apiKey: "<YOUR_API_KEY_HERE>",
};
const url = `https://api.openweathermap.org/data/2.5/weather?q=${params.city}&appid=${params.apiKey}`;
const response = await fetch(url);
const data = await response.json();
return {
queryResult: data,
};
};
In this function, we first confirm that a city
was collected (which should always be the case, because the function
step is preceded by a collect
step that prompts the user for a city.
Next, we send an API request to an API and put its response in the queryResult
portion of the response, which makes it available to the next step in the instructions.
Typing the Function
Yext functions use the Deno runtime, which supports Typescript out of the box. If you want Typescript typings for your Chat serverless functions, you can use the types below, which are written using Zod, so that you can use them for runtime validation as well.
import { z } from "https://deno.land/x/zod/mod.ts";
const ChatFunctionPayloadSchema = z.object({
messages: z.array(
z.object({
text: z.string(),
timestamp: z.string(),
source: z.enum(["USER", "BOT"]),
})
),
notes: z
.object({
currentGoal: z.string().optional(),
currentStepIndices: z.array(z.number()).optional(),
searchQuery: z.string().optional(),
queryResult: z.any(),
collectedData: z.record(z.string()).optional(),
goalFirstMsgIndex: z.number().optional(),
})
.optional(),
});
export type ChatFunctionPayload = z.infer<typeof ChatFunctionPayloadSchema>;
export type ChatFunctionReturn = Partial<ChatFunctionPayload["notes"]>;
export type ChatFunction = (
payload: ChatFunctionPayload
) => Promise<ChatFunctionReturn>;