populate if empty context and
add trimming to messages
This commit is contained in:
parent
a5517264a7
commit
aa3d85c10b
17
bot/bot.go
17
bot/bot.go
@ -1,20 +1,19 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"bitbot/pb"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/charmbracelet/log"
|
||||
openai "github.com/sashabaranov/go-openai"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
var (
|
||||
CryptoToken string
|
||||
BotToken string
|
||||
OpenAIToken string
|
||||
CryptoToken string
|
||||
)
|
||||
|
||||
func Run() {
|
||||
@ -28,7 +27,6 @@ func Run() {
|
||||
discord.Open()
|
||||
defer discord.Close()
|
||||
log.Info("BitBot is running...")
|
||||
pb.Run()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
@ -44,16 +42,21 @@ func newMessage(discord *discordgo.Session, message *discordgo.MessageCreate) {
|
||||
|
||||
isPrivateChannel := message.GuildID == ""
|
||||
|
||||
conversationHistory := conversationHistoryMap[message.Author.ID]
|
||||
userID := message.Author.ID
|
||||
conversationHistory := conversationHistoryMap[userID]
|
||||
|
||||
channelID := message.ChannelID
|
||||
conversationHistory = populateConversationHistory(discord, channelID, conversationHistory)
|
||||
|
||||
userMessage := openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: message.Content,
|
||||
}
|
||||
|
||||
conversationHistory = append(conversationHistory, userMessage)
|
||||
|
||||
if strings.Contains(message.Content, "!bit") || isPrivateChannel {
|
||||
gptResponse := chatGPT(message.Content, conversationHistory)
|
||||
gptResponse := chatGPT(discord, message.ChannelID, message.Content, conversationHistory)
|
||||
discord.ChannelTyping(message.ChannelID)
|
||||
discord.ChannelMessageSendComplex(message.ChannelID, gptResponse)
|
||||
|
||||
@ -67,5 +70,5 @@ func newMessage(discord *discordgo.Session, message *discordgo.MessageCreate) {
|
||||
discord.ChannelMessageSendComplex(message.ChannelID, currentCryptoPrice)
|
||||
}
|
||||
|
||||
conversationHistoryMap[message.Author.ID] = conversationHistory
|
||||
conversationHistoryMap[userID] = conversationHistory
|
||||
}
|
||||
|
||||
138
bot/chat.go
138
bot/chat.go
@ -8,53 +8,125 @@ import (
|
||||
openai "github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
const maxTokens = 3000
|
||||
const maxContextTokens = 4097
|
||||
const (
|
||||
maxTokens = 3000
|
||||
maxContextTokens = 4097
|
||||
maxMessageTokens = 1000
|
||||
systemMessageText = "1. Identify the key points or main ideas of the original answers.\n2. Summarize each answer using concise and informative language.\n3. Prioritize clarity and brevity, capturing the essence of the information provided.\n4. Trim down unnecessary details and avoid elaboration.\n5. Make sure the summarized answers still convey accurate and meaningful information."
|
||||
)
|
||||
|
||||
func chatGPT(message string, conversationHistory []openai.ChatCompletionMessage) *discordgo.MessageSend {
|
||||
client := openai.NewClient(OpenAIToken)
|
||||
func populateConversationHistory(session *discordgo.Session, channelID string, conversationHistory []openai.ChatCompletionMessage) []openai.ChatCompletionMessage {
|
||||
messages, err := session.ChannelMessages(channelID, 100, "", "", "")
|
||||
if err != nil {
|
||||
log.Error("Error retrieving channel history:", err)
|
||||
return conversationHistory
|
||||
}
|
||||
|
||||
// Calculate the total number of tokens used in the conversation history and completion.
|
||||
totalTokens := 0
|
||||
for _, msg := range conversationHistory {
|
||||
totalTokens += len(msg.Content) + len(msg.Role) + 2 // Account for role and content tokens, plus two extra for delimiters.
|
||||
totalTokens += len(msg.Content) + len(msg.Role) + 2
|
||||
}
|
||||
|
||||
// Calculate the number of tokens used in the completion.
|
||||
maxHistoryTokens := maxTokens - totalTokens
|
||||
|
||||
for _, message := range messages {
|
||||
if message.Author.ID == session.State.User.ID {
|
||||
continue // Skip the bot's own messages
|
||||
}
|
||||
|
||||
if len(message.Content) > 0 {
|
||||
tokens := len(message.Content) + 2 // Account for role and content tokens
|
||||
if totalTokens+tokens <= maxContextTokens && len(conversationHistory) < maxHistoryTokens {
|
||||
conversationHistory = append(conversationHistory, openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: message.Content,
|
||||
})
|
||||
totalTokens += tokens
|
||||
} else {
|
||||
if totalTokens+tokens > maxContextTokens {
|
||||
log.Warn("Message token count exceeds maxContextTokens:", len(message.Content), len(message.Content)+2)
|
||||
} else {
|
||||
log.Warn("Conversation history length exceeds maxContextTokens:", len(conversationHistory), maxHistoryTokens)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conversationHistory
|
||||
}
|
||||
|
||||
func chatGPT(session *discordgo.Session, channelID string, message string, conversationHistory []openai.ChatCompletionMessage) *discordgo.MessageSend {
|
||||
conversationHistory = populateConversationHistory(session, channelID, conversationHistory)
|
||||
|
||||
client := openai.NewClient(OpenAIToken)
|
||||
|
||||
// Calculate the total tokens in the conversation history
|
||||
totalTokens := 0
|
||||
for _, msg := range conversationHistory {
|
||||
totalTokens += len(msg.Content) + len(msg.Role) + 2
|
||||
}
|
||||
|
||||
log.Info("Total tokens in conversation history:", totalTokens)
|
||||
|
||||
// Calculate the tokens in the completion message
|
||||
completionTokens := len(message)
|
||||
log.Info("Tokens in completion message:", completionTokens)
|
||||
|
||||
// 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
|
||||
// Calculate the total tokens including the new message
|
||||
totalMessageTokens := len(message) + 2 // Account for role and content tokens
|
||||
|
||||
// Ensure the total tokens of messages including new message doesn't exceed maxMessageTokens
|
||||
for totalTokens+totalMessageTokens > maxMessageTokens {
|
||||
tokensToRemove := totalTokens + totalMessageTokens - maxMessageTokens
|
||||
tokensRemoved := 0
|
||||
trimmedMessages := []openai.ChatCompletionMessage{} // Store trimmed messages
|
||||
for _, msg := range conversationHistory {
|
||||
tokens := len(msg.Content) + len(msg.Role) + 2
|
||||
if tokensRemoved+tokens <= tokensToRemove {
|
||||
tokensRemoved += tokens
|
||||
log.Info("Removing message with tokens:", tokens)
|
||||
} else {
|
||||
// If removing the last message reduces the context within the limit, remove it.
|
||||
if totalTokens-len(conversationHistory[len(conversationHistory)-1].Content)-len(conversationHistory[len(conversationHistory)-1].Role)-2 <= maxTokens {
|
||||
totalTokens -= len(conversationHistory[len(conversationHistory)-1].Content) + len(conversationHistory[len(conversationHistory)-1].Role) + 2
|
||||
conversationHistory = conversationHistory[:len(conversationHistory)-1]
|
||||
trimmedMessages = append(trimmedMessages, msg)
|
||||
}
|
||||
}
|
||||
if tokensRemoved > 0 {
|
||||
conversationHistory = trimmedMessages
|
||||
totalTokens -= tokensRemoved
|
||||
} else {
|
||||
// Otherwise, remove the first message from the conversation history.
|
||||
totalTokens -= len(conversationHistory[0].Content) + len(conversationHistory[0].Role) + 2
|
||||
conversationHistory = conversationHistory[1:]
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Add a system message at the beginning of the conversation history with the instructions.
|
||||
systemMessage := openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleSystem,
|
||||
Content: "1. Identify the key points or main ideas of the original answers.\n2. Summarize each answer using concise and informative language.\n3. Prioritize clarity and brevity, capturing the essence of the information provided.\n4. Trim down unnecessary details and avoid elaboration.\n5. Make sure the summarized answers still convey accurate and meaningful information.",
|
||||
}
|
||||
|
||||
// Combine the system message, previous conversation history, and the current user message.
|
||||
messages := append([]openai.ChatCompletionMessage{systemMessage}, conversationHistory...)
|
||||
messages = append(messages, openai.ChatCompletionMessage{
|
||||
// Add user message to conversation history
|
||||
userMessage := openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: message,
|
||||
})
|
||||
}
|
||||
conversationHistory = append(conversationHistory, userMessage)
|
||||
|
||||
// Construct system message
|
||||
systemMessage := openai.ChatCompletionMessage{
|
||||
Role: openai.ChatMessageRoleSystem,
|
||||
Content: systemMessageText,
|
||||
}
|
||||
|
||||
// Combine messages, ensuring they don't exceed maxTokens
|
||||
messages := []openai.ChatCompletionMessage{systemMessage}
|
||||
totalTokens = len(systemMessage.Content) + len(systemMessage.Role) + 2
|
||||
|
||||
for _, msg := range conversationHistory {
|
||||
tokens := len(msg.Content) + len(msg.Role) + 2
|
||||
if totalTokens+tokens <= maxTokens {
|
||||
messages = append(messages, msg)
|
||||
totalTokens += tokens
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Perform GPT-3.5 Turbo completion
|
||||
log.Info("Starting GPT-3.5 Turbo completion...")
|
||||
resp, err := client.CreateChatCompletion(
|
||||
context.Background(),
|
||||
openai.ChatCompletionRequest{
|
||||
@ -65,7 +137,9 @@ func chatGPT(message string, conversationHistory []openai.ChatCompletionMessage)
|
||||
Messages: messages,
|
||||
},
|
||||
)
|
||||
log.Info("GPT-3.5 Turbo completion done.")
|
||||
|
||||
// Handle API errors
|
||||
if err != nil {
|
||||
log.Error("Error connecting to the OpenAI API:", err)
|
||||
return &discordgo.MessageSend{
|
||||
@ -73,8 +147,8 @@ func chatGPT(message string, conversationHistory []openai.ChatCompletionMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// Construct and return the bot's response
|
||||
gptResponse := resp.Choices[0].Message.Content
|
||||
|
||||
embed := &discordgo.MessageSend{
|
||||
Content: gptResponse,
|
||||
}
|
||||
|
||||
114
go.mod
114
go.mod
@ -3,39 +3,38 @@ module bitbot
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/bwmarrin/discordgo v0.26.1
|
||||
github.com/charmbracelet/log v0.2.1
|
||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198
|
||||
github.com/pocketbase/pocketbase v0.14.3
|
||||
github.com/bwmarrin/discordgo v0.27.1
|
||||
github.com/charmbracelet/log v0.2.3
|
||||
github.com/pocketbase/pocketbase v0.17.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.234 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.60 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.320 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.77 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.39 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.33 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.2 // indirect
|
||||
github.com/aws/smithy-go v1.14.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.7.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.5.0 // indirect
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
@ -47,58 +46,61 @@ require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.1 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/pocketbase/dbx v1.10.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.6.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/cobra v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
gocloud.dev v0.29.0 // indirect
|
||||
golang.org/x/image v0.6.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/oauth2 v0.6.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
gocloud.dev v0.33.0 // indirect
|
||||
golang.org/x/image v0.11.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/oauth2 v0.11.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/tools v0.12.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.114.0 // indirect
|
||||
google.golang.org/api v0.136.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 // indirect
|
||||
google.golang.org/grpc v1.54.0 // indirect
|
||||
google.golang.org/protobuf v1.30.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230807174057-1744710a1577 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
|
||||
google.golang.org/grpc v1.57.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.22.3 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/cc/v3 v3.41.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.14 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/sqlite v1.21.1 // indirect
|
||||
modernc.org/sqlite v1.25.0 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/joho/godotenv v1.4.0
|
||||
github.com/sashabaranov/go-openai v1.10.1
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/sashabaranov/go-openai v1.14.1
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
)
|
||||
|
||||
BIN
pb_data/data.db
BIN
pb_data/data.db
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user