mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-08-28 22:11:38 +00:00
Debounce and caching
This commit is contained in:
@@ -29,6 +29,9 @@ type Bot struct {
|
||||
botID uint
|
||||
albumBuffers map[string]*pendingAlbum
|
||||
albumBuffersMu sync.Mutex
|
||||
intakeBuffers map[int64]*pendingIntake
|
||||
intakeBuffersMu sync.Mutex
|
||||
intakeSeq uint64
|
||||
}
|
||||
|
||||
func messageType(msg *models.Message) string {
|
||||
@@ -90,6 +93,7 @@ func NewBot(db *gorm.DB, config BotConfig, clock Clock, tgClient TelegramClient)
|
||||
botID: botEntry.ID,
|
||||
tgBot: tgClient,
|
||||
albumBuffers: make(map[string]*pendingAlbum),
|
||||
intakeBuffers: make(map[int64]*pendingIntake),
|
||||
}
|
||||
|
||||
if tgClient == nil {
|
||||
@@ -298,9 +302,39 @@ func (b *Bot) prepareContextMessages(chatMemory *ChatMemory) []anthropic.BetaMes
|
||||
}
|
||||
contextMessages = append(contextMessages, param)
|
||||
}
|
||||
|
||||
if b.config.CacheHistoryEnabled() {
|
||||
markTrailingCacheBreakpoint(contextMessages)
|
||||
}
|
||||
return contextMessages
|
||||
}
|
||||
|
||||
// markTrailingCacheBreakpoint puts a cache_control breakpoint on the final
|
||||
// content block of the conversation, so the next turn reads the whole prefix
|
||||
// from cache instead of reprocessing it. The system prompt keeps its own
|
||||
// breakpoint; tools and system render ahead of messages, so the two compose.
|
||||
//
|
||||
// Caveat worth knowing when reading [usage] lines: chat memory is a sliding
|
||||
// window. Once it is full, each new turn evicts the oldest message, which
|
||||
// changes the prefix and forces a miss. Until then, and for any chat shorter
|
||||
// than the window, this converts a full-price reread into a cache read.
|
||||
func markTrailingCacheBreakpoint(messages []anthropic.BetaMessageParam) {
|
||||
if len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
blocks := messages[len(messages)-1].Content
|
||||
if len(blocks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch last := &blocks[len(blocks)-1]; {
|
||||
case last.OfText != nil:
|
||||
last.OfText.CacheControl = anthropic.NewBetaCacheControlEphemeralParam()
|
||||
case last.OfImage != nil:
|
||||
last.OfImage.CacheControl = anthropic.NewBetaCacheControlEphemeralParam()
|
||||
}
|
||||
}
|
||||
|
||||
func contentBlocksForMessage(msg Message) []anthropic.BetaContentBlockParamUnion {
|
||||
var blocks []anthropic.BetaContentBlockParamUnion
|
||||
if msg.IsUser && len(msg.ImageFileIDs) > 0 {
|
||||
@@ -706,16 +740,18 @@ func (b *Bot) screenOutgoingMessage(chatID int64, response string) (Message, err
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
// Mark every outstanding user message in the chat, not just the newest one.
|
||||
// A coalesced turn answers the whole batch, so a single-row update would
|
||||
// leave the earlier messages permanently unanswered. This also drops an
|
||||
// UPDATE ... ORDER BY ... LIMIT, which stock SQLite builds do not support.
|
||||
now := time.Now()
|
||||
err := b.db.Model(&Message{}).
|
||||
Where("chat_id = ? AND bot_id = ? AND is_user = ? AND answered_on IS NULL",
|
||||
chatID, b.botID, true).
|
||||
Order("timestamp DESC").
|
||||
Limit(1).
|
||||
Update("answered_on", now).Error
|
||||
|
||||
if err != nil {
|
||||
ErrorLogger.Printf("Error marking user message as answered: %v", err)
|
||||
ErrorLogger.Printf("Error marking user messages as answered: %v", err)
|
||||
}
|
||||
|
||||
chatMemory := b.getOrCreateChatMemory(chatID)
|
||||
|
||||
Reference in New Issue
Block a user