mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-08 17:06:42 +00:00
total cost tracker
This commit is contained in:
@@ -30,7 +30,6 @@ whiskr is a private, self-hosted web chat interface for interacting with AI mode
|
||||
|
||||
## TODO
|
||||
|
||||
- total cost tracker
|
||||
- settings
|
||||
- auto-retry on edit
|
||||
- ctrl+enter vs enter for sending
|
||||
|
@@ -73,29 +73,26 @@ body.resizing * {
|
||||
cursor: grabbing !important;
|
||||
}
|
||||
|
||||
#total,
|
||||
#version {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
top: 3px;
|
||||
right: 6px;
|
||||
right: 4px;
|
||||
color: #a5adcb;
|
||||
}
|
||||
|
||||
#total {
|
||||
right: unset;
|
||||
left: 4px;
|
||||
}
|
||||
|
||||
#version a {
|
||||
color: #a5adcb;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body.loading #version {
|
||||
font-size: 0;
|
||||
animation: rotating 1.2s linear infinite;
|
||||
background-image: url(icons/spinner.svg);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@@ -16,6 +16,7 @@
|
||||
<title>whiskr</title>
|
||||
</head>
|
||||
<body class="loading">
|
||||
<div id="total" title="Accumulated total cost, middle-click to reset"></div>
|
||||
<div id="version"></div>
|
||||
|
||||
<div id="loading">
|
||||
|
@@ -1,5 +1,6 @@
|
||||
(() => {
|
||||
const $version = document.getElementById("version"),
|
||||
$total = document.getElementById("total"),
|
||||
$messages = document.getElementById("messages"),
|
||||
$chat = document.getElementById("chat"),
|
||||
$message = document.getElementById("message"),
|
||||
@@ -41,7 +42,14 @@
|
||||
let searchAvailable = false,
|
||||
activeMessage = null,
|
||||
isResizing = false,
|
||||
scrollResize = false;
|
||||
scrollResize = false,
|
||||
totalCost = 0;
|
||||
|
||||
function updateTotalCost() {
|
||||
storeValue("total-cost", totalCost);
|
||||
|
||||
$total.textContent = formatMoney(totalCost);
|
||||
}
|
||||
|
||||
function updateScrollButton() {
|
||||
const bottom = $messages.scrollHeight - ($messages.scrollTop + $messages.offsetHeight);
|
||||
@@ -448,8 +456,7 @@
|
||||
if (this.#statistics) {
|
||||
const { provider, model, ttft, time, input, output, cost } = this.#statistics;
|
||||
|
||||
const tps = output / (time / 1000),
|
||||
price = cost < 1 ? `${fixed(cost * 100, 1)}ct` : `$${fixed(cost, 2)}`;
|
||||
const tps = output / (time / 1000);
|
||||
|
||||
html = [
|
||||
provider ? `<div class="provider">${provider} (${model.split("/").pop()})</div>` : "",
|
||||
@@ -462,7 +469,7 @@
|
||||
=
|
||||
<div class="total">${input + output}t</div>
|
||||
</div>`,
|
||||
`<div class="cost">${price}</div>`,
|
||||
`<div class="cost">${formatMoney(cost)}</div>`,
|
||||
].join("");
|
||||
}
|
||||
|
||||
@@ -587,6 +594,10 @@
|
||||
}
|
||||
|
||||
this.setStatistics(data);
|
||||
|
||||
totalCost += data.cost;
|
||||
|
||||
updateTotalCost();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
@@ -1030,6 +1041,11 @@
|
||||
// start icon preload
|
||||
preloadIcons(data.icons);
|
||||
|
||||
// render total cost
|
||||
totalCost = loadValue("total-cost", 0);
|
||||
|
||||
updateTotalCost();
|
||||
|
||||
// render version
|
||||
if (data.version === "dev") {
|
||||
$version.remove();
|
||||
@@ -1223,6 +1239,16 @@
|
||||
return message;
|
||||
}
|
||||
|
||||
$total.addEventListener("auxclick", (event) => {
|
||||
if (event.button !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
totalCost = 0;
|
||||
|
||||
updateTotalCost();
|
||||
});
|
||||
|
||||
$messages.addEventListener("scroll", () => {
|
||||
updateScrollButton();
|
||||
});
|
||||
@@ -1244,7 +1270,7 @@
|
||||
$resizeBar.addEventListener("mousedown", event => {
|
||||
const isAtBottom = $messages.scrollHeight - ($messages.scrollTop + $messages.offsetHeight) <= 10;
|
||||
|
||||
if (event.buttons === 4) {
|
||||
if (event.button === 1) {
|
||||
$chat.style.height = "";
|
||||
|
||||
storeValue("resized", false);
|
||||
@@ -1252,7 +1278,7 @@
|
||||
scroll(isAtBottom, true);
|
||||
|
||||
return;
|
||||
} else if (event.buttons !== 1) {
|
||||
} else if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -84,6 +84,26 @@ function fixed(num, decimals = 0) {
|
||||
return num.toFixed(decimals).replace(/\.?0+$/m, "");
|
||||
}
|
||||
|
||||
function formatMoney(num) {
|
||||
if (num === 0) {
|
||||
return "0ct";
|
||||
}
|
||||
|
||||
if (num < 1) {
|
||||
let decimals = 1;
|
||||
|
||||
if (num < 0.0001) {
|
||||
decimals = 3;
|
||||
} else if (num < 0.001) {
|
||||
decimals = 2;
|
||||
}
|
||||
|
||||
return `${fixed(num * 100, decimals)}ct`;
|
||||
}
|
||||
|
||||
return `$${fixed(num, 2)}`;
|
||||
}
|
||||
|
||||
function clamp(num, min, max) {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
}
|
||||
|
Reference in New Issue
Block a user