Fix: recover bot; llama.cpp fix
This commit is contained in:
		| @@ -1,5 +1,5 @@ | ||||
| {{define "actionhistory"}} | ||||
| <div class="overflow-y-auto max-h-96 border-2 border-gray-300 p-4 rounded-lg space-y-2"> | ||||
| <div id="actionHistoryContainer" class="overflow-y-auto max-h-96 border-2 border-gray-300 p-4 rounded-lg space-y-2"> | ||||
|   Backlog: | ||||
|   {{range .}} | ||||
|     <div class="flex items-center justify-between p-2 rounded"> | ||||
| @@ -14,4 +14,11 @@ | ||||
|     </div> | ||||
|   {{end}} | ||||
| </div> | ||||
| <script> | ||||
|   // Scroll to the bottom of the action history container | ||||
|   const container = document.getElementById('actionHistoryContainer'); | ||||
|   if (container) { | ||||
|     container.scrollTop = container.scrollHeight; | ||||
|   } | ||||
| </script> | ||||
| {{end}} | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| {{define "cardcounter"}} | ||||
| <div class="flex justify-center"> | ||||
|     <p>Blue cards left: {{.BlueCounter}}</p> | ||||
|     <p>Red cards left: {{.RedCounter}}</p> | ||||
|     <p>Blue cards left: {{.BlueCounter}} </p> | ||||
|     <p>Red cards left: {{.RedCounter}} </p> | ||||
|     <hr> | ||||
|     <p>Limit of cards to open: {{.ThisTurnLimit}} </p> | ||||
|     <p>Opened this turn: {{.OpenedThisTurn}} </p> | ||||
| </div> | ||||
| {{end}} | ||||
|   | ||||
| @@ -32,7 +32,6 @@ | ||||
|   {{if .Room.IsRunning}} | ||||
|     {{template "cardcounter" .Room}} | ||||
|   {{end}} | ||||
|  | ||||
|   <div id="addbot"> | ||||
|     {{if and (eq .State.Username .Room.CreatorName) (not .Room.IsRunning)}} | ||||
|       {{template "addbot" .}} | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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") | ||||
| 	} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
							
								
								
									
										2
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								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) | ||||
|   | ||||
| @@ -8,6 +8,7 @@ var ( | ||||
| 	// cache | ||||
| 	CacheRoomPrefix  = "room#" | ||||
| 	CacheStatePrefix = "state-" | ||||
| 	CacheBotPredix   = "botkey_" | ||||
| 	// sse | ||||
| 	NotifyRoomListUpdate   = "roomlistupdate" | ||||
| 	NotifyRoomUpdatePrefix = "roomupdate_" | ||||
|   | ||||
| @@ -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 "" | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										8
									
								
								todos.md
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder