Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions cmd/internal/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,19 @@ func ConvertConfig(raw []byte) ([]byte, error) {
encoder := yaml.NewEncoder(&buf)

v1keys := []string{"sources", "authServices", "embeddingModels", "tools", "toolsets", "prompts"}
docIndex := 0
for {
if err := decoder.Decode(&input); err != nil {
if err == io.EOF {
break
}
return nil, err
}
docIndex++
for _, item := range input {
key, ok := item.Key.(string)
if !ok {
return nil, fmt.Errorf("unexpected non-string key in input: %v", item.Key)
return nil, fmt.Errorf("doc %d: unexpected non-string key in input: %v", docIndex, item.Key)
}
// check if the key is config file v1's key
if slices.Contains(v1keys, key) {
Expand All @@ -163,7 +165,7 @@ func ConvertConfig(raw []byte) ([]byte, error) {
}
transformed, err := transformDocs(key, slice)
if err != nil {
return nil, err
return nil, fmt.Errorf("doc %d: invalid v1 config at key %q: %w; if you intended v2 format, each doc needs kind + name", docIndex, key, err)
}
// encode per-doc
for _, doc := range transformed {
Expand All @@ -172,15 +174,14 @@ func ConvertConfig(raw []byte) ([]byte, error) {
}
}
} else {
// invalid input will be ignored
// we don't want to throw error here since the config could
// be valid but with a different order such as:
// ---
// tools:
// - tool_a
// kind: toolset
// ---
continue
if hasKindField(input) {
// this doc is already v2, encode to buf
if err := encoder.Encode(input); err != nil {
return nil, err
}
break
}
return nil, fmt.Errorf("doc %d: invalid v1 config at key %q: expected map; if you intended v2 format, each doc needs kind + name", docIndex, key)
}
} else {
// this doc is already v2, encode to buf
Expand All @@ -194,6 +195,15 @@ func ConvertConfig(raw []byte) ([]byte, error) {
return buf.Bytes(), nil
}

func hasKindField(input yaml.MapSlice) bool {
for _, item := range input {
if key, ok := item.Key.(string); ok && key == "kind" {
return true
}
}
return false
}

// transformDocs transforms the configuration file from v1 format to v2
// yaml.MapSlice will preserve the order in a map
func transformDocs(kind string, input yaml.MapSlice) ([]yaml.MapSlice, error) {
Expand Down
23 changes: 17 additions & 6 deletions cmd/internal/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,19 +531,30 @@ tools:
`,
},
{
desc: "invalid source",
in: `sources: invalid`,
want: "",
desc: "invalid source",
in: `sources: invalid`,
isErr: true,
errStr: `doc 1: invalid v1 config at key "sources": expected map; if you intended v2 format, each doc needs kind + name`,
},
{
desc: "invalid toolset",
in: `toolsets: invalid`,
want: "",
desc: "invalid toolset",
in: `toolsets: invalid`,
isErr: true,
errStr: `doc 1: invalid v1 config at key "toolsets": expected map; if you intended v2 format, each doc needs kind + name`,
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
output, err := ConvertConfig([]byte(tc.in))
if tc.isErr {
if err == nil {
t.Fatalf("expected error")
}
if tc.errStr != "" && err.Error() != tc.errStr {
t.Fatalf("incorrect error string: got %s, want %s", err, tc.errStr)
}
return
}
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
Expand Down