feat: handle polling events on client side

pull/31/head
hanchchch 1 year ago
parent 94db570acf
commit a4eb16d4bf

@ -40,7 +40,7 @@
<div class="d-flex flex-row">
<div
class="d-flex flex-column flex-shrink-0 p-3 bg-body-tertiary"
style="width: 280px; height: 80vh"
style="width: 240px; height: 80vh"
>
<ul id="nav-sidebar" class="nav nav-pills flex-column mb-auto">
<li class="nav-item">

@ -35,10 +35,14 @@
</button>
</div>
</div>
<div class="bg-body-tertiary border rounded-3 p-3">
<div>
<div id="answer"></div>
<div id="response-files"></div>
<div class="bg-body-tertiary border rounded-3 p-2">
<div id="actions" class="m-2"></div>
<div class="card m-2">
<div class="card-body">
<div id="answer" class="card-text"></div>
<div id="response-files" class="card-text"></div>
</div>
</div>
</div>
</div>
</div>

@ -1,6 +1,8 @@
const $ = (selector) => document.querySelector(selector);
const setLoader = (isLoading) => {
const button = document.getElementById("submit");
const loader = document.getElementById("submit-loader");
const button = $("#submit");
const loader = $("#submit-loader");
if (isLoading) {
button.style.display = "none";
loader.style.display = "block";
@ -10,9 +12,14 @@ const setLoader = (isLoading) => {
}
};
const setAnswer = (answer, files) => {
document.getElementById("answer").textContent = answer;
const filesDiv = document.getElementById("response-files");
const setAnswer = (answer, files = []) => {
if (answer) {
$("#answer").textContent = answer;
} else {
$("#answer").innerHTML = createSpinner();
}
const filesDiv = $("#response-files");
filesDiv.innerHTML = "";
files.forEach((file) => {
const a = document.createElement("a");
@ -25,7 +32,7 @@ const setAnswer = (answer, files) => {
};
class EvalApi {
constructor({ onComplete, onError, onSettle }) {
constructor({ onComplete, onError, onSettle, onLLMEnd, onToolEnd }) {
this.executionId = null;
this.pollInterval = null;
this.onComplete = (answer, files) => {
@ -36,8 +43,13 @@ class EvalApi {
onError(error);
onSettle();
};
this.onLLMEnd = (info) => {
onLLMEnd(info);
};
this.onToolEnd = (info) => {
onToolEnd(info);
};
}
async uploadFiles(rawfiles) {
const files = [];
@ -90,13 +102,22 @@ class EvalApi {
if (response.status !== 200) {
throw new Error(await response.text());
}
const { status, result } = await response.json();
if (status === "FAILURE") {
throw new Error("Execution failed");
}
if (status === "SUCCESS") {
clearInterval(this.pollInterval);
this.onComplete(result.answer, result.files);
const { status, result, info } = await response.json();
switch (status) {
case "PENDING":
break;
case "FAILURE":
throw new Error("Execution failed");
case "LLM_END":
this.onLLMEnd(info);
break;
case "TOOL_END":
this.onToolEnd(info);
break;
case "SUCCESS":
clearInterval(this.pollInterval);
this.onComplete(result.answer, result.files);
break;
}
} catch (e) {
clearInterval(this.pollInterval);
@ -106,23 +127,143 @@ class EvalApi {
}
const submit = async () => {
setAnswer("Thinking...", []);
setAnswer("");
setLoader(true);
const actions = $("#actions");
const api = new EvalApi({
onComplete: (answer, files) => setAnswer(answer, files),
onError: (error) => setAnswer(`Error: ${error.message}`, []),
onSettle: () => setLoader(false),
onLLMEnd: (info) => {
const w = document.createElement("div");
w.innerHTML = createActionCard(
1,
info.action,
info.action_input,
info.what_i_did,
info.plan
);
actions.innerHTML = "";
actions.appendChild(w);
},
onToolEnd: (info) => {
const w = document.createElement("div");
w.innerHTML = createActionCard(
1,
info.action,
info.action_input,
info.what_i_did,
info.plan,
info.observation
);
actions.innerHTML = "";
actions.appendChild(w);
},
});
const prompt = document.getElementById("prompt").value;
const session = document.getElementById("session").value;
const files = await api.uploadFiles(document.getElementById("files").files);
const prompt = $("#prompt").value;
const session = $("#session").value;
const files = await api.uploadFiles($("#files").files);
await api.execute(prompt, session, files);
};
const setRandomSessionId = () => {
const sessionId = Math.random().toString(36).substring(2, 15);
document.getElementById("session").value = sessionId;
$("#session").value = sessionId;
};
const createSpinner = () => `
<div class="text-center">
<div class="spinner-border m-3"></div>
</div>
`;
const createActionCard = (
index,
action,
input,
whatIdid,
plan,
observation
) => `
<div class="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button">
Action #${index} - ${action}
</button>
</h2>
<div class="accordion-collapse collapse show">
<div class="accordion-body">
<table class="table">
<tbody>
<tr>
<th>Input</th>
<td><div>${input}</div></td>
</tr>
<tr>
<th>What I Did</th>
<td><div>${whatIdid}</div></td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<th colspan="2">Plan</th>
</tr>
</thead>
<tbody>
${plan
.split("- ")
.map((p) => p.trim())
.filter((p) => p.length > 0)
.map(
(p) => `
<tr>
${
p.startsWith("[ ]")
? `<td><input class="form-check-input" type="checkbox" /></td>
<td>${p.replace("[ ]", "")}</td>`
: ""
}
${
p.startsWith("[x]")
? `<td><input class="form-check-input" type="checkbox" checked/></td>
<td>${p.replace("[x]", "")}</td>`
: ""
}
${
!p.startsWith("[ ]") && !p.startsWith("[x]")
? `<td></td><td>${p}</td>`
: ""
}
</tr>`
)
.join("")}
</tbody>
</table>
<table class="table">
<thead>
<tr>
<th colspan="2">Observation</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div style="white-space: pre">${observation}</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>`;

Loading…
Cancel
Save