diff --git a/bot/bot.go b/bot/bot.go index 42afb0b..02748de 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -2,11 +2,13 @@ package bot import ( "bitbot/pb" - "github.com/bwmarrin/discordgo" - "github.com/charmbracelet/log" "os" "os/signal" "strings" + + "github.com/bwmarrin/discordgo" + "github.com/charmbracelet/log" + openai "github.com/sashabaranov/go-openai" ) var ( @@ -33,19 +35,36 @@ func Run() { <-c } +var conversationHistoryMap = make(map[string][]openai.ChatCompletionMessage) + func newMessage(discord *discordgo.Session, message *discordgo.MessageCreate) { if message.Author.ID == discord.State.User.ID { return } + conversationHistory := conversationHistoryMap[message.Author.ID] + + userMessage := openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleUser, + Content: message.Content, + } + conversationHistory = append(conversationHistory, userMessage) + switch { case strings.Contains(message.Content, "!bit"): - gptResponse := chatGPT(message.Content) + gptResponse := chatGPT(message.Content, conversationHistory) discord.ChannelTyping(message.ChannelID) discord.ChannelMessageSendComplex(message.ChannelID, gptResponse) + + botMessage := openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleSystem, + Content: gptResponse.Content, + } + conversationHistory = append(conversationHistory, botMessage) case strings.Contains(message.Content, "!cry"): currentCryptoPrice := getCurrentCryptoPrice(message.Content) discord.ChannelMessageSendComplex(message.ChannelID, currentCryptoPrice) } + conversationHistoryMap[message.Author.ID] = conversationHistory } diff --git a/bot/chat.go b/bot/chat.go index e4e84a4..e7075b3 100644 --- a/bot/chat.go +++ b/bot/chat.go @@ -2,39 +2,71 @@ package bot import ( "context" + "github.com/bwmarrin/discordgo" + "github.com/charmbracelet/log" openai "github.com/sashabaranov/go-openai" ) -func chatGPT(message string) *discordgo.MessageSend { +const maxTokens = 2000 +const maxContextTokens = 4097 + +func chatGPT(message string, conversationHistory []openai.ChatCompletionMessage) *discordgo.MessageSend { client := openai.NewClient(OpenAIToken) + + // Calculate the total number of tokens used in the conversation history and completion. + totalTokens := 0 + for _, msg := range conversationHistory { + totalTokens += len(msg.Content) + } + + // Calculate the number of tokens used in the completion. + completionTokens := len(message) + + // If the total tokens (context + completion) exceed the maxTokens limit, truncate the completion first. + for totalTokens+completionTokens > maxTokens { + // Remove tokens from the beginning of the completion message. + if completionTokens > maxTokens { + message = message[:maxTokens] + completionTokens = maxTokens + } else { + // If removing the last message reduces the context within the limit, remove it. + if totalTokens-len(conversationHistory[len(conversationHistory)-1].Content) <= maxTokens { + totalTokens -= len(conversationHistory[len(conversationHistory)-1].Content) + conversationHistory = conversationHistory[:len(conversationHistory)-1] + } else { + // Otherwise, remove the first message from the conversation history. + totalTokens -= len(conversationHistory[0].Content) + conversationHistory = conversationHistory[1:] + } + } + } + + // Combine the previous conversation history with the current user message. + messages := append(conversationHistory, openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleUser, + Content: message, + }) + resp, err := client.CreateChatCompletion( context.Background(), openai.ChatCompletionRequest{ - MaxTokens: 800, + MaxTokens: maxTokens, FrequencyPenalty: 0.3, PresencePenalty: 0.6, - Model: openai.GPT3Dot5Turbo0301, - Messages: []openai.ChatCompletionMessage{ - { - Role: openai.ChatMessageRoleSystem, - Content: "You are an amazing and versatile person! As an ethical hacker, coder, polyglot, and poet.", - }, - { - Role: openai.ChatMessageRoleUser, - Content: message, - }, - }, + Model: openai.GPT3Dot5Turbo, + Messages: messages, }, ) if err != nil { + log.Error("Error connecting to the OpenAI API:", err) return &discordgo.MessageSend{ - Content: "Sorry, there was an error trying to connect to api", + Content: "Sorry, there was an error trying to connect to the API", } } - gptResponse := (resp.Choices[0].Message.Content) + gptResponse := resp.Choices[0].Message.Content embed := &discordgo.MessageSend{ Content: gptResponse,