Skip to content

Commit 465248f

Browse files
authored
TypeAhead Solution Feature (microsoft#253)
* Create readme.txt * Add files via upload * Create README.md * Delete TypeaheadSuggestions/readme.txt * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Add files via upload * Update TypeAheadSuggestions_V1.0.html * Update TypeAheadSuggestions_V1.0.html Updated to use Direct line token url instead of secret based on feedback. * Update README.md * Update TypeAheadSuggestions_V1.0.html * Update TypeAheadSuggestions_V1.0.html * Create TypeAheadSuggestions_V2.0[REACT Component].html Added additional V2 version using REACT Component * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update README.md * Update TypeAheadSuggestions_V2.0[REACT Component].html * Update TypeAheadSuggestions_V1.0.html
1 parent 192bb9b commit 465248f

4 files changed

Lines changed: 606 additions & 0 deletions

File tree

TypeaheadSuggestions/README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Typeahed Suggestions for Copilot Studio
2+
This code sample allows you to create a typeahead suggestion functionality for your custome copilot that can assist users finding things like frequently asked questions, auto correcting typos and showing a list of menu items like product names or topic names before sending a message to the copilot. The version one uses an API that returns topic names for given bot but the sample can be used with any RestFul endpoint to show relevant suggestions based on your usecase.
3+
4+
Please note that version 1 calls the API once and loads the suggestions for your bot when the chat is initiated. It then filters through these suggestions based on users questions.
5+
Please note that version 2 which is built using REACT component calls the API every time users asks a questions and shows the relevant suggestion based on each question when user type '/'
6+
7+
8+
9+
## Getting Started
10+
11+
1. Create your custom copilot using copilot studio with required topics and actions.
12+
2. Once you have published your copilot go to mobile channel and copy the directline endpoint url.
13+
3. Download the TypeAheadSuggestion.HTML canvas to your local machine and open in any code editor.
14+
4. On line 158 replace the direct enpoint you copied for your copilot. Please note that for best practices you can use directline token instead of using secret in the canvas.
15+
<img width="853" alt="image" src="https://github.com/user-attachments/assets/fd8a9800-2e95-40e1-b10f-a8b060fa61bb">
16+
17+
18+
5. Next you will need to add a RestAPI that returns an array of suggestions for your bot. For testing, you can use the TypeAheadSolutionAPI Zip file that has a power automate flow that returns topic names for a given copilot bot. On the power automate flow you will need to edit the list row action and replace the exisiting botid with your botid.
19+
20+
<img width="731" alt="image" src="https://github.com/user-attachments/assets/628fa504-e534-4e5c-bd03-29fa351d96bf">
21+
22+
23+
24+
25+
7. On line 222 Add your RestAPI endpoint that returns an array of suggested topics.
26+
<img width="830" alt="image" src="https://github.com/user-attachments/assets/58f1608e-c87f-42e9-b8de-547142edefe6">
27+
28+
29+
8. Optional : If you would like to exclude any suggestions based on your business logic you can create a exclusion list as shown below on line 173.
30+
<img width="306" alt="image" src="https://github.com/user-attachments/assets/c7abb501-e3f4-4e67-a6d4-c7852fadca4a">
31+
32+
9. Click Save and open the file in the browser to test. You can launch the bot by clicking on the chat bubble and initiating the chat.
33+
34+
10. To see list of suggestions before typying click the space bar which opens a dropdow list of suggested topcis
35+
36+
11. For getting type ahead suggestions continue to type in the chat bot canvas and suggestions will be shown below the chat area. Clicking on any suggestion submits the utterance to the bot.
37+
38+
# Watch below Demo to Get Started with Version 1
39+
40+
[![Watch the video](https://th.bing.com/th/id/OIP.9k6Gz3sbmi5b8r6YxTSG-QHaEK?w=289&h=180&c=7&r=0&o=5&dpr=1.5&pid=1.7
41+
)](https://www.youtube.com/watch?v=7xbSpzmQcIg)
42+
43+
# Watch below Demo to Get Started with Version 2
44+
[![Watch the video](https://th.bing.com/th/id/OIP.9k6Gz3sbmi5b8r6YxTSG-QHaEK?w=289&h=180&c=7&r=0&o=5&dpr=1.5&pid=1.7
45+
)](https://youtu.be/H9_r7R4iNOU)
46+
47+
48+
49+
50+
51+
52+
3.49 KB
Binary file not shown.
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Copilot Studio Intelligent Suggestions Version 1.0</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
background-color: #f4f4f4;
11+
margin: 0;
12+
padding: 0;
13+
height: 100vh;
14+
display: flex;
15+
background: url('https://github.com/paragdessai/PVAClickAnalytics/blob/main/Chatbotbackground.png?raw=true') no-repeat center center;
16+
background-size: cover;
17+
justify-content: center;
18+
align-items: center;
19+
}
20+
21+
/* Chat bubble style */
22+
#chatBubble {
23+
position: fixed;
24+
bottom: 30px;
25+
right: 30px;
26+
background-color: #0078D4;
27+
color: white;
28+
padding: 15px;
29+
border-radius: 50%;
30+
cursor: pointer;
31+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
32+
}
33+
34+
/* Bot container hidden initially */
35+
#botContainer {
36+
position: fixed;
37+
display: none;
38+
justify-content: center;
39+
align-items: center;
40+
height: 100%;
41+
width: 100%;
42+
z-index: 999;
43+
background: rgba(0, 0, 0, 0.5); /* Semi-transparent background */
44+
}
45+
46+
/* Chat window style */
47+
.chat-window {
48+
background-color: white;
49+
border-radius: 8px;
50+
width: 600px;
51+
height: 600px;
52+
display: flex;
53+
flex-direction: column;
54+
position: relative;
55+
padding: 10px;
56+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
57+
}
58+
59+
/* Close button inside chat */
60+
#closeBot {
61+
position: absolute;
62+
top: 10px;
63+
right: 50px;
64+
background-color: #ff4d4f;
65+
color: white;
66+
border: none;
67+
border-radius: 50%;
68+
width: 30px;
69+
height: 30px;
70+
cursor: pointer;
71+
text-align: center;
72+
font-weight: bold;
73+
line-height: 30px;
74+
}
75+
76+
/* Refresh button inside chat */
77+
#refreshBot {
78+
position: absolute;
79+
top: 10px;
80+
right: 10px;
81+
background-color: #4CAF50; /* Green */
82+
color: white;
83+
border: none;
84+
border-radius: 50%;
85+
width: 30px;
86+
height: 30px;
87+
cursor: pointer;
88+
text-align: center;
89+
font-weight: bold;
90+
line-height: 30px;
91+
}
92+
93+
#webchat {
94+
flex: 1;
95+
border: 1px solid #ddd;
96+
border-radius: 4px;
97+
background-color: rgba(255, 255, 255, 0.9);
98+
margin-top: 20px;
99+
overflow: hidden;
100+
position: relative; /* Make it a positioning context */
101+
}
102+
103+
/* Suggestions list Styling */
104+
.suggestion-list {
105+
position: relative;
106+
background-color: white;
107+
border: 1px solid #ddd;
108+
border-radius: 4px;
109+
width: calc(100% - 20px);
110+
max-width: 560px;
111+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
112+
z-index: 1000;
113+
padding: 0;
114+
display: none;
115+
margin-top: 5px;
116+
}
117+
118+
.suggestion-item {
119+
padding: 10px;
120+
cursor: pointer;
121+
list-style-type: none;
122+
border-bottom: 1px solid #ddd;
123+
}
124+
125+
.suggestion-item:hover {
126+
background-color: #f4f4f4;
127+
}
128+
129+
.suggestion-item:last-child {
130+
border-bottom: none;
131+
}
132+
</style>
133+
</head>
134+
<body>
135+
136+
<!-- Chat bubble to open the bot -->
137+
<div id="chatBubble">💬</div>
138+
139+
<!-- Bot container with chat window -->
140+
<div id="botContainer">
141+
<div class="chat-window">
142+
<button id="closeBot">X</button>
143+
<button id="refreshBot">🔄</button> <!-- Refresh button -->
144+
<div id="webchat" role="main"></div>
145+
<ul id="suggestions" class="suggestion-list"></ul> <!-- Suggestions now positioned relative to the input -->
146+
</div>
147+
</div>
148+
149+
<!-- Include BotFramework WebChat -->
150+
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
151+
152+
<script>
153+
let directLine;
154+
155+
// Function to retrieve the Direct Line token
156+
async function getDirectLineToken() {
157+
try {
158+
const response = await fetch('Insert your directline token API');
159+
if (!response.ok) throw new Error('Network response was not ok');
160+
const data = await response.json();
161+
return data.token; // Assuming the token is in the 'token' field
162+
} catch (error) {
163+
console.error('Error fetching token:', error);
164+
return null;
165+
}
166+
}
167+
168+
// Direct Line connection initialization
169+
async function initializeDirectLine() {
170+
const token = await getDirectLineToken(); // Fetch the token
171+
if (token) {
172+
directLine = window.WebChat.createDirectLine({
173+
token: token // Use the fetched token
174+
});
175+
176+
// Render Web Chat in bot container
177+
window.WebChat.renderWebChat(
178+
{
179+
directLine: directLine,
180+
userID: 'user1',
181+
username: 'User',
182+
locale: 'en-US'
183+
},
184+
document.getElementById('webchat')
185+
);
186+
187+
// Listen for user input in Web Chat (via Direct Line connection)
188+
directLine.activity$.subscribe(activity => {
189+
if (activity.type === 'message' && activity.from.id === 'user1') {
190+
handleInput(activity.text); // Handle input to show suggestions
191+
}
192+
});
193+
} else {
194+
console.error('Failed to initialize Direct Line due to missing token');
195+
}
196+
}
197+
198+
// Initialize topics array
199+
let topics = [];
200+
201+
// List of values to exclude from API Response
202+
const excludedTopics = [
203+
'Reset Conversation',
204+
'Thank you',
205+
'Sign in',
206+
'Sign in ',
207+
'Conversation Start',
208+
'End of Conversation',
209+
'On Error',
210+
'Fallback',
211+
'Conversational boosting',
212+
'Multiple Topics Matched',
213+
'Escalate',
214+
'Start Over'
215+
];
216+
217+
const suggestionsList = document.getElementById('suggestions');
218+
219+
// Function to fetch Suggestion using API. The API needs to return an Array of Suggestions
220+
async function fetchTopics() {
221+
try {
222+
const response = await fetch('https://prod-177.westus.logic.azure.com:443/workflows/7539cccafbaa45a3b139c8e83492aa44/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=1iEAjAT50LuQI_54hHJYult5P1ygpj9G3i2qxQsoOKI'); // Add your RestAPI here
223+
if (!response.ok) throw new Error('Network response was not ok');
224+
const fetchedTopics = await response.json(); // Get the response data
225+
226+
// Filter out excluded topics
227+
topics = fetchedTopics.filter(topic => !excludedTopics.includes(topic));
228+
console.log('Topics fetched:', topics);
229+
} catch (error) {
230+
console.error('Error fetching topics:', error);
231+
}
232+
}
233+
234+
// Function to handle user typing and show suggestions
235+
function handleInput(input) {
236+
const userInput = input.toLowerCase();
237+
suggestionsList.innerHTML = ''; // Clear previous suggestions
238+
239+
if (userInput) {
240+
const filteredTopics = topics.filter(topic => topic.toLowerCase().includes(userInput));
241+
242+
// Show suggestions list only if there are filtered topics
243+
if (filteredTopics.length > 0) {
244+
filteredTopics.forEach(topic => {
245+
const suggestionItem = document.createElement('li');
246+
suggestionItem.classList.add('suggestion-item');
247+
suggestionItem.textContent = topic;
248+
249+
// Handle click on suggestion
250+
suggestionItem.addEventListener('click', function() {
251+
if (directLine) {
252+
// Send the selected suggestion as a message to the bot
253+
directLine.postActivity({
254+
from: { id: 'user1' }, // User ID
255+
type: 'message',
256+
text: topic // Send the selected topic
257+
}).subscribe(
258+
id => console.log('Message sent with ID:', id),
259+
error => console.error('Error sending message:', error)
260+
);
261+
262+
suggestionsList.style.display = 'none'; // Hide suggestions after selection
263+
} else {
264+
console.error('DirectLine is not initialized');
265+
}
266+
});
267+
268+
suggestionsList.appendChild(suggestionItem);
269+
});
270+
271+
suggestionsList.style.display = 'block'; // Show suggestions list
272+
} else {
273+
suggestionsList.style.display = 'none'; // Hide if no suggestions
274+
}
275+
} else {
276+
suggestionsList.style.display = 'none'; // Hide if input is cleared
277+
}
278+
}
279+
280+
// Open bot on chat bubble click
281+
document.getElementById('chatBubble').addEventListener('click', async function() {
282+
const botContainer = document.getElementById('botContainer');
283+
botContainer.style.display = 'flex'; // Show the bot container
284+
this.style.display = 'none'; // Hide chat bubble
285+
286+
await fetchTopics(); // Fetch topics when the chat bubble is clicked
287+
initializeDirectLine(); // Initialize Direct Line after fetching topics
288+
});
289+
290+
// Close bot on 'X' button click
291+
document.getElementById('closeBot').addEventListener('click', function() {
292+
document.getElementById('botContainer').style.display = 'none'; // Hide bot container
293+
document.getElementById('chatBubble').style.display = 'block'; // Show chat bubble
294+
});
295+
296+
// Refresh page on refresh button click
297+
document.getElementById('refreshBot').addEventListener('click', function() {
298+
location.reload(); // Refresh the page
299+
});
300+
301+
// Display suggestions based on user typing in the web chat
302+
document.getElementById('webchat').addEventListener('input', function(event) {
303+
const userInput = event.target.value;
304+
handleInput(userInput); // Call handleInput function to show suggestions
305+
});
306+
</script>
307+
</body>
308+
</html>

0 commit comments

Comments
 (0)