This commit is contained in:
HugeFrog24
2026-09-03 21:55:36 +02:00
parent c36e1846f5
commit 89458480f3
17 changed files with 1593 additions and 127 deletions
+43
View File
@@ -32,6 +32,9 @@ type Bot struct {
intakeBuffers map[int64]*pendingIntake
intakeBuffersMu sync.Mutex
intakeSeq uint64
turns map[int64]*chatTurn
turnsMu sync.Mutex
turnSeq uint64
}
func messageType(msg *models.Message) string {
@@ -94,6 +97,7 @@ func NewBot(db *gorm.DB, config BotConfig, clock Clock, tgClient TelegramClient)
tgBot: tgClient,
albumBuffers: make(map[string]*pendingAlbum),
intakeBuffers: make(map[int64]*pendingIntake),
turns: make(map[int64]*chatTurn),
}
if tgClient == nil {
@@ -276,10 +280,48 @@ func (b *Bot) addMessageToChatMemory(chatMemory *ChatMemory, message Message) {
}
}
// prepareContextMessages converts chat memory into API messages.
func (b *Bot) prepareContextMessages(chatMemory *ChatMemory) []anthropic.BetaMessageParam {
b.chatMemoriesMu.RLock()
defer b.chatMemoriesMu.RUnlock()
return b.buildContextMessages(chatMemory)
}
// snapshotForTurn builds the model context for turn and records on it the
// newest user message that context includes, both read under one lock so they
// cannot disagree. The recorded id is what lets a later flush tell whether the
// running turn already answers it.
func (b *Bot) snapshotForTurn(turn *chatTurn, chatMemory *ChatMemory) []anthropic.BetaMessageParam {
b.chatMemoriesMu.RLock()
contextMessages := b.buildContextMessages(chatMemory)
covered := newestUserMessageID(chatMemory)
b.chatMemoriesMu.RUnlock()
b.markCovered(turn, covered)
return contextMessages
}
// newestUserMessageID returns the highest database id among the user messages
// in memory. Ids are monotonic, so "newer" and "higher" agree.
func newestUserMessageID(chatMemory *ChatMemory) uint {
var newest uint
for _, msg := range chatMemory.Messages {
if msg.IsUser && msg.ID > newest {
newest = msg.ID
}
}
return newest
}
func (b *Bot) newestUserMessageIDInChat(chatID int64) uint {
chatMemory := b.getOrCreateChatMemory(chatID)
b.chatMemoriesMu.RLock()
defer b.chatMemoriesMu.RUnlock()
return newestUserMessageID(chatMemory)
}
// buildContextMessages is prepareContextMessages without the lock.
func (b *Bot) buildContextMessages(chatMemory *ChatMemory) []anthropic.BetaMessageParam {
InfoLogger.Printf("Chat memory contains %d messages", len(chatMemory.Messages))
for i, msg := range chatMemory.Messages {
InfoLogger.Printf("Message %d: IsUser=%v, Text=%q Images=%d", i, msg.IsUser, msg.Text, len(msg.ImageFileIDs))
@@ -701,6 +743,7 @@ func (b *Bot) screenIncomingMessage(message *models.Message) (Message, error) {
}
userMessage := b.createMessage(message.Chat.ID, message.From.ID, message.From.Username, userRole, messageText, true)
userMessage.TelegramMessageID = message.ID
if message.Sticker != nil {
userMessage.StickerFileID = message.Sticker.FileID