Skip to content

Commit 44123ad

Browse files
committed
Freeze v7
Signed-off-by: David Gageot <david.gageot@docker.com>
1 parent 389fd52 commit 44123ad

11 files changed

Lines changed: 2670 additions & 31 deletions

File tree

pkg/config/latest/parse.go

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"github.com/goccy/go-yaml"
55

66
"github.com/docker/docker-agent/pkg/config/types"
7-
previous "github.com/docker/docker-agent/pkg/config/v6"
7+
previous "github.com/docker/docker-agent/pkg/config/v7"
88
)
99

1010
func Register(parsers map[string]func([]byte) (any, error), upgraders *[]func(any, []byte) (any, error)) {
@@ -26,34 +26,5 @@ func upgradeIfNeeded(c any, _ []byte) (any, error) {
2626

2727
var config Config
2828
types.CloneThroughJSON(old, &config)
29-
30-
// Migrate AgentConfig.RAG []string → toolsets with type: rag + ref
31-
for i, agent := range old.Agents {
32-
if len(agent.RAG) == 0 {
33-
continue
34-
}
35-
for _, ragName := range agent.RAG {
36-
config.Agents[i].Toolsets = append(config.Agents[i].Toolsets, Toolset{
37-
Type: "rag",
38-
Ref: ragName,
39-
})
40-
}
41-
}
42-
43-
// Migrate top-level RAG map from RAGConfig to RAGToolset
44-
if len(old.RAG) > 0 && config.RAG == nil {
45-
config.RAG = make(map[string]RAGToolset)
46-
}
47-
for name, oldRAG := range old.RAG {
48-
var ragCfg RAGConfig
49-
types.CloneThroughJSON(oldRAG, &ragCfg)
50-
config.RAG[name] = RAGToolset{
51-
Toolset: Toolset{
52-
Type: "rag",
53-
RAGConfig: &ragCfg,
54-
},
55-
}
56-
}
57-
5829
return config, nil
5930
}

pkg/config/latest/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/docker/docker-agent/pkg/effort"
1717
)
1818

19-
const Version = "7"
19+
const Version = "8"
2020

2121
// Config represents the entire configuration file
2222
type Config struct {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package latest
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestModelConfig_Clone_DeepCopiesPointerFields(t *testing.T) {
10+
t.Parallel()
11+
12+
temp := 0.7
13+
maxTokens := int64(4096)
14+
topP := 0.9
15+
parallel := true
16+
trackUsage := true
17+
18+
original := &ModelConfig{
19+
Provider: "openai",
20+
Model: "gpt-4o",
21+
Temperature: &temp,
22+
MaxTokens: &maxTokens,
23+
TopP: &topP,
24+
ParallelToolCalls: &parallel,
25+
TrackUsage: &trackUsage,
26+
ThinkingBudget: &ThinkingBudget{Effort: "high"},
27+
ProviderOpts: map[string]any{"key": "value"},
28+
Routing: []RoutingRule{
29+
{Model: "fast", Examples: []string{"quick question"}},
30+
},
31+
}
32+
33+
clone := original.Clone()
34+
35+
// Mutate every pointer/collection field in the original.
36+
*original.Temperature = 0.1
37+
*original.MaxTokens = 1
38+
*original.TopP = 0.1
39+
*original.ParallelToolCalls = false
40+
*original.TrackUsage = false
41+
original.ThinkingBudget.Effort = "low"
42+
original.ProviderOpts["key"] = "mutated"
43+
original.Routing[0].Examples[0] = "mutated"
44+
45+
// Clone must be unaffected.
46+
assert.InDelta(t, 0.7, *clone.Temperature, 0.001)
47+
assert.Equal(t, int64(4096), *clone.MaxTokens)
48+
assert.InDelta(t, 0.9, *clone.TopP, 0.001)
49+
assert.True(t, *clone.ParallelToolCalls)
50+
assert.True(t, *clone.TrackUsage)
51+
assert.Equal(t, "high", clone.ThinkingBudget.Effort)
52+
assert.Equal(t, "value", clone.ProviderOpts["key"])
53+
assert.Equal(t, "quick question", clone.Routing[0].Examples[0])
54+
}
55+
56+
func TestModelConfig_Clone_Nil(t *testing.T) {
57+
t.Parallel()
58+
59+
var m *ModelConfig
60+
assert.Nil(t, m.Clone())
61+
}
62+
63+
func TestModelConfig_Clone_MinimalFields(t *testing.T) {
64+
t.Parallel()
65+
66+
original := &ModelConfig{
67+
Provider: "anthropic",
68+
Model: "claude-sonnet-4-5",
69+
}
70+
71+
clone := original.Clone()
72+
73+
assert.Equal(t, "anthropic", clone.Provider)
74+
assert.Equal(t, "claude-sonnet-4-5", clone.Model)
75+
assert.Nil(t, clone.Temperature)
76+
assert.Nil(t, clone.MaxTokens)
77+
assert.Nil(t, clone.ProviderOpts)
78+
assert.Nil(t, clone.Routing)
79+
}

pkg/config/v7/model_ref.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package latest
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// ParseModelRef parses an inline "provider/model" reference into a
9+
// ModelConfig. It returns an error when the string does not contain
10+
// exactly one "/" separator or when either part is empty.
11+
//
12+
// cfg, err := ParseModelRef("openai/gpt-4o")
13+
// // cfg.Provider == "openai", cfg.Model == "gpt-4o"
14+
func ParseModelRef(ref string) (ModelConfig, error) {
15+
providerName, model, ok := strings.Cut(ref, "/")
16+
if !ok || providerName == "" || model == "" {
17+
return ModelConfig{}, fmt.Errorf("invalid model reference %q: expected 'provider/model' format", ref)
18+
}
19+
return ModelConfig{Provider: providerName, Model: model}, nil
20+
}

pkg/config/v7/parse.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package latest
2+
3+
import (
4+
"github.com/goccy/go-yaml"
5+
6+
"github.com/docker/docker-agent/pkg/config/types"
7+
previous "github.com/docker/docker-agent/pkg/config/v6"
8+
)
9+
10+
func Register(parsers map[string]func([]byte) (any, error), upgraders *[]func(any, []byte) (any, error)) {
11+
parsers[Version] = func(d []byte) (any, error) { return parse(d) }
12+
*upgraders = append(*upgraders, upgradeIfNeeded)
13+
}
14+
15+
func parse(data []byte) (Config, error) {
16+
var cfg Config
17+
err := yaml.UnmarshalWithOptions(data, &cfg, yaml.Strict())
18+
return cfg, err
19+
}
20+
21+
func upgradeIfNeeded(c any, _ []byte) (any, error) {
22+
old, ok := c.(previous.Config)
23+
if !ok {
24+
return c, nil
25+
}
26+
27+
var config Config
28+
types.CloneThroughJSON(old, &config)
29+
30+
// Migrate AgentConfig.RAG []string → toolsets with type: rag + ref
31+
for i, agent := range old.Agents {
32+
if len(agent.RAG) == 0 {
33+
continue
34+
}
35+
for _, ragName := range agent.RAG {
36+
config.Agents[i].Toolsets = append(config.Agents[i].Toolsets, Toolset{
37+
Type: "rag",
38+
Ref: ragName,
39+
})
40+
}
41+
}
42+
43+
// Migrate top-level RAG map from RAGConfig to RAGToolset
44+
if len(old.RAG) > 0 && config.RAG == nil {
45+
config.RAG = make(map[string]RAGToolset)
46+
}
47+
for name, oldRAG := range old.RAG {
48+
var ragCfg RAGConfig
49+
types.CloneThroughJSON(oldRAG, &ragCfg)
50+
config.RAG[name] = RAGToolset{
51+
Toolset: Toolset{
52+
Type: "rag",
53+
RAGConfig: &ragCfg,
54+
},
55+
}
56+
}
57+
58+
return config, nil
59+
}

0 commit comments

Comments
 (0)