Add council review wait command
This commit is contained in:
@@ -2,9 +2,11 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var councilReviewerRoles = []string{
|
||||
@@ -53,6 +55,18 @@ type CouncilStartResult struct {
|
||||
Reviewers []CouncilReviewer `json:"reviewers"`
|
||||
}
|
||||
|
||||
type CouncilWaitInput struct {
|
||||
RunID string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
type CouncilWaitResult struct {
|
||||
Woke bool `json:"woke"`
|
||||
RunID string `json:"run_id"`
|
||||
AllComplete bool `json:"all_complete"`
|
||||
ReviewerStatuses []CouncilReviewer `json:"reviewers"`
|
||||
}
|
||||
|
||||
func (s *OrchStore) StartCouncil(ctx context.Context, input CouncilStartInput) (CouncilStartResult, error) {
|
||||
runID := strings.TrimSpace(input.RunID)
|
||||
if runID == "" {
|
||||
@@ -442,3 +456,168 @@ func boolToInt(value bool) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *OrchStore) WaitForCouncil(ctx context.Context, input CouncilWaitInput) (CouncilWaitResult, error) {
|
||||
runID := strings.TrimSpace(input.RunID)
|
||||
if runID == "" {
|
||||
return CouncilWaitResult{}, fmt.Errorf("%w: run id is required", ErrInvalidInput)
|
||||
}
|
||||
|
||||
if _, err := s.GetCouncilRun(ctx, runID); err != nil {
|
||||
return CouncilWaitResult{}, err
|
||||
}
|
||||
|
||||
waitCtx := ctx
|
||||
cancel := func() {}
|
||||
if input.Timeout > 0 {
|
||||
waitCtx, cancel = context.WithTimeout(ctx, input.Timeout)
|
||||
}
|
||||
defer cancel()
|
||||
|
||||
for {
|
||||
reviewers, allComplete, err := s.GetCouncilReviewerStatuses(waitCtx, runID)
|
||||
if err != nil {
|
||||
if isDeadlineExceeded(waitCtx) {
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
return CouncilWaitResult{}, err
|
||||
}
|
||||
if allComplete {
|
||||
return CouncilWaitResult{
|
||||
Woke: true,
|
||||
RunID: runID,
|
||||
AllComplete: true,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if _, err := s.ReconcileRun(waitCtx, runID); err != nil {
|
||||
if isSQLiteBusyError(err) {
|
||||
ok, waitErr := waitForNextPoll(waitCtx, 25*time.Millisecond)
|
||||
if waitErr != nil {
|
||||
if errors.Is(waitErr, context.DeadlineExceeded) {
|
||||
reviewers, _, _ := s.GetCouncilReviewerStatuses(ctx, runID)
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
return CouncilWaitResult{}, waitErr
|
||||
}
|
||||
if !ok {
|
||||
reviewers, _, _ := s.GetCouncilReviewerStatuses(ctx, runID)
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isDeadlineExceeded(waitCtx) {
|
||||
reviewers, _, _ := s.GetCouncilReviewerStatuses(ctx, runID)
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
return CouncilWaitResult{}, err
|
||||
}
|
||||
|
||||
ok, err := waitForNextPoll(waitCtx, 200*time.Millisecond)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
reviewers, _, _ := s.GetCouncilReviewerStatuses(ctx, runID)
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
return CouncilWaitResult{}, err
|
||||
}
|
||||
if !ok {
|
||||
reviewers, _, _ := s.GetCouncilReviewerStatuses(ctx, runID)
|
||||
return CouncilWaitResult{
|
||||
Woke: false,
|
||||
RunID: runID,
|
||||
AllComplete: false,
|
||||
ReviewerStatuses: reviewers,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OrchStore) GetCouncilRun(ctx context.Context, runID string) (CouncilRun, error) {
|
||||
row := s.db.QueryRowContext(
|
||||
ctx,
|
||||
`SELECT run_id, mode, target_type, output_mode, only_unanimous
|
||||
FROM council_runs
|
||||
WHERE run_id = ?`,
|
||||
runID,
|
||||
)
|
||||
|
||||
var (
|
||||
run CouncilRun
|
||||
onlyUnanimous int
|
||||
)
|
||||
err := row.Scan(&run.RunID, &run.Mode, &run.TargetType, &run.OutputMode, &onlyUnanimous)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return CouncilRun{}, fmt.Errorf("%w: council run %s not found", ErrRunNotFound, runID)
|
||||
}
|
||||
if err != nil {
|
||||
return CouncilRun{}, fmt.Errorf("scan council run: %w", err)
|
||||
}
|
||||
run.OnlyUnanimous = onlyUnanimous != 0
|
||||
return run, nil
|
||||
}
|
||||
|
||||
func (s *OrchStore) GetCouncilReviewerStatuses(ctx context.Context, runID string) ([]CouncilReviewer, bool, error) {
|
||||
rows, err := s.db.QueryContext(
|
||||
ctx,
|
||||
`SELECT cr.reviewer_role, cr.task_id, t.status
|
||||
FROM council_reviewers cr
|
||||
JOIN tasks t
|
||||
ON t.run_id = cr.run_id
|
||||
AND t.task_id = cr.task_id
|
||||
WHERE cr.run_id = ?
|
||||
ORDER BY cr.reviewer_role ASC`,
|
||||
runID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("query council reviewer statuses: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
reviewers := make([]CouncilReviewer, 0, len(councilReviewerRoles))
|
||||
allComplete := true
|
||||
for rows.Next() {
|
||||
var reviewer CouncilReviewer
|
||||
if err := rows.Scan(&reviewer.ReviewerRole, &reviewer.TaskID, &reviewer.Status); err != nil {
|
||||
return nil, false, fmt.Errorf("scan council reviewer status: %w", err)
|
||||
}
|
||||
if reviewer.Status != "done" && reviewer.Status != "failed" && reviewer.Status != "cancelled" {
|
||||
allComplete = false
|
||||
}
|
||||
reviewers = append(reviewers, reviewer)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, false, fmt.Errorf("iterate council reviewer statuses: %w", err)
|
||||
}
|
||||
if len(reviewers) == 0 {
|
||||
return nil, false, fmt.Errorf("%w: council reviewers for run %s not found", ErrRunNotFound, runID)
|
||||
}
|
||||
|
||||
return reviewers, allComplete, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user