Avoid copying output during basic validation

This commit is contained in:
2026-10-09 00:13:23 +00:00
parent 05fe13d80b
commit ed7c27324d
3 changed files with 45 additions and 24 deletions

View File

@@ -151,7 +151,8 @@ control into the rendered prompt. The
`internal/validate` provides validators backed by an operating-system `internal/validate` provides validators backed by an operating-system
filesystem or an `fs.FS`. Invalid generated content is returned as a validation filesystem or an `fs.FS`. Invalid generated content is returned as a validation
result; inability to load, register, or compile a schema is an operational result; inability to load, register, or compile a schema is an operational
error. error. Basic validation checks body emptiness on the byte slice directly
instead of first making a response-sized string copy.
Every preparation operation creates one operation-local validation plan. None, Every preparation operation creates one operation-local validation plan. None,
basic, and JSON modes retain the effective output contract without source basic, and JSON modes retain the effective output contract without source

View File

@@ -188,7 +188,7 @@ func validateArtifact(ctx context.Context, artifact *domain.Artifact, contract d
res.IsValid = true res.IsValid = true
return res, nil return res, nil
case domain.ValidationBasic: case domain.ValidationBasic:
empty := strings.TrimSpace(string(artifact.Body)) == "" empty := len(bytes.TrimSpace(artifact.Body)) == 0
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return domain.ValidationResult{}, err return domain.ValidationResult{}, err
} }

View File

@@ -36,8 +36,15 @@ func TestStandardValidatorNoneSkipped(t *testing.T) {
func TestStandardValidatorBasicSuccess(t *testing.T) { func TestStandardValidatorBasicSuccess(t *testing.T) {
v := NewStandardValidator("") v := NewStandardValidator("")
for _, tc := range []struct {
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte("hello")}, domain.OutputContract{ name string
body []byte
}{
{name: "text", body: []byte("hello")},
{name: "invalid UTF-8 byte", body: []byte{0xff}},
} {
t.Run(tc.name, func(t *testing.T) {
res, err := v.Validate(context.Background(), &domain.Artifact{Body: tc.body}, domain.OutputContract{
ValidationMode: domain.ValidationBasic, ValidationMode: domain.ValidationBasic,
}) })
if err != nil { if err != nil {
@@ -46,12 +53,22 @@ func TestStandardValidatorBasicSuccess(t *testing.T) {
if res.Status != domain.ValidationPassed || !res.IsValid { if res.Status != domain.ValidationPassed || !res.IsValid {
t.Fatalf("expected passed/valid, got status=%q valid=%v", res.Status, res.IsValid) t.Fatalf("expected passed/valid, got status=%q valid=%v", res.Status, res.IsValid)
} }
})
}
} }
func TestStandardValidatorBasicFailureEmpty(t *testing.T) { func TestStandardValidatorBasicFailureEmpty(t *testing.T) {
v := NewStandardValidator("") v := NewStandardValidator("")
for _, tc := range []struct {
res, err := v.Validate(context.Background(), &domain.Artifact{Body: []byte(" \n\t ")}, domain.OutputContract{ name string
body []byte
}{
{name: "empty bytes", body: []byte{}},
{name: "ASCII whitespace", body: []byte(" \n\t ")},
{name: "Unicode whitespace", body: []byte("\u2003\u00a0\u3000")},
} {
t.Run(tc.name, func(t *testing.T) {
res, err := v.Validate(context.Background(), &domain.Artifact{Body: tc.body}, domain.OutputContract{
ValidationMode: domain.ValidationBasic, ValidationMode: domain.ValidationBasic,
}) })
if err != nil { if err != nil {
@@ -63,6 +80,8 @@ func TestStandardValidatorBasicFailureEmpty(t *testing.T) {
if len(res.Errors) == 0 { if len(res.Errors) == 0 {
t.Fatal("expected validation errors") t.Fatal("expected validation errors")
} }
})
}
} }
func TestStandardValidatorReportsZeroRepairAttempts(t *testing.T) { func TestStandardValidatorReportsZeroRepairAttempts(t *testing.T) {
@@ -947,7 +966,8 @@ func BenchmarkBasicValidation(b *testing.B) {
} }
b.ReportAllocs() b.ReportAllocs()
b.SetBytes(int64(len(benchmark.body))) // Nonempty input can be classified from its boundary bytes without
// scanning the body, so report latency rather than bytes per second.
b.ResetTimer() b.ResetTimer()
for range b.N { for range b.N {
result, err := validator.Validate(ctx, artifact, contract) result, err := validator.Validate(ctx, artifact, contract)