Files
byre/internal/workflow/workflow.go
T

79 lines
1.8 KiB
Go
Raw Normal View History

package workflow
import (
"errors"
"fmt"
"strings"
"unicode"
"unicode/utf8"
"g.fc.run/misc/byre/internal/api"
)
var ErrInvalid = errors.New("invalid workflow")
const maxNameRunes = 100
func VendorOf(model string) api.AgentSessionVendor {
if strings.HasPrefix(model, "gpt-") {
return api.Codex
}
return api.Claude
}
func CleanName(name string) (string, error) {
name = strings.TrimSpace(name)
problem := nameProblem(name)
if problem != "" {
return "", fmt.Errorf("%w: workflow name %s", ErrInvalid, problem)
}
return name, nil
}
func nameProblem(name string) string {
switch {
case strings.TrimSpace(name) == "":
return "is empty"
case strings.TrimSpace(name) != name:
return fmt.Sprintf("%q has leading or trailing spaces", name)
case utf8.RuneCountInString(name) > maxNameRunes:
return fmt.Sprintf("%q is longer than %d characters", name, maxNameRunes)
case strings.ContainsFunc(name, unicode.IsControl):
return fmt.Sprintf("%q isn't a single line", name)
}
return ""
}
func Validate(def api.WorkflowDefinition) error {
problems := []string{}
if len(def.Stages) == 0 {
problems = append(problems, "it has no stages")
}
seen := map[string]bool{}
for i, s := range def.Stages {
where := fmt.Sprintf("stage %d (%q)", i+1, s.Name)
p := nameProblem(s.Name)
if p != "" {
problems = append(problems, fmt.Sprintf("%s: name %s", where, p))
}
if seen[s.Name] {
problems = append(problems, fmt.Sprintf("%s: another stage has the same name", where))
}
seen[s.Name] = true
if strings.TrimSpace(s.Model) == "" {
problems = append(problems, fmt.Sprintf("%s: model is required", where))
}
if strings.TrimSpace(s.Prompt) == "" {
problems = append(problems, fmt.Sprintf("%s: prompt is empty", where))
}
}
if len(problems) > 0 {
return fmt.Errorf("%w: %s", ErrInvalid, strings.Join(problems, "; "))
}
return nil
}