mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-09 13:31:26 -10:00
* fix(agent): stop the tool-schema ratchet meaning a different thing per host TestEagerToolSchemaTokenBudget counted exec_command shell guidance along with the schemas, and that guidance is host-specific. The same tree measured 3498 on Linux, about 3600 on a Windows host with pwsh 7, and 3653 on Windows PowerShell 5.1, because shellGuidanceForRuntime appends a longer block for 5.1 (it has to explain that && and || do not work there). 5.1 is stock Windows, and that case has been over the 3650 ceiling since view_image landed. So `go test ./...` failed for anyone on a stock Windows box while CI stayed green: the runners have pwsh 7 and take the cheaper branch. The workflow does run the tests on windows-latest, so this was never a coverage gap, just a ceiling sized against whichever shell the runner happened to have. The two halves are now charged separately. The schema ceiling measures the schemas alone and reads 3498 on Linux and on Windows PowerShell 5.1, so it ratchets the same thing everywhere. The host guidance gets its own ceiling at 200, above the worst case rather than above this machine: 5.1 is 155 tokens, pwsh 7 is 110, cmd.exe is 87, POSIX is 0. No guidance text was trimmed. It is what stops the model writing bash on Windows, and the 5.1 note is the reason a chained command does not silently do half its work. Paying 155 tokens for it is the right trade; measuring it as if it were a schema was not. HostExecCommandShellGuidance is exported for the budget, which is production surface added for a test. It is one accessor over existing logic, and the alternative is a ratchet that cannot tell the two costs apart. A guard in internal/tools pins that the guidance really is the tail of the description, so the subtraction cannot silently start measuring the wrong thing. * test(tools,agent): make the guidance contract checkable off Windows The ratchet separates the fixed tool schemas from this host's exec_command shell guidance, and both halves of that separation were only being checked on Windows. The description test returned early off Windows once the accessor reported no guidance, so it never looked at the description. Dropping the GOOS condition in the constructor would have a POSIX host append its own guidance (about 150 characters on darwin) with the accessor still reporting none and the ratchet charging every byte of it to the schemas, and nothing would fail. Both the description and the accessor now take goos, so the contract is checked for all three platforms from any one of them. The budget also went from subtracting a separately rounded guidance estimate to removing the guidance from the description before estimating. ApproxTextTokens is an integer division, so floor((schema+guidance)/4) - floor(guidance/4) is one token high whenever the two remainders add past four, and which side of that a host lands on depends on the length of its guidance. That is the same host-dependence the ratchet was being fixed for, one token smaller. No behaviour change: exec_command's description is byte for byte what it was on every platform.
84 lines
4.2 KiB
Go
84 lines
4.2 KiB
Go
package tools
|
|
|
|
import (
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// THE BUDGET TEST REMOVES THIS TEXT, SO IT HAS TO BE EXACTLY WHAT IS ADDED.
|
|
//
|
|
// internal/agent's eager-schema ratchet charges the tool schemas and this
|
|
// host's shell guidance separately, and it separates them by stripping
|
|
// HostExecCommandShellGuidance off exec_command's description before
|
|
// estimating. That is only valid while the guidance is appended verbatim as the
|
|
// tail of that description, and only while a platform that reports no guidance
|
|
// really appends none. If the two drift apart the ratchet keeps reporting a
|
|
// number, just not the one it claims.
|
|
//
|
|
// Checked for all three platforms rather than for the running one: the
|
|
// appending is itself conditional on GOOS, so a host-only check is the shape of
|
|
// bug this pins. Off Windows it would pass while reporting nothing at all.
|
|
func TestExecCommandDescriptionMatchesReportedGuidance(t *testing.T) {
|
|
for _, goos := range []string{"windows", "linux", "darwin"} {
|
|
t.Run(goos, func(t *testing.T) {
|
|
description := execCommandDescription(goos)
|
|
guidance := hostExecCommandShellGuidance(goos)
|
|
|
|
if goos == "windows" {
|
|
if guidance == "" {
|
|
t.Fatal("exec_command appends shell guidance on Windows, but the accessor reports none; the budget would charge it to the schemas")
|
|
}
|
|
if !strings.HasSuffix(description, guidance) {
|
|
t.Fatalf("the guidance is not the tail of the description, so removing it from the schema total is wrong.\nguidance: %q\ndescription tail: %q",
|
|
guidance, description[max(0, len(description)-len(guidance)-40):])
|
|
}
|
|
} else if guidance != "" {
|
|
t.Fatalf("no shell guidance is appended off Windows, but the accessor reported %q", guidance)
|
|
}
|
|
|
|
// Whatever guidance THIS platform would produce, none of it may be
|
|
// left in the description once the reported guidance is removed.
|
|
// Off Windows the accessor reports nothing, so this is the check
|
|
// that the description really carries nothing: drop the GOOS
|
|
// condition in execCommandDescription and a POSIX host starts
|
|
// appending its own guidance (about 150 characters on darwin) with
|
|
// the ratchet still charging every byte of it to the schemas.
|
|
platformGuidance := shellGuidanceForGOOS(goos)
|
|
if platformGuidance == "" {
|
|
t.Fatalf("SETUP INVALID: %s produces no shell guidance at all, so the check below cannot fail", goos)
|
|
}
|
|
if remainder := strings.TrimSuffix(description, guidance); strings.Contains(remainder, platformGuidance) {
|
|
t.Fatalf("exec_command's description still carries %s shell guidance after removing the %d characters the accessor reports, so the ratchet charges the rest to the tool schemas.\nremainder: %q",
|
|
goos, len(guidance), remainder)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// And the running host agrees with the table above, so the exported accessor
|
|
// and the real constructor cannot drift away from the platform contract.
|
|
func TestHostExecCommandShellGuidanceMatchesTheDescription(t *testing.T) {
|
|
tool := NewExecCommandTool(t.TempDir(), nil)
|
|
if got, want := tool.Description(), execCommandDescription(runtime.GOOS); got != want {
|
|
t.Fatalf("the constructed tool's description is not what the platform contract says.\ngot: %q\nwant: %q", got, want)
|
|
}
|
|
if got, want := HostExecCommandShellGuidance(), hostExecCommandShellGuidance(runtime.GOOS); got != want {
|
|
t.Fatalf("the exported accessor disagrees with the platform contract.\ngot: %q\nwant: %q", got, want)
|
|
}
|
|
}
|
|
|
|
// And the guidance a Windows host gets is never empty, whichever shell was
|
|
// detected, since an empty one would mean the model gets no syntax rule at all.
|
|
func TestWindowsShellGuidanceIsNeverEmpty(t *testing.T) {
|
|
for _, shell := range []shellRuntime{
|
|
{GOOS: "windows", Executable: `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe`, Kind: shellKindPowerShell, Syntax: "PowerShell"},
|
|
{GOOS: "windows", Executable: `C:\Program Files\PowerShell\7\pwsh.exe`, Kind: shellKindPowerShell, Syntax: "PowerShell"},
|
|
{GOOS: "windows", Executable: `C:\Windows\System32\cmd.exe`, Kind: shellKindCmd, Syntax: "cmd.exe"},
|
|
} {
|
|
if guidance := shellGuidanceForRuntime(shell); strings.TrimSpace(guidance) == "" {
|
|
t.Errorf("%s produced no shell guidance", shell.Executable)
|
|
}
|
|
}
|
|
}
|