From 592262f95cbb5cfc55aa49d328d616b4d3ac4f69 Mon Sep 17 00:00:00 2001 From: Laura Date: Tue, 30 Sep 2025 22:25:01 +0200 Subject: [PATCH] show pricing and better title format --- models.go | 25 ++++++++++++++++++++----- static/js/chat.js | 12 +++++++++++- static/js/dropdown.js | 2 ++ static/js/markdown.js | 21 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/models.go b/models.go index 7082ab3..1980938 100644 --- a/models.go +++ b/models.go @@ -3,6 +3,7 @@ package main import ( "context" "sort" + "strconv" "strings" "sync" "time" @@ -10,12 +11,18 @@ import ( "github.com/revrost/go-openrouter" ) +type ModelPricing struct { + Input float64 `json:"input"` + Output float64 `json:"output"` +} + type Model struct { - ID string `json:"id"` - Created int64 `json:"created"` - Name string `json:"name"` - Description string `json:"description"` - Tags []string `json:"tags,omitempty"` + ID string `json:"id"` + Created int64 `json:"created"` + Name string `json:"name"` + Description string `json:"description"` + Pricing ModelPricing `json:"pricing"` + Tags []string `json:"tags,omitempty"` Reasoning bool `json:"-"` Vision bool `json:"-"` @@ -90,11 +97,19 @@ func LoadModels(initial bool) error { name = name[index+2:] } + input, _ := strconv.ParseFloat(model.Pricing.Prompt, 64) + output, _ := strconv.ParseFloat(model.Pricing.Completion, 64) + m := &Model{ ID: model.ID, Created: model.Created, Name: name, Description: model.Description, + + Pricing: ModelPricing{ + Input: input * 1000000, + Output: output * 1000000, + }, } GetModelTags(model, m) diff --git a/static/js/chat.js b/static/js/chat.js index 67da5ad..119a585 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -1450,8 +1450,18 @@ // render models fillSelect($model, data.models, (el, model) => { + const separator = "─".repeat(24); + + el.title = [ + model.name, + separator, + `Created:\t\t${formatTimestamp(model.created)}`, + `Pricing/1M:\t$${fixed(model.pricing.input, 2)} In | $${fixed(model.pricing.output, 2)} Out`, + separator, + stripMarkdown(model.description), + ].join("\n"); + el.value = model.id; - el.title = `${model.name} (${formatTimestamp(model.created)})\n---\n${model.description}`; el.textContent = model.name; el.dataset.tags = (model.tags || []).join(","); diff --git a/static/js/dropdown.js b/static/js/dropdown.js index a30b60a..aad5e5c 100644 --- a/static/js/dropdown.js +++ b/static/js/dropdown.js @@ -168,6 +168,7 @@ #render() { if (this.#selected === false) { + this.#_selected.title = ""; this.#_selected.innerHTML = ""; return; @@ -175,6 +176,7 @@ const selection = this.#options[this.#selected]; + this.#_selected.title = selection.title; this.#_selected.innerHTML = selection.el.innerHTML; } diff --git a/static/js/markdown.js b/static/js/markdown.js index f3d885d..b77da43 100644 --- a/static/js/markdown.js +++ b/static/js/markdown.js @@ -162,4 +162,25 @@ window.renderInline = markdown => { return marked.parseInline(markdown.trim()); }; + + window.stripMarkdown = markdown => { + return ( + markdown + // Remove headings + .replace(/^#+\s*/gm, "") + // Remove links, keeping the link text + .replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1") + // Remove bold/italics, keeping the text + .replace(/(\*\*|__|\*|_|~~|`)(.*?)\1/g, "$2") + // Remove images + .replace(/!\[[^\]]*\]\([^)]*\)/g, "") + // Remove horizontal rules + .replace(/^-{3,}\s*$/gm, "") + // Remove list markers + .replace(/^\s*([*-]|\d+\.)\s+/gm, "") + // Collapse multiple newlines into one + .replace(/\n{2,}/g, "\n") + .trim() + ); + }; })();