89 lines
4.1 KiB
Go
89 lines
4.1 KiB
Go
package domain
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.cin.su/m/Assistant/contracts/capability"
|
|
"git.cin.su/m/Assistant/contracts/work"
|
|
)
|
|
|
|
var ErrInvalidTaskEvaluationGraph = errors.New("invalid task evaluation graph request")
|
|
|
|
// BuildTaskEvaluationGraph turns one bounded profile into durable specialist
|
|
// nodes. It deliberately has no provider, endpoint or hardware inputs: those
|
|
// are resolved later by the cognitive worker for each binding.
|
|
func BuildTaskEvaluationGraph(request work.TaskEvaluationGraphRequest) (work.WorkGraph, error) {
|
|
if request.GraphID == "" || request.IdempotencyPrefix == "" || len(request.BindingIDs) == 0 || len(request.BindingIDs) != len(request.DispatchIDs) || request.Budget.MaxParallel < 1 || request.Budget.MaxAttempts < 1 {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if err := request.Profile.Validate(); err != nil {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if len(request.Cases) != len(request.Profile.RequiredCaseIDs) {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
seenCases := make(map[string]struct{}, len(request.Cases))
|
|
for _, item := range request.Cases {
|
|
if item.CaseID == "" || item.Prompt == "" || item.EvidenceArtifactRef == "" {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if _, exists := seenCases[item.CaseID]; exists {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
seenCases[item.CaseID] = struct{}{}
|
|
}
|
|
for _, requiredID := range request.Profile.RequiredCaseIDs {
|
|
if _, exists := seenCases[requiredID]; !exists {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
}
|
|
if request.Profile.Stage == capability.TaskEvaluationGeneral {
|
|
if request.ParentGraphID != "" || request.ParentGraphRevision != 0 || request.ParentGraphState != "" || request.TriggerEvidenceRef != "" {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
} else {
|
|
if request.ParentGraphID == "" || request.ParentGraphRevision < 1 || request.TriggerEvidenceRef == "" || (request.ParentGraphState != work.GraphCompleted && request.ParentGraphState != work.GraphPartial) {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if request.Profile.ParentProfileID == "" {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
}
|
|
seenBindings := make(map[capability.CapabilityBindingID]struct{}, len(request.BindingIDs))
|
|
seenDispatches := make(map[string]struct{}, len(request.DispatchIDs))
|
|
nodes := make([]work.GraphNode, 0, len(request.BindingIDs))
|
|
for i, bindingID := range request.BindingIDs {
|
|
if bindingID == "" || request.DispatchIDs[i] == "" {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if _, ok := seenBindings[bindingID]; ok {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
if _, ok := seenDispatches[request.DispatchIDs[i]]; ok {
|
|
return work.WorkGraph{}, ErrInvalidTaskEvaluationGraph
|
|
}
|
|
seenBindings[bindingID] = struct{}{}
|
|
seenDispatches[request.DispatchIDs[i]] = struct{}{}
|
|
nodes = append(nodes, work.GraphNode{
|
|
ID: work.GraphNodeID(fmt.Sprintf("%s-node-%d", request.GraphID, i+1)),
|
|
DispatchID: request.DispatchIDs[i],
|
|
IdempotencyKey: fmt.Sprintf("%s:%s:%d", request.IdempotencyPrefix, bindingID, request.Profile.Revision),
|
|
State: work.GraphNodeBlocked,
|
|
Purpose: work.GraphNodePurposeTaskEvaluation,
|
|
TaskProfileID: request.Profile.ID,
|
|
BindingID: bindingID,
|
|
CaseIDs: append([]string(nil), request.Profile.RequiredCaseIDs...),
|
|
TaskProfile: cloneTaskEvaluationProfile(request.Profile),
|
|
TaskCases: append([]capability.TaskEvaluationCase(nil), request.Cases...),
|
|
})
|
|
}
|
|
return work.WorkGraph{ID: request.GraphID, Revision: 1, State: work.GraphDraft, Nodes: nodes, Budget: request.Budget, ParentGraphID: request.ParentGraphID, ParentGraphRevision: request.ParentGraphRevision, TriggerEvidenceRef: request.TriggerEvidenceRef}, nil
|
|
}
|
|
|
|
func cloneTaskEvaluationProfile(value capability.TaskEvaluationProfile) *capability.TaskEvaluationProfile {
|
|
copyValue := value
|
|
copyValue.RequiredCaseIDs = append([]string(nil), value.RequiredCaseIDs...)
|
|
return ©Value
|
|
}
|