Fail empty ideas jobs, clear ideas on failure, show each idea as it grounds
CI / ci (push) Successful in 5m24s
CI / ci (pull_request) Successful in 6m11s

- A run that keeps no idea fails with no_ideas_found instead of ending
  done with an empty list.
- Before an ideas job fails it republishes its result with no ideas, so
  GET /api/v1/jobs/{id} never shows ideas on a failed job, even ones a
  previous grounding round already published.
- Each idea is published as soon as its grounding finishes, placed in the
  model's order. recipes.GroundEach reports items as they finish;
  GroundMany is rebuilt on it with unchanged semantics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 19:22:51 -04:00
co-authored by Claude Opus 5.5
parent b566be7ddc
commit 4146f31275
6 changed files with 325 additions and 68 deletions
+18 -4
View File
@@ -292,7 +292,22 @@ type GroundResult struct {
// affect the others.
func (g *Grounder) GroundMany(ctx context.Context, reqs []GroundRequest) []GroundResult {
results := make([]GroundResult, len(reqs))
g.GroundEach(ctx, reqs, func(i int, r GroundResult) { results[i] = r })
return results
}
// GroundEach grounds each request as GroundMany does, but calls done with
// each item's index and result as soon as that item finishes, one call at
// a time. It returns once every item has finished and every call has
// returned.
func (g *Grounder) GroundEach(ctx context.Context, reqs []GroundRequest, done func(i int, r GroundResult)) {
slots := make(chan struct{}, GroundConcurrency)
var mu sync.Mutex
report := func(i int, r GroundResult) {
mu.Lock()
defer mu.Unlock()
done(i, r)
}
var wg sync.WaitGroup
for i, req := range reqs {
wg.Add(1)
@@ -301,16 +316,15 @@ func (g *Grounder) GroundMany(ctx context.Context, reqs []GroundRequest) []Groun
select {
case slots <- struct{}{}:
case <-ctx.Done():
results[i] = GroundResult{Err: ctx.Err()}
report(i, GroundResult{Err: ctx.Err()})
return
}
defer func() { <-slots }()
gr, err := g.Ground(ctx, req.DishName, req.Query)
results[i] = GroundResult{Grounded: gr, Err: err}
<-slots
report(i, GroundResult{Grounded: gr, Err: err})
}()
}
wg.Wait()
return results
}
// stored returns the grounding of the recipe stored for sourceURL, and