diff --git a/pkg/config/examples_test.go b/pkg/config/examples_test.go index b44cac389..6d494147f 100644 --- a/pkg/config/examples_test.go +++ b/pkg/config/examples_test.go @@ -103,3 +103,23 @@ func TestJsonSchemaWorksForExamples(t *testing.T) { }) } } + +func TestParseExamplesAfterMarshalling(t *testing.T) { + for _, file := range collectExamples(t) { + t.Run(file, func(t *testing.T) { + t.Parallel() + + src := NewFileSource(file) + cfg, err := Load(t.Context(), NewFileSource(file)) + require.NoError(t, err) + + // Make sure that a config can be marshalled and parsed again. + // We've had marshalling issues in the past. + buf, err := yaml.Marshal(cfg) + require.NoError(t, err) + + _, err = Load(t.Context(), NewBytesSource(src.Name(), buf)) + require.NoError(t, err) + }) + } +} diff --git a/pkg/config/latest/types.go b/pkg/config/latest/types.go index 2fe0999d2..71ba84883 100644 --- a/pkg/config/latest/types.go +++ b/pkg/config/latest/types.go @@ -59,7 +59,7 @@ func (c *Agents) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (c Agents) MarshalYAML() ([]byte, error) { +func (c Agents) MarshalYAML() (any, error) { mapSlice := make(yaml.MapSlice, 0, len(c)) for _, agent := range c { @@ -69,7 +69,7 @@ func (c Agents) MarshalYAML() ([]byte, error) { }) } - return yaml.Marshal(mapSlice) + return mapSlice, nil } func (c *Agents) First() AgentConfig { @@ -165,11 +165,11 @@ func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling for Duration to string format -func (d Duration) MarshalYAML() ([]byte, error) { +func (d Duration) MarshalYAML() (any, error) { if d.Duration == 0 { - return yaml.Marshal("") + return "", nil } - return yaml.Marshal(d.String()) + return d.String(), nil } // UnmarshalJSON implements custom unmarshaling for Duration from string format @@ -288,14 +288,14 @@ func (s *SkillsConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (s SkillsConfig) MarshalYAML() ([]byte, error) { +func (s SkillsConfig) MarshalYAML() (any, error) { if len(s.Sources) == 0 { - return yaml.Marshal(false) + return false, nil } if len(s.Sources) == 1 && s.Sources[0] == SkillSourceLocal { - return yaml.Marshal(true) + return true, nil } - return yaml.Marshal(s.Sources) + return s.Sources, nil } func (s *SkillsConfig) UnmarshalJSON(data []byte) error { @@ -438,11 +438,11 @@ func (f *FlexibleModelConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML outputs shorthand format if only provider/model are set -func (f FlexibleModelConfig) MarshalYAML() ([]byte, error) { +func (f FlexibleModelConfig) MarshalYAML() (any, error) { if f.isShorthandOnly() { - return yaml.Marshal(f.Provider + "/" + f.Model) + return f.Provider + "/" + f.Model, nil } - return yaml.Marshal(f.ModelConfig) + return f.ModelConfig, nil } // isShorthandOnly returns true if only provider and model are set @@ -640,15 +640,15 @@ func (d *DeferConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (d DeferConfig) MarshalYAML() ([]byte, error) { +func (d DeferConfig) MarshalYAML() (any, error) { if d.DeferAll { - return yaml.Marshal(true) + return true, nil } if len(d.Tools) == 0 { // Return false for empty config - this will be omitted by yaml encoder - return yaml.Marshal(false) + return false, nil } - return yaml.Marshal(d.Tools) + return d.Tools, nil } // ThinkingBudget represents reasoning budget configuration. @@ -681,14 +681,14 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to output simple string or int format -func (t ThinkingBudget) MarshalYAML() ([]byte, error) { +func (t ThinkingBudget) MarshalYAML() (any, error) { // If Effort string is set (non-empty), marshal as string if t.Effort != "" { - return yaml.Marshal(t.Effort) + return t.Effort, nil } // Otherwise marshal as integer (includes 0, -1, and positive values) - return yaml.Marshal(t.Tokens) + return t.Tokens, nil } // MarshalJSON implements custom marshaling to output simple string or int format @@ -829,9 +829,9 @@ func (s *RAGStrategyConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to flatten Params into parent level -func (s RAGStrategyConfig) MarshalYAML() ([]byte, error) { +func (s RAGStrategyConfig) MarshalYAML() (any, error) { result := s.buildFlattenedMap() - return yaml.Marshal(result) + return result, nil } // MarshalJSON implements custom marshaling to flatten Params into parent level diff --git a/pkg/config/v2/types.go b/pkg/config/v2/types.go index b320ecc79..a51ee7a3b 100644 --- a/pkg/config/v2/types.go +++ b/pkg/config/v2/types.go @@ -5,8 +5,6 @@ import ( "fmt" "maps" - "github.com/goccy/go-yaml" - "github.com/docker/cagent/pkg/config/types" ) @@ -319,9 +317,9 @@ func (s *RAGStrategyConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to flatten Params into parent level -func (s RAGStrategyConfig) MarshalYAML() ([]byte, error) { +func (s RAGStrategyConfig) MarshalYAML() (any, error) { result := s.buildFlattenedMap() - return yaml.Marshal(result) + return result, nil } // MarshalJSON implements custom marshaling to flatten Params into parent level @@ -508,11 +506,11 @@ func (d *RAGDatabaseConfig) IsEmpty() bool { } // MarshalYAML implements custom marshaling for DatabaseConfig -func (d RAGDatabaseConfig) MarshalYAML() ([]byte, error) { +func (d RAGDatabaseConfig) MarshalYAML() (any, error) { if d.value == nil { - return yaml.Marshal(nil) + return nil, nil } - return yaml.Marshal(d.value) + return d.value, nil } // MarshalJSON implements custom marshaling for DatabaseConfig diff --git a/pkg/config/v3/types.go b/pkg/config/v3/types.go index a2951ded8..735cc748e 100644 --- a/pkg/config/v3/types.go +++ b/pkg/config/v3/types.go @@ -5,8 +5,6 @@ import ( "fmt" "maps" - "github.com/goccy/go-yaml" - "github.com/docker/cagent/pkg/config/types" ) @@ -260,15 +258,15 @@ func (d *DeferConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (d DeferConfig) MarshalYAML() ([]byte, error) { +func (d DeferConfig) MarshalYAML() (any, error) { if d.DeferAll { - return yaml.Marshal(true) + return true, nil } if len(d.Tools) == 0 { // Return false for empty config - this will be omitted by yaml encoder - return yaml.Marshal(false) + return false, nil } - return yaml.Marshal(d.Tools) + return d.Tools, nil } // ThinkingBudget represents reasoning budget configuration. @@ -301,14 +299,14 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to output simple string or int format -func (t ThinkingBudget) MarshalYAML() ([]byte, error) { +func (t ThinkingBudget) MarshalYAML() (any, error) { // If Effort string is set (non-empty), marshal as string if t.Effort != "" { - return yaml.Marshal(t.Effort) + return t.Effort, nil } // Otherwise marshal as integer (includes 0, -1, and positive values) - return yaml.Marshal(t.Tokens) + return t.Tokens, nil } // MarshalJSON implements custom marshaling to output simple string or int format @@ -449,9 +447,9 @@ func (s *RAGStrategyConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to flatten Params into parent level -func (s RAGStrategyConfig) MarshalYAML() ([]byte, error) { +func (s RAGStrategyConfig) MarshalYAML() (any, error) { result := s.buildFlattenedMap() - return yaml.Marshal(result) + return result, nil } // MarshalJSON implements custom marshaling to flatten Params into parent level diff --git a/pkg/config/v4/types.go b/pkg/config/v4/types.go index da41a0649..0d43e32b4 100644 --- a/pkg/config/v4/types.go +++ b/pkg/config/v4/types.go @@ -58,7 +58,7 @@ func (c *Agents) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (c Agents) MarshalYAML() ([]byte, error) { +func (c Agents) MarshalYAML() (any, error) { mapSlice := make(yaml.MapSlice, 0, len(c)) for _, agent := range c { @@ -68,7 +68,7 @@ func (c Agents) MarshalYAML() ([]byte, error) { }) } - return yaml.Marshal(mapSlice) + return mapSlice, nil } func (c *Agents) First() AgentConfig { @@ -164,11 +164,11 @@ func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling for Duration to string format -func (d Duration) MarshalYAML() ([]byte, error) { +func (d Duration) MarshalYAML() (any, error) { if d.Duration == 0 { - return yaml.Marshal("") + return "", nil } - return yaml.Marshal(d.String()) + return d.String(), nil } // UnmarshalJSON implements custom unmarshaling for Duration from string format @@ -316,11 +316,11 @@ func (f *FlexibleModelConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML outputs shorthand format if only provider/model are set -func (f FlexibleModelConfig) MarshalYAML() ([]byte, error) { +func (f FlexibleModelConfig) MarshalYAML() (any, error) { if f.isShorthandOnly() { - return yaml.Marshal(f.Provider + "/" + f.Model) + return f.Provider + "/" + f.Model, nil } - return yaml.Marshal(f.ModelConfig) + return f.ModelConfig, nil } // isShorthandOnly returns true if only provider and model are set @@ -518,15 +518,15 @@ func (d *DeferConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (d DeferConfig) MarshalYAML() ([]byte, error) { +func (d DeferConfig) MarshalYAML() (any, error) { if d.DeferAll { - return yaml.Marshal(true) + return true, nil } if len(d.Tools) == 0 { // Return false for empty config - this will be omitted by yaml encoder - return yaml.Marshal(false) + return false, nil } - return yaml.Marshal(d.Tools) + return d.Tools, nil } // ThinkingBudget represents reasoning budget configuration. @@ -559,14 +559,14 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to output simple string or int format -func (t ThinkingBudget) MarshalYAML() ([]byte, error) { +func (t ThinkingBudget) MarshalYAML() (any, error) { // If Effort string is set (non-empty), marshal as string if t.Effort != "" { - return yaml.Marshal(t.Effort) + return t.Effort, nil } // Otherwise marshal as integer (includes 0, -1, and positive values) - return yaml.Marshal(t.Tokens) + return t.Tokens, nil } // MarshalJSON implements custom marshaling to output simple string or int format @@ -707,9 +707,9 @@ func (s *RAGStrategyConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to flatten Params into parent level -func (s RAGStrategyConfig) MarshalYAML() ([]byte, error) { +func (s RAGStrategyConfig) MarshalYAML() (any, error) { result := s.buildFlattenedMap() - return yaml.Marshal(result) + return result, nil } // MarshalJSON implements custom marshaling to flatten Params into parent level diff --git a/pkg/config/v5/types.go b/pkg/config/v5/types.go index f23abbf8b..10a68db38 100644 --- a/pkg/config/v5/types.go +++ b/pkg/config/v5/types.go @@ -59,7 +59,7 @@ func (c *Agents) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (c Agents) MarshalYAML() ([]byte, error) { +func (c Agents) MarshalYAML() (any, error) { mapSlice := make(yaml.MapSlice, 0, len(c)) for _, agent := range c { @@ -69,7 +69,7 @@ func (c Agents) MarshalYAML() ([]byte, error) { }) } - return yaml.Marshal(mapSlice) + return mapSlice, nil } func (c *Agents) First() AgentConfig { @@ -165,11 +165,11 @@ func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling for Duration to string format -func (d Duration) MarshalYAML() ([]byte, error) { +func (d Duration) MarshalYAML() (any, error) { if d.Duration == 0 { - return yaml.Marshal("") + return "", nil } - return yaml.Marshal(d.String()) + return d.String(), nil } // UnmarshalJSON implements custom unmarshaling for Duration from string format @@ -288,14 +288,14 @@ func (s *SkillsConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (s SkillsConfig) MarshalYAML() ([]byte, error) { +func (s SkillsConfig) MarshalYAML() (any, error) { if len(s.Sources) == 0 { - return yaml.Marshal(false) + return false, nil } if len(s.Sources) == 1 && s.Sources[0] == SkillSourceLocal { - return yaml.Marshal(true) + return true, nil } - return yaml.Marshal(s.Sources) + return s.Sources, nil } func (s *SkillsConfig) UnmarshalJSON(data []byte) error { @@ -438,11 +438,11 @@ func (f *FlexibleModelConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML outputs shorthand format if only provider/model are set -func (f FlexibleModelConfig) MarshalYAML() ([]byte, error) { +func (f FlexibleModelConfig) MarshalYAML() (any, error) { if f.isShorthandOnly() { - return yaml.Marshal(f.Provider + "/" + f.Model) + return f.Provider + "/" + f.Model, nil } - return yaml.Marshal(f.ModelConfig) + return f.ModelConfig, nil } // isShorthandOnly returns true if only provider and model are set @@ -640,15 +640,15 @@ func (d *DeferConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -func (d DeferConfig) MarshalYAML() ([]byte, error) { +func (d DeferConfig) MarshalYAML() (any, error) { if d.DeferAll { - return yaml.Marshal(true) + return true, nil } if len(d.Tools) == 0 { // Return false for empty config - this will be omitted by yaml encoder - return yaml.Marshal(false) + return false, nil } - return yaml.Marshal(d.Tools) + return d.Tools, nil } // ThinkingBudget represents reasoning budget configuration. @@ -681,14 +681,14 @@ func (t *ThinkingBudget) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to output simple string or int format -func (t ThinkingBudget) MarshalYAML() ([]byte, error) { +func (t ThinkingBudget) MarshalYAML() (any, error) { // If Effort string is set (non-empty), marshal as string if t.Effort != "" { - return yaml.Marshal(t.Effort) + return t.Effort, nil } // Otherwise marshal as integer (includes 0, -1, and positive values) - return yaml.Marshal(t.Tokens) + return t.Tokens, nil } // MarshalJSON implements custom marshaling to output simple string or int format @@ -829,9 +829,9 @@ func (s *RAGStrategyConfig) UnmarshalYAML(unmarshal func(any) error) error { } // MarshalYAML implements custom marshaling to flatten Params into parent level -func (s RAGStrategyConfig) MarshalYAML() ([]byte, error) { +func (s RAGStrategyConfig) MarshalYAML() (any, error) { result := s.buildFlattenedMap() - return yaml.Marshal(result) + return result, nil } // MarshalJSON implements custom marshaling to flatten Params into parent level