-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathApp.jsx
218 lines (184 loc) · 6.02 KB
/
App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
import { useEffect, useState } from "react";
import ActiveCallDetail from "./components/ActiveCallDetail";
import Button from "./components/base/Button";
import Vapi from "@vapi-ai/web";
import { isPublicKeyMissingError } from "./utils";
// Put your Vapi Public Key below.
const vapi = new Vapi("b3a49550-061c-470c-a5c5-d97157a310ea");
const App = () => {
const [connecting, setConnecting] = useState(false);
const [connected, setConnected] = useState(false);
const [assistantIsSpeaking, setAssistantIsSpeaking] = useState(false);
const [volumeLevel, setVolumeLevel] = useState(0);
const { showPublicKeyInvalidMessage, setShowPublicKeyInvalidMessage } = usePublicKeyInvalid();
// hook into Vapi events
useEffect(() => {
vapi.on("call-start", () => {
setConnecting(false);
setConnected(true);
setShowPublicKeyInvalidMessage(false);
});
vapi.on("call-end", () => {
setConnecting(false);
setConnected(false);
setShowPublicKeyInvalidMessage(false);
});
vapi.on("speech-start", () => {
setAssistantIsSpeaking(true);
});
vapi.on("speech-end", () => {
setAssistantIsSpeaking(false);
});
vapi.on("volume-level", (level) => {
setVolumeLevel(level);
});
vapi.on("error", (error) => {
console.error(error);
setConnecting(false);
if (isPublicKeyMissingError({ vapiError: error })) {
setShowPublicKeyInvalidMessage(true);
}
});
// we only want this to fire on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// call start handler
const startCallInline = () => {
setConnecting(true);
vapi.start(assistantOptions);
};
const endCall = () => {
vapi.stop();
};
return (
<div
style={{
display: "flex",
width: "100vw",
height: "100vh",
justifyContent: "center",
alignItems: "center",
}}
>
{!connected ? (
<Button
label="Call Vapi’s Pizza Front Desk"
onClick={startCallInline}
isLoading={connecting}
/>
) : (
<ActiveCallDetail
assistantIsSpeaking={assistantIsSpeaking}
volumeLevel={volumeLevel}
onEndCallClick={endCall}
/>
)}
{showPublicKeyInvalidMessage ? <PleaseSetYourPublicKeyMessage /> : null}
<ReturnToDocsLink />
</div>
);
};
const assistantOptions = {
name: "Vapi’s Pizza Front Desk",
firstMessage: "Vappy’s Pizzeria speaking, how can I help you?",
transcriber: {
provider: "deepgram",
model: "nova-2",
language: "en-US",
},
voice: {
provider: "playht",
voiceId: "jennifer",
},
model: {
provider: "openai",
model: "gpt-4",
messages: [
{
role: "system",
content: `You are a voice assistant for Vappy’s Pizzeria, a pizza shop located on the Internet.
Your job is to take the order of customers calling in. The menu has only 3 types
of items: pizza, sides, and drinks. There are no other types of items on the menu.
1) There are 3 kinds of pizza: cheese pizza, pepperoni pizza, and vegetarian pizza
(often called "veggie" pizza).
2) There are 3 kinds of sides: french fries, garlic bread, and chicken wings.
3) There are 2 kinds of drinks: soda, and water. (if a customer asks for a
brand name like "coca cola", just let them know that we only offer "soda")
Customers can only order 1 of each item. If a customer tries to order more
than 1 item within each category, politely inform them that only 1 item per
category may be ordered.
Customers must order 1 item from at least 1 category to have a complete order.
They can order just a pizza, or just a side, or just a drink.
Be sure to introduce the menu items, don't assume that the caller knows what
is on the menu (most appropriate at the start of the conversation).
If the customer goes off-topic or off-track and talks about anything but the
process of ordering, politely steer the conversation back to collecting their order.
Once you have all the information you need pertaining to their order, you can
end the conversation. You can say something like "Awesome, we'll have that ready
for you in 10-20 minutes." to naturally let the customer know the order has been
fully communicated.
It is important that you collect the order in an efficient manner (succinct replies
& direct questions). You only have 1 task here, and it is to collect the customers
order, then end the conversation.
- Be sure to be kind of funny and witty!
- Keep all your responses short and simple. Use casual language, phrases like "Umm...", "Well...", and "I mean" are preferred.
- This is a voice conversation, so keep your responses short, like in a real conversation. Don't ramble for too long.`,
},
],
},
};
const usePublicKeyInvalid = () => {
const [showPublicKeyInvalidMessage, setShowPublicKeyInvalidMessage] = useState(false);
// close public key invalid message after delay
useEffect(() => {
if (showPublicKeyInvalidMessage) {
setTimeout(() => {
setShowPublicKeyInvalidMessage(false);
}, 3000);
}
}, [showPublicKeyInvalidMessage]);
return {
showPublicKeyInvalidMessage,
setShowPublicKeyInvalidMessage,
};
};
const PleaseSetYourPublicKeyMessage = () => {
return (
<div
style={{
position: "fixed",
bottom: "25px",
left: "25px",
padding: "10px",
color: "#fff",
backgroundColor: "#f03e3e",
borderRadius: "5px",
boxShadow: "0 2px 5px rgba(0,0,0,0.2)",
}}
>
Is your Vapi Public Key missing? (recheck your code)
</div>
);
};
const ReturnToDocsLink = () => {
return (
<a
href="https://docs.vapi.ai"
target="_blank"
rel="noopener noreferrer"
style={{
position: "fixed",
top: "25px",
right: "25px",
padding: "5px 10px",
color: "#fff",
textDecoration: "none",
borderRadius: "5px",
boxShadow: "0 2px 5px rgba(0,0,0,0.2)",
}}
>
return to docs
</a>
);
};
export default App;