Fix: recover bot; llama.cpp fix

This commit is contained in:
Grail Finder
2025-06-15 11:44:39 +03:00
parent 30e322d9c6
commit d4daa02155
12 changed files with 113 additions and 38 deletions

View File

@ -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}}

View File

@ -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}}

View File

@ -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" .}}

View File

@ -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
}

View File

@ -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 {

View File

@ -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")
}

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -8,6 +8,7 @@ var (
// cache
CacheRoomPrefix = "room#"
CacheStatePrefix = "state-"
CacheBotPredix = "botkey_"
// sse
NotifyRoomListUpdate = "roomlistupdate"
NotifyRoomUpdatePrefix = "roomupdate_"

View File

@ -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 ""
}

View File

@ -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;