First commit
This commit is contained in:
commit
b60e72c88e
34
index.html
Normal file
34
index.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
<title>Super Stupidly Simple Ollama Client</title>
|
||||||
|
<link rel="icon" type="image/png" href="favicon/128/favicon.png" sizes="any">
|
||||||
|
<link rel="icon" type="image/png" href="favicon/16/favicon.png" sizes="16x16">
|
||||||
|
<link rel="icon" type="image/png" href="favicon/32/favicon.png" sizes="32x32">
|
||||||
|
<link rel="icon" type="image/png" href="favicon/64/favicon.png" sizes="64x64">
|
||||||
|
<link rel="icon" type="image/png" href="favicon/128/favicon.png" sizes="128x128">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="chatbox"></div>
|
||||||
|
<div id="statusbox"></div>
|
||||||
|
<textarea id="prompt" placeholder="Type a message..." rows="2"></textarea>
|
||||||
|
<div class="controlbox">
|
||||||
|
<select id="model">
|
||||||
|
<option value="deepseek-v2:16b">deepseek-v2:16b</option>
|
||||||
|
<option value="deepseek-coder-v2:16b">deepseek-coder-v2:16b</option>
|
||||||
|
</select>
|
||||||
|
<select id="role">
|
||||||
|
<option value="user">user</option>
|
||||||
|
<option value="system">system</option>
|
||||||
|
<option value="assistant">assistant</option>
|
||||||
|
</select>
|
||||||
|
<label>Response? <input type="checkbox" id="response" checked></label>
|
||||||
|
<button onclick="sendPrompt()">Send</button>
|
||||||
|
</div>
|
||||||
|
<script src="marked.min.js"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
96
script.js
Normal file
96
script.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
const messages = [];
|
||||||
|
const statusEl = document.getElementById("statusbox" );
|
||||||
|
const chatContainer = document.getElementById("chatbox" );
|
||||||
|
|
||||||
|
function setStatus(msg, stts) {
|
||||||
|
if (stts == "idle" ) statusEl.className = "status-idle";
|
||||||
|
if (stts == "responding" ) statusEl.className = "status-responding";
|
||||||
|
if (stts == "completed" ) statusEl.className = "status-completed";
|
||||||
|
if (stts == "failed" ) statusEl.className = "status-failed";
|
||||||
|
statusEl.textContent = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendPrompt() {
|
||||||
|
const role = document.getElementById("role" ).value;
|
||||||
|
const model = document.getElementById("model" ).value;
|
||||||
|
const response = document.getElementById("response" ).checked;
|
||||||
|
const promptBox = document.getElementById("prompt" );
|
||||||
|
const prompt = promptBox.value.trim();
|
||||||
|
|
||||||
|
if (!prompt) return;
|
||||||
|
|
||||||
|
const bubUser = document.createElement("div" );
|
||||||
|
const tagRole = document.createElement("span" );
|
||||||
|
const msgUser = document.createElement("div" );
|
||||||
|
|
||||||
|
const bubBot = document.createElement("div" );
|
||||||
|
const tagModel = document.createElement("span" );
|
||||||
|
const msgBot = document.createElement("div" );
|
||||||
|
|
||||||
|
bubUser .className = "bub bub-r bub-primary";
|
||||||
|
tagRole .className = "tag";
|
||||||
|
msgUser .className = "message";
|
||||||
|
|
||||||
|
bubBot .className = "bub bub-l bub-secondary";
|
||||||
|
tagModel .className = "tag";
|
||||||
|
msgBot .className = "message";
|
||||||
|
|
||||||
|
tagRole .textContent = `Role: ${role}`;
|
||||||
|
tagModel .textContent = `Model: ${model}`;
|
||||||
|
|
||||||
|
msgUser.innerHTML = marked.parse(prompt, { sanitize: true });
|
||||||
|
|
||||||
|
bubUser.appendChild(tagRole);
|
||||||
|
bubUser.appendChild(msgUser);
|
||||||
|
chatContainer.appendChild(bubUser);
|
||||||
|
|
||||||
|
bubBot.appendChild(tagModel);
|
||||||
|
bubBot.appendChild(msgBot);
|
||||||
|
if (response) chatContainer.appendChild(bubBot);
|
||||||
|
|
||||||
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||||
|
messages.push({ role: role, content: prompt }); // Add prompt to history
|
||||||
|
if (response) setStatus(`⏳ ${model} Loading…`, "responding");
|
||||||
|
|
||||||
|
if (response) try {
|
||||||
|
const res = await fetch("http://localhost:11434/api/chat", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ model, messages, stream: true })
|
||||||
|
});
|
||||||
|
|
||||||
|
const reader = res.body.getReader();
|
||||||
|
const decoder = new TextDecoder("utf-8");
|
||||||
|
let botReply = "";
|
||||||
|
|
||||||
|
setStatus(`✏️ ${model} Typing…`, "responding");
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
const chunk = decoder.decode(value, { stream: true }).trim();
|
||||||
|
for (const line of chunk.split("\n")) {
|
||||||
|
if (!line) continue;
|
||||||
|
try {
|
||||||
|
const j = JSON.parse(line);
|
||||||
|
if (j.message?.content) {
|
||||||
|
botReply += j.message.content;
|
||||||
|
msgBot.innerHTML = marked.parse(botReply, { sanitize: true });
|
||||||
|
msgBot.prepend(tagModel);
|
||||||
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.push({ role: "assistant", content: botReply });
|
||||||
|
setStatus(`✅ Response completed by ${model}`, "completed");
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
msgBot.innerHTML = "<em>[Failed to load answer]</em>";
|
||||||
|
msgBot.prepend(tagModel);
|
||||||
|
setStatus(`❌ Failed to get a response from ${model}`, "failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
promptBox.value = "";
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user