Add sequential conversation coordinator

This commit is contained in:
2026-11-14 01:23:20 +00:00
parent c149f7274c
commit 5a6f73e0bb
3 changed files with 177 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
package usecase
import (
"context"
"go.maximumdirect.net/promptkit/internal/domain"
)
// ConversationTurn is one delivery to a coordinator callback.
type ConversationTurn[R any] struct {
Exchange *domain.Exchange
Result *R
RemainingTurns int
}
// CoordinationOutcome describes a normal coordinator termination.
type CoordinationOutcome string
const (
CoordinationStopped CoordinationOutcome = "stopped"
CoordinationExhausted CoordinationOutcome = "exhausted"
)
// CoordinationResult holds a normal outcome or a completed undelivered result.
type CoordinationResult[R any] struct {
Outcome CoordinationOutcome
Undelivered *R
}
// Coordinate advances one branch using the supplied callback and ordinary run.
func Coordinate[Q, R any](ctx context.Context, start *domain.Exchange, turns int,
next func(context.Context, ConversationTurn[R]) (*Q, error),
run func(context.Context, *domain.Exchange, *Q) (*R, *domain.Exchange, error),
) (CoordinationResult[R], error) {
current := start
var pending *R
for {
if err := ctx.Err(); err != nil {
return CoordinationResult[R]{Undelivered: pending}, err
}
request, err := next(ctx, ConversationTurn[R]{Exchange: current, Result: pending, RemainingTurns: turns})
pending = nil
if contextErr := ctx.Err(); contextErr != nil {
return CoordinationResult[R]{}, contextErr
}
if err != nil {
return CoordinationResult[R]{}, err
}
if request == nil {
return CoordinationResult[R]{Outcome: CoordinationStopped}, nil
}
if turns == 0 {
return CoordinationResult[R]{Outcome: CoordinationExhausted}, nil
}
if err := ctx.Err(); err != nil {
return CoordinationResult[R]{}, err
}
turns--
var nextExchange *domain.Exchange
pending, nextExchange, err = run(ctx, current, request)
if err != nil {
return CoordinationResult[R]{}, err
}
current = nextExchange
}
}