compact: retain newest ~20% of context; refactor startup into internal/app

- Replace fixed 6-message compact retention with ratio-based logic:
  retain ~20% of compactable estimated tokens, with a 6-message floor
  and user-turn boundary alignment
- Add compactRetainedTokenBudget, compactRecentStart, compactTurnBoundary
  helpers in internal/agent/agent.go
- Add TestAgentCompactKeepsTwentyPercentRecentContext
- Move startup/session/repl/bootstrap logic from root into internal/app
  so main.go is a thin binary entry point (15 lines)
- Update /compact docs in README.md
This commit is contained in:
loveuer
2026-06-24 23:59:21 -07:00
parent 9abd5c3b5f
commit dabf5bfecc
9 changed files with 408 additions and 224 deletions
+101
View File
@@ -0,0 +1,101 @@
package app
import (
"context"
"path/filepath"
"testing"
"agentu/internal/agent"
"agentu/internal/session"
"agentu/pkg/config"
"agentu/pkg/llm"
)
type startupProvider struct{}
func (p *startupProvider) ChatStream(ctx context.Context, req llm.ChatRequest, emit func(llm.StreamEvent) error) error {
return emit(llm.StreamEvent{Content: "ok"})
}
func startupTestManager(t *testing.T) (*session.Manager, *agent.Agent) {
t.Helper()
cfg := &config.Config{
Providers: map[string]config.ProviderConfig{
"one": {
Name: "one",
BaseURL: "https://one.example.com",
APIKey: "sk-test",
Model: "model-a",
Models: []string{"model-a"},
Thinking: "none",
ThinkingParam: "thinking",
},
},
}
assistant := agent.New(agent.Options{
Provider: &startupProvider{},
ProviderName: "one",
Model: "model-a",
})
store, err := session.NewStore(filepath.Join(t.TempDir(), "sessions"))
if err != nil {
t.Fatal(err)
}
manager, err := session.NewManager(cfg, assistant, nil, store)
if err != nil {
t.Fatal(err)
}
return manager, assistant
}
func TestInitializeSessionStartsNewByDefault(t *testing.T) {
manager, assistant := startupTestManager(t)
if _, err := manager.NewSession(); err != nil {
t.Fatalf("new session: %v", err)
}
savedID := manager.CurrentSessionID()
assistant.SetMessages([]llm.Message{{Role: llm.RoleUser, Content: "old context"}})
if err := manager.Save(); err != nil {
t.Fatalf("save: %v", err)
}
if err := initializeSession(manager, ""); err != nil {
t.Fatalf("initialize: %v", err)
}
if manager.CurrentSessionID() == savedID {
t.Fatalf("default startup resumed latest session %s", savedID)
}
for _, msg := range assistant.Messages() {
if msg.Content == "old context" {
t.Fatal("default startup carried old context into new session")
}
}
}
func TestInitializeSessionResumesExplicitID(t *testing.T) {
manager, assistant := startupTestManager(t)
if _, err := manager.NewSession(); err != nil {
t.Fatalf("new session: %v", err)
}
savedID := manager.CurrentSessionID()
assistant.SetMessages([]llm.Message{{Role: llm.RoleUser, Content: "old context"}})
if err := manager.Save(); err != nil {
t.Fatalf("save: %v", err)
}
if err := initializeSession(manager, savedID); err != nil {
t.Fatalf("initialize: %v", err)
}
if manager.CurrentSessionID() != savedID {
t.Fatalf("expected resumed session %s, got %s", savedID, manager.CurrentSessionID())
}
found := false
for _, msg := range assistant.Messages() {
if msg.Content == "old context" {
found = true
}
}
if !found {
t.Fatal("explicit resume did not restore saved context")
}
}