Skip to content

Commit 2e35bfe

Browse files
authored
Merge pull request #266 from kiwamizamurai/issue-199
feat: Add num_history_items for limiting agent conversation memory
2 parents 30765f3 + 4a13dbe commit 2e35bfe

9 files changed

Lines changed: 359 additions & 32 deletions

File tree

pkg/agent/agent.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Agent struct {
4040
addDate bool
4141
addEnvironmentInfo bool
4242
maxIterations int
43+
numHistoryItems int
4344
toolWrapper toolWrapper
4445
memoryManager memorymanager.Manager
4546
}
@@ -80,6 +81,10 @@ func (a *Agent) MaxIterations() int {
8081
return a.maxIterations
8182
}
8283

84+
func (a *Agent) NumHistoryItems() int {
85+
return a.numHistoryItems
86+
}
87+
8388
// Description returns the agent's description
8489
func (a *Agent) Description() string {
8590
return a.description

pkg/agent/opts.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,9 @@ func WithMaxIterations(maxIterations int) Opt {
7878
a.maxIterations = maxIterations
7979
}
8080
}
81+
82+
func WithNumHistoryItems(numHistoryItems int) Opt {
83+
return func(a *Agent) {
84+
a.numHistoryItems = numHistoryItems
85+
}
86+
}

pkg/config/v0/types.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,17 @@ func (t *TodoConfig) UnmarshalYAML(unmarshal func(any) error) error {
7777

7878
// AgentConfig represents a single agent configuration
7979
type AgentConfig struct {
80-
Name string `json:"name,omitempty" yaml:"name,omitempty"`
81-
Model string `json:"model,omitempty" yaml:"model,omitempty"`
82-
Description string `json:"description,omitempty" yaml:"description,omitempty"`
83-
Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"`
84-
Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"`
85-
SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"`
86-
AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"`
87-
Think bool `json:"think,omitempty" yaml:"think,omitempty"`
88-
Todo TodoConfig `json:"todo,omitempty" yaml:"todo,omitempty"`
89-
MemoryConfig MemoryConfig `json:"memory,omitempty" yaml:"memory,omitempty"`
80+
Name string `json:"name,omitempty" yaml:"name,omitempty"`
81+
Model string `json:"model,omitempty" yaml:"model,omitempty"`
82+
Description string `json:"description,omitempty" yaml:"description,omitempty"`
83+
Toolsets []Toolset `json:"toolsets,omitempty" yaml:"toolsets,omitempty"`
84+
Instruction string `json:"instruction,omitempty" yaml:"instruction,omitempty"`
85+
SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"`
86+
AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"`
87+
Think bool `json:"think,omitempty" yaml:"think,omitempty"`
88+
Todo TodoConfig `json:"todo,omitempty" yaml:"todo,omitempty"`
89+
MemoryConfig MemoryConfig `json:"memory,omitempty" yaml:"memory,omitempty"`
90+
NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"`
9091
}
9192

9293
type MemoryConfig struct {

pkg/config/v1/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ type AgentConfig struct {
110110
SubAgents []string `json:"sub_agents,omitempty" yaml:"sub_agents,omitempty"`
111111
AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"`
112112
AddEnvironmentInfo bool `json:"add_environment_info,omitempty" yaml:"add_environment_info,omitempty"`
113+
NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"`
113114
}
114115

115116
// ModelConfig represents the configuration for a model

pkg/config/v2/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type AgentConfig struct {
2323
AddDate bool `json:"add_date,omitempty" yaml:"add_date,omitempty"`
2424
AddEnvironmentInfo bool `json:"add_environment_info,omitempty" yaml:"add_environment_info,omitempty"`
2525
MaxIterations int `json:"max_iterations,omitempty" yaml:"max_iterations,omitempty"`
26+
NumHistoryItems int `json:"num_history_items,omitempty" yaml:"num_history_items,omitempty"`
2627
}
2728

2829
// ModelConfig represents the configuration for a model

pkg/session/session.go

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -275,13 +275,31 @@ func (s *Session) GetMessages(a *agent.Agent) []chat.Message {
275275
}
276276
}
277277

278-
trimmed := trimMessages(messages)
278+
maxItems := a.NumHistoryItems()
279+
if maxItems <= 0 {
280+
maxItems = maxMessages
281+
}
282+
283+
trimmed := trimMessages(messages, maxItems)
284+
285+
systemCount := 0
286+
conversationCount := 0
287+
for i := range trimmed {
288+
if trimmed[i].Role == chat.MessageRoleSystem {
289+
systemCount++
290+
} else {
291+
conversationCount++
292+
}
293+
}
279294

280295
slog.Debug("Retrieved messages for agent",
281296
"agent", a.Name(),
282297
"session_id", s.ID,
283298
"total_messages", len(messages),
284-
"trimmed_messages", len(trimmed))
299+
"trimmed_total", len(trimmed),
300+
"system_messages", systemCount,
301+
"conversation_messages", conversationCount,
302+
"max_history_items", maxItems)
285303

286304
return trimmed
287305
}
@@ -304,32 +322,51 @@ func (s *Session) GetMostRecentAgentFilename() string {
304322
}
305323

306324
// trimMessages ensures we don't exceed the maximum number of messages while maintaining
307-
// consistency between assistant messages and their tool call results
308-
func trimMessages(messages []chat.Message) []chat.Message {
309-
if len(messages) <= maxMessages {
325+
// consistency between assistant messages and their tool call results.
326+
// System messages are always preserved and not counted against the limit.
327+
func trimMessages(messages []chat.Message, maxItems int) []chat.Message {
328+
// Separate system messages from conversation messages
329+
var systemMessages []chat.Message
330+
var conversationMessages []chat.Message
331+
332+
for i := range messages {
333+
if messages[i].Role == chat.MessageRoleSystem {
334+
systemMessages = append(systemMessages, messages[i])
335+
} else {
336+
conversationMessages = append(conversationMessages, messages[i])
337+
}
338+
}
339+
340+
// If conversation messages fit within limit, return all messages
341+
if len(conversationMessages) <= maxItems {
310342
return messages
311343
}
312344

313345
// Keep track of tool call IDs that need to be removed
314346
toolCallsToRemove := make(map[string]bool)
315347

316-
// Calculate how many messages we need to remove
317-
toRemove := len(messages) - maxMessages
348+
// Calculate how many conversation messages we need to remove
349+
toRemove := len(conversationMessages) - maxItems
318350

319351
// Start from the beginning (oldest messages)
320352
for i := range toRemove {
321353
// If this is an assistant message with tool calls, mark them for removal
322-
if messages[i].Role == chat.MessageRoleAssistant {
323-
for _, toolCall := range messages[i].ToolCalls {
354+
if conversationMessages[i].Role == chat.MessageRoleAssistant {
355+
for _, toolCall := range conversationMessages[i].ToolCalls {
324356
toolCallsToRemove[toolCall.ID] = true
325357
}
326358
}
327359
}
328360

329-
// Filter messages keeping only those we want to keep
330-
result := make([]chat.Message, 0, maxMessages)
331-
for i := toRemove; i < len(messages); i++ {
332-
msg := messages[i]
361+
// Combine system messages with trimmed conversation messages
362+
result := make([]chat.Message, 0, len(systemMessages)+maxItems)
363+
364+
// Add all system messages first
365+
result = append(result, systemMessages...)
366+
367+
// Add the most recent conversation messages
368+
for i := toRemove; i < len(conversationMessages); i++ {
369+
msg := conversationMessages[i]
333370

334371
// Skip tool messages that correspond to removed assistant messages
335372
if msg.Role == chat.MessageRoleTool && toolCallsToRemove[msg.ToolCallID] {

0 commit comments

Comments
 (0)