mirror of
https://github.com/coalaura/whiskr.git
synced 2025-09-09 09:19:54 +00:00
tags and caching
This commit is contained in:
17
main.go
17
main.go
@@ -2,6 +2,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/coalaura/logger"
|
"github.com/coalaura/logger"
|
||||||
adapter "github.com/coalaura/logger/http"
|
adapter "github.com/coalaura/logger/http"
|
||||||
@@ -23,7 +25,7 @@ func main() {
|
|||||||
r.Use(adapter.Middleware(log))
|
r.Use(adapter.Middleware(log))
|
||||||
|
|
||||||
fs := http.FileServer(http.Dir("./static"))
|
fs := http.FileServer(http.Dir("./static"))
|
||||||
r.Handle("/*", http.StripPrefix("/", fs))
|
r.Handle("/*", cache(http.StripPrefix("/", fs)))
|
||||||
|
|
||||||
r.Get("/-/models", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/-/models", func(w http.ResponseWriter, r *http.Request) {
|
||||||
RespondJson(w, http.StatusOK, models)
|
RespondJson(w, http.StatusOK, models)
|
||||||
@@ -34,3 +36,16 @@ func main() {
|
|||||||
log.Debug("Listening at http://localhost:3443/")
|
log.Debug("Listening at http://localhost:3443/")
|
||||||
http.ListenAndServe(":3443", r)
|
http.ListenAndServe(":3443", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cache(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
path := strings.ToLower(r.URL.Path)
|
||||||
|
ext := filepath.Ext(path)
|
||||||
|
|
||||||
|
if ext == ".svg" || ext == ".ttf" || strings.HasSuffix(path, ".min.js") {
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=3024000, immutable")
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
20
models.go
20
models.go
@@ -4,13 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/revrost/go-openrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
SupportedParameters []string `json:"supported_parameters,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var ModelMap = make(map[string]*Model)
|
var ModelMap = make(map[string]*Model)
|
||||||
@@ -40,7 +42,7 @@ func LoadModels() ([]*Model, error) {
|
|||||||
ID: model.ID,
|
ID: model.ID,
|
||||||
Name: name,
|
Name: name,
|
||||||
Description: model.Description,
|
Description: model.Description,
|
||||||
SupportedParameters: model.SupportedParameters,
|
Tags: GetModelTags(model),
|
||||||
}
|
}
|
||||||
|
|
||||||
models[index] = m
|
models[index] = m
|
||||||
@@ -50,3 +52,17 @@ func LoadModels() ([]*Model, error) {
|
|||||||
|
|
||||||
return models, nil
|
return models, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetModelTags(model openrouter.Model) []string {
|
||||||
|
var tags []string
|
||||||
|
|
||||||
|
for _, parameter := range model.SupportedParameters {
|
||||||
|
if parameter == "reasoning" || parameter == "tools" {
|
||||||
|
tags = append(tags, parameter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(tags)
|
||||||
|
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
@@ -34,13 +34,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: #181926;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background-color: #cad3f5;
|
background-color: #cad3f5;
|
||||||
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #181926;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #cad3f5 #181926;
|
scrollbar-color: #cad3f5 transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
@@ -48,6 +48,14 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown .selected,
|
||||||
|
.dropdown .opt {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown .opt {
|
.dropdown .opt {
|
||||||
padding: 4px 6px;
|
padding: 4px 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -66,6 +74,34 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown .label {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown .tags .tag {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
background-position: center;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags .tag.reasoning {
|
||||||
|
background-image: url(icons/tags/reasoning.svg)
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags .tag.tools {
|
||||||
|
background-image: url(icons/tags/tools.svg)
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown .search {
|
.dropdown .search {
|
||||||
background: #2a2e41;
|
background: #2a2e41;
|
||||||
border-top: 2px solid #494d64;
|
border-top: 2px solid #494d64;
|
||||||
|
7
static/css/icons/tags/reasoning.svg
Normal file
7
static/css/icons/tags/reasoning.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
After Width: | Height: | Size: 1.2 KiB |
7
static/css/icons/tags/tools.svg
Normal file
7
static/css/icons/tags/tools.svg
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||||
|
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
After Width: | Height: | Size: 583 B |
@@ -448,8 +448,11 @@
|
|||||||
const el = document.createElement("option");
|
const el = document.createElement("option");
|
||||||
|
|
||||||
el.value = model.id;
|
el.value = model.id;
|
||||||
|
el.title = model.description;
|
||||||
el.textContent = model.name;
|
el.textContent = model.name;
|
||||||
|
|
||||||
|
el.dataset.tags = (model.tags || []).join(",");
|
||||||
|
|
||||||
$model.appendChild(el);
|
$model.appendChild(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,9 +15,15 @@
|
|||||||
this.#search = "searchable" in el.dataset;
|
this.#search = "searchable" in el.dataset;
|
||||||
|
|
||||||
this.#_select.querySelectorAll("option").forEach((option) => {
|
this.#_select.querySelectorAll("option").forEach((option) => {
|
||||||
|
const tags = option.dataset.tags?.trim();
|
||||||
|
|
||||||
this.#options.push({
|
this.#options.push({
|
||||||
value: option.value,
|
value: option.value,
|
||||||
label: option.textContent,
|
label: option.textContent,
|
||||||
|
|
||||||
|
title: option.title || false,
|
||||||
|
tags: tags ? tags.split(",") : [],
|
||||||
|
|
||||||
search: searchable(option.textContent),
|
search: searchable(option.textContent),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -75,9 +81,10 @@
|
|||||||
|
|
||||||
// options
|
// options
|
||||||
for (const option of this.#options) {
|
for (const option of this.#options) {
|
||||||
|
// option wrapper
|
||||||
const _opt = make("div", "opt");
|
const _opt = make("div", "opt");
|
||||||
|
|
||||||
_opt.textContent = option.label;
|
_opt.title = option.title || "";
|
||||||
|
|
||||||
_opt.addEventListener("click", () => {
|
_opt.addEventListener("click", () => {
|
||||||
this.#_select.value = option.value;
|
this.#_select.value = option.value;
|
||||||
@@ -85,6 +92,30 @@
|
|||||||
this.#_dropdown.classList.remove("open");
|
this.#_dropdown.classList.remove("open");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// option label
|
||||||
|
const _label = make("div", "label");
|
||||||
|
|
||||||
|
_label.title = option.label;
|
||||||
|
_label.textContent = option.label;
|
||||||
|
|
||||||
|
_opt.appendChild(_label);
|
||||||
|
|
||||||
|
// option tags (optional)
|
||||||
|
if (option.tags?.length) {
|
||||||
|
const _tags = make("div", "tags");
|
||||||
|
|
||||||
|
for (const tag of option.tags) {
|
||||||
|
const _tag = make("div", "tag", tag);
|
||||||
|
|
||||||
|
_tag.title = tag;
|
||||||
|
|
||||||
|
_tags.appendChild(_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
_opt.appendChild(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to options
|
||||||
_options.appendChild(_opt);
|
_options.appendChild(_opt);
|
||||||
|
|
||||||
option.el = _opt;
|
option.el = _opt;
|
||||||
@@ -128,14 +159,14 @@
|
|||||||
|
|
||||||
#render() {
|
#render() {
|
||||||
if (this.#selected === false) {
|
if (this.#selected === false) {
|
||||||
this.#_selected.textContent = "";
|
this.#_selected.innerHTML = "";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = this.#options[this.#selected];
|
const selection = this.#options[this.#selected];
|
||||||
|
|
||||||
this.#_selected.textContent = selection.label;
|
this.#_selected.innerHTML = selection.el.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filter() {
|
#filter() {
|
||||||
|
@@ -45,7 +45,9 @@ function uid() {
|
|||||||
function make(tag, ...classes) {
|
function make(tag, ...classes) {
|
||||||
const el = document.createElement(tag);
|
const el = document.createElement(tag);
|
||||||
|
|
||||||
|
if (classes.length) {
|
||||||
el.classList.add(...classes);
|
el.classList.add(...classes);
|
||||||
|
}
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user