diff --git a/pkg/config/runtime.go b/pkg/config/runtime.go index ff3d3eb35..cc1e8fb80 100644 --- a/pkg/config/runtime.go +++ b/pkg/config/runtime.go @@ -2,6 +2,7 @@ package config import ( "log/slog" + "maps" "slices" "sync" @@ -23,6 +24,7 @@ type Config struct { DefaultModel *latest.ModelConfig GlobalCodeMode bool WorkingDir string + Models map[string]latest.ModelConfig // Hook overrides from CLI flags HookPreToolUse []string @@ -37,6 +39,7 @@ func (runConfig *RuntimeConfig) Clone() *RuntimeConfig { Config: runConfig.Config, } clone.EnvFiles = slices.Clone(runConfig.EnvFiles) + clone.Models = maps.Clone(runConfig.Models) clone.DefaultModel = runConfig.DefaultModel.Clone() clone.HookPreToolUse = slices.Clone(runConfig.HookPreToolUse) clone.HookPostToolUse = slices.Clone(runConfig.HookPostToolUse) diff --git a/pkg/config/runtime_clone_test.go b/pkg/config/runtime_clone_test.go index 77b0bd43e..941909d34 100644 --- a/pkg/config/runtime_clone_test.go +++ b/pkg/config/runtime_clone_test.go @@ -59,3 +59,56 @@ func TestClone_EnvFilesIsolated(t *testing.T) { assert.Len(t, original.EnvFiles, 2, "original must not be modified when clone is mutated") assert.Len(t, clone.EnvFiles, 3) } + +func TestClone_ModelsIsolated(t *testing.T) { + temp := 0.7 + original := &RuntimeConfig{ + Config: Config{ + Models: map[string]latest.ModelConfig{ + "model1": { + Provider: "openai", + Model: "gpt-4o", + Temperature: &temp, + }, + }, + }, + } + + clone := original.Clone() + + // Mutate the clone's Models map + clone.Models["model2"] = latest.ModelConfig{ + Provider: "anthropic", + Model: "claude-3", + } + + // Mutate an existing model in the clone + newTemp := 0.9 + clone.Models["model1"] = latest.ModelConfig{ + Provider: "openai", + Model: "gpt-4o-mini", + Temperature: &newTemp, + } + + // Original must not be affected by mutations to the clone + assert.Len(t, original.Models, 1, "original must not have new models added") + assert.Equal(t, "gpt-4o", original.Models["model1"].Model) + assert.InDelta(t, 0.7, *original.Models["model1"].Temperature, 0.001) + + // Clone should have the mutations + assert.Len(t, clone.Models, 2) + assert.Equal(t, "gpt-4o-mini", clone.Models["model1"].Model) + assert.Equal(t, "claude-3", clone.Models["model2"].Model) +} + +func TestClone_NilModels(t *testing.T) { + original := &RuntimeConfig{ + Config: Config{ + Models: nil, + }, + } + + clone := original.Clone() + + assert.Nil(t, clone.Models) +} diff --git a/pkg/teamloader/registry.go b/pkg/teamloader/registry.go index 3cf729053..df128b7a1 100644 --- a/pkg/teamloader/registry.go +++ b/pkg/teamloader/registry.go @@ -368,6 +368,7 @@ func createRAGTool(ctx context.Context, toolset latest.Toolset, parentDir string ParentDir: parentDir, ModelsGateway: runConfig.ModelsGateway, Env: runConfig.EnvProvider(), + Models: runConfig.Models, }) if err != nil { return nil, fmt.Errorf("failed to create RAG manager: %w", err) diff --git a/pkg/teamloader/teamloader.go b/pkg/teamloader/teamloader.go index 459c04064..04b023724 100644 --- a/pkg/teamloader/teamloader.go +++ b/pkg/teamloader/teamloader.go @@ -121,6 +121,9 @@ func LoadWithConfig(ctx context.Context, agentSource config.Source, runConfig *c return nil, err } + // Make model definitions available to toolset creators (e.g., RAG reranking) + runConfig.Models = cfg.Models + // Load agents parentDir := cmp.Or(agentSource.ParentDir(), runConfig.WorkingDir) configName := configNameFromSource(agentSource.Name())