Feat: openrouter fetch/update free model list
This commit is contained in:
@@ -95,3 +95,52 @@ func (b *Bot) ToPlayer() *models.Player {
|
|||||||
IsBot: true,
|
IsBot: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ORModel struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
CanonicalSlug string `json:"canonical_slug"`
|
||||||
|
HuggingFaceID string `json:"hugging_face_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Created int `json:"created"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
ContextLength int `json:"context_length"`
|
||||||
|
Architecture struct {
|
||||||
|
Modality string `json:"modality"`
|
||||||
|
InputModalities []string `json:"input_modalities"`
|
||||||
|
OutputModalities []string `json:"output_modalities"`
|
||||||
|
Tokenizer string `json:"tokenizer"`
|
||||||
|
InstructType any `json:"instruct_type"`
|
||||||
|
} `json:"architecture"`
|
||||||
|
Pricing struct {
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
Completion string `json:"completion"`
|
||||||
|
Request string `json:"request"`
|
||||||
|
Image string `json:"image"`
|
||||||
|
Audio string `json:"audio"`
|
||||||
|
WebSearch string `json:"web_search"`
|
||||||
|
InternalReasoning string `json:"internal_reasoning"`
|
||||||
|
} `json:"pricing,omitempty"`
|
||||||
|
TopProvider struct {
|
||||||
|
ContextLength int `json:"context_length"`
|
||||||
|
MaxCompletionTokens int `json:"max_completion_tokens"`
|
||||||
|
IsModerated bool `json:"is_moderated"`
|
||||||
|
} `json:"top_provider"`
|
||||||
|
PerRequestLimits any `json:"per_request_limits"`
|
||||||
|
SupportedParameters []string `json:"supported_parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://openrouter.ai/api/v1/models
|
||||||
|
type ORModels struct {
|
||||||
|
Data []ORModel `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (orm *ORModels) ListFree() []string {
|
||||||
|
resp := []string{}
|
||||||
|
for _, model := range orm.Data {
|
||||||
|
if model.Pricing.Prompt == "0" && model.Pricing.Request == "0" &&
|
||||||
|
model.Pricing.Completion == "0" {
|
||||||
|
resp = append(resp, model.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|||||||
64
llmapi/or.go
Normal file
64
llmapi/or.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package llmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ormodelsLink = "https://openrouter.ai/api/v1/models"
|
||||||
|
ORFreeModels = []string{
|
||||||
|
"google/gemini-2.0-flash-exp:free",
|
||||||
|
"deepseek/deepseek-chat-v3-0324:free",
|
||||||
|
"mistralai/mistral-small-3.2-24b-instruct:free",
|
||||||
|
"qwen/qwen3-14b:free",
|
||||||
|
"google/gemma-3-27b-it:free",
|
||||||
|
"meta-llama/llama-3.3-70b-instruct:free",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListORModels() ([]string, error) {
|
||||||
|
resp, err := http.Get(ormodelsLink)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
err := fmt.Errorf("failed to fetch or models; status: %s", resp.Status)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := &ORModels{}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
freeModels := data.ListFree()
|
||||||
|
return freeModels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ORModelListUpdateTicker(ctx context.Context) {
|
||||||
|
ticker := time.NewTicker(time.Hour * 2)
|
||||||
|
freeModels, err := ListORModels()
|
||||||
|
slog.Info("updated free models list", "list", freeModels)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to update free models list", "list", freeModels)
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
freeModels, err := ListORModels()
|
||||||
|
slog.Info("updated free models list", "list", freeModels)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("failed to update free models list", "list", freeModels)
|
||||||
|
// log
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ORFreeModels = freeModels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
main.go
2
main.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"gralias/config"
|
"gralias/config"
|
||||||
"gralias/crons"
|
"gralias/crons"
|
||||||
"gralias/handlers"
|
"gralias/handlers"
|
||||||
|
"gralias/llmapi"
|
||||||
"gralias/repos"
|
"gralias/repos"
|
||||||
"gralias/telemetry"
|
"gralias/telemetry"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
@@ -22,6 +23,7 @@ var cfg *config.Config
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cfg = config.LoadConfigOrDefault("")
|
cfg = config.LoadConfigOrDefault("")
|
||||||
|
go llmapi.ORModelListUpdateTicker(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GzipFileServer serves pre-compressed .gz files if available
|
// GzipFileServer serves pre-compressed .gz files if available
|
||||||
|
|||||||
3
todos.md
3
todos.md
@@ -36,6 +36,9 @@
|
|||||||
- player stats: played games, lost, won, rating elo, opened opposite words, opened white words, opened black words. +
|
- player stats: played games, lost, won, rating elo, opened opposite words, opened white words, opened black words. +
|
||||||
- at the end of the game, all colors should be revealed;
|
- at the end of the game, all colors should be revealed;
|
||||||
- tracing;
|
- tracing;
|
||||||
|
====
|
||||||
|
- action history to bot (not json); basic stuff: what words were previously given as clue and guessed (maybe what team bot is on);
|
||||||
|
- automate getting list of free and non-thinking openrouter models;
|
||||||
|
|
||||||
#### sse points
|
#### sse points
|
||||||
- clue sse update;
|
- clue sse update;
|
||||||
|
|||||||
Reference in New Issue
Block a user