From e01ee65ab68d010afdfe7f61a725ba7dafd3718a Mon Sep 17 00:00:00 2001 From: Jon Langevin Date: Mon, 25 May 2026 03:27:53 -0400 Subject: [PATCH] feat: add core placement constraint evaluator --- protocol/types.go | 41 +++++++++++++++++++++++++++++ protocol/types_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/protocol/types.go b/protocol/types.go index 3c6221a..10c1e4f 100644 --- a/protocol/types.go +++ b/protocol/types.go @@ -1589,6 +1589,47 @@ type PlacementConstraints struct { StorageGuidance StorageGuidance `json:"storage_guidance,omitzero"` } +type PlacementCapabilities struct { + DiskBytes int64 `json:"disk_bytes,omitempty"` + MemoryBytes int64 `json:"memory_bytes,omitempty"` + BandwidthMbps int64 `json:"bandwidth_mbps,omitempty"` + IngressCapable bool `json:"ingress_capable,omitempty"` + CapabilityTags []string `json:"capability_tags,omitempty"` +} + +type PlacementNetworkPolicy struct { + AllowIngress bool `json:"allow_ingress,omitempty"` +} + +func PlacementConstraintsSatisfiedBy(c PlacementConstraints, caps PlacementCapabilities, task PlacementNetworkPolicy) error { + if c.IsZero() { + return nil + } + var errs []error + if c.MinDiskBytes > 0 && caps.DiskBytes < c.MinDiskBytes { + errs = append(errs, fmt.Errorf("worker disk_bytes %d below placement min_disk_bytes %d", caps.DiskBytes, c.MinDiskBytes)) + } + if c.MinMemoryBytes > 0 && caps.MemoryBytes < c.MinMemoryBytes { + errs = append(errs, fmt.Errorf("worker memory_bytes %d below placement min_memory_bytes %d", caps.MemoryBytes, c.MinMemoryBytes)) + } + if c.MinBandwidthMbps > 0 && caps.BandwidthMbps < c.MinBandwidthMbps { + errs = append(errs, fmt.Errorf("worker bandwidth_mbps %d below placement min_bandwidth_mbps %d", caps.BandwidthMbps, c.MinBandwidthMbps)) + } + if c.RequiresIngress && (!caps.IngressCapable || !task.AllowIngress) { + errs = append(errs, errors.New("worker/task does not satisfy placement ingress requirement")) + } + capabilityTags := map[string]struct{}{} + for _, tag := range caps.CapabilityTags { + capabilityTags[tag] = struct{}{} + } + for _, tag := range c.RequiredCapabilities { + if _, ok := capabilityTags[tag]; !ok { + errs = append(errs, fmt.Errorf("worker missing placement required capability %q", tag)) + } + } + return errors.Join(errs...) +} + func (c PlacementConstraints) Validate(required bool, target SettlementTarget) error { if c.IsZero() { if required { diff --git a/protocol/types_test.go b/protocol/types_test.go index 137f07a..e95b46c 100644 --- a/protocol/types_test.go +++ b/protocol/types_test.go @@ -240,6 +240,65 @@ func TestRuntimeAdapterContractRequiresServiceWorkloadForServiceAdapters(t *test } } +func TestPlacementConstraintsSatisfiedByEvaluatesWorkerAndTaskInputs(t *testing.T) { + constraints := protocol.PlacementConstraints{ + MinDiskBytes: 100, + MinMemoryBytes: 200, + MinBandwidthMbps: 300, + RequiresIngress: true, + RequiredCapabilities: []string{"gpu", "tee"}, + } + + err := protocol.PlacementConstraintsSatisfiedBy( + constraints, + protocol.PlacementCapabilities{ + DiskBytes: 50, + MemoryBytes: 150, + BandwidthMbps: 250, + CapabilityTags: []string{ + "gpu", + }, + }, + protocol.PlacementNetworkPolicy{}, + ) + if err == nil { + t.Fatal("expected insufficient placement inputs to fail") + } + for _, want := range []string{ + "disk_bytes", + "memory_bytes", + "bandwidth_mbps", + "ingress", + `required capability "tee"`, + } { + if !strings.Contains(err.Error(), want) { + t.Fatalf("expected placement error to contain %q, got %v", want, err) + } + } + + if err := protocol.PlacementConstraintsSatisfiedBy( + constraints, + protocol.PlacementCapabilities{ + DiskBytes: 100, + MemoryBytes: 200, + BandwidthMbps: 300, + IngressCapable: true, + CapabilityTags: []string{"gpu", "tee"}, + }, + protocol.PlacementNetworkPolicy{AllowIngress: true}, + ); err != nil { + t.Fatalf("expected placement inputs to satisfy constraints: %v", err) + } + + if err := protocol.PlacementConstraintsSatisfiedBy( + protocol.PlacementConstraints{}, + protocol.PlacementCapabilities{}, + protocol.PlacementNetworkPolicy{}, + ); err != nil { + t.Fatalf("zero constraints should be satisfied: %v", err) + } +} + func TestRuntimeDescriptorFallsBackToProviderNameAndDevVersion(t *testing.T) { ref := (protocol.RuntimeDescriptor{}).ExecutorRef("command")