Fix: cookie; add llm parser interface
This commit is contained in:
@ -142,19 +142,18 @@ func makeCookie(username string, remote string) (*http.Cookie, error) {
|
|||||||
cookieValue := base64.URLEncoding.EncodeToString([]byte(
|
cookieValue := base64.URLEncoding.EncodeToString([]byte(
|
||||||
string(signature) + sessionToken))
|
string(signature) + sessionToken))
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: cookieName,
|
Name: cookieName,
|
||||||
Value: cookieValue,
|
Value: cookieValue,
|
||||||
// Secure: true,
|
Secure: true,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
SameSite: http.SameSiteNoneMode,
|
SameSite: http.SameSiteNoneMode,
|
||||||
// Domain: cfg.ServerConfig.Host,
|
|
||||||
}
|
}
|
||||||
log.Info("check remote addr for cookie set",
|
log.Info("check remote addr for cookie set",
|
||||||
"remote", remote, "session", session)
|
"remote", remote, "session", session)
|
||||||
if strings.Contains(remote, "192.168.0") {
|
if strings.Contains(remote, "192.168.0") {
|
||||||
// cookie.Domain = "192.168.0.101"
|
cookie.Domain = "192.168.0.106"
|
||||||
cookie.Domain = ""
|
|
||||||
cookie.SameSite = http.SameSiteLaxMode
|
cookie.SameSite = http.SameSiteLaxMode
|
||||||
|
cookie.Secure = false
|
||||||
log.Info("changing cookie domain", "domain", cookie.Domain)
|
log.Info("changing cookie domain", "domain", cookie.Domain)
|
||||||
}
|
}
|
||||||
// set ctx?
|
// set ctx?
|
||||||
|
@ -254,6 +254,7 @@ func HandleGiveClue(w http.ResponseWriter, r *http.Request) {
|
|||||||
fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
|
fi.Room.ActionHistory = append(fi.Room.ActionHistory, action)
|
||||||
fi.Room.MimeDone = true
|
fi.Room.MimeDone = true
|
||||||
notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num)
|
notify(models.NotifyBacklogPrefix+fi.Room.ID, clue+num)
|
||||||
|
notifyBotIfNeeded(fi)
|
||||||
if err := saveFullInfo(fi); err != nil {
|
if err := saveFullInfo(fi); err != nil {
|
||||||
abortWithError(w, err.Error())
|
abortWithError(w, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -21,7 +21,7 @@ var (
|
|||||||
DoneChanMap = 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
|
// 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 %s team (people who would guess by your clue want open the %s 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`
|
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 %s team (people who would guess by your clue want open the %s 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 %s team (people who would guess by your clue want open the %s 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`
|
GuesserPrompt = `we are playing alias;\nyou are to guess words of the %s team (you want open %s cards) by given clue and a number of meant guesses;\nplease return your guesses and words that could be meant by the clue, but you do not wish to open yet, 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`
|
||||||
)
|
)
|
||||||
|
|
||||||
type DSResp struct {
|
type DSResp struct {
|
||||||
@ -37,6 +37,20 @@ 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 MimeResp struct {
|
type MimeResp struct {
|
||||||
Clue string `json:"clue"`
|
Clue string `json:"clue"`
|
||||||
Number string `json:"number"`
|
Number string `json:"number"`
|
||||||
@ -49,12 +63,13 @@ type GusserResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Bot struct {
|
type Bot struct {
|
||||||
Role string // gueeser | mime
|
Role string // gueeser | mime
|
||||||
Team string
|
Team string
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
RoomID string // can we get a room from here?
|
RoomID string // can we get a room from here?
|
||||||
BotName string
|
BotName string
|
||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
|
LLMParser RespParser
|
||||||
// channels for communicaton
|
// channels for communicaton
|
||||||
// channels are not serializable
|
// channels are not serializable
|
||||||
// SignalsCh chan bool
|
// SignalsCh chan bool
|
||||||
@ -82,43 +97,16 @@ func (b *Bot) StartBot() {
|
|||||||
b.log.Error("bot loop", "error", err)
|
b.log.Error("bot loop", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dsResp := DSResp{}
|
tempMap, err := b.LLMParser.ParseBytes(llmResp)
|
||||||
if err := json.Unmarshal(llmResp, &dsResp); err != nil {
|
if err != nil {
|
||||||
b.log.Error("failed to unmarshall", "error", err)
|
b.log.Error("bot loop", "error", err)
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
if len(dsResp.Choices) == 0 {
|
|
||||||
b.log.Error("empty choices", "dsResp", dsResp)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
text := dsResp.Choices[0].Text
|
|
||||||
li := strings.Index(text, "{")
|
|
||||||
ri := strings.LastIndex(text, "}")
|
|
||||||
if li < 0 || ri < 1 {
|
|
||||||
b.log.Error("not a json", "msg", text)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
sj := text[li : ri+1]
|
|
||||||
// jb, err := json.Marshal(sj)
|
|
||||||
// if err != nil {
|
|
||||||
// b.log.Error("failed to marshal", "error", err, "string-json", sj)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// parse response
|
|
||||||
// if mime -> give clue
|
|
||||||
// if guesser -> open card (does opening one card prompting new loop?)
|
|
||||||
// send notification to sse broker
|
|
||||||
eventName := models.NotifyBacklogPrefix + room.ID
|
eventName := models.NotifyBacklogPrefix + room.ID
|
||||||
eventPayload := ""
|
eventPayload := ""
|
||||||
tempMap := make(map[string]any)
|
|
||||||
switch b.Role {
|
switch b.Role {
|
||||||
case models.UserRoleMime:
|
case models.UserRoleMime:
|
||||||
// respMap := make(map[string]any)
|
|
||||||
mimeResp := MimeResp{}
|
mimeResp := MimeResp{}
|
||||||
if err := json.Unmarshal([]byte(sj), &tempMap); err != nil {
|
|
||||||
b.log.Error("failed to unmarshal mime resp", "error", err, "string-json", sj)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.log.Info("mime resp log", "mimeResp", tempMap)
|
b.log.Info("mime resp log", "mimeResp", tempMap)
|
||||||
mimeResp.Clue = tempMap["clue"].(string)
|
mimeResp.Clue = tempMap["clue"].(string)
|
||||||
mimeResp.Number = tempMap["number"].(string)
|
mimeResp.Number = tempMap["number"].(string)
|
||||||
@ -186,7 +174,7 @@ func (b *Bot) StartBot() {
|
|||||||
eventName = models.NotifyRoomUpdatePrefix + room.ID
|
eventName = models.NotifyRoomUpdatePrefix + room.ID
|
||||||
eventPayload = ""
|
eventPayload = ""
|
||||||
default:
|
default:
|
||||||
b.log.Error("unexpected role", "role", b.Role, "llmResp", sj)
|
b.log.Error("unexpected role", "role", b.Role, "resp-map", tempMap)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// save room
|
// save room
|
||||||
@ -223,6 +211,11 @@ func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) {
|
|||||||
AddSource: true,
|
AddSource: true,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
|
// there might be a better way
|
||||||
|
bot.LLMParser = NewLCPRespParser(bot.log)
|
||||||
|
if strings.Contains(cfg.LLMConfig.URL, "api.deepseek.com") {
|
||||||
|
bot.LLMParser = NewDeepSeekParser(bot.log)
|
||||||
|
}
|
||||||
// add to room
|
// add to room
|
||||||
room, err := getRoomByID(bot.RoomID)
|
room, err := getRoomByID(bot.RoomID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -321,7 +314,7 @@ func (b *Bot) BuildPrompt(room *models.Room) string {
|
|||||||
toText["cards"] = room.Cards
|
toText["cards"] = room.Cards
|
||||||
}
|
}
|
||||||
if b.Role == models.UserRoleGuesser {
|
if b.Role == models.UserRoleGuesser {
|
||||||
copiedCards := []models.WordCard{}
|
copiedCards := make([]models.WordCard, len(room.Cards))
|
||||||
copy(copiedCards, room.Cards)
|
copy(copiedCards, room.Cards)
|
||||||
for i, card := range copiedCards {
|
for i, card := range copiedCards {
|
||||||
if !card.Revealed {
|
if !card.Revealed {
|
||||||
@ -383,6 +376,6 @@ func (b *Bot) CallLLM(prompt string) ([]byte, error) {
|
|||||||
b.log.Error("failed to read resp body", "error", err, "url", b.cfg.LLMConfig.URL)
|
b.log.Error("failed to read resp body", "error", err, "url", b.cfg.LLMConfig.URL)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b.log.Debug("llm resp", "body", string(body))
|
b.log.Debug("llm resp", "body", string(body), "url", b.cfg.LLMConfig.URL)
|
||||||
return body, nil
|
return body, nil
|
||||||
}
|
}
|
||||||
|
88
llmapi/parser.go
Normal file
88
llmapi/parser.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package llmapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RespParser interface {
|
||||||
|
ParseBytes(body []byte) (map[string]any, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepSeekParser: deepseek implementation of RespParser
|
||||||
|
type deepSeekParser struct {
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeepSeekParser(log *slog.Logger) *deepSeekParser {
|
||||||
|
return &deepSeekParser{log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *deepSeekParser) ParseBytes(body []byte) (map[string]any, error) {
|
||||||
|
// parsing logic here
|
||||||
|
dsResp := DSResp{}
|
||||||
|
if err := json.Unmarshal(body, &dsResp); err != nil {
|
||||||
|
p.log.Error("failed to unmarshall", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(dsResp.Choices) == 0 {
|
||||||
|
p.log.Error("empty choices", "dsResp", dsResp)
|
||||||
|
err := fmt.Errorf("empty choices in dsResp")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
text := dsResp.Choices[0].Text
|
||||||
|
li := strings.Index(text, "{")
|
||||||
|
ri := strings.LastIndex(text, "}")
|
||||||
|
if li < 0 || ri < 1 {
|
||||||
|
p.log.Error("not a json", "msg", text)
|
||||||
|
err := fmt.Errorf("fn: ParseBytes, not a json")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sj := text[li : ri+1]
|
||||||
|
respMap := make(map[string]any)
|
||||||
|
if err := json.Unmarshal([]byte(sj), &respMap); err != nil {
|
||||||
|
p.log.Error("failed to unmarshal response", "error", err, "string-json", sj)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return respMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// llama.cpp implementation of RespParser
|
||||||
|
type lcpRespParser struct {
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLCPRespParser(log *slog.Logger) *lcpRespParser {
|
||||||
|
return &lcpRespParser{log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *lcpRespParser) ParseBytes(body []byte) (map[string]any, error) {
|
||||||
|
// parsing logic here
|
||||||
|
resp := LLMResp{}
|
||||||
|
if err := json.Unmarshal(body, &resp); err != nil {
|
||||||
|
p.log.Error("failed to unmarshal", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resp.Choices) == 0 {
|
||||||
|
p.log.Error("empty choices", "resp", resp)
|
||||||
|
err := fmt.Errorf("empty choices in resp")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
text := resp.Choices[0].Message.Content
|
||||||
|
li := strings.Index(text, "{")
|
||||||
|
ri := strings.LastIndex(text, "}")
|
||||||
|
if li < 0 || ri < 1 {
|
||||||
|
p.log.Error("not a json", "msg", text)
|
||||||
|
err := fmt.Errorf("fn: ParseBytes, not a json")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sj := text[li : ri+1]
|
||||||
|
respMap := make(map[string]any)
|
||||||
|
if err := json.Unmarshal([]byte(sj), &respMap); err != nil {
|
||||||
|
p.log.Error("failed to unmarshal response", "error", err, "string-json", sj)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return respMap, nil
|
||||||
|
}
|
@ -103,6 +103,15 @@ func (r *Room) CanStart() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGuesser(m map[string]BotPlayer, team UserTeam) string {
|
||||||
|
for k, v := range m {
|
||||||
|
if v.Team == team && v.Role == UserRoleGuesser {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
@ -116,16 +125,18 @@ func (r *Room) WhichBotToMove() string {
|
|||||||
if ok {
|
if ok {
|
||||||
return r.BlueTeam.Mime
|
return r.BlueTeam.Mime
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return getGuesser(r.BotMap, UserTeamBlue)
|
||||||
}
|
}
|
||||||
// check gussers
|
|
||||||
case UserTeamRed:
|
case UserTeamRed:
|
||||||
if !r.MimeDone {
|
if !r.MimeDone {
|
||||||
_, ok := r.BotMap[r.RedTeam.Mime]
|
_, ok := r.BotMap[r.RedTeam.Mime]
|
||||||
if ok {
|
if ok {
|
||||||
return r.RedTeam.Mime
|
return r.RedTeam.Mime
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return getGuesser(r.BotMap, UserTeamRed)
|
||||||
}
|
}
|
||||||
// check gussers
|
|
||||||
default:
|
default:
|
||||||
// how did we got here?
|
// how did we got here?
|
||||||
return ""
|
return ""
|
||||||
|
3
todos.md
3
todos.md
@ -9,6 +9,9 @@
|
|||||||
- there two places for bot to check if its its move: start-game; end-turn;
|
- there two places for bot to check if its its move: start-game; end-turn;
|
||||||
- remove bot button (if game is not running);
|
- 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;
|
- show in backlog (and with that in prompt to llm) how many cards are left to open, also additional comment: if guess was right;
|
||||||
|
- if bot already added; remove add bot button;
|
||||||
|
- hide clue input for mime when it's not their turn;
|
||||||
|
- needs resend to llm btn;
|
||||||
|
|
||||||
#### sse points
|
#### sse points
|
||||||
- clue sse update;
|
- clue sse update;
|
||||||
|
Reference in New Issue
Block a user