Keep observer composition storage proportional to selected callbacks
This commit is contained in:
@@ -140,7 +140,13 @@ statically. Do not add public allocation guarantees or exact-allocation tests.
|
|||||||
|
|
||||||
## 3. Size Observer Storage For Selected Callbacks
|
## 3. Size Observer Storage For Selected Callbacks
|
||||||
|
|
||||||
**Priority:** P3. **Status:** For consideration. **Plan stage:** 9.
|
**Priority:** P3. **Status:** Complete. **Plan stage:** 9.
|
||||||
|
|
||||||
|
**Completion:** Observer composition counts selected callbacks before allocating,
|
||||||
|
sizes retained storage to that count, and handles zero/single selections without
|
||||||
|
a fan-out closure. Sparse-selection ownership and duplicate-delivery tests pass.
|
||||||
|
The complete offline race suite, maintained examples, vet, and diff checks passed
|
||||||
|
with Go 1.27.3 after all remediations.
|
||||||
|
|
||||||
**Location:** [`Compose`](../../internal/observation/compose.go).
|
**Location:** [`Compose`](../../internal/observation/compose.go).
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,26 @@ package observation
|
|||||||
|
|
||||||
// Compose copies and synchronously invokes non-nil observers in order.
|
// Compose copies and synchronously invokes non-nil observers in order.
|
||||||
func Compose[T any, F ~func(T)](observers ...F) F {
|
func Compose[T any, F ~func(T)](observers ...F) F {
|
||||||
selected := make([]F, 0, len(observers))
|
count := 0
|
||||||
|
var single F
|
||||||
|
for _, observer := range observers {
|
||||||
|
if observer != nil {
|
||||||
|
count++
|
||||||
|
single = observer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if count == 1 {
|
||||||
|
return single
|
||||||
|
}
|
||||||
|
selected := make([]F, 0, count)
|
||||||
for _, observer := range observers {
|
for _, observer := range observers {
|
||||||
if observer != nil {
|
if observer != nil {
|
||||||
selected = append(selected, observer)
|
selected = append(selected, observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(selected) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return func(event T) {
|
return func(event T) {
|
||||||
for _, observer := range selected {
|
for _, observer := range selected {
|
||||||
observer(event)
|
observer(event)
|
||||||
|
|||||||
@@ -31,6 +31,31 @@ func TestComposeObserversCopiesAndOrders(t *testing.T) {
|
|||||||
promptkit.ComposeObservers(func(promptkit.ExecutionEvent) { panic("panic") }, second)(promptkit.ExecutionEvent{})
|
promptkit.ComposeObservers(func(promptkit.ExecutionEvent) { panic("panic") }, second)(promptkit.ExecutionEvent{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestComposeObserversSparseSelection(t *testing.T) {
|
||||||
|
for _, selected := range []int{0, 1, 2} {
|
||||||
|
calls := 0
|
||||||
|
observers := make([]promptkit.ExecutionObserver, 1024)
|
||||||
|
callback := func(promptkit.ExecutionEvent) { calls++ }
|
||||||
|
for index := 0; index < selected; index++ {
|
||||||
|
observers[len(observers)-1-index] = callback
|
||||||
|
}
|
||||||
|
composed := promptkit.ComposeObservers(observers...)
|
||||||
|
for index := range observers {
|
||||||
|
observers[index] = func(promptkit.ExecutionEvent) { calls += 100 }
|
||||||
|
}
|
||||||
|
if selected == 0 {
|
||||||
|
if composed != nil {
|
||||||
|
t.Fatal("all-nil composition must be nil")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
composed(promptkit.ExecutionEvent{})
|
||||||
|
if calls != selected {
|
||||||
|
t.Fatalf("selected %d callbacks, got %d calls", selected, calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCoordinateObserverCompositionAndNestedSelection(t *testing.T) {
|
func TestCoordinateObserverCompositionAndNestedSelection(t *testing.T) {
|
||||||
engine := newSimpleEngine(t, "question", promptkit.WithLLMClient(&fakeLLMClient{response: &promptkit.GenerateResponse{Content: "answer"}}))
|
engine := newSimpleEngine(t, "question", promptkit.WithLLMClient(&fakeLLMClient{response: &promptkit.GenerateResponse{Content: "answer"}}))
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|||||||
Reference in New Issue
Block a user