Add DuckLM service scripts and utility model benchmark
This commit is contained in:
+118
-9
@@ -16,6 +16,100 @@ function escapeText(value) {
|
||||
return String(value ?? "");
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function renderInlineMarkdown(text) {
|
||||
let html = escapeHtml(text);
|
||||
html = html.replace(/`([^`]+)`/g, "<code>$1</code>");
|
||||
html = html.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
||||
html = html.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
||||
html = html.replace(
|
||||
/(https?:\/\/[^\s<]+)/g,
|
||||
'<a href="$1" target="_blank" rel="noreferrer">$1</a>',
|
||||
);
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderMarkdown(markdown) {
|
||||
const blocks = [];
|
||||
const lines = String(markdown ?? "").replace(/\r\n/g, "\n").split("\n");
|
||||
let paragraph = [];
|
||||
let listItems = [];
|
||||
let inFence = false;
|
||||
let fenceLines = [];
|
||||
|
||||
const flushParagraph = () => {
|
||||
if (!paragraph.length) return;
|
||||
blocks.push(`<p>${renderInlineMarkdown(paragraph.join(" "))}</p>`);
|
||||
paragraph = [];
|
||||
};
|
||||
const flushList = () => {
|
||||
if (!listItems.length) return;
|
||||
blocks.push(`<ul>${listItems.map((item) => `<li>${renderInlineMarkdown(item)}</li>`).join("")}</ul>`);
|
||||
listItems = [];
|
||||
};
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith("```")) {
|
||||
if (inFence) {
|
||||
blocks.push(`<pre><code>${escapeHtml(fenceLines.join("\n"))}</code></pre>`);
|
||||
fenceLines = [];
|
||||
inFence = false;
|
||||
} else {
|
||||
flushParagraph();
|
||||
flushList();
|
||||
inFence = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inFence) {
|
||||
fenceLines.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
const heading = /^(#{1,4})\s+(.+)$/.exec(line);
|
||||
if (heading) {
|
||||
flushParagraph();
|
||||
flushList();
|
||||
const level = heading[1].length + 2;
|
||||
blocks.push(`<h${level}>${renderInlineMarkdown(heading[2])}</h${level}>`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const listItem = /^\s*[-*]\s+(.+)$/.exec(line);
|
||||
if (listItem) {
|
||||
flushParagraph();
|
||||
listItems.push(listItem[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line.trim()) {
|
||||
flushParagraph();
|
||||
flushList();
|
||||
continue;
|
||||
}
|
||||
flushList();
|
||||
paragraph.push(line.trim());
|
||||
}
|
||||
if (inFence) blocks.push(`<pre><code>${escapeHtml(fenceLines.join("\n"))}</code></pre>`);
|
||||
flushParagraph();
|
||||
flushList();
|
||||
return blocks.join("");
|
||||
}
|
||||
|
||||
function setMarkdownContent(node, content) {
|
||||
if (!node) return;
|
||||
node.dataset.markdown = String(content ?? "");
|
||||
node.innerHTML = renderMarkdown(node.dataset.markdown);
|
||||
}
|
||||
|
||||
function setStatus(id, text, tone = "neutral") {
|
||||
const node = document.querySelector(id);
|
||||
if (!node) return;
|
||||
@@ -67,7 +161,12 @@ function addMessage(role, content, meta = "", options = {}) {
|
||||
messageMeta.innerHTML = `<strong>${role === "user" ? "You" : "DuckLM"}</strong><span>${escapeText(meta)}</span>`;
|
||||
|
||||
const text = document.createElement("p");
|
||||
text.textContent = content;
|
||||
text.className = role === "assistant" ? "message-body markdown-body" : "message-body";
|
||||
if (role === "assistant") {
|
||||
setMarkdownContent(text, content);
|
||||
} else {
|
||||
text.textContent = content;
|
||||
}
|
||||
|
||||
bubble.append(messageMeta);
|
||||
if (role === "assistant" && options.reasoning) {
|
||||
@@ -204,11 +303,10 @@ function updateToolTerminal(article, eventPayload) {
|
||||
terminal.classList.remove("is-waiting");
|
||||
status.textContent = result.ok ? "ok" : "error";
|
||||
|
||||
const title = terminal.querySelector(".tool-terminal-title")?.textContent || body.textContent.trim();
|
||||
const parts = [title];
|
||||
if (result.output) parts.push("\nstdout\n" + result.output.trimEnd());
|
||||
if (result.error) parts.push("\nstderr\n" + result.error.trimEnd());
|
||||
body.textContent = parts.join("\n");
|
||||
const parts = [];
|
||||
if (result.output) parts.push("stdout\n" + result.output.trimEnd());
|
||||
if (result.error) parts.push("stderr\n" + result.error.trimEnd());
|
||||
body.textContent = parts.join("\n\n") || "completed with no output";
|
||||
document.querySelector("#messages").scrollTop = document.querySelector("#messages").scrollHeight;
|
||||
}
|
||||
|
||||
@@ -359,13 +457,22 @@ function humanApprovalDecision(action) {
|
||||
|
||||
function setMessagePending(article, text) {
|
||||
const paragraph = article?.querySelector("p");
|
||||
if (paragraph) paragraph.textContent = text;
|
||||
if (!paragraph) return;
|
||||
if (paragraph.classList.contains("markdown-body")) {
|
||||
setMarkdownContent(paragraph, text);
|
||||
} else {
|
||||
paragraph.textContent = text;
|
||||
}
|
||||
}
|
||||
|
||||
function appendMessageText(article, delta) {
|
||||
const paragraph = article?.querySelector("p");
|
||||
if (!paragraph) return;
|
||||
paragraph.textContent += delta;
|
||||
if (paragraph.classList.contains("markdown-body")) {
|
||||
setMarkdownContent(paragraph, `${paragraph.dataset.markdown || ""}${delta}`);
|
||||
} else {
|
||||
paragraph.textContent += delta;
|
||||
}
|
||||
document.querySelector("#messages").scrollTop = document.querySelector("#messages").scrollHeight;
|
||||
}
|
||||
|
||||
@@ -758,7 +865,8 @@ async function sendMessage() {
|
||||
setStatus("#task-status", "running", "warn");
|
||||
addMessage("user", message, "submitted");
|
||||
input.value = "";
|
||||
const pending = addMessage("assistant", "", "thinking", {reasoning: true});
|
||||
const reasoningEnabled = document.querySelector("#reasoning")?.checked || false;
|
||||
const pending = addMessage("assistant", "", "thinking", {reasoning: reasoningEnabled});
|
||||
const context = {taskId: "", contentStarted: false};
|
||||
|
||||
try {
|
||||
@@ -767,6 +875,7 @@ async function sendMessage() {
|
||||
conversation_id: state.currentConversationId || null,
|
||||
workspace: document.querySelector("#workspace").value,
|
||||
debug: document.querySelector("#debug").checked,
|
||||
reasoning: reasoningEnabled ? "on" : "off",
|
||||
}, async ({name, data}) => {
|
||||
await handleAssistantStreamEvent(pending, name, data, context);
|
||||
});
|
||||
|
||||
@@ -500,6 +500,60 @@ dd {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.bubble .markdown-body {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body p,
|
||||
.markdown-body ul,
|
||||
.markdown-body pre {
|
||||
margin: 8px 0 0;
|
||||
}
|
||||
|
||||
.markdown-body h3 {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.markdown-body h4,
|
||||
.markdown-body h5 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.markdown-body ul {
|
||||
padding-left: 22px;
|
||||
}
|
||||
|
||||
.markdown-body code {
|
||||
padding: 1px 4px;
|
||||
border-radius: 5px;
|
||||
background: #e2e8f0;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
.markdown-body pre {
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
background: #0f172a;
|
||||
color: #d1fae5;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.markdown-body pre code {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.markdown-body a {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.message-reasoning {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
<input id="debug" type="checkbox" checked>
|
||||
<span>Debug mode</span>
|
||||
</label>
|
||||
<label class="toggle-row">
|
||||
<input id="reasoning" type="checkbox">
|
||||
<span>Reasoning</span>
|
||||
</label>
|
||||
</section>
|
||||
|
||||
<section class="status-panel" aria-labelledby="status-title">
|
||||
|
||||
Reference in New Issue
Block a user