• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

smallnest / langgraphgo / 28917420249

08 Jul 2026 04:25AM UTC coverage: 58.269% (-1.2%) from 59.456%
28917420249

push

github

chaoyuepan
fix: update showcases submodule to valid remote HEAD (#97)

The showcases gitlink pointed to 8ecb63e which no longer exists on the
remote, causing GitHub's /tree/<sha> submodule link to 404. Repoint to
the current remote master HEAD (4c7faed).

10743 of 18437 relevant lines covered (58.27%)

124.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.66
/memory/buffer.go
1
package memory
2

3
import (
4
        "context"
5
        "slices"
6
        "sync"
7
)
8

9
// BufferMemory is a simple buffer-based memory implementation
10
// Similar to LangChain's ConversationBufferMemory
11
// Combines sliding window with optional summarization
12
type BufferMemory struct {
13
        messages      []*Message
14
        maxMessages   int  // 0 = unlimited
15
        maxTokens     int  // 0 = unlimited
16
        autoSummarize bool // Auto-summarize when limits exceeded
17
        mu            sync.RWMutex
18

19
        // Optional summarizer
20
        Summarizer func(ctx context.Context, messages []*Message) (string, error)
21
}
22

23
// BufferConfig holds configuration for buffer memory
24
type BufferConfig struct {
25
        MaxMessages   int  // Maximum number of messages (0 = unlimited)
26
        MaxTokens     int  // Maximum total tokens (0 = unlimited)
27
        AutoSummarize bool // Enable automatic summarization
28
        Summarizer    func(ctx context.Context, messages []*Message) (string, error)
29
}
30

31
// NewBufferMemory creates a new buffer memory
32
func NewBufferMemory(config *BufferConfig) *BufferMemory {
1✔
33
        if config == nil {
1✔
34
                config = &BufferConfig{
×
35
                        MaxMessages:   0, // Unlimited by default
×
36
                        MaxTokens:     0,
×
37
                        AutoSummarize: false,
×
38
                }
×
39
        }
×
40

41
        summarizer := config.Summarizer
1✔
42
        if summarizer == nil {
2✔
43
                summarizer = defaultSummarizer
1✔
44
        }
1✔
45

46
        return &BufferMemory{
1✔
47
                messages:      make([]*Message, 0),
1✔
48
                maxMessages:   config.MaxMessages,
1✔
49
                maxTokens:     config.MaxTokens,
1✔
50
                autoSummarize: config.AutoSummarize,
1✔
51
                Summarizer:    summarizer,
1✔
52
        }
1✔
53
}
54

55
// AddMessage adds a message to the buffer
56
func (b *BufferMemory) AddMessage(ctx context.Context, msg *Message) error {
3✔
57
        b.mu.Lock()
3✔
58
        defer b.mu.Unlock()
3✔
59

3✔
60
        b.messages = append(b.messages, msg)
3✔
61

3✔
62
        // Check limits and trim if necessary
3✔
63
        if b.maxMessages > 0 && len(b.messages) > b.maxMessages {
4✔
64
                if b.autoSummarize {
1✔
65
                        // Summarize oldest messages
×
66
                        toSummarize := b.messages[:len(b.messages)-b.maxMessages]
×
67
                        summary, err := b.Summarizer(ctx, toSummarize)
×
68
                        if err == nil {
×
69
                                summaryMsg := NewMessage("system", summary)
×
70
                                b.messages = append([]*Message{summaryMsg}, b.messages[len(b.messages)-b.maxMessages:]...)
×
71
                        } else {
×
72
                                // Fallback: just trim
×
73
                                b.messages = b.messages[len(b.messages)-b.maxMessages:]
×
74
                        }
×
75
                } else {
1✔
76
                        // Simple trim
1✔
77
                        b.messages = b.messages[len(b.messages)-b.maxMessages:]
1✔
78
                }
1✔
79
        }
80

81
        // Check token limit
82
        if b.maxTokens > 0 {
3✔
83
                totalTokens := 0
×
84
                for i, v := range slices.Backward(b.messages) {
×
85
                        totalTokens += v.TokenCount
×
86
                        if totalTokens > b.maxTokens {
×
87
                                // Remove oldest messages
×
88
                                if b.autoSummarize && i > 0 {
×
89
                                        toSummarize := b.messages[:i]
×
90
                                        summary, err := b.Summarizer(ctx, toSummarize)
×
91
                                        if err == nil {
×
92
                                                summaryMsg := NewMessage("system", summary)
×
93
                                                b.messages = append([]*Message{summaryMsg}, b.messages[i:]...)
×
94
                                        } else {
×
95
                                                b.messages = b.messages[i:]
×
96
                                        }
×
97
                                } else {
×
98
                                        b.messages = b.messages[i:]
×
99
                                }
×
100
                                break
×
101
                        }
102
                }
103
        }
104

105
        return nil
3✔
106
}
107

108
// GetContext returns all messages in the buffer
109
func (b *BufferMemory) GetContext(ctx context.Context, query string) ([]*Message, error) {
2✔
110
        b.mu.RLock()
2✔
111
        defer b.mu.RUnlock()
2✔
112

2✔
113
        result := make([]*Message, len(b.messages))
2✔
114
        copy(result, b.messages)
2✔
115
        return result, nil
2✔
116
}
2✔
117

118
// Clear removes all messages
119
func (b *BufferMemory) Clear(ctx context.Context) error {
×
120
        b.mu.Lock()
×
121
        defer b.mu.Unlock()
×
122

×
123
        b.messages = make([]*Message, 0)
×
124
        return nil
×
125
}
×
126

127
// GetStats returns buffer memory statistics
128
func (b *BufferMemory) GetStats(ctx context.Context) (*Stats, error) {
×
129
        b.mu.RLock()
×
130
        defer b.mu.RUnlock()
×
131

×
132
        totalTokens := 0
×
133
        for _, msg := range b.messages {
×
134
                totalTokens += msg.TokenCount
×
135
        }
×
136

137
        return &Stats{
×
138
                TotalMessages:   len(b.messages),
×
139
                TotalTokens:     totalTokens,
×
140
                ActiveMessages:  len(b.messages),
×
141
                ActiveTokens:    totalTokens,
×
142
                CompressionRate: 1.0,
×
143
        }, nil
×
144
}
145

146
// GetMessages returns a copy of all messages
147
func (b *BufferMemory) GetMessages() []*Message {
1✔
148
        b.mu.RLock()
1✔
149
        defer b.mu.RUnlock()
1✔
150

1✔
151
        result := make([]*Message, len(b.messages))
1✔
152
        copy(result, b.messages)
1✔
153
        return result
1✔
154
}
1✔
155

156
// LoadMessages loads messages into the buffer (replaces existing)
157
func (b *BufferMemory) LoadMessages(messages []*Message) {
1✔
158
        b.mu.Lock()
1✔
159
        defer b.mu.Unlock()
1✔
160

1✔
161
        b.messages = make([]*Message, len(messages))
1✔
162
        copy(b.messages, messages)
1✔
163
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc