fix: make forceCompression snap cut point to tool pair boundaries
The midpoint calculation now walks forward past tool_result messages to avoid splitting an assistant+tool_calls from its tool_results.
This commit is contained in:
parent
3d1d480975
commit
04d047fd37
2 changed files with 82 additions and 0 deletions
|
|
@ -811,6 +811,16 @@ func (al *AgentLoop) forceCompression(agent *AgentInstance, sessionKey string) {
|
|||
// Helper to find the mid-point of the conversation
|
||||
mid := len(conversation) / 2
|
||||
|
||||
// Snap the cut point forward past any tool_result messages so we don't
|
||||
// separate them from their assistant+tool_calls header.
|
||||
for mid < len(conversation) && conversation[mid].Role == "tool" {
|
||||
mid++
|
||||
}
|
||||
// Safety: if we walked past the end, step back to keep at least 1 message
|
||||
if mid >= len(conversation) {
|
||||
mid = len(conversation) - 1
|
||||
}
|
||||
|
||||
// New history structure:
|
||||
// 1. System Prompt (with compression note appended)
|
||||
// 2. Second half of conversation
|
||||
|
|
|
|||
|
|
@ -858,6 +858,78 @@ func TestHandleCommand_New(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestForceCompression_PreservesToolPairs(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
Model: "test-model",
|
||||
MaxTokens: 4096,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msgBus := bus.NewMessageBus()
|
||||
provider := &mockProvider{}
|
||||
al := NewAgentLoop(cfg, msgBus, provider)
|
||||
agent := al.registry.GetDefaultAgent()
|
||||
if agent == nil {
|
||||
t.Fatal("No default agent found")
|
||||
}
|
||||
sessionKey := "agent:main:test-compression"
|
||||
agent.Sessions.GetOrCreate(sessionKey)
|
||||
|
||||
// Build: system, user, assistant+tool, tool_result, tool_result, user, assistant, user
|
||||
// = 8 messages. Conversation (indices 1-6) has 6 msgs, mid=3 lands on
|
||||
// the second tool_result, splitting it from its assistant+tool_calls.
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "system", Content: "sys"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "user", Content: "q1"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{
|
||||
Role: "assistant", Content: "a1",
|
||||
ToolCalls: []providers.ToolCall{{ID: "c1", Name: "exec"}, {ID: "c2", Name: "read"}},
|
||||
})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "tool", Content: "r1", ToolCallID: "c1"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "tool", Content: "r2", ToolCallID: "c2"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "user", Content: "q2"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "assistant", Content: "a2"})
|
||||
agent.Sessions.AddFullMessage(sessionKey, providers.Message{Role: "user", Content: "q3"})
|
||||
|
||||
al.forceCompression(agent, sessionKey)
|
||||
|
||||
history := agent.Sessions.GetHistory(sessionKey)
|
||||
|
||||
toolCallIDs := map[string]bool{}
|
||||
toolResultIDs := map[string]bool{}
|
||||
for _, m := range history {
|
||||
for _, tc := range m.ToolCalls {
|
||||
if tc.ID != "" {
|
||||
toolCallIDs[tc.ID] = true
|
||||
}
|
||||
}
|
||||
if m.Role == "tool" && m.ToolCallID != "" {
|
||||
toolResultIDs[m.ToolCallID] = true
|
||||
}
|
||||
}
|
||||
|
||||
for id := range toolResultIDs {
|
||||
if !toolCallIDs[id] {
|
||||
t.Errorf("orphaned tool_result %q after forceCompression", id)
|
||||
}
|
||||
}
|
||||
for id := range toolCallIDs {
|
||||
if !toolResultIDs[id] {
|
||||
t.Errorf("orphaned tool_call %q after forceCompression", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleCommand_Status(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue