feat(t211): return SKU-matched image search candidates
This commit is contained in:
@@ -72,12 +72,20 @@ type ExecutionProvenance struct {
|
||||
}
|
||||
|
||||
type CandidateEvaluation struct {
|
||||
Decision string `json:"decision"`
|
||||
Score float64 `json:"score"`
|
||||
Matched []string `json:"matched"`
|
||||
MissingOrUncertain []string `json:"missing_or_uncertain"`
|
||||
RejectionReasons []string `json:"rejection_reasons"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
Decision string `json:"decision"`
|
||||
Score float64 `json:"score"`
|
||||
Matched []string `json:"matched"`
|
||||
MissingOrUncertain []string `json:"missing_or_uncertain"`
|
||||
RejectionReasons []string `json:"rejection_reasons"`
|
||||
Confidence float64 `json:"confidence"`
|
||||
HardConstraints []CandidateHardConstraintEvaluation `json:"hard_constraints"`
|
||||
}
|
||||
|
||||
type CandidateHardConstraintEvaluation struct {
|
||||
Kind string `json:"kind"`
|
||||
Expected string `json:"expected"`
|
||||
Status string `json:"status"`
|
||||
Evidence string `json:"evidence"`
|
||||
}
|
||||
|
||||
type ExecutionCandidate struct {
|
||||
@@ -523,11 +531,23 @@ func validateCandidateCommand(command StoreExecutionCandidatesCommand) error {
|
||||
} else if command.Provenance != nil {
|
||||
return executionResultInvalid("provenance", "must be omitted for MANUAL_FIRST")
|
||||
}
|
||||
strictSKUMatching := command.ExecutionMode == aiAssistedMode &&
|
||||
command.Provenance.SchemaVersion >= 2
|
||||
for index, candidate := range command.Candidates {
|
||||
if candidate.Ordinal != index+1 || !validCandidate(candidate, command.ExecutionMode) {
|
||||
if candidate.Ordinal != index+1 ||
|
||||
!validCandidate(candidate, command.ExecutionMode) ||
|
||||
(strictSKUMatching && !validSKUMatchedCandidate(candidate)) {
|
||||
return executionResultInvalid("candidates", "must be continuous, bounded observations")
|
||||
}
|
||||
}
|
||||
if strictSKUMatching && len(command.Candidates) > 0 &&
|
||||
(command.Recommendation == nil ||
|
||||
command.Recommendation.CandidateOrdinal != 1) {
|
||||
return executionResultInvalid(
|
||||
"recommendation",
|
||||
"must select the first sorted SKU-matched candidate",
|
||||
)
|
||||
}
|
||||
if command.Recommendation != nil {
|
||||
recommendation := command.Recommendation
|
||||
if recommendation.CandidateOrdinal < 1 ||
|
||||
@@ -607,7 +627,43 @@ func validEvaluation(value *CandidateEvaluation) bool {
|
||||
}
|
||||
return validStringList(value.Matched, 12, 160) &&
|
||||
validStringList(value.MissingOrUncertain, 12, 160) &&
|
||||
validStringList(value.RejectionReasons, 12, 160)
|
||||
validStringList(value.RejectionReasons, 12, 160) &&
|
||||
validCandidateHardConstraints(value.HardConstraints)
|
||||
}
|
||||
|
||||
func validSKUMatchedCandidate(candidate ExecutionCandidate) bool {
|
||||
value := candidate.Evaluation
|
||||
return value != nil &&
|
||||
value.Decision == "REVIEW" &&
|
||||
value.Score >= 0.75 &&
|
||||
value.Confidence >= 0.75 &&
|
||||
len(value.RejectionReasons) == 0 &&
|
||||
len(value.HardConstraints) == 2
|
||||
}
|
||||
|
||||
func validCandidateHardConstraints(
|
||||
values []CandidateHardConstraintEvaluation,
|
||||
) bool {
|
||||
if len(values) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(values) != 2 {
|
||||
return false
|
||||
}
|
||||
seen := map[string]struct{}{}
|
||||
for _, value := range values {
|
||||
if (value.Kind != "COLOR" && value.Kind != "SIZE") ||
|
||||
value.Status != "MATCH" ||
|
||||
!validAuditText(value.Expected, 128) ||
|
||||
!validAuditText(value.Evidence, 160) {
|
||||
return false
|
||||
}
|
||||
if _, duplicate := seen[value.Kind]; duplicate {
|
||||
return false
|
||||
}
|
||||
seen[value.Kind] = struct{}{}
|
||||
}
|
||||
return len(seen) == 2
|
||||
}
|
||||
|
||||
func validProvenance(value *ExecutionProvenance) bool {
|
||||
|
||||
Reference in New Issue
Block a user