Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] fix ray assistant, closes #45416 #45491

Merged
merged 1 commit into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions doc/source/_static/css/assistant.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
/* Kapa Ask AI button */
#kapa-widget-container figure {
padding: 0 !important;
}
padding: 0 !important;
}

.mantine-Modal-root figure {
padding: 0 !important;
}

.mantine-Modal-root figure {
padding: 0 !important;
}
.assistant-title {
margin-top: 1em;
}

.container-xl.blurred {
filter: blur(5px);
Expand All @@ -24,8 +28,8 @@
top: 20%;
left: 50%;
transform: translate(-50%, -20%);
width: 50%;
height: 70%;
width: 55%;
height: 75%;
background-color: var(--pst-color-background);
border: 1px solid var(--pst-color-border);
border-radius: 10px;
Expand Down Expand Up @@ -88,9 +92,9 @@
}

.chatContentContainer {
padding: 15px;
max-height: calc(100% - 100px);
overflow-y: auto;
padding: 15px;
max-height: calc(100% - 100px);
overflow-y: auto;
}

.chatContentContainer input {
Expand All @@ -115,13 +119,13 @@ hr {
height: 1px;
}

#result{
#result {
padding: 15px;
border-radius: 10px;
margin-top: 10px;
margin-bottom: 10px;
background-color: var(--pst-color-surface);
max-height: calc(100% - 20px);
max-height: 50vh; /* Ensure the result area does not take too much vertical space */
overflow-y: auto;
}

Expand All @@ -138,8 +142,8 @@ hr {
}

.input-group {
display: flex;
align-items: stretch;
display: flex;
align-items: stretch;
}

#blurDiv {
Expand Down
255 changes: 129 additions & 126 deletions doc/source/_static/js/assistant.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
// Create chat-widget div
var chatWidgetDiv = document.createElement("div");
chatWidgetDiv.className = "chat-widget";
var chatWidgetDiv = document.createElement('div');
chatWidgetDiv.className = 'chat-widget';
chatWidgetDiv.innerHTML = `
<button id="openChatBtn">Ask AI</button>
`
`;
document.body.appendChild(chatWidgetDiv);

// Create chat-popup div
const date = new Date()
var chatPopupDiv = document.createElement("div");
chatPopupDiv.className = "chat-popup";
chatPopupDiv.id = "chatPopup";
const date = new Date();
var chatPopupDiv = document.createElement('div');
chatPopupDiv.className = 'chat-popup';
chatPopupDiv.id = 'chatPopup';
chatPopupDiv.innerHTML = `
<div class="chatHeader">
<div class="header-wrapper">
<h3>Ray Docs AI - Ask a question</h3>
<h3 class="assistant-title">Ray Docs AI - Ask a question</h3>
</div>
<button id="closeChatBtn" class="btn">
<i class="fas fa-times"></i>
Expand All @@ -33,153 +33,156 @@ chatPopupDiv.innerHTML = `
<div class="chatFooter text-right p-2">
© Copyright ${date.getFullYear()}, The Ray Team.
</div>
`
chatPopupDiv.messages = []
`;
chatPopupDiv.messages = [];
document.body.appendChild(chatPopupDiv);

const anchorDiv = document.getElementById('anchor');
const chatContainerDiv = document.getElementById('chatContainer')
const chatContainerDiv = document.getElementById('chatContainer');

const blurDiv = document.createElement('div')
blurDiv.id = "blurDiv"
blurDiv.classList.add("blurDiv-hidden")
const blurDiv = document.createElement('div');
blurDiv.id = 'blurDiv';
blurDiv.classList.add('blurDiv-hidden');
document.body.appendChild(blurDiv);

// blur background when chat popup is open
document.getElementById('openChatBtn').addEventListener('click', function() {
document.getElementById('openChatBtn').addEventListener('click', function () {
document.getElementById('chatPopup').style.display = 'block';
blurDiv.classList.remove("blurDiv-hidden");
blurDiv.classList.remove('blurDiv-hidden');
});

// un-blur background when chat popup is closed
document.getElementById('closeChatBtn').addEventListener('click', function() {
document.getElementById('closeChatBtn').addEventListener('click', function () {
document.getElementById('chatPopup').style.display = 'none';
blurDiv.classList.add("blurDiv-hidden");
blurDiv.classList.add('blurDiv-hidden');
});

// set code highlighting options
marked.setOptions({
renderer: new marked.Renderer(),
highlight: function(code) {
return hljs.highlight('python', code).value;
},
renderer: new marked.Renderer(),
highlight: function (code) {
return hljs.highlight('python', code).value;
},
});

function highlightCode() {
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
}

function renderCopyButtons(resultDiv) {
let preElements = resultDiv.querySelectorAll('pre');
preElements.forEach((preElement, index) => {
preElement.style.position = 'relative';

let uniqueId = `button-id-${index}`;
preElement.id = uniqueId;

// Set the proper attributes to the button to make is a sphinx copy button
let copyButton = document.createElement('button');
copyButton.className = 'copybtn o-tooltip--left';
copyButton.setAttribute('data-tooltip', 'Copy');
copyButton.setAttribute('data-clipboard-target', `#${uniqueId}`);
copyButton.style.position = 'absolute';
copyButton.style.top = '10px';
copyButton.style.right = '10px';
copyButton.style.opacity = 'inherit';

let imgElement = document.createElement('img');
imgElement.src = './_static/copy-button.svg'
imgElement.alt = 'Copy to clipboard';

copyButton.appendChild(imgElement);
preElement.appendChild(copyButton);
});
let preElements = resultDiv.querySelectorAll('pre');
preElements.forEach((preElement, index) => {
preElement.style.position = 'relative';

let uniqueId = `button-id-${index}`;
preElement.id = uniqueId;

// Set the proper attributes to the button to make is a sphinx copy button
let copyButton = document.createElement('button');
copyButton.className = 'copybtn o-tooltip--left';
copyButton.setAttribute('data-tooltip', 'Copy');
copyButton.setAttribute('data-clipboard-target', `#${uniqueId}`);
copyButton.style.position = 'absolute';
copyButton.style.top = '10px';
copyButton.style.right = '10px';
copyButton.style.opacity = 'inherit';

let imgElement = document.createElement('img');
imgElement.src = './_static/copy-button.svg';
imgElement.alt = 'Copy to clipboard';

copyButton.appendChild(imgElement);
preElement.appendChild(copyButton);
});
}

const searchBar = document.getElementById('searchBar')
const searchBtn = document.getElementById('searchBtn')


const searchBar = document.getElementById('searchBar');
const searchBtn = document.getElementById('searchBtn');

function rayAssistant(event) {
const resultDiv = document.getElementById('result');

const searchTerm = searchBar.value;
// handle empty input
if (!searchTerm) {
return
const resultDiv = document.getElementById('result');

const searchTerm = searchBar.value;
// handle empty input
if (!searchTerm) {
return;
}

if (
event.type === 'click' ||
(event.type === 'keydown' && event.key === 'Enter')
) {
// for improved UX, we want to equate a carriage return as hitting the send button and prevent default behavior of entering a new line
if (event.type === 'keydown' && event.key === 'Enter') {
event.preventDefault();
}

if (event.type === 'click' || event.type === 'keydown' && event.key === 'Enter'){
// for improved UX, we want to equate a carriage return as hitting the send button and prevent default behavior of entering a new line
if (event.type === 'keydown' && event.key === 'Enter') {
event.preventDefault();
}
// clear search bar value and change placeholder to prompt user to ask follow up question
searchBar.value = ""
searchBar.placeholder = "Ask follow up question here"

// Add query to the list of messages
chatPopupDiv.messages.push({"role": "user", "content": searchTerm})

let msgBlock = document.createElement('div');

resultDiv.insertBefore(msgBlock, anchorDiv);

let divider = document.createElement('hr')
msgBlock.appendChild(divider)

let msgHeader = document.createElement('div')
msgHeader.style.fontWeight = 'bold';
// capitalize first letter of question header
msgHeader.innerHTML=searchTerm.charAt(0).toUpperCase()+ searchTerm.slice(1)
msgBlock.appendChild(msgHeader)

async function readStream() {
try {
const response = await fetch('https://ray-assistant-public-bxauk.cld-kvedzwag2qa8i5bj.s.anyscaleuserdata.com/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({messages: chatPopupDiv.messages})
});
const reader = response.body.getReader();
let decoder = new TextDecoder('utf-8');
let msgContent = document.createElement('div')
msgBlock.appendChild(msgContent)
msgContent.innerHTML = '';

let collectChunks = "";

while (true) {
const { done, value } = await reader.read();
if (done) {
renderCopyButtons(resultDiv);
break;
}

const chunk = decoder.decode(value, {stream: true});
collectChunks += chunk;
let html = marked.parse(collectChunks);
html = DOMPurify.sanitize(html);
msgContent.innerHTML = html;
highlightCode();
}
chatPopupDiv.messages.push({"role": "assistant", "content": collectChunks})
// we want to autoscroll to the bottom of the chat container after the answer is streamed in
chatContainerDiv.scrollTo(0, chatContainerDiv.scrollHeight);
} catch (error) {
console.error('Fetch API failed:', error);
}
}
readStream().then(
res => console.log(res)
// clear search bar value and change placeholder to prompt user to ask follow up question
searchBar.value = '';
searchBar.placeholder = 'Ask follow up question here';

// Add query to the list of messages
chatPopupDiv.messages.push({role: 'user', content: searchTerm});

let msgBlock = document.createElement('div');

resultDiv.insertBefore(msgBlock, anchorDiv);

let divider = document.createElement('hr');
msgBlock.appendChild(divider);

let msgHeader = document.createElement('div');
msgHeader.style.fontWeight = 'bold';
// capitalize first letter of question header
msgHeader.innerHTML =
searchTerm.charAt(0).toUpperCase() + searchTerm.slice(1);
msgBlock.appendChild(msgHeader);

async function readStream() {
try {
const response = await fetch(
'https://ray-assistant-public-bxauk.cld-kvedzwag2qa8i5bj.s.anyscaleuserdata.com/chat',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({messages: chatPopupDiv.messages}),
}
);
const reader = response.body.getReader();
let decoder = new TextDecoder('utf-8');
let msgContent = document.createElement('div');
msgBlock.appendChild(msgContent);
msgContent.innerHTML = '';

let collectChunks = '';

while (true) {
const {done, value} = await reader.read();
if (done) {
renderCopyButtons(resultDiv);
break;
}

const chunk = decoder.decode(value, {stream: true});
collectChunks += chunk;
let html = marked.parse(collectChunks);
html = DOMPurify.sanitize(html);
msgContent.innerHTML = html;
highlightCode();
}
chatPopupDiv.messages.push({role: 'assistant', content: collectChunks});
// we want to autoscroll to the bottom of the chat container after the answer is streamed in
chatContainerDiv.scrollTo(0, chatContainerDiv.scrollHeight);
} catch (error) {
console.error('Fetch API failed:', error);
}
}
readStream().then((res) => console.log(res));
}
}

searchBtn.addEventListener('click', rayAssistant);
Expand Down
Loading