mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-17 02:16:53 +00:00
Apply Go 1.21+ min/max builtins and improved switch pattern matching across test files and utilities. No functional changes.
204 lines
6.2 KiB
Go
204 lines
6.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/providers"
|
|
)
|
|
|
|
// BenchmarkLimitHistoryTurns_200Messages_Limit20 benchmarks limiting history to last 20 user turns.
|
|
// Simulates a conversation with 200 messages (100 user + 100 assistant).
|
|
func BenchmarkLimitHistoryTurns_200Messages_Limit20(b *testing.B) {
|
|
msgs := makeHistoryMessages(100) // 100 user-assistant pairs = 200 messages
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
limitHistoryTurns(msgs, 20)
|
|
}
|
|
}
|
|
|
|
// BenchmarkLimitHistoryTurns_500Messages_Limit10 benchmarks limiting history to last 10 user turns.
|
|
// Simulates a longer conversation with 500 messages.
|
|
func BenchmarkLimitHistoryTurns_500Messages_Limit10(b *testing.B) {
|
|
msgs := makeHistoryMessages(250) // 250 user-assistant pairs = 500 messages
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
limitHistoryTurns(msgs, 10)
|
|
}
|
|
}
|
|
|
|
// BenchmarkLimitHistoryTurns_1000Messages_Limit5 benchmarks limiting history to last 5 user turns.
|
|
// Simulates a very long conversation with 1000 messages.
|
|
func BenchmarkLimitHistoryTurns_1000Messages_Limit5(b *testing.B) {
|
|
msgs := makeHistoryMessages(500) // 500 user-assistant pairs = 1000 messages
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
limitHistoryTurns(msgs, 5)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSanitizeHistory_Clean benchmarks sanitizing already-clean history.
|
|
// No orphaned messages, tool calls matched correctly.
|
|
func BenchmarkSanitizeHistory_Clean(b *testing.B) {
|
|
msgs := makeHistoryMessages(50) // 50 user-assistant pairs
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
sanitizeHistory(msgs)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSanitizeHistory_WithOrphaned benchmarks sanitizing history with orphaned tool messages.
|
|
// Common after history truncation.
|
|
func BenchmarkSanitizeHistory_WithOrphaned(b *testing.B) {
|
|
msgs := makeHistoryMessages(50)
|
|
// Insert orphaned tool messages at the start
|
|
msgs = append([]providers.Message{
|
|
{Role: "tool", ToolCallID: "orphan1", Content: "orphaned result 1"},
|
|
{Role: "tool", ToolCallID: "orphan2", Content: "orphaned result 2"},
|
|
{Role: "tool", ToolCallID: "orphan3", Content: "orphaned result 3"},
|
|
}, msgs...)
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
sanitizeHistory(msgs)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSanitizeHistory_WithToolCalls benchmarks sanitizing history with tool calls and results.
|
|
// Tests the tool call matching and deduplication logic.
|
|
func BenchmarkSanitizeHistory_WithToolCalls(b *testing.B) {
|
|
msgs := []providers.Message{
|
|
{Role: "user", Content: "Use the database tool"},
|
|
{Role: "assistant", Content: "I'll query the database", ToolCalls: []providers.ToolCall{
|
|
{ID: "call_123", Name: "database", Arguments: map[string]any{"query": "SELECT * FROM users"}},
|
|
}},
|
|
{Role: "tool", ToolCallID: "call_123", Content: "Results: 5 users"},
|
|
{Role: "user", Content: "What about the logs?"},
|
|
{Role: "assistant", Content: "Let me check the logs", ToolCalls: []providers.ToolCall{
|
|
{ID: "call_456", Name: "file_read", Arguments: map[string]any{"path": "/var/log/app.log"}},
|
|
}},
|
|
{Role: "tool", ToolCallID: "call_456", Content: "Log contents here"},
|
|
{Role: "user", Content: "Thanks"},
|
|
}
|
|
|
|
// Repeat to make it longer
|
|
for range 20 {
|
|
msgs = append(msgs, msgs[:7]...)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
sanitizeHistory(msgs)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSanitizeHistory_WithMissingToolResults benchmarks sanitizing history
|
|
// with missing tool results that need to be synthesized.
|
|
func BenchmarkSanitizeHistory_WithMissingToolResults(b *testing.B) {
|
|
msgs := []providers.Message{
|
|
{Role: "user", Content: "Use multiple tools"},
|
|
{Role: "assistant", Content: "I'll call multiple tools", ToolCalls: []providers.ToolCall{
|
|
{ID: "call_1", Name: "tool1", Arguments: map[string]any{"param": "value1"}},
|
|
{ID: "call_2", Name: "tool2", Arguments: map[string]any{"param": "value2"}},
|
|
{ID: "call_3", Name: "tool3", Arguments: map[string]any{"param": "value3"}},
|
|
}},
|
|
// Only return result for first tool call
|
|
{Role: "tool", ToolCallID: "call_1", Content: "Result 1"},
|
|
{Role: "user", Content: "What happened?"},
|
|
}
|
|
|
|
// Repeat to make it longer
|
|
for range 25 {
|
|
msgs = append(msgs, msgs[:5]...)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
sanitizeHistory(msgs)
|
|
}
|
|
}
|
|
|
|
// BenchmarkSanitizeHistory_ConsecutiveSameRole benchmarks merging consecutive same-role messages.
|
|
// Tests role alternation fixing.
|
|
func BenchmarkSanitizeHistory_ConsecutiveSameRole(b *testing.B) {
|
|
msgs := []providers.Message{
|
|
{Role: "user", Content: "First user message"},
|
|
{Role: "user", Content: "Second user message"},
|
|
{Role: "user", Content: "Third user message"},
|
|
{Role: "assistant", Content: "Response"},
|
|
{Role: "user", Content: "Next turn"},
|
|
{Role: "assistant", Content: "First assistant response"},
|
|
{Role: "assistant", Content: "Second assistant response"},
|
|
{Role: "user", Content: "Final user message"},
|
|
}
|
|
|
|
// Repeat to make it longer
|
|
for range 30 {
|
|
msgs = append(msgs, msgs[:8]...)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.ReportAllocs()
|
|
for i := 0; i < b.N; i++ {
|
|
sanitizeHistory(msgs)
|
|
}
|
|
}
|
|
|
|
// makeHistoryMessages creates a realistic conversation history with alternating user/assistant messages.
|
|
// Each pair includes content and some assistant messages have tool calls.
|
|
func makeHistoryMessages(pairs int) []providers.Message {
|
|
msgs := make([]providers.Message, 0, pairs*2)
|
|
content := strings.Repeat("message content ", 10)
|
|
|
|
for i := range pairs {
|
|
// Add user message
|
|
msgs = append(msgs, providers.Message{
|
|
Role: "user",
|
|
Content: content,
|
|
})
|
|
|
|
// Add assistant message (some with tool calls)
|
|
assistantMsg := providers.Message{
|
|
Role: "assistant",
|
|
Content: content,
|
|
}
|
|
|
|
if i%5 == 0 {
|
|
assistantMsg.ToolCalls = []providers.ToolCall{
|
|
{
|
|
ID: "call_" + string(rune('a'+i%26)),
|
|
Name: "tool_name",
|
|
Arguments: map[string]any{
|
|
"param1": "value1",
|
|
"param2": 42,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
msgs = append(msgs, assistantMsg)
|
|
|
|
// If assistant had tool calls, add tool result
|
|
if i%5 == 0 {
|
|
msgs = append(msgs, providers.Message{
|
|
Role: "tool",
|
|
ToolCallID: "call_" + string(rune('a'+i%26)),
|
|
Content: "Tool result content",
|
|
})
|
|
}
|
|
}
|
|
|
|
return msgs
|
|
}
|