161 lines
4.7 KiB
Go
161 lines
4.7 KiB
Go
package bot
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
"github.com/charmbracelet/log"
|
|
)
|
|
|
|
const (
|
|
maxTokens = 2000
|
|
maxContextTokens = 2000
|
|
maxMessageTokens = 2000
|
|
systemMessageText = "your name is !bit you are a discord bot, you use brief answers untill asked to elaborate or explain"
|
|
)
|
|
|
|
func populateConversationHistory(session *discordgo.Session, channelID string, conversationHistory []map[string]interface{}) []map[string]interface{} {
|
|
// Retrieve recent messages from the Discord channel
|
|
messages, err := session.ChannelMessages(channelID, 20, "", "", "")
|
|
if err != nil {
|
|
log.Error("Error retrieving channel history:", err)
|
|
return conversationHistory
|
|
}
|
|
|
|
// Define max tokens for the conversation history
|
|
maxTokens := 2000
|
|
totalTokens := 0
|
|
|
|
// Calculate current token count in conversation history
|
|
for _, msg := range conversationHistory {
|
|
content, okContent := msg["Content"].(string)
|
|
role, okRole := msg["Role"].(string)
|
|
if okContent && okRole {
|
|
tokens := len(content) + len(role) + 2 // Account for tokens in content and role
|
|
totalTokens += tokens
|
|
log.Infof("Existing message tokens: %d", tokens)
|
|
}
|
|
}
|
|
|
|
// Process messages in reverse order (newest to oldest)
|
|
for i := len(messages) - 1; i >= 0; i-- {
|
|
message := messages[i]
|
|
|
|
// Check if the message is older than 30 minutes
|
|
if time.Since(message.Timestamp) < 30*time.Minute {
|
|
tokens := len(message.Content) + 2
|
|
if totalTokens+tokens <= maxTokens {
|
|
// Append as map[string]interface{} instead of map[string]string
|
|
conversationHistory = append(conversationHistory, map[string]interface{}{
|
|
"role": "user",
|
|
"content": message.Content,
|
|
})
|
|
totalTokens += tokens
|
|
log.Infof("Adding message with tokens: %d", tokens)
|
|
} else {
|
|
log.Warnf("Skipping message with tokens: %d", tokens)
|
|
}
|
|
} else {
|
|
log.Infof("Skipping message, older than 30 minutes: %s", message.Content)
|
|
}
|
|
|
|
// Ensure the current message is included (regardless of token limit)
|
|
conversationHistory = append(conversationHistory, map[string]interface{}{
|
|
"role": "user",
|
|
"content": message.Content,
|
|
})
|
|
totalTokens += len(message.Content) + 2
|
|
log.Infof("Adding message with tokens: %d", totalTokens)
|
|
|
|
// Now check if the token limit is exceeded and trim older messages
|
|
if totalTokens > maxTokens && len(conversationHistory) > 1 {
|
|
// Remove the oldest message from the history
|
|
conversationHistory = conversationHistory[1:]
|
|
content, okContent := conversationHistory[0]["Content"].(string)
|
|
role, okRole := conversationHistory[0]["Role"].(string)
|
|
if okContent && okRole {
|
|
tokens := len(content) + len(role) + 2
|
|
totalTokens -= tokens
|
|
log.Infof("Trimming message with tokens: %d", tokens)
|
|
}
|
|
log.Info("Trimming oldest message to maintain token limit")
|
|
}
|
|
}
|
|
|
|
log.Info("Final Conversation History Order: %s", conversationHistory)
|
|
return conversationHistory
|
|
}
|
|
|
|
// Function to handle Groq API requests and pagination
|
|
func chatGPT(session *discordgo.Session, channelID string, conversationHistory []map[string]interface{}) {
|
|
OpenAIToken := OpenAIToken
|
|
GroqBaseURL := "https://api.groq.com/openai/v1"
|
|
GroqModel := "llama-3.1-70b-versatile"
|
|
|
|
// Add system message at the start of conversation history
|
|
conversationHistory = append([]map[string]interface{}{
|
|
{"role": "system", "content": systemMessageText},
|
|
}, conversationHistory...)
|
|
|
|
client := http.Client{}
|
|
requestBody, err := json.Marshal(map[string]interface{}{
|
|
"model": GroqModel,
|
|
"messages": conversationHistory,
|
|
"max_tokens": maxTokens,
|
|
"frequency_penalty": 0.3,
|
|
"presence_penalty": 0.6,
|
|
})
|
|
if err != nil {
|
|
log.Errorf("Failed to marshal request body: %v", err)
|
|
return
|
|
}
|
|
|
|
req, err := http.NewRequest("POST", GroqBaseURL+"/chat/completions", bytes.NewBuffer(requestBody))
|
|
if err != nil {
|
|
log.Errorf("Failed to create request: %v", err)
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+OpenAIToken)
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Errorf("Failed to make request: %v", err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var groqResp struct {
|
|
Choices []struct {
|
|
Message struct {
|
|
Content string `json:"content"`
|
|
} `json:"message"`
|
|
} `json:"choices"`
|
|
}
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Errorf("Failed to read response body: %v", err)
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(body, &groqResp)
|
|
if err != nil {
|
|
log.Errorf("Failed to decode response: %v", err)
|
|
return
|
|
}
|
|
|
|
if len(groqResp.Choices) > 0 {
|
|
gptResponse := groqResp.Choices[0].Message.Content
|
|
_, err := session.ChannelMessageSend(channelID, gptResponse)
|
|
if err != nil {
|
|
log.Error("Error sending message:", err)
|
|
return
|
|
}
|
|
}
|
|
}
|