77 lines
3.2 KiB
Go
77 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"git.cin.su/m/Assistant/contracts/capability"
|
|
)
|
|
|
|
const verifierIdentity = "deterministic-task-verifier-v1"
|
|
|
|
func main() {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"runtime":"task-verifier","status":"ok"}`))
|
|
})
|
|
mux.HandleFunc("POST /v1/task-evaluations/verify", verifyHandler)
|
|
addr := ":8091"
|
|
if value := strings.TrimSpace(os.Getenv("LISTEN_ADDR")); value != "" {
|
|
addr = value
|
|
}
|
|
if err := http.ListenAndServe(addr, mux); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func verifyHandler(w http.ResponseWriter, r *http.Request) {
|
|
var request capability.TaskCaseVerificationRequest
|
|
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 64<<10))
|
|
decoder.DisallowUnknownFields()
|
|
if err := decoder.Decode(&request); err != nil || request.CaseID == "" || request.ResponseText == "" || request.EvidenceArtifactRef == "" || request.LatencyMicros < 0 || (request.Verification.Mode != capability.TaskCaseVerificationExactText && request.Verification.Mode != capability.TaskCaseVerificationJSONFieldEquals) || (request.Verification.Mode == capability.TaskCaseVerificationJSONFieldEquals && request.Verification.FieldPath == "") || request.Verification.ExpectedText == "" || request.Verification.MinimumQualityMillis < 0 || request.Verification.MinimumQualityMillis > 1000 {
|
|
http.Error(w, "invalid task verification request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
matched := matches(request)
|
|
quality := int64(0)
|
|
result := capability.EvaluationUnsupported
|
|
if matched {
|
|
quality = request.Verification.MinimumQualityMillis
|
|
if quality == 0 {
|
|
quality = 1000
|
|
}
|
|
result = capability.EvaluationSupported
|
|
}
|
|
evidence := verificationEvidence(request, matched)
|
|
writeJSON(w, capability.TaskCaseVerificationResponse{CaseID: request.CaseID, Result: result, QualityScoreMillis: quality, LatencyMicros: request.LatencyMicros, EvidenceArtifactRef: request.EvidenceArtifactRef, VerificationEvidenceRef: evidence, VerifierIdentity: verifierIdentity})
|
|
}
|
|
|
|
func verificationEvidence(request capability.TaskCaseVerificationRequest, matched bool) string {
|
|
payload := fmt.Sprintf("%s|%s|%s|%s|%s|%t", verifierIdentity, request.CaseID, request.EvidenceArtifactRef, request.Verification.Mode, request.Verification.FieldPath, matched)
|
|
digest := sha256.Sum256([]byte(payload))
|
|
return "verification:" + hex.EncodeToString(digest[:])
|
|
}
|
|
|
|
func matches(request capability.TaskCaseVerificationRequest) bool {
|
|
if request.Verification.Mode == capability.TaskCaseVerificationExactText {
|
|
return strings.TrimSpace(request.ResponseText) == strings.TrimSpace(request.Verification.ExpectedText)
|
|
}
|
|
var document map[string]json.RawMessage
|
|
if json.Unmarshal([]byte(request.ResponseText), &document) != nil {
|
|
return false
|
|
}
|
|
value, ok := document[request.Verification.FieldPath]
|
|
return ok && strings.TrimSpace(string(value)) == strings.TrimSpace(request.Verification.ExpectedText)
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, value any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(value)
|
|
}
|