mirror of
https://github.com/coalaura/whiskr.git
synced 2025-12-02 20:22:52 +00:00
auto-update model list and show creation date
This commit is contained in:
4
chat.go
4
chat.go
@@ -101,8 +101,8 @@ func (r *Request) Parse() (*openrouter.ChatCompletionRequest, int, error) {
|
|||||||
toolIndex int
|
toolIndex int
|
||||||
)
|
)
|
||||||
|
|
||||||
model, ok := ModelMap[r.Model]
|
model := GetModel(r.Model)
|
||||||
if !ok {
|
if model == nil {
|
||||||
return nil, 0, fmt.Errorf("unknown model: %q", r.Model)
|
return nil, 0, fmt.Errorf("unknown model: %q", r.Model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -19,7 +19,7 @@ func main() {
|
|||||||
icons, err := LoadIcons()
|
icons, err := LoadIcons()
|
||||||
log.MustFail(err)
|
log.MustFail(err)
|
||||||
|
|
||||||
models, err := LoadModels()
|
err = StartModelUpdateLoop()
|
||||||
log.MustFail(err)
|
log.MustFail(err)
|
||||||
|
|
||||||
tokenizer, err := LoadTokenizer(TikTokenSource)
|
tokenizer, err := LoadTokenizer(TikTokenSource)
|
||||||
@@ -35,6 +35,9 @@ func main() {
|
|||||||
r.Handle("/*", cache(http.StripPrefix("/", fs)))
|
r.Handle("/*", cache(http.StripPrefix("/", fs)))
|
||||||
|
|
||||||
r.Get("/-/data", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/-/data", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
modelMx.RLock()
|
||||||
|
defer modelMx.RUnlock()
|
||||||
|
|
||||||
RespondJson(w, http.StatusOK, map[string]any{
|
RespondJson(w, http.StatusOK, map[string]any{
|
||||||
"authenticated": IsAuthenticated(r),
|
"authenticated": IsAuthenticated(r),
|
||||||
"config": map[string]any{
|
"config": map[string]any{
|
||||||
@@ -43,7 +46,7 @@ func main() {
|
|||||||
"motion": env.UI.ReducedMotion,
|
"motion": env.UI.ReducedMotion,
|
||||||
},
|
},
|
||||||
"icons": icons,
|
"icons": icons,
|
||||||
"models": models,
|
"models": ModelList,
|
||||||
"prompts": Prompts,
|
"prompts": Prompts,
|
||||||
"version": Version,
|
"version": Version,
|
||||||
})
|
})
|
||||||
|
|||||||
71
models.go
71
models.go
@@ -4,12 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/revrost/go-openrouter"
|
"github.com/revrost/go-openrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Model struct {
|
type Model struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Tags []string `json:"tags,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
@@ -21,23 +24,64 @@ type Model struct {
|
|||||||
Images bool `json:"-"`
|
Images bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var ModelMap = make(map[string]*Model)
|
var (
|
||||||
|
modelMx sync.RWMutex
|
||||||
|
|
||||||
func LoadModels() ([]*Model, error) {
|
ModelMap map[string]*Model
|
||||||
log.Println("Loading models...")
|
ModelList []*Model
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetModel(name string) *Model {
|
||||||
|
modelMx.RLock()
|
||||||
|
defer modelMx.RUnlock()
|
||||||
|
|
||||||
|
return ModelMap[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartModelUpdateLoop() error {
|
||||||
|
err := LoadModels(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(2 * time.Hour)
|
||||||
|
|
||||||
|
for range ticker.C {
|
||||||
|
err := LoadModels(false)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadModels(initial bool) error {
|
||||||
|
log.Println("Refreshing model list...")
|
||||||
|
|
||||||
client := OpenRouterClient()
|
client := OpenRouterClient()
|
||||||
|
|
||||||
list, err := client.ListUserModels(context.Background())
|
list, err := client.ListUserModels(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !initial && len(list) == len(ModelList) {
|
||||||
|
log.Println("No new models, skipping update")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(list, func(i, j int) bool {
|
sort.Slice(list, func(i, j int) bool {
|
||||||
return list[i].Created > list[j].Created
|
return list[i].Created > list[j].Created
|
||||||
})
|
})
|
||||||
|
|
||||||
models := make([]*Model, len(list))
|
var (
|
||||||
|
newList = make([]*Model, len(list))
|
||||||
|
newMap = make(map[string]*Model, len(list))
|
||||||
|
)
|
||||||
|
|
||||||
for index, model := range list {
|
for index, model := range list {
|
||||||
name := model.Name
|
name := model.Name
|
||||||
@@ -48,20 +92,27 @@ func LoadModels() ([]*Model, error) {
|
|||||||
|
|
||||||
m := &Model{
|
m := &Model{
|
||||||
ID: model.ID,
|
ID: model.ID,
|
||||||
|
Created: model.Created,
|
||||||
Name: name,
|
Name: name,
|
||||||
Description: model.Description,
|
Description: model.Description,
|
||||||
}
|
}
|
||||||
|
|
||||||
GetModelTags(model, m)
|
GetModelTags(model, m)
|
||||||
|
|
||||||
models[index] = m
|
newList[index] = m
|
||||||
|
newMap[model.ID] = m
|
||||||
ModelMap[model.ID] = m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Loaded %d models\n", len(models))
|
log.Printf("Loaded %d models\n", len(newList))
|
||||||
|
|
||||||
return models, nil
|
modelMx.Lock()
|
||||||
|
|
||||||
|
ModelList = newList
|
||||||
|
ModelMap = newMap
|
||||||
|
|
||||||
|
modelMx.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetModelTags(model openrouter.Model, m *Model) {
|
func GetModelTags(model openrouter.Model, m *Model) {
|
||||||
|
|||||||
@@ -1451,7 +1451,7 @@
|
|||||||
// render models
|
// render models
|
||||||
fillSelect($model, data.models, (el, model) => {
|
fillSelect($model, data.models, (el, model) => {
|
||||||
el.value = model.id;
|
el.value = model.id;
|
||||||
el.title = model.description;
|
el.title = `${model.name} (${formatTimestamp(model.created)})\n---\n${model.description}`;
|
||||||
el.textContent = model.name;
|
el.textContent = model.name;
|
||||||
|
|
||||||
el.dataset.tags = (model.tags || []).join(",");
|
el.dataset.tags = (model.tags || []).join(",");
|
||||||
|
|||||||
@@ -97,7 +97,6 @@
|
|||||||
// option label
|
// option label
|
||||||
const _label = make("div", "label");
|
const _label = make("div", "label");
|
||||||
|
|
||||||
_label.title = option.label;
|
|
||||||
_label.textContent = option.label;
|
_label.textContent = option.label;
|
||||||
|
|
||||||
_opt.appendChild(_label);
|
_opt.appendChild(_label);
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ function formatMilliseconds(ms) {
|
|||||||
return `${Math.round(ms / 1000)}s`;
|
return `${Math.round(ms / 1000)}s`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatTimestamp(ts) {
|
||||||
|
return new Date(ts * 1000).toLocaleDateString();
|
||||||
|
}
|
||||||
|
|
||||||
function dataBlob(dataUrl) {
|
function dataBlob(dataUrl) {
|
||||||
const [header, data] = dataUrl.split(","),
|
const [header, data] = dataUrl.split(","),
|
||||||
mime = header.match(/data:(.*?)(;|$)/)[1];
|
mime = header.match(/data:(.*?)(;|$)/)[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user