mirror of
https://github.com/mudler/cogito.git
synced 2026-07-23 10:25:44 -04:00
b4d85ec71b
The tool-execution loops run `for range o.maxAttempts`, so maxAttempts=0 iterated zero times: the tool was never called and an EMPTY result was handed back to the model with no error and no warning. A caller that forwards an unset config field (e.g. AgentOptions.MaxAttempts) straight into WithMaxAttempts hits this — the model then flies blind and hallucinates, with nothing in the logs to explain why. Clamp anything below 1 to 1 (the documented default single attempt). Found while smoke-testing nib's browser tool: the agent got empty tool results because the test harness set MaxRetries but not MaxAttempts. Real callers that set MaxAttempts>0 (e.g. Dante = 3) were unaffected, but the silent-no-op is a footgun worth removing at the option boundary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8Z5dz1nVHAQUpjb5pgGCZ
27 lines
746 B
Go
27 lines
746 B
Go
package cogito
|
|
|
|
import "testing"
|
|
|
|
// TestWithMaxAttemptsClampsBelowOne locks the fix for a silent tool-execution
|
|
// no-op: the tool-run loops iterate `for range o.maxAttempts`, so a maxAttempts
|
|
// of 0 (a caller forwarding an unset config field into WithMaxAttempts) would
|
|
// run the tool zero times and hand the model an empty result with no error.
|
|
// WithMaxAttempts must clamp anything below 1 up to 1.
|
|
func TestWithMaxAttemptsClampsBelowOne(t *testing.T) {
|
|
cases := []struct {
|
|
in, want int
|
|
}{
|
|
{0, 1},
|
|
{-5, 1},
|
|
{1, 1},
|
|
{3, 3},
|
|
}
|
|
for _, c := range cases {
|
|
o := &Options{}
|
|
WithMaxAttempts(c.in)(o)
|
|
if o.maxAttempts != c.want {
|
|
t.Errorf("WithMaxAttempts(%d): maxAttempts=%d, want %d", c.in, o.maxAttempts, c.want)
|
|
}
|
|
}
|
|
}
|