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
	 Grail Finder
					Grail Finder