Mike0021's picture
Deploy static pi web agent
21e6b9b verified
import { MODEL_ID, createPiAgent } from "./piAgent.js";
import { createSandbox } from "./sandbox.js";
import "./styles.css";
const nodes = {
status: document.querySelector("#status"),
modelStatus: document.querySelector("#model-status"),
sandboxStatus: document.querySelector("#sandbox-status"),
transcript: document.querySelector("#transcript"),
eventLog: document.querySelector("#event-log"),
files: document.querySelector("#files"),
prompt: document.querySelector("#prompt"),
run: document.querySelector("#run"),
boot: document.querySelector("#boot-sandbox"),
reset: document.querySelector("#reset-sandbox"),
demo: document.querySelector("#demo-prompt"),
mode: document.querySelector("#mode"),
device: document.querySelector("#device"),
maxTokens: document.querySelector("#max-new-tokens"),
temperature: document.querySelector("#temperature"),
modelLabel: document.querySelector("#model-label"),
};
nodes.modelLabel.textContent = MODEL_ID;
if (!navigator.gpu) nodes.device.value = "wasm";
const params = new URLSearchParams(window.location.search);
if (params.get("mode") === "mock") nodes.mode.value = "mock";
if (params.get("device")) nodes.device.value = params.get("device");
const sandbox = createSandbox({
onStatus: (text) => setSandboxStatus(text),
onLog: (text) => logEvent("sandbox", text),
});
let agent = createAgent();
function textFromContent(content) {
if (typeof content === "string") return content;
if (!Array.isArray(content)) return "";
return content
.filter((part) => part.type === "text")
.map((part) => part.text)
.join("\n");
}
function setStatus(text) {
nodes.status.textContent = text;
}
function setSandboxStatus(text) {
nodes.sandboxStatus.textContent = text;
}
function setModelStatus(text) {
nodes.modelStatus.textContent = text;
}
function logEvent(kind, text) {
const line = `[${new Date().toLocaleTimeString()}] ${kind}: ${text}`;
nodes.eventLog.textContent = `${nodes.eventLog.textContent}${line}\n`;
nodes.eventLog.scrollTop = nodes.eventLog.scrollHeight;
}
function createAgent() {
const next = createPiAgent({
sandbox,
modelMode: () => nodes.mode.value,
device: () => nodes.device.value,
maxTokens: () => nodes.maxTokens.value,
temperature: () => nodes.temperature.value,
onModelStatus: setModelStatus,
});
next.subscribe((event) => {
switch (event.type) {
case "agent_start":
setStatus("Agent running");
logEvent("agent", "start");
break;
case "message_end":
renderTranscript();
break;
case "tool_execution_start":
logEvent("tool", `${event.toolName} started`);
break;
case "tool_execution_end":
logEvent("tool", `${event.toolName} finished`);
break;
case "agent_end":
setStatus("Ready");
renderTranscript();
refreshFiles().catch((error) => logEvent("files", error.message));
break;
default:
break;
}
});
return next;
}
function resetAgent() {
agent.abort();
agent = createAgent();
renderTranscript();
}
function renderTranscript() {
const rendered = agent.state.messages
.map((message) => {
if (message.role === "toolResult") {
return `TOOL ${message.toolName}${message.isError ? " ERROR" : ""}\n${textFromContent(message.content)}`;
}
const heading = message.role.toUpperCase();
const toolCalls =
message.role === "assistant"
? message.content
.filter((part) => part.type === "toolCall")
.map((part) => `\nTOOL CALL ${part.name} ${JSON.stringify(part.arguments)}`)
.join("")
: "";
return `${heading}\n${textFromContent(message.content)}${toolCalls}`;
})
.join("\n\n");
nodes.transcript.textContent = rendered || "No messages yet.";
nodes.transcript.scrollTop = nodes.transcript.scrollHeight;
}
async function refreshFiles() {
if (!sandbox.isReady) {
nodes.files.textContent = "Sandbox not booted.";
return;
}
nodes.files.textContent = await sandbox.listFiles(".");
}
async function bootSandbox() {
nodes.boot.disabled = true;
try {
await sandbox.boot();
await refreshFiles();
} catch (error) {
setSandboxStatus("Sandbox error");
logEvent("sandbox", error.stack || error.message || String(error));
} finally {
nodes.boot.disabled = false;
}
}
nodes.boot.addEventListener("click", bootSandbox);
nodes.reset.addEventListener("click", async () => {
nodes.reset.disabled = true;
try {
await sandbox.reset();
resetAgent();
await refreshFiles();
} catch (error) {
logEvent("reset", error.stack || error.message || String(error));
} finally {
nodes.reset.disabled = false;
}
});
nodes.demo.addEventListener("click", () => {
nodes.prompt.value =
"Create hello.js that prints the result of 21 * 2, run it with Node, and tell me the command output.";
});
nodes.run.addEventListener("click", async () => {
nodes.run.disabled = true;
try {
await bootSandbox();
await agent.prompt(nodes.prompt.value);
} catch (error) {
setStatus("Error");
logEvent("agent", error.stack || error.message || String(error));
} finally {
nodes.run.disabled = false;
}
});
nodes.mode.addEventListener("change", () => {
resetAgent();
setModelStatus(nodes.mode.value === "mock" ? "Deterministic test model" : "Model idle");
});
setStatus("Ready");
setSandboxStatus("Not booted");
setModelStatus(nodes.mode.value === "mock" ? "Deterministic test model" : "Model idle");
renderTranscript();
refreshFiles().catch(() => {});