Store workflow definitions as table rows and drop YAML
This commit is contained in:
@@ -1,107 +0,0 @@
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"g.fc.run/ai/byre/internal/api"
|
||||
)
|
||||
|
||||
var keyOrder = []string{"description", "kind", "steps", "stage", "name", "model", "prompt", "agents", "order", "parallel"}
|
||||
|
||||
func Parse(data []byte) (api.WorkflowDefinition, error) {
|
||||
raw := any(nil)
|
||||
err := yaml.Unmarshal(data, &raw)
|
||||
if err != nil {
|
||||
return api.WorkflowDefinition{}, fmt.Errorf("%w: %v", ErrInvalid, err)
|
||||
}
|
||||
j, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return api.WorkflowDefinition{}, fmt.Errorf("%w: %v", ErrInvalid, err)
|
||||
}
|
||||
|
||||
def := api.WorkflowDefinition{}
|
||||
dec := json.NewDecoder(bytes.NewReader(j))
|
||||
dec.DisallowUnknownFields()
|
||||
err = dec.Decode(&def)
|
||||
if err != nil {
|
||||
return api.WorkflowDefinition{}, fmt.Errorf("%w: %v", ErrInvalid, err)
|
||||
}
|
||||
return def, Validate(def)
|
||||
}
|
||||
|
||||
func Format(def api.WorkflowDefinition) ([]byte, error) {
|
||||
j, err := json.Marshal(def)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc := yaml.Node{}
|
||||
err = yaml.Unmarshal(j, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restyle(&doc)
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
enc := yaml.NewEncoder(out)
|
||||
enc.SetIndent(2)
|
||||
err = enc.Encode(&doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = enc.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func Filename(name string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if strings.ContainsRune(`/\:*?"<>|`, r) {
|
||||
return '_'
|
||||
}
|
||||
return r
|
||||
}, name) + ".yaml"
|
||||
}
|
||||
|
||||
func restyle(n *yaml.Node) {
|
||||
switch n.Kind {
|
||||
case yaml.ScalarNode:
|
||||
n.Style = 0
|
||||
if n.Tag == "!!str" && strings.Contains(n.Value, "\n") {
|
||||
n.Style = yaml.LiteralStyle
|
||||
}
|
||||
case yaml.MappingNode:
|
||||
n.Style = 0
|
||||
pairs := [][2]*yaml.Node{}
|
||||
for i := 0; i+1 < len(n.Content); i += 2 {
|
||||
pairs = append(pairs, [2]*yaml.Node{n.Content[i], n.Content[i+1]})
|
||||
}
|
||||
slices.SortStableFunc(pairs, func(a, b [2]*yaml.Node) int {
|
||||
return keyRank(a[0].Value) - keyRank(b[0].Value)
|
||||
})
|
||||
n.Content = n.Content[:0]
|
||||
for _, p := range pairs {
|
||||
n.Content = append(n.Content, p[0], p[1])
|
||||
}
|
||||
case yaml.SequenceNode:
|
||||
n.Style = 0
|
||||
}
|
||||
for _, c := range n.Content {
|
||||
restyle(c)
|
||||
}
|
||||
}
|
||||
|
||||
func keyRank(key string) int {
|
||||
i := slices.Index(keyOrder, key)
|
||||
if i < 0 {
|
||||
return len(keyOrder)
|
||||
}
|
||||
return i
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package workflow
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"g.fc.run/ai/byre/internal/api"
|
||||
)
|
||||
|
||||
func Parse(data []byte) (api.WorkflowDefinition, error) {
|
||||
def := api.WorkflowDefinition{}
|
||||
dec := json.NewDecoder(bytes.NewReader(data))
|
||||
dec.DisallowUnknownFields()
|
||||
err := dec.Decode(&def)
|
||||
if err != nil {
|
||||
return api.WorkflowDefinition{}, fmt.Errorf("%w: %v", ErrInvalid, err)
|
||||
}
|
||||
return def, Validate(def)
|
||||
}
|
||||
Reference in New Issue
Block a user