Fix: show color; add bot [WIP]
This commit is contained in:
		
							
								
								
									
										200
									
								
								llmapi/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								llmapi/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| package llmapi | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"golias/config" | ||||
| 	"golias/models" | ||||
| 	"golias/pkg/cache" | ||||
| 	"io" | ||||
| 	"log/slog" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // TODO: config for url and token | ||||
| // completion prompt | ||||
| // MIME: llm needs to know all the cards, colors and previous actions | ||||
| // GUESSER: llm needs to know all the cards and previous actions | ||||
|  | ||||
| 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 | ||||
| 	// channels for communicaton | ||||
| 	SignalsCh chan bool | ||||
| 	DoneCh    chan bool | ||||
| } | ||||
|  | ||||
| // StartBot | ||||
| func (b *Bot) StartBot() { | ||||
| 	for { | ||||
| 		select { | ||||
| 		case <-b.SignalsCh: | ||||
| 			// get room cards and actions | ||||
| 			room, err := getRoomByID(b.RoomID) | ||||
| 			if err != nil { | ||||
| 				b.log.Error("bot loop", "error", err) | ||||
| 				return | ||||
| 			} | ||||
| 			// form prompt | ||||
| 			prompt := b.BuildPrompt(room) | ||||
| 			b.log.Debug("got prompt", "prompt", prompt) | ||||
| 			// call llm | ||||
| 			if err := b.CallLLM(prompt); 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 | ||||
| 		case <-b.DoneCh: | ||||
| 			b.log.Debug("got done signal", "bot-name", b.BotName) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // EndBot | ||||
|  | ||||
| func NewBot(role, team, name, roomID string, cfg *config.Config) (*Bot, error) { | ||||
| 	bot := &Bot{ | ||||
| 		Role:    role, | ||||
| 		RoomID:  roomID, | ||||
| 		BotName: name, | ||||
| 		Team:    team, | ||||
| 		cfg:     cfg, | ||||
| 	} | ||||
| 	// add to room | ||||
| 	room, err := getRoomByID(bot.RoomID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	// check if not running | ||||
| 	if role == "mime" && room.IsRunning { | ||||
| 		return nil, errors.New("cannot join after game started") | ||||
| 	} | ||||
| 	room.PlayerList = append(room.PlayerList, name) | ||||
| 	switch team { | ||||
| 	case "red": | ||||
| 		if role == "mime" { | ||||
| 			room.RedTeam.Mime = name | ||||
| 		} else if role == "guesser" { | ||||
| 			room.RedTeam.Guessers = append(room.RedTeam.Guessers, name) | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("uknown role: %s", role) | ||||
| 		} | ||||
| 	case "blue": | ||||
| 		if role == "mime" { | ||||
| 			room.BlueTeam.Mime = name | ||||
| 		} else if role == "guesser" { | ||||
| 			room.BlueTeam.Guessers = append(room.BlueTeam.Guessers, name) | ||||
| 		} else { | ||||
| 			return nil, fmt.Errorf("uknown role: %s", role) | ||||
| 		} | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("uknown team: %s", team) | ||||
| 	} | ||||
| 	if err := saveRoom(room); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	go bot.StartBot() // run bot routine | ||||
| 	return bot, nil | ||||
| } | ||||
|  | ||||
| func getRoomByID(roomID string) (*models.Room, error) { | ||||
| 	roomBytes, err := cache.MemCache.Get(models.CacheRoomPrefix + roomID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp := &models.Room{} | ||||
| 	if err := json.Unmarshal(roomBytes, &resp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return resp, nil | ||||
| } | ||||
|  | ||||
| func saveRoom(room *models.Room) error { | ||||
| 	key := models.CacheRoomPrefix + room.ID | ||||
| 	data, err := json.Marshal(room) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	cache.MemCache.Set(key, data) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (b *Bot) BuildPrompt(room *models.Room) string { | ||||
| 	if b.Role == "" { | ||||
| 		return "" | ||||
| 	} | ||||
| 	toText := make(map[string]any) | ||||
| 	toText["backlog"] = room.ActionHistory | ||||
| 	// mime sees all colors; | ||||
| 	// guesser sees only revealed ones | ||||
| 	if b.Role == models.UserRoleMime { | ||||
| 		toText["cards"] = room.Cards | ||||
| 	} | ||||
| 	if b.Role == models.UserRoleGuesser { | ||||
| 		copiedCards := []models.WordCard{} | ||||
| 		copy(copiedCards, room.Cards) | ||||
| 		for i, card := range copiedCards { | ||||
| 			if !card.Revealed { | ||||
| 				copiedCards[i].Color = models.WordColorUknown | ||||
| 			} | ||||
| 		} | ||||
| 		toText["cards"] = copiedCards | ||||
| 	} | ||||
| 	data, err := json.MarshalIndent(toText, "", "  ") | ||||
| 	if err != nil { | ||||
| 		// log | ||||
| 		return "" | ||||
| 	} | ||||
| 	return string(data) | ||||
| } | ||||
|  | ||||
| func (b *Bot) CallLLM(prompt string) error { | ||||
| 	method := "POST" | ||||
| 	payload := strings.NewReader(fmt.Sprintf(`{ | ||||
|   "model": "deepseek-chat", | ||||
|   "prompt": "%s", | ||||
|   "echo": false, | ||||
|   "frequency_penalty": 0, | ||||
|   "logprobs": 0, | ||||
|   "max_tokens": 1024, | ||||
|   "presence_penalty": 0, | ||||
|   "stop": null, | ||||
|   "stream": false, | ||||
|   "stream_options": null, | ||||
|   "suffix": null, | ||||
|   "temperature": 1, | ||||
|   "top_p": 1 | ||||
| }`, prompt)) | ||||
| 	client := &http.Client{} | ||||
| 	req, err := http.NewRequest(method, b.cfg.LLMConfig.URL, payload) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	req.Header.Add("Content-Type", "application/json") | ||||
| 	req.Header.Add("Accept", "application/json") | ||||
| 	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", b.cfg.LLMConfig.TOKEN)) | ||||
| 	res, err := client.Do(req) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	defer res.Body.Close() | ||||
| 	body, err := io.ReadAll(res.Body) | ||||
| 	if err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		return err | ||||
| 	} | ||||
| 	fmt.Println(string(body)) | ||||
| 	return nil | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Grail Finder
					Grail Finder