Files
aego/tests/integration/agent_budget_test.go
sanspie 867efcf2f8
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 44m58s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 12m46s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: success
GitHub / darwin arm64 native GitHub-hosted native CI: success
GitHub / darwin amd64 native GitHub-hosted native CI: success
GitHub / linux amd64 native GitHub-hosted native CI: success
test: synchronize idle allocations and camera probe
2026-11-10 13:15:15 +03:00

99 lines
3.3 KiB
Go

package integration
import (
"strconv"
"strings"
"testing"
"time"
"aego/app/agentui/backend"
agentpanel "aego/editor/panels/agent"
)
// A long answer stays cheap: 500 messages with Markdown, code and tool steps cost the frame no more than the panel's budget, and an idle frame allocates nothing (11b5a).
func TestAgentPanelBudgetOn500Messages(t *testing.T) {
skipUnderRace(t)
var steps []backend.Step
for i := range 500 {
if i%5 == 4 {
steps = append(steps, backend.Step{Tool: "scene_get", Args: map[string]any{"file": bridgeScene, "node": "Player"}})
continue
}
steps = append(steps, backend.Step{Text: "### Step " + strconv.Itoa(i) + "\n\nThe player has **Motion 2D**; its `MaxSpeed` sets how fast it runs.\n\n```aes\nfunc Update(dt float) {\n\tm.Move(input.Axis(\"Move\"))\n}\n```\n" + strings.Repeat("A line of the answer that wraps in a narrow panel. ", 3)})
}
steps = append(steps, backend.Step{Text: "done"})
s, _ := agentSession(t, &backend.Fake{Turns: [][]backend.Step{steps}})
panel := s.App.Agent()
activePanel := func() bool {
tree := s.App.Workspace().Tree
n, ok := tree.Node(tree.FindPanel(agentpanel.ID))
if !ok {
return false
}
id, ok := n.ActivePanel()
return ok && id == agentpanel.ID
}
type frameTiming struct{ wall, cpu time.Duration }
frame := func(n int) frameTiming {
cpu := cpuTime()
start := time.Now()
for range n {
s.Step()
}
wall := time.Since(start) / time.Duration(n)
used := cpuTime()
if cpu != 0 && used >= cpu {
used = (used - cpu) / time.Duration(n)
} else {
used = 0 // CPU time is unavailable on some platforms.
}
return frameTiming{wall: wall, cpu: used}
}
for range 30 {
s.Step()
}
waitEditorSettled(t, s)
if !activePanel() {
t.Fatal("the empty Agent panel is not the active tab")
}
emptyTiming := frame(60)
empty := emptyTiming.wall
emptyAllocs := editorFrameAllocs(t, s, func() { s.Step() })
panel.Submit("explain the player")
stepUntil(t, s, func() bool { return !panel.Busy() })
for range 30 {
s.Step()
}
waitEditorSettled(t, s)
if !activePanel() {
t.Fatal("the Agent transcript is not the active tab")
}
fullTiming := frame(120)
full := fullTiming.wall
t.Logf("frame: %v with an empty panel, %v with 500 messages", empty, full)
if extra := full - empty; extra > 3*time.Millisecond {
t.Errorf("a 500-message transcript costs %v a frame over an empty panel (%v vs %v); the budget is 3 ms", extra, full, empty)
// Diagnostic only: the original wall-clock measurement above decides
// the result. CPU includes every process thread and may be unavailable.
t.Logf("process CPU per frame: empty %v, transcript %v (0 means unavailable)", emptyTiming.cpu, fullTiming.cpu)
var slowest time.Duration
var over3ms int
for range 120 {
start := time.Now()
s.Step()
d := time.Since(start)
slowest = max(slowest, d)
if d > 3*time.Millisecond {
over3ms++
}
}
t.Logf("subsequent 120 transcript frames: max %v, %d frames over 3 ms absolute", slowest, over3ms)
}
// a few allocations of noise are allowed; a transcript that allocates per message adds hundreds
a := editorFrameAllocs(t, s, func() { s.Step() })
t.Logf("allocations per frame: %v empty, %v with 500 messages", emptyAllocs, a)
if a > emptyAllocs+16 {
t.Errorf("an idle frame allocates %v times with a 500-message transcript, %v with an empty panel", a, emptyAllocs)
}
}