From d4daa021553bc64c0d34edf88ead6e681c870121 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Sun, 15 Jun 2025 11:44:39 +0300 Subject: [PATCH] Fix: recover bot; llama.cpp fix --- components/actionhistory.html | 9 +++++- components/cardcounter.html | 7 +++-- components/room.html | 1 - handlers/actions.go | 42 ++++++++++++++++++++++++-- handlers/auth.go | 2 +- handlers/handlers.go | 7 +++-- llmapi/main.go | 55 +++++++++++++++++++++++------------ llmapi/parser.go | 13 +++++---- main.go | 2 +- models/keys.go | 1 + models/main.go | 4 ++- todos.md | 8 +++-- 12 files changed, 113 insertions(+), 38 deletions(-) diff --git a/components/actionhistory.html b/components/actionhistory.html index ef3bf73..9535130 100644 --- a/components/actionhistory.html +++ b/components/actionhistory.html @@ -1,5 +1,5 @@ {{define "actionhistory"}} -
+
Backlog: {{range .}}
@@ -14,4 +14,11 @@
{{end}}
+ {{end}} diff --git a/components/cardcounter.html b/components/cardcounter.html index 0eb3b43..15c1579 100644 --- a/components/cardcounter.html +++ b/components/cardcounter.html @@ -1,6 +1,9 @@ {{define "cardcounter"}}
-

Blue cards left: {{.BlueCounter}}

-

Red cards left: {{.RedCounter}}

+

Blue cards left: {{.BlueCounter}}

+

Red cards left: {{.RedCounter}}

+
+

Limit of cards to open: {{.ThisTurnLimit}}

+

Opened this turn: {{.OpenedThisTurn}}

{{end}} diff --git a/components/room.html b/components/room.html index df752a9..c7fc2b9 100644 --- a/components/room.html +++ b/components/room.html @@ -32,7 +32,6 @@ {{if .Room.IsRunning}} {{template "cardcounter" .Room}} {{end}} -
{{if and (eq .State.Username .Room.CreatorName) (not .Room.IsRunning)}} {{template "addbot" .}} diff --git a/handlers/actions.go b/handlers/actions.go index 9be7068..1ccace2 100644 --- a/handlers/actions.go +++ b/handlers/actions.go @@ -83,6 +83,7 @@ func saveFullInfo(fi *models.FullInfo) error { func notifyBotIfNeeded(fi *models.FullInfo) { if botName := fi.Room.WhichBotToMove(); botName != "" { + log.Debug("got botname", "name", botName) llmapi.SignalChanMap[botName] <- true } log.Debug("no bot", "room_id", fi.Room.ID) @@ -231,7 +232,7 @@ func joinTeam(ctx context.Context, role, team string) (*models.FullInfo, error) } // get all rooms -func listPublicRooms() []*models.Room { +func listRooms(allRooms bool) []*models.Room { cacheMap := memcache.GetAll() publicRooms := []*models.Room{} // no way to know if room is public until unmarshal -_-; @@ -243,7 +244,7 @@ func listPublicRooms() []*models.Room { continue } log.Debug("consider room for list", "room", room, "key", key) - if room.IsPublic { + if room.IsPublic || allRooms { publicRooms = append(publicRooms, room) } } @@ -251,6 +252,24 @@ func listPublicRooms() []*models.Room { return publicRooms } +// get bots +func listBots() map[string]map[string]string { + cacheMap := memcache.GetAll() + resp := make(map[string]map[string]string) + // no way to know if room is public until unmarshal -_-; + for key, value := range cacheMap { + if strings.HasPrefix(key, models.CacheBotPredix) { + botMap := make(map[string]string) + if err := json.Unmarshal(value, &botMap); err != nil { + log.Warn("failed to unmarshal bot", "error", err) + continue + } + resp[botMap["bot_name"]] = botMap + } + } + return resp +} + func notify(event, msg string) { Notifier.Notifier <- broker.NotificationEvent{ EventName: event, @@ -271,3 +290,22 @@ func loadCards(room *models.Room) { room.WCMap[card.Word] = card.Color } } + +func recoverBots() { + bots := listBots() + for botName, botMap := range bots { + if err := recoverBot(botMap); err != nil { + log.Warn("failed to recover bot", "botName", botName, "error", err) + } + } +} + +func recoverBot(bm map[string]string) error { + // TODO: check if room still exists + log.Debug("recovering bot", "bot", bm) + _, err := llmapi.NewBot(bm["role"], bm["team"], bm["bot_name"], bm["room_id"], cfg) + if err != nil { + return err + } + return nil +} diff --git a/handlers/auth.go b/handlers/auth.go index 81b73d0..9ebd8b9 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -107,7 +107,7 @@ func HandleFrontLogin(w http.ResponseWriter, r *http.Request) { } } else { log.Debug("no room_id in login") - fi.List = listPublicRooms() + fi.List = listRooms(false) // save state to cache if err := saveState(cleanName, userstate); err != nil { // if err := saveFullInfo(fi); err != nil { diff --git a/handlers/handlers.go b/handlers/handlers.go index 119d60c..749ce60 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -28,6 +28,9 @@ func init() { cfg = config.LoadConfigOrDefault("") Notifier = broker.Notifier cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval + // bot loader + // check the rooms if it has bot_{digits} in them, create bots if have + recoverBots() } func HandlePing(w http.ResponseWriter, r *http.Request) { @@ -51,7 +54,7 @@ func HandleHome(w http.ResponseWriter, r *http.Request) { } } if fi != nil && fi.Room == nil { - fi.List = listPublicRooms() + fi.List = listRooms(false) } if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { log.Error("failed to exec templ;", "error", err, "templ", "base") @@ -92,7 +95,7 @@ func HandleExit(w http.ResponseWriter, r *http.Request) { abortWithError(w, err.Error()) return } - fi.List = listPublicRooms() + fi.List = listRooms(false) if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil { log.Error("failed to exec templ;", "error", err, "templ", "base") } diff --git a/llmapi/main.go b/llmapi/main.go index 171abc5..08fb39f 100644 --- a/llmapi/main.go +++ b/llmapi/main.go @@ -37,18 +37,35 @@ type DSResp struct { Object string `json:"object"` } +// type LLMResp struct { +// Choices []struct { +// FinishReason string `json:"finish_reason"` +// Index int `json:"index"` +// Message struct { +// Content string `json:"content"` +// Role string `json:"role"` +// } `json:"message"` +// } `json:"choices"` +// Created int `json:"created"` +// Model string `json:"model"` +// Object string `json:"object"` +// } + type LLMResp struct { - Choices []struct { - FinishReason string `json:"finish_reason"` - Index int `json:"index"` - Message struct { - Content string `json:"content"` - Role string `json:"role"` - } `json:"message"` - } `json:"choices"` - Created int `json:"created"` - Model string `json:"model"` - Object string `json:"object"` + Index int `json:"index"` + Content string `json:"content"` + Tokens []any `json:"tokens"` + IDSlot int `json:"id_slot"` + Stop bool `json:"stop"` + Model string `json:"model"` + TokensPredicted int `json:"tokens_predicted"` + TokensEvaluated int `json:"tokens_evaluated"` + Prompt string `json:"prompt"` + HasNewLine bool `json:"has_new_line"` + Truncated bool `json:"truncated"` + StopType string `json:"stop_type"` + StoppingWord string `json:"stopping_word"` + TokensCached int `json:"tokens_cached"` } type MimeResp struct { @@ -63,13 +80,13 @@ type GusserResp struct { } type Bot struct { - Role string // gueeser | mime - Team string - cfg *config.Config - RoomID string // can we get a room from here? - BotName string - log *slog.Logger - LLMParser RespParser + Role string `json:"role"` + Team string `json:"team"` + cfg *config.Config `json:"-"` + RoomID string `json:"room_id"` // can we get a room from here? + BotName string `json:"bot_name"` + log *slog.Logger `json:"-"` + LLMParser RespParser `json:"-"` // channels for communicaton // channels are not serializable // SignalsCh chan bool @@ -271,7 +288,7 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { } func saveBot(bot *Bot) error { - key := "botkey_" + bot.RoomID + bot.BotName + key := models.CacheBotPredix + bot.RoomID + bot.BotName data, err := json.Marshal(bot) if err != nil { return err diff --git a/llmapi/parser.go b/llmapi/parser.go index 24a8253..5ee9003 100644 --- a/llmapi/parser.go +++ b/llmapi/parser.go @@ -66,12 +66,13 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) { p.log.Error("failed to unmarshal", "error", err) return nil, err } - if len(resp.Choices) == 0 { - p.log.Error("empty choices", "resp", resp) - err := errors.New("empty choices in resp") - return nil, err - } - text := resp.Choices[0].Message.Content + // if len(resp.Choices) == 0 { + // p.log.Error("empty choices", "resp", resp) + // err := errors.New("empty choices in resp") + // return nil, err + // } + // text := resp.Choices[0].Message.Content + text := resp.Content li := strings.Index(text, "{") ri := strings.LastIndex(text, "}") if li < 0 || ri < 1 { diff --git a/main.go b/main.go index b21ae34..97be504 100644 --- a/main.go +++ b/main.go @@ -69,7 +69,7 @@ func main() { <-stop slog.Info("Shutting down server...") - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() if err := server.Shutdown(ctx); err != nil { slog.Error("server shutdown failed", "error", err) diff --git a/models/keys.go b/models/keys.go index 967105a..ee4fc76 100644 --- a/models/keys.go +++ b/models/keys.go @@ -8,6 +8,7 @@ var ( // cache CacheRoomPrefix = "room#" CacheStatePrefix = "state-" + CacheBotPredix = "botkey_" // sse NotifyRoomListUpdate = "roomlistupdate" NotifyRoomUpdatePrefix = "roomupdate_" diff --git a/models/main.go b/models/main.go index c74583c..019f1ba 100644 --- a/models/main.go +++ b/models/main.go @@ -116,7 +116,9 @@ func getGuesser(m map[string]BotPlayer, team UserTeam) string { // WhichBotToMove returns bot name that have to move or empty string func (r *Room) WhichBotToMove() string { - fmt.Println("looking for bot to move", "team-turn", r.TeamTurn, "mime-done", r.MimeDone, "bot-map", r.BotMap, "is_running", r.IsRunning) + fmt.Println("looking for bot to move", "team-turn:", r.TeamTurn, + "mime-done:", r.MimeDone, "bot-map:", r.BotMap, "is_running:", r.IsRunning, + "blueMime:", r.BlueTeam.Mime, "redMime:", r.RedTeam.Mime) if !r.IsRunning { return "" } diff --git a/todos.md b/todos.md index 3d6586c..94ece65 100644 --- a/todos.md +++ b/todos.md @@ -13,6 +13,9 @@ - needs resend to llm btn; + - better styles and fluff; - common auth system between sites; +- autoscroll down backlog on update; +- gameover to backlog; +- ended turn action to backlog; #### sse points - clue sse update; @@ -31,7 +34,8 @@ - 0 should mean without limit; - sse hangs / fails connection which causes to wait for cards to open a few seconds (on local machine); - after starting a new game (after old one) blue mime has no clue input; -- gameover to backlog; - remove verbs from word file; - invite link gets cutoff; -- mime rejoined the room: does not see colors; state save in store.json has empty role and team +- mime rejoined the room: does not see colors; state save in store.json has empty role and team + +- restart bot routines after server restart; + +- guesser did not have same number of guesses (move ended after 1 guess); show how much guesses left on the page;