Skip to content

Commit

Permalink
refactored code
Browse files Browse the repository at this point in the history
  • Loading branch information
tmbo committed Aug 20, 2023
1 parent be95ad2 commit abae418
Showing 1 changed file with 164 additions and 149 deletions.
313 changes: 164 additions & 149 deletions rasa/core/channels/chat.html
Original file line number Diff line number Diff line change
Expand Up @@ -104,123 +104,129 @@ <h2>Happy chatting!</h2>
<div id="rasa-chat-widget" data-websocket-url="" data-default-open=true data-height="500"></div>

<script>
let chatDiv = document.getElementById("rasa-chat-widget");
let websocketUrl = window.location.origin.replace("http", "ws")
let maxHeight = document.documentElement.scrollHeight - 150;
chatDiv.setAttribute("data-websocket-url", websocketUrl);
chatDiv.setAttribute("data-height", maxHeight)
</script>

<script>
mermaid.initialize({ startOnLoad: false });
async function updateChatDiv() {
const chatDiv = document.getElementById("rasa-chat-widget");
const websocketUrl = window.location.origin.replace("http", "ws");
const maxHeight = document.documentElement.scrollHeight - 150;
chatDiv.setAttribute("data-websocket-url", websocketUrl);
chatDiv.setAttribute("data-height", maxHeight);
}

var previousTrackerState = null;
async function fetchFlows() {
try {
const response = await fetch("/flows");
if (!response?.ok) {
console.error("Error: Flows not found");
return [];
}
return await response.json();
} catch (error) {
console.error(error);
return [];
}
}

let get_flows = fetch("/flows")
.catch(error => console.error(error))
.then(response => {
if (!response.ok) {
console.error("Error: Flows not found");
return [];
async function fetchStory() {
const conversationId = window.rasaChatSessionId || "";
try {
const response = await fetch(`/conversations/${conversationId}/story`);
const data = await response.text();
const storyBlock = document.getElementById("json-data");
if (storyBlock.textContent !== data) {
storyBlock.textContent = data;
if (window.hljs) {
hljs.highlightElement(storyBlock);
}
}
} catch (error) {
console.error("Error fetching story:", error);
}
return response.json()
})
.then(data => {
return data;
});

function fetchStory() {
let conversationId = window.rasaChatSessionId || "";
fetch("/conversations/" + conversationId + "/story")
.catch(error => console.error(error))
.then(response => {
if (!response?.ok) {
console.error("Error: Story not found");
return "No story yet";
}
return response.text();
})
.then(data => {
let storyBlock = document.getElementById("json-data");
if (storyBlock.textContent === data) {
return;
}
storyBlock.textContent = data;
if (window.hljs) {
hljs.highlightElement(storyBlock);
}
});
}

async function fetchTracker() {
let conversationId = window.rasaChatSessionId || "";
await fetch("/conversations/" + conversationId + "/tracker?start_session=false")
.catch(error => console.error(error))
.then(response => {
if (!response?.ok) {
console.error("Error: Story not found");
return [];
}
return response.json()
})
.then(async (data) => {
let slots = data.slots;
if(JSON.stringify(previousTrackerState) === JSON.stringify(data)){
return;
}
previousTrackerState = data;

// update e2e-test
let e2eTest = document.getElementById("e2e-test");
let steps = []
for (var i = 0; i < data.events.length; i++) {
let event = data.events[i];
if(event.event == "user"){
steps.push({"user": event.text});
const conversationId = window.rasaChatSessionId || "";

try {
const response = await fetch(`/conversations/${conversationId}/tracker?start_session=false`);

if (!response?.ok) {
console.error("Error: Story not found");
return;
}
if(event.event == "bot"){
if (event.metadata?.utter_action) {
steps.push({"utter": event.metadata.utter_action});
} else {
steps.push({"utter": event.text});
}

const data = await response.json();

if (JSON.stringify(previousTrackerState) !== JSON.stringify(data)) {
previousTrackerState = data;

updateE2ETest(data);
updateSlots(data.slots);
updateStackFrames(data.slots.flow_stack || []);
await renderMermaidDiagram(data.slots.flow_stack || []);
}
}
let e2eTestCase = {"test_cases": [{"test_case": data.sender_id, "steps": steps}]}
e2eTest.textContent = jsyaml.dump(e2eTestCase);
if (window.hljs) {
hljs.highlightElement(e2eTest);
}

let root = document.getElementById('slots');
root.innerHTML = '';
let sortedSlotNames = Object.keys(slots).sort()
for (var i = 0; i < sortedSlotNames.length; i++) {
let name = sortedSlotNames[i];
let slotValue = JSON.stringify(slots[name], null, 2);
if(name == "flow_stack" || slotValue == 'null'){
continue;
} catch (error) {
console.error(error);
}
}

function updateE2ETest(data) {
const e2eTest = document.getElementById("e2e-test");
const steps = data.events.map(event => {
if (event.event === "user") return { "user": event.text };
if (event.event === "bot") {
return event.metadata?.utter_action ?
{ "utter": event.metadata.utter_action } :
{ "utter": event.text };
}
root.insertAdjacentHTML('beforeend', `<tr><td>${name}</td><td>${slotValue}</td></tr>`);
}
root.insertAdjacentHTML('beforeend', `<tr><td>\<\<\< all others \>\>\></td><td>None</td></tr>`);
}).filter(Boolean);

let stackEl = document.getElementById('stack-frames');
let stackFrames = slots.flow_stack ?? [];
stackEl.innerHTML = '';
if(stackFrames.length == 0){
stackEl.insertAdjacentHTML('afterbegin', `<tr class="center"><td colspan=4>--- Empty ---</td></tr>`);
} else {
for (var i = 0; i < stackFrames.length; i++) {
let stackFrame = stackFrames[i];
stackEl.insertAdjacentHTML('afterbegin', `<tr><td>${i+1}</td><td>${stackFrame.frame_type}</td><td>${stackFrame.flow_id}</td><td>${stackFrame.step_id}</td></tr>`);
const e2eTestCase = {
"test_cases": [{
"test_case": data.sender_id,
"steps": steps
}]
};
e2eTest.textContent = jsyaml.dump(e2eTestCase);

if (window.hljs) hljs.highlightElement(e2eTest);
}

function updateSlots(slots) {
const root = document.getElementById('slots');
root.innerHTML = '';
const sortedSlotNames = Object.keys(slots).sort();

sortedSlotNames.forEach(name => {
const slotValue = JSON.stringify(slots[name], null, 2);
if (name !== "flow_stack" && slotValue !== 'null') {
root.insertAdjacentHTML('beforeend', `<tr><td>${name}</td><td>${slotValue}</td></tr>`);
}
}
let activeFlow = stackFrames[stackFrames.length - 1]?.flow_id;
let activeStep = stackFrames[stackFrames.length - 1]?.step_id;
let flows = await get_flows;
// find flow by going through array and checking for the id field
let flow = flows.find(flow => flow.id === activeFlow);
var mermaidText = `---
});

root.insertAdjacentHTML('beforeend', `<tr><td><<< all others >>></td><td>None</td></tr>`);
}

function updateStackFrames(stackFrames) {
const stackEl = document.getElementById('stack-frames');
stackEl.innerHTML = '';

if (!stackFrames.length) {
stackEl.insertAdjacentHTML('afterbegin', `<tr class="center"><td colspan=4>--- Empty ---</td></tr>`);
} else {
stackFrames.forEach((frame, i) => {
stackEl.insertAdjacentHTML('afterbegin', `<tr><td>${i + 1}</td><td>${frame.frame_type}</td><td>${frame.flow_id}</td><td>${frame.step_id}</td></tr>`);
});
}
}

async function renderMermaidDiagram(stackFrames) {
let activeFlow = stackFrames[stackFrames.length - 1]?.flow_id;
let activeStep = stackFrames[stackFrames.length - 1]?.step_id;
let flows = await flowsData;
// find flow by going through array and checking for the id field
let flow = flows.find(flow => flow.id === activeFlow);
var mermaidText = `---
title: ${flow?.id ?? "No Flow"}
---
flowchart TD
Expand All @@ -229,59 +235,68 @@ <h2>Happy chatting!</h2>
classDef link fill:#f43
classDef slot fill:#aaa
classDef active stroke:#5A17ED,stroke-width:3px,stroke-dasharray: 5 3
`;

if (flow) {
for (var i = 0; i < flow.steps.length; i++) {
let step = flow.steps[i]
if (step.question){
let slotValue = slots[step.question] ? `_${slots[step.question]}_` : "\uD83D\uDCAC";
mermaidText += `${flow.steps[i].id}["\`${flow.steps[i].question}\n${slotValue}\`"]:::question\n`;
}
if (step.action){
mermaidText += `${flow.steps[i].id}["${flow.steps[i].action}"]:::action\n`;
}
if (step.link){
mermaidText += `${flow.steps[i].id}["\uD83D\uDD17 ${flow.steps[i].link}"]:::link\n`;
}
if (step.set_slots){
mermaidText += `${flow.steps[i].id}["\uD83E\uDEA3 ${flow.steps[i].id}"]:::slot\n`;
}
`;

if (step.id === activeStep) {
mermaidText += `class ${step.id} active\n`;
}
if (flow) {
for (var i = 0; i < flow.steps.length; i++) {
let step = flow.steps[i]
if (step.question){
let slotValue = slots[step.question] ? `_${slots[step.question]}_` : "\uD83D\uDCAC";
mermaidText += `${flow.steps[i].id}["\`${flow.steps[i].question}\n${slotValue}\`"]:::question\n`;
}
if (step.action){
mermaidText += `${flow.steps[i].id}["${flow.steps[i].action}"]:::action\n`;
}
if (step.link){
mermaidText += `${flow.steps[i].id}["\uD83D\uDD17 ${flow.steps[i].link}"]:::link\n`;
}
if (step.set_slots){
mermaidText += `${flow.steps[i].id}["\uD83E\uDEA3 ${flow.steps[i].id}"]:::slot\n`;
}

// if next is an id, then it is a link
if (step.next && typeof step.next === "string") {
mermaidText += `${step.id} --> ${step.next}\n`;
}
// if next is an array, then it is a list of conditions
if (step.next && Array.isArray(step.next)) {
for (var j = 0; j < step.next.length; j++) {
let condition = step.next[j];
if(condition.then){
mermaidText += `${step.id} -->|${condition.if}| ${condition.then}\n`;
}
if(condition.else){
mermaidText += `${step.id} -->|else| ${condition.else}\n`;
}
if (step.id === activeStep) {
mermaidText += `class ${step.id} active\n`;
}

// if next is an id, then it is a link
if (step.next && typeof step.next === "string") {
mermaidText += `${step.id} --> ${step.next}\n`;
}
// if next is an array, then it is a list of conditions
if (step.next && Array.isArray(step.next)) {
for (var j = 0; j < step.next.length; j++) {
let condition = step.next[j];
if(condition.then){
mermaidText += `${step.id} -->|${condition.if}| ${condition.then}\n`;
}
if(condition.else){
mermaidText += `${step.id} -->|else| ${condition.else}\n`;
}
}
}
}
let flowEl = document.getElementById('flow');
console.log(mermaidText);
const { svg } = await mermaid.render('graphDiv', mermaidText);
flowEl.innerHTML = svg;
});
}
let flowEl = document.getElementById('flow');
const { svg } = await mermaid.render('graphDiv', mermaidText);
flowEl.innerHTML = svg;
}

setInterval(fetchStory, 1000); // fetch the Story every second
setInterval(fetchTracker, 1000); // fetch the Tracker every second
async function updateData() {
await Promise.all([fetchStory(), fetchTracker()]);
setTimeout(updateData, 1000);
}

mermaid.initialize({ startOnLoad: false });

let previousTrackerState = null;
let flowsData = fetchFlows();

// Call functions initially
updateChatDiv();
updateData();
</script>

<script src="https://unpkg.com/@rasahq/rasa-chat" type="application/javascript"></script>
</body>

</html>
</html>

0 comments on commit abae418

Please sign in to comment.