From e8507463e03e13f6ba7b40d478347347f5bafdd1 Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 28 May 2025 11:00:24 +0300 Subject: [PATCH] Enha: parsing llm resp [WIP] --- llmapi/main.go | 77 ++++++++++++++++++++++++++++++++++++++++++-------- todos.md | 1 + 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/llmapi/main.go b/llmapi/main.go index 1aa6eb1..76aa058 100644 --- a/llmapi/main.go +++ b/llmapi/main.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "golias/broker" "golias/config" "golias/models" "golias/pkg/cache" @@ -26,9 +27,22 @@ var ( SignalChanMap = make(map[string]chan bool) DoneChanMap = make(map[string]chan bool) // got prompt: control character (\\u0000-\\u001F) found while parsing a string at line 4 column 0 - MimePrompt = `we are playing alias;\nyou are a mime (player who gives a clue of one noun word and number of cards you expect them to open) of the red team (people who would guess by your clue want open the red cards);\nplease return your clue, number of cards to open and what words you mean them to find using that clue in json like:\n{\n\"clue\": \"one-word-noun\",\n\"number\": \"number-from-0-to-9\",\n\"word_I_mean_my_team_to_open\": [\"this\", \"that\", ...]\n}\nthe team who openes all their cards first wins.\nplease return json only.\nunopen Blue cards left: %d;\nunopen Red cards left: %d;\nhere is the game info in json:\n%s` + MimePrompt = `we are playing alias;\nyou are a mime (player who gives a clue of one noun word and number of cards you expect them to open) of the red team (people who would guess by your clue want open the red cards);\nplease return your clue, number of cards to open and what words you mean them to find using that clue in json like:\n{\n\"clue\": \"one-word-noun\",\n\"number\": \"number-from-0-to-9\",\n\"words_I_mean_my_team_to_open\": [\"this\", \"that\", ...]\n}\nthe team who openes all their cards first wins.\nplease return json only.\nunopen Blue cards left: %d;\nunopen Red cards left: %d;\nhere is the game info in json:\n%s` + GuesserPrompt = `we are playing alias;\nyou are to guess words of the red team (people who would guess by your clue want open the red cards) by given clue and a number of meant guesses;\nplease return your guesses and words that you did not wish to open, but think that they could be also meant by the clue in json like:\n{\n\"guesses\": [\"word1\", \"word2\", ...],\n\"could_be\": [\"this\", \"that\", ...]\n}\nthe team who openes all their cards first wins.\nplease return json only.\nunopen Blue cards left: %d;\nunopen Red cards left: %d;\nhere is the game info in json:\n%s` + // notifier = ) +type MimeResp struct { + Clue string `json:"clue"` + Number string `json:"number"` + Answer []string `json:"words_I_mean_my_team_to_open"` +} + +type GusserResp struct { + Guesses []string `json:"guesses"` + CouldBe []string `json:"could_be"` +} + type Bot struct { Role string // gueeser | mime Team string @@ -58,14 +72,49 @@ func (b *Bot) StartBot() { prompt := b.BuildPrompt(room) b.log.Debug("got prompt", "prompt", prompt) // call llm - if err := b.CallLLM(prompt); err != nil { + llmResp, err := b.CallLLM(prompt) + if err != nil { b.log.Error("bot loop", "error", err) return } - // parse response - // if mime -> give clue - // if guesser -> open card (does opening one card prompting new loop?) - // send notification to sse broker + // parse response + // if mime -> give clue + // if guesser -> open card (does opening one card prompting new loop?) + // send notification to sse broker + switch b.Role { + case models.UserRoleMime: + // respMap := make(map[string]any) + mimeResp := MimeResp{} + if err := json.Unmarshal(llmResp, &mimeResp); err != nil { + b.log.Error("failed to unmarshal mime resp", "error", err) + return + } + action := models.Action{ + Actor: b.BotName, + ActorColor: b.Team, + WordColor: b.Team, + Action: "gave clue", + Word: mimeResp.Clue, + Number: mimeResp.Number, + } + room.ActionHistory = append(room.ActionHistory, action) + room.MimeDone = true + // notify(models.NotifyBacklogPrefix+room.ID, clue+num) + case models.UserRoleGuesser: + gr := GusserResp{} + if err := json.Unmarshal(llmResp, &gr); err != nil { + b.log.Error("failed to unmarshal guesser resp", "error", err) + return + } + default: + b.log.Error("unexpected role", "role", b.Role) + return + } + broker.Notifier.Notifier <- broker.NotificationEvent{ + EventName: "", + Payload: "", + } + // update room info case <-DoneChanMap[b.BotName]: b.log.Debug("got done signal", "bot-name", b.BotName) return @@ -204,7 +253,7 @@ func (b *Bot) BuildPrompt(room *models.Room) string { return "" } -func (b *Bot) CallLLM(prompt string) error { +func (b *Bot) CallLLM(prompt string) ([]byte, error) { method := "POST" payload := strings.NewReader(fmt.Sprintf(`{ "model": "deepseek-chat", @@ -225,7 +274,7 @@ func (b *Bot) CallLLM(prompt string) error { req, err := http.NewRequest(method, b.cfg.LLMConfig.URL, payload) if err != nil { b.log.Error("failed to make new request", "error", err, "url", b.cfg.LLMConfig.URL) - return err + return nil, err } req.Header.Add("Content-Type", "application/json") req.Header.Add("Accept", "application/json") @@ -233,14 +282,20 @@ func (b *Bot) CallLLM(prompt string) error { res, err := client.Do(req) if err != nil { b.log.Error("failed to make request", "error", err, "url", b.cfg.LLMConfig.URL) - return err + return nil, err } defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { b.log.Error("failed to read resp body", "error", err, "url", b.cfg.LLMConfig.URL) - return err + return nil, err } b.log.Debug("llm resp", "body", string(body)) - return nil + // TODO: case where it has other text, not only json + // TODO: how to know which resp it is? mime or guessser? + // resp := &MimeResp{} + // if err := json.Unmarshal(body, &resp); err != nil { + // return nil, err + // } + return body, nil } diff --git a/todos.md b/todos.md index dcfd802..1d13446 100644 --- a/todos.md +++ b/todos.md @@ -8,6 +8,7 @@ - add html icons of whos turn it is (like an image of big ? when mime is thinking); - there two places for bot to check if its its move: start-game; end-turn; - remove bot button (if game is not running); +- show in backlog (and with that in prompt to llm) how many cards are left to open, also additional comment: if guess was right; #### sse points - clue sse update;