135 lines
3.5 KiB
Go
135 lines
3.5 KiB
Go
package store
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"database/sql"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"g.fc.run/ai/byre/internal/api"
|
||
|
|
)
|
||
|
|
|
||
|
|
func insertDefinition(ctx context.Context, tx *sql.Tx, def api.WorkflowDefinition) (string, error) {
|
||
|
|
id := NewID()
|
||
|
|
_, err := tx.ExecContext(ctx, `INSERT INTO workflow_definitions (id, description) VALUES (?, ?)`, id, def.Description)
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
next := 0
|
||
|
|
var insert func(steps []api.WorkflowStep, parent *int) error
|
||
|
|
insert = func(steps []api.WorkflowStep, parent *int) error {
|
||
|
|
for pos, s := range steps {
|
||
|
|
stepID := next
|
||
|
|
next++
|
||
|
|
kind, children := "stage", (*[]api.WorkflowStep)(nil)
|
||
|
|
switch {
|
||
|
|
case s.Order != nil:
|
||
|
|
kind, children = "order", s.Order
|
||
|
|
case s.Parallel != nil:
|
||
|
|
kind, children = "parallel", s.Parallel
|
||
|
|
}
|
||
|
|
_, err := tx.ExecContext(ctx, `INSERT INTO workflow_steps (definition_id, id, parent, position, kind, stage, model, prompt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||
|
|
id, stepID, parent, pos, kind, s.Stage, s.Model, s.Prompt)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if s.Agents != nil {
|
||
|
|
for i, a := range *s.Agents {
|
||
|
|
_, err = tx.ExecContext(ctx, `INSERT INTO workflow_agents (definition_id, step_id, position, name, model, prompt) VALUES (?, ?, ?, ?, ?, ?)`,
|
||
|
|
id, stepID, i, a.Name, a.Model, a.Prompt)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if children != nil {
|
||
|
|
err = insert(*children, &stepID)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return id, insert(def.Steps, nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
type stepRow struct {
|
||
|
|
id int
|
||
|
|
kind string
|
||
|
|
step api.WorkflowStep
|
||
|
|
}
|
||
|
|
|
||
|
|
func loadDefinition(ctx context.Context, q querier, id string) (api.WorkflowDefinition, error) {
|
||
|
|
def := api.WorkflowDefinition{}
|
||
|
|
err := q.QueryRowContext(ctx, `SELECT description FROM workflow_definitions WHERE id = ?`, id).Scan(&def.Description)
|
||
|
|
if errors.Is(err, sql.ErrNoRows) {
|
||
|
|
return def, fmt.Errorf("%w: no workflow definition %s", ErrNotFound, id)
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
|
||
|
|
agents := map[int][]api.TeamMember{}
|
||
|
|
rows, err := q.QueryContext(ctx, `SELECT step_id, name, model, prompt FROM workflow_agents WHERE definition_id = ? ORDER BY step_id, position`, id)
|
||
|
|
if err != nil {
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
for rows.Next() {
|
||
|
|
step := 0
|
||
|
|
a := api.TeamMember{}
|
||
|
|
err = rows.Scan(&step, &a.Name, &a.Model, &a.Prompt)
|
||
|
|
if err != nil {
|
||
|
|
rows.Close()
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
agents[step] = append(agents[step], a)
|
||
|
|
}
|
||
|
|
err = errors.Join(rows.Err(), rows.Close())
|
||
|
|
if err != nil {
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
|
||
|
|
children := map[int][]stepRow{}
|
||
|
|
rows, err = q.QueryContext(ctx, `SELECT id, coalesce(parent, -1), kind, stage, model, prompt FROM workflow_steps WHERE definition_id = ? ORDER BY coalesce(parent, -1), position`, id)
|
||
|
|
if err != nil {
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
for rows.Next() {
|
||
|
|
r := stepRow{}
|
||
|
|
parent := 0
|
||
|
|
err = rows.Scan(&r.id, &parent, &r.kind, &r.step.Stage, &r.step.Model, &r.step.Prompt)
|
||
|
|
if err != nil {
|
||
|
|
rows.Close()
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
if a, found := agents[r.id]; found {
|
||
|
|
r.step.Agents = &a
|
||
|
|
}
|
||
|
|
children[parent] = append(children[parent], r)
|
||
|
|
}
|
||
|
|
err = errors.Join(rows.Err(), rows.Close())
|
||
|
|
if err != nil {
|
||
|
|
return def, err
|
||
|
|
}
|
||
|
|
|
||
|
|
var build func(parent int) []api.WorkflowStep
|
||
|
|
build = func(parent int) []api.WorkflowStep {
|
||
|
|
steps := []api.WorkflowStep{}
|
||
|
|
for _, r := range children[parent] {
|
||
|
|
switch r.kind {
|
||
|
|
case "order":
|
||
|
|
sub := build(r.id)
|
||
|
|
r.step.Order = &sub
|
||
|
|
case "parallel":
|
||
|
|
sub := build(r.id)
|
||
|
|
r.step.Parallel = &sub
|
||
|
|
}
|
||
|
|
steps = append(steps, r.step)
|
||
|
|
}
|
||
|
|
return steps
|
||
|
|
}
|
||
|
|
def.Steps = build(-1)
|
||
|
|
return def, nil
|
||
|
|
}
|