fix(discord): Implement character-aware message splitting and soften memory warnings
This commit is contained in:
parent
5ac9a46f59
commit
e60728cf96
2 changed files with 71 additions and 67 deletions
|
|
@ -740,7 +740,7 @@ func (al *AgentLoop) maybeSummarize(sessionKey, channel, chatID string) {
|
|||
al.bus.PublishOutbound(bus.OutboundMessage{
|
||||
Channel: channel,
|
||||
ChatID: chatID,
|
||||
Content: "⚠️ Memory threshold reached. Optimizing conversation history...",
|
||||
Content: "🔄 Context optimized. (Memory threshold reached)",
|
||||
})
|
||||
}
|
||||
al.summarizeSession(sessionKey)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
|
|
@ -120,97 +119,102 @@ func (c *DiscordChannel) Send(ctx context.Context, msg bus.OutboundMessage) erro
|
|||
// splitMessage splits long messages into chunks, preserving code block integrity
|
||||
// Uses natural boundaries (newlines, spaces) and extends messages slightly to avoid breaking code blocks
|
||||
func splitMessage(content string, limit int) []string {
|
||||
var messages []string
|
||||
if limit > 1900 {
|
||||
limit = 1900
|
||||
}
|
||||
runes := []rune(content)
|
||||
if len(runes) <= limit {
|
||||
return []string{content}
|
||||
}
|
||||
|
||||
for len(content) > 0 {
|
||||
if len(content) <= limit {
|
||||
messages = append(messages, content)
|
||||
var chunks []string
|
||||
for len(runes) > 0 {
|
||||
if len(runes) <= limit {
|
||||
chunks = append(chunks, string(runes))
|
||||
break
|
||||
}
|
||||
|
||||
msgEnd := limit
|
||||
splitAt := limit
|
||||
|
||||
// Find natural split point within the limit
|
||||
msgEnd = findLastNewline(content[:limit], 200)
|
||||
if msgEnd <= 0 {
|
||||
msgEnd = findLastSpace(content[:limit], 100)
|
||||
}
|
||||
if msgEnd <= 0 {
|
||||
msgEnd = limit
|
||||
// Look for natural split points (newlines) in a window
|
||||
window := 300
|
||||
if splitAt < window {
|
||||
window = splitAt
|
||||
}
|
||||
|
||||
// Check if this would end with an incomplete code block
|
||||
candidate := content[:msgEnd]
|
||||
unclosedIdx := findLastUnclosedCodeBlock(candidate)
|
||||
foundNatural := false
|
||||
for i := splitAt - 1; i >= splitAt-window; i-- {
|
||||
if runes[i] == '\n' {
|
||||
splitAt = i + 1
|
||||
foundNatural = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if unclosedIdx >= 0 {
|
||||
// Message would end with incomplete code block
|
||||
// Try to extend to include the closing ``` (with some buffer)
|
||||
extendedLimit := limit + 500 // Allow 500 char buffer for code blocks
|
||||
if len(content) > extendedLimit {
|
||||
closingIdx := findNextClosingCodeBlock(content, msgEnd)
|
||||
if closingIdx > 0 && closingIdx <= extendedLimit {
|
||||
// Extend to include the closing ```
|
||||
msgEnd = closingIdx
|
||||
} else {
|
||||
// Can't find closing, split before the code block
|
||||
msgEnd = findLastNewline(content[:unclosedIdx], 200)
|
||||
if msgEnd <= 0 {
|
||||
msgEnd = findLastSpace(content[:unclosedIdx], 100)
|
||||
}
|
||||
if msgEnd <= 0 {
|
||||
msgEnd = unclosedIdx
|
||||
if !foundNatural {
|
||||
for i := splitAt - 1; i >= splitAt-window/2; i-- {
|
||||
if runes[i] == ' ' || runes[i] == '\t' {
|
||||
splitAt = i + 1
|
||||
foundNatural = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unclosed code blocks
|
||||
chunkCandidate := runes[:splitAt]
|
||||
if isInsideCodeBlock(chunkCandidate) {
|
||||
// Try to find the closing code block within extended limit
|
||||
extendedLimit := limit + 300
|
||||
if extendedLimit > 2000 {
|
||||
extendedLimit = 2000
|
||||
}
|
||||
|
||||
foundClosing := false
|
||||
for i := splitAt; i < len(runes)-2 && i < extendedLimit-3; i++ {
|
||||
if runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
|
||||
splitAt = i + 3
|
||||
foundClosing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundClosing {
|
||||
// Can't find closing within reasonable limit, split before the block
|
||||
for i := splitAt - 1; i >= 0; i-- {
|
||||
if i+2 < len(runes) && runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
|
||||
if i > 0 {
|
||||
splitAt = i
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Remaining content fits within extended limit
|
||||
msgEnd = len(content)
|
||||
}
|
||||
}
|
||||
|
||||
if msgEnd <= 0 {
|
||||
msgEnd = limit
|
||||
if splitAt <= 0 {
|
||||
splitAt = limit
|
||||
}
|
||||
|
||||
messages = append(messages, content[:msgEnd])
|
||||
content = strings.TrimSpace(content[msgEnd:])
|
||||
chunks = append(chunks, string(runes[:splitAt]))
|
||||
runes = runes[splitAt:]
|
||||
}
|
||||
|
||||
return messages
|
||||
return chunks
|
||||
}
|
||||
|
||||
// findLastUnclosedCodeBlock finds the last opening ``` that doesn't have a closing ```
|
||||
// Returns the position of the opening ``` or -1 if all code blocks are complete
|
||||
func findLastUnclosedCodeBlock(text string) int {
|
||||
func isInsideCodeBlock(runes []rune) bool {
|
||||
count := 0
|
||||
lastOpenIdx := -1
|
||||
|
||||
for i := 0; i < len(text); i++ {
|
||||
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
|
||||
if count == 0 {
|
||||
lastOpenIdx = i
|
||||
}
|
||||
for i := 0; i < len(runes)-2; i++ {
|
||||
if runes[i] == '`' && runes[i+1] == '`' && runes[i+2] == '`' {
|
||||
count++
|
||||
i += 2
|
||||
}
|
||||
}
|
||||
|
||||
// If odd number of ``` markers, last one is unclosed
|
||||
if count%2 == 1 {
|
||||
return lastOpenIdx
|
||||
}
|
||||
return -1
|
||||
return count%2 != 0
|
||||
}
|
||||
|
||||
// findNextClosingCodeBlock finds the next closing ``` starting from a position
|
||||
// Returns the position after the closing ``` or -1 if not found
|
||||
func findNextClosingCodeBlock(text string, startIdx int) int {
|
||||
for i := startIdx; i < len(text); i++ {
|
||||
if i+2 < len(text) && text[i] == '`' && text[i+1] == '`' && text[i+2] == '`' {
|
||||
return i + 3
|
||||
}
|
||||
}
|
||||
return -1
|
||||
func _unused_marker_() {
|
||||
}
|
||||
|
||||
// findLastNewline finds the last newline character within the last N characters
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue