Strengthen validation retries and checkpoint safety
This commit is contained in:
@@ -18,7 +18,7 @@ const (
|
||||
validationSkipped validationOutcome = "skipped"
|
||||
)
|
||||
|
||||
const defaultCorrectionGuidance = "Correct the candidate to satisfy the validator requirements."
|
||||
const correctionRequestIntroduction = "The previous response failed semantic validation. Return one complete corrected replacement response, not a patch, explanation, or commentary.\n\nCorrect all of the following:\n"
|
||||
|
||||
// validationRecord captures the settled result of one configured validator.
|
||||
// Its fields remain private so reports cannot expose mutable warning storage.
|
||||
@@ -82,33 +82,39 @@ func (report validationReport) FirstFailure() *validationRecord {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (report validationReport) CorrectionGuidance() string {
|
||||
func (report validationReport) CorrectionRequest() (string, error) {
|
||||
seen := make(map[string]struct{})
|
||||
parts := make([]string, 0, len(report.records))
|
||||
used := 0
|
||||
for _, record := range report.records {
|
||||
if record.outcome != validationRejected {
|
||||
continue
|
||||
}
|
||||
guidance := strings.TrimSpace(record.correctionGuidance)
|
||||
if guidance == "" {
|
||||
guidance = defaultCorrectionGuidance
|
||||
return "", fmt.Errorf("validator %q rejected output without correction guidance", record.validatorName)
|
||||
}
|
||||
if _, exists := seen[guidance]; exists {
|
||||
continue
|
||||
}
|
||||
separator := 0
|
||||
if len(parts) > 0 {
|
||||
separator = 1
|
||||
}
|
||||
if used+separator+len(guidance) > contracts.MaxCorrectionGuidanceBytes {
|
||||
break
|
||||
}
|
||||
seen[guidance] = struct{}{}
|
||||
parts = append(parts, guidance)
|
||||
used += separator + len(guidance)
|
||||
}
|
||||
return strings.Join(parts, "\n")
|
||||
if len(parts) == 0 {
|
||||
return "", errors.New("validation report contains no correction guidance")
|
||||
}
|
||||
var builder strings.Builder
|
||||
builder.WriteString(correctionRequestIntroduction)
|
||||
for index, guidance := range parts {
|
||||
fmt.Fprintf(&builder, "%d. %s", index+1, guidance)
|
||||
if index+1 < len(parts) {
|
||||
builder.WriteByte('\n')
|
||||
}
|
||||
}
|
||||
request := builder.String()
|
||||
if len(request) > contracts.MaxCorrectionGuidanceBytes {
|
||||
return "", fmt.Errorf("aggregate correction request exceeds maximum length of %d bytes", contracts.MaxCorrectionGuidanceBytes)
|
||||
}
|
||||
return request, nil
|
||||
}
|
||||
|
||||
type validationInvocation struct {
|
||||
@@ -184,15 +190,11 @@ func executeValidationChain(ctx context.Context, chain preparedValidatorChain, i
|
||||
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationApproved, attemptCount: attempt, warnings: cloneWarnings(invocation.result.Warnings), diagnosticPath: invocation.result.DiagnosticArtifactPath})
|
||||
break
|
||||
}
|
||||
reason := invocation.result.ReasonCode
|
||||
if reason == "" {
|
||||
reason = "output_rejected"
|
||||
}
|
||||
message := invocation.result.Message
|
||||
if message == "" {
|
||||
message = "output rejected"
|
||||
}
|
||||
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationRejected, attemptCount: attempt, reasonCode: reason, message: message, diagnosticPath: invocation.result.DiagnosticArtifactPath, warnings: cloneWarnings(invocation.result.Warnings), correctionGuidance: invocation.result.CorrectionGuidance})
|
||||
report.records = append(report.records, validationRecord{validatorName: binding.Module, outcome: validationRejected, attemptCount: attempt, reasonCode: invocation.result.ReasonCode, message: message, diagnosticPath: invocation.result.DiagnosticArtifactPath, warnings: cloneWarnings(invocation.result.Warnings), correctionGuidance: invocation.result.CorrectionGuidance})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user