Fix: recover bot; llama.cpp fix
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
{{define "actionhistory"}}
|
{{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:
|
Backlog:
|
||||||
{{range .}}
|
{{range .}}
|
||||||
<div class="flex items-center justify-between p-2 rounded">
|
<div class="flex items-center justify-between p-2 rounded">
|
||||||
@ -14,4 +14,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
// Scroll to the bottom of the action history container
|
||||||
|
const container = document.getElementById('actionHistoryContainer');
|
||||||
|
if (container) {
|
||||||
|
container.scrollTop = container.scrollHeight;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{{define "cardcounter"}}
|
{{define "cardcounter"}}
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<p>Blue cards left: {{.BlueCounter}}</p>
|
<p>Blue cards left: {{.BlueCounter}} </p>
|
||||||
<p>Red cards left: {{.RedCounter}}</p>
|
<p>Red cards left: {{.RedCounter}} </p>
|
||||||
|
<hr>
|
||||||
|
<p>Limit of cards to open: {{.ThisTurnLimit}} </p>
|
||||||
|
<p>Opened this turn: {{.OpenedThisTurn}} </p>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
{{if .Room.IsRunning}}
|
{{if .Room.IsRunning}}
|
||||||
{{template "cardcounter" .Room}}
|
{{template "cardcounter" .Room}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div id="addbot">
|
<div id="addbot">
|
||||||
{{if and (eq .State.Username .Room.CreatorName) (not .Room.IsRunning)}}
|
{{if and (eq .State.Username .Room.CreatorName) (not .Room.IsRunning)}}
|
||||||
{{template "addbot" .}}
|
{{template "addbot" .}}
|
||||||
|
@ -83,6 +83,7 @@ func saveFullInfo(fi *models.FullInfo) error {
|
|||||||
|
|
||||||
func notifyBotIfNeeded(fi *models.FullInfo) {
|
func notifyBotIfNeeded(fi *models.FullInfo) {
|
||||||
if botName := fi.Room.WhichBotToMove(); botName != "" {
|
if botName := fi.Room.WhichBotToMove(); botName != "" {
|
||||||
|
log.Debug("got botname", "name", botName)
|
||||||
llmapi.SignalChanMap[botName] <- true
|
llmapi.SignalChanMap[botName] <- true
|
||||||
}
|
}
|
||||||
log.Debug("no bot", "room_id", fi.Room.ID)
|
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
|
// get all rooms
|
||||||
func listPublicRooms() []*models.Room {
|
func listRooms(allRooms bool) []*models.Room {
|
||||||
cacheMap := memcache.GetAll()
|
cacheMap := memcache.GetAll()
|
||||||
publicRooms := []*models.Room{}
|
publicRooms := []*models.Room{}
|
||||||
// no way to know if room is public until unmarshal -_-;
|
// no way to know if room is public until unmarshal -_-;
|
||||||
@ -243,7 +244,7 @@ func listPublicRooms() []*models.Room {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debug("consider room for list", "room", room, "key", key)
|
log.Debug("consider room for list", "room", room, "key", key)
|
||||||
if room.IsPublic {
|
if room.IsPublic || allRooms {
|
||||||
publicRooms = append(publicRooms, room)
|
publicRooms = append(publicRooms, room)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,6 +252,24 @@ func listPublicRooms() []*models.Room {
|
|||||||
return publicRooms
|
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) {
|
func notify(event, msg string) {
|
||||||
Notifier.Notifier <- broker.NotificationEvent{
|
Notifier.Notifier <- broker.NotificationEvent{
|
||||||
EventName: event,
|
EventName: event,
|
||||||
@ -271,3 +290,22 @@ func loadCards(room *models.Room) {
|
|||||||
room.WCMap[card.Word] = card.Color
|
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 {
|
} else {
|
||||||
log.Debug("no room_id in login")
|
log.Debug("no room_id in login")
|
||||||
fi.List = listPublicRooms()
|
fi.List = listRooms(false)
|
||||||
// save state to cache
|
// save state to cache
|
||||||
if err := saveState(cleanName, userstate); err != nil {
|
if err := saveState(cleanName, userstate); err != nil {
|
||||||
// if err := saveFullInfo(fi); err != nil {
|
// if err := saveFullInfo(fi); err != nil {
|
||||||
|
@ -28,6 +28,9 @@ func init() {
|
|||||||
cfg = config.LoadConfigOrDefault("")
|
cfg = config.LoadConfigOrDefault("")
|
||||||
Notifier = broker.Notifier
|
Notifier = broker.Notifier
|
||||||
cache.MemCache.StartBackupRoutine(15 * time.Second) // Reduced backup interval
|
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) {
|
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 {
|
if fi != nil && fi.Room == nil {
|
||||||
fi.List = listPublicRooms()
|
fi.List = listRooms(false)
|
||||||
}
|
}
|
||||||
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
||||||
log.Error("failed to exec templ;", "error", err, "templ", "base")
|
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())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fi.List = listPublicRooms()
|
fi.List = listRooms(false)
|
||||||
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
if err := tmpl.ExecuteTemplate(w, "base", fi); err != nil {
|
||||||
log.Error("failed to exec templ;", "error", err, "templ", "base")
|
log.Error("failed to exec templ;", "error", err, "templ", "base")
|
||||||
}
|
}
|
||||||
|
@ -37,18 +37,35 @@ type DSResp struct {
|
|||||||
Object string `json:"object"`
|
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 {
|
type LLMResp struct {
|
||||||
Choices []struct {
|
Index int `json:"index"`
|
||||||
FinishReason string `json:"finish_reason"`
|
Content string `json:"content"`
|
||||||
Index int `json:"index"`
|
Tokens []any `json:"tokens"`
|
||||||
Message struct {
|
IDSlot int `json:"id_slot"`
|
||||||
Content string `json:"content"`
|
Stop bool `json:"stop"`
|
||||||
Role string `json:"role"`
|
Model string `json:"model"`
|
||||||
} `json:"message"`
|
TokensPredicted int `json:"tokens_predicted"`
|
||||||
} `json:"choices"`
|
TokensEvaluated int `json:"tokens_evaluated"`
|
||||||
Created int `json:"created"`
|
Prompt string `json:"prompt"`
|
||||||
Model string `json:"model"`
|
HasNewLine bool `json:"has_new_line"`
|
||||||
Object string `json:"object"`
|
Truncated bool `json:"truncated"`
|
||||||
|
StopType string `json:"stop_type"`
|
||||||
|
StoppingWord string `json:"stopping_word"`
|
||||||
|
TokensCached int `json:"tokens_cached"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MimeResp struct {
|
type MimeResp struct {
|
||||||
@ -63,13 +80,13 @@ type GusserResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
Role string // gueeser | mime
|
Role string `json:"role"`
|
||||||
Team string
|
Team string `json:"team"`
|
||||||
cfg *config.Config
|
cfg *config.Config `json:"-"`
|
||||||
RoomID string // can we get a room from here?
|
RoomID string `json:"room_id"` // can we get a room from here?
|
||||||
BotName string
|
BotName string `json:"bot_name"`
|
||||||
log *slog.Logger
|
log *slog.Logger `json:"-"`
|
||||||
LLMParser RespParser
|
LLMParser RespParser `json:"-"`
|
||||||
// channels for communicaton
|
// channels for communicaton
|
||||||
// channels are not serializable
|
// channels are not serializable
|
||||||
// SignalsCh chan bool
|
// SignalsCh chan bool
|
||||||
@ -271,7 +288,7 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveBot(bot *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)
|
data, err := json.Marshal(bot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -66,12 +66,13 @@ func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) {
|
|||||||
p.log.Error("failed to unmarshal", "error", err)
|
p.log.Error("failed to unmarshal", "error", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(resp.Choices) == 0 {
|
// if len(resp.Choices) == 0 {
|
||||||
p.log.Error("empty choices", "resp", resp)
|
// p.log.Error("empty choices", "resp", resp)
|
||||||
err := errors.New("empty choices in resp")
|
// err := errors.New("empty choices in resp")
|
||||||
return nil, err
|
// return nil, err
|
||||||
}
|
// }
|
||||||
text := resp.Choices[0].Message.Content
|
// text := resp.Choices[0].Message.Content
|
||||||
|
text := resp.Content
|
||||||
li := strings.Index(text, "{")
|
li := strings.Index(text, "{")
|
||||||
ri := strings.LastIndex(text, "}")
|
ri := strings.LastIndex(text, "}")
|
||||||
if li < 0 || ri < 1 {
|
if li < 0 || ri < 1 {
|
||||||
|
2
main.go
2
main.go
@ -69,7 +69,7 @@ func main() {
|
|||||||
|
|
||||||
<-stop
|
<-stop
|
||||||
slog.Info("Shutting down server...")
|
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()
|
defer cancel()
|
||||||
if err := server.Shutdown(ctx); err != nil {
|
if err := server.Shutdown(ctx); err != nil {
|
||||||
slog.Error("server shutdown failed", "error", err)
|
slog.Error("server shutdown failed", "error", err)
|
||||||
|
@ -8,6 +8,7 @@ var (
|
|||||||
// cache
|
// cache
|
||||||
CacheRoomPrefix = "room#"
|
CacheRoomPrefix = "room#"
|
||||||
CacheStatePrefix = "state-"
|
CacheStatePrefix = "state-"
|
||||||
|
CacheBotPredix = "botkey_"
|
||||||
// sse
|
// sse
|
||||||
NotifyRoomListUpdate = "roomlistupdate"
|
NotifyRoomListUpdate = "roomlistupdate"
|
||||||
NotifyRoomUpdatePrefix = "roomupdate_"
|
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
|
// WhichBotToMove returns bot name that have to move or empty string
|
||||||
func (r *Room) WhichBotToMove() 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 {
|
if !r.IsRunning {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
8
todos.md
8
todos.md
@ -13,6 +13,9 @@
|
|||||||
- needs resend to llm btn; +
|
- needs resend to llm btn; +
|
||||||
- better styles and fluff;
|
- better styles and fluff;
|
||||||
- common auth system between sites;
|
- common auth system between sites;
|
||||||
|
- autoscroll down backlog on update;
|
||||||
|
- gameover to backlog;
|
||||||
|
- ended turn action to backlog;
|
||||||
|
|
||||||
#### sse points
|
#### sse points
|
||||||
- clue sse update;
|
- clue sse update;
|
||||||
@ -31,7 +34,8 @@
|
|||||||
- 0 should mean without limit;
|
- 0 should mean without limit;
|
||||||
- sse hangs / fails connection which causes to wait for cards to open a few seconds (on local machine);
|
- 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;
|
- after starting a new game (after old one) blue mime has no clue input;
|
||||||
- gameover to backlog;
|
|
||||||
- remove verbs from word file;
|
- remove verbs from word file;
|
||||||
- invite link gets cutoff;
|
- 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