108 lines
4.3 KiB
Go
108 lines
4.3 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"sort"
|
|
|
|
"git.cin.su/m/Assistant/contracts/work"
|
|
)
|
|
|
|
var (
|
|
ErrGraphBudgetExceeded = errors.New("work graph budget exceeded")
|
|
ErrConflictUnresolved = errors.New("conflict resolution is not explicit")
|
|
ErrGraphSynthesisRejected = errors.New("graph synthesis rejected")
|
|
)
|
|
|
|
// BuildVerifiedGraphSynthesis resolves a conflict only after an independent
|
|
// verifier has evaluated all candidates. The verifier may not be any result
|
|
// producer, and every candidate remains in the returned lineage.
|
|
func BuildVerifiedGraphSynthesis(conflict work.ConflictSet, candidates []work.ResultCandidate, selectedCandidate string, verifierRunID work.AgentRunID, verifierIdentity string, evidence []work.EvidenceRef, summary string, verified bool) (work.ConflictSet, work.GraphSynthesisCandidate, error) {
|
|
if conflict.ID == "" || conflict.GraphID == "" || conflict.GraphRevision < 1 || conflict.NodeID == "" || conflict.Resolved || len(conflict.CandidateIDs) < 2 || selectedCandidate == "" || verifierRunID == "" || verifierIdentity == "" || len(evidence) == 0 || summary == "" || !verified {
|
|
return work.ConflictSet{}, work.GraphSynthesisCandidate{}, ErrGraphSynthesisRejected
|
|
}
|
|
candidateByID := make(map[string]work.ResultCandidate, len(candidates))
|
|
for _, candidate := range candidates {
|
|
if candidate.ID == "" || candidate.GraphID != conflict.GraphID || candidate.GraphRevision != conflict.GraphRevision || candidate.NodeID != conflict.NodeID || candidate.AuthorRunID == "" {
|
|
return work.ConflictSet{}, work.GraphSynthesisCandidate{}, ErrGraphSynthesisRejected
|
|
}
|
|
candidateByID[candidate.ID] = candidate
|
|
if candidate.AuthorRunID == verifierRunID {
|
|
return work.ConflictSet{}, work.GraphSynthesisCandidate{}, ErrGraphSynthesisRejected
|
|
}
|
|
}
|
|
ordered := append([]string(nil), conflict.CandidateIDs...)
|
|
for _, id := range ordered {
|
|
if _, ok := candidateByID[id]; !ok {
|
|
return work.ConflictSet{}, work.GraphSynthesisCandidate{}, ErrGraphSynthesisRejected
|
|
}
|
|
}
|
|
if !containsString(ordered, selectedCandidate) {
|
|
return work.ConflictSet{}, work.GraphSynthesisCandidate{}, ErrGraphSynthesisRejected
|
|
}
|
|
dissent := make([]string, 0, len(ordered)-1)
|
|
for _, id := range ordered {
|
|
if id != selectedCandidate {
|
|
dissent = append(dissent, id)
|
|
}
|
|
}
|
|
conflict.Resolved = true
|
|
synthesis := work.GraphSynthesisCandidate{ID: conflict.ID + ":synthesis", GraphID: conflict.GraphID, GraphRevision: conflict.GraphRevision, NodeID: conflict.NodeID, CandidateIDs: ordered, SelectedCandidateID: selectedCandidate, DissentCandidateIDs: dissent, VerifierRunID: verifierRunID, VerifierIdentity: verifierIdentity, EvidenceRefs: append([]work.EvidenceRef(nil), evidence...), Summary: summary, State: work.GraphSynthesisVerified}
|
|
return conflict, synthesis, nil
|
|
}
|
|
|
|
func containsString(values []string, target string) bool {
|
|
for _, value := range values {
|
|
if value == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func ScheduleReadyNodes(graph work.WorkGraph, active int) ([]work.GraphNodeID, error) {
|
|
if err := ValidateWorkGraph(graph); err != nil {
|
|
return nil, err
|
|
}
|
|
if active < 0 || active > graph.Budget.MaxParallel {
|
|
return nil, ErrGraphBudgetExceeded
|
|
}
|
|
ready, err := ReadyGraphNodes(graph)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
available := graph.Budget.MaxParallel - active
|
|
if len(ready) > available {
|
|
ready = ready[:available]
|
|
}
|
|
return ready, nil
|
|
}
|
|
|
|
func AdmitGraph(graph work.WorkGraph) (work.WorkGraph, error) {
|
|
if err := ValidateWorkGraph(graph); err != nil {
|
|
return work.WorkGraph{}, err
|
|
}
|
|
for _, node := range graph.Nodes {
|
|
if node.Attempts > graph.Budget.MaxAttempts {
|
|
return work.WorkGraph{}, ErrGraphBudgetExceeded
|
|
}
|
|
}
|
|
graph.State = work.GraphAdmitted
|
|
return graph, nil
|
|
}
|
|
|
|
func ResolveGraphConflict(conflict work.ConflictSet, selectedCandidate string) (work.ConflictSet, error) {
|
|
if conflict.ID == "" || conflict.GraphID == "" || conflict.GraphRevision < 1 || conflict.NodeID == "" || conflict.Resolved || selectedCandidate == "" {
|
|
return work.ConflictSet{}, ErrConflictUnresolved
|
|
}
|
|
candidates := append([]string(nil), conflict.CandidateIDs...)
|
|
sort.Strings(candidates)
|
|
for _, candidate := range candidates {
|
|
if candidate == selectedCandidate {
|
|
conflict.CandidateIDs = candidates
|
|
conflict.Resolved = true
|
|
return conflict, nil
|
|
}
|
|
}
|
|
return work.ConflictSet{}, ErrConflictUnresolved
|
|
}
|