From c790a27d7a2de57067229bd0452f72023d9af5a6 Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Mon, 16 Mar 2026 14:29:41 -0400 Subject: [PATCH 1/2] Log selected model in Agent.Model() for alloy observability Add slog.Info to Model() so the chosen provider/model ID and pool size are emitted on every model selection. This surfaces in the verbose log artifact, making it possible to see which model handled each agent turn without needing API provider dashboard access. --- pkg/agent/agent.go | 8 ++++++-- pkg/agent/agent_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 3b3999200..e7873b7a7 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -131,11 +131,15 @@ func (a *Agent) HasSubAgents() bool { // If model override(s) are set, it returns one of the overrides (randomly for alloy). // Otherwise, it returns a random model from the available models. func (a *Agent) Model() provider.Provider { + var selected provider.Provider // Check for model override first (set via TUI model switching) if overrides := a.modelOverrides.Load(); overrides != nil && len(*overrides) > 0 { - return (*overrides)[rand.Intn(len(*overrides))] + selected = (*overrides)[rand.Intn(len(*overrides))] + } else { + selected = a.models[rand.Intn(len(a.models))] } - return a.models[rand.Intn(len(a.models))] + slog.Info("Model selected", "agent", a.name, "model", selected.ID(), "pool_size", len(a.models)) + return selected } // SetModelOverride sets runtime model override(s) for this agent. diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index acec738a7..f9c7abed2 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -1,8 +1,10 @@ package agent import ( + "bytes" "context" "errors" + "log/slog" "testing" "github.com/stretchr/testify/assert" @@ -139,6 +141,28 @@ func TestModelOverride(t *testing.T) { assert.Equal(t, "openai/gpt-4o", model.ID()) } +func TestModel_LogsSelection(t *testing.T) { + t.Parallel() + + var buf bytes.Buffer + handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelInfo}) + prev := slog.Default() + slog.SetDefault(slog.New(handler)) + t.Cleanup(func() { slog.SetDefault(prev) }) + + model1 := &mockProvider{id: "anthropic/claude-sonnet-4-0"} + model2 := &mockProvider{id: "openai/gpt-4o"} + + a := New("scanner", "test", WithModel(model1), WithModel(model2)) + + _ = a.Model() + logOutput := buf.String() + + assert.Contains(t, logOutput, "Model selected") + assert.Contains(t, logOutput, "agent=scanner") + assert.Contains(t, logOutput, "pool_size=2") +} + func TestModelOverride_ConcurrentAccess(t *testing.T) { t.Parallel() From c9b69f94cb82026ff4d883bb2d517cc945e0a42b Mon Sep 17 00:00:00 2001 From: Derek Misler Date: Mon, 16 Mar 2026 14:37:04 -0400 Subject: [PATCH 2/2] Fix pool_size to reflect actual selection pool When model overrides are active, pool_size now reports the override count instead of always reporting len(a.models). Also extends the test to cover the override scenario. --- pkg/agent/agent.go | 5 ++++- pkg/agent/agent_test.go | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index e7873b7a7..f410e8e5d 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -132,13 +132,16 @@ func (a *Agent) HasSubAgents() bool { // Otherwise, it returns a random model from the available models. func (a *Agent) Model() provider.Provider { var selected provider.Provider + var poolSize int // Check for model override first (set via TUI model switching) if overrides := a.modelOverrides.Load(); overrides != nil && len(*overrides) > 0 { selected = (*overrides)[rand.Intn(len(*overrides))] + poolSize = len(*overrides) } else { selected = a.models[rand.Intn(len(a.models))] + poolSize = len(a.models) } - slog.Info("Model selected", "agent", a.name, "model", selected.ID(), "pool_size", len(a.models)) + slog.Info("Model selected", "agent", a.name, "model", selected.ID(), "pool_size", poolSize) return selected } diff --git a/pkg/agent/agent_test.go b/pkg/agent/agent_test.go index f9c7abed2..9eb2f1783 100644 --- a/pkg/agent/agent_test.go +++ b/pkg/agent/agent_test.go @@ -155,12 +155,26 @@ func TestModel_LogsSelection(t *testing.T) { a := New("scanner", "test", WithModel(model1), WithModel(model2)) - _ = a.Model() + // Verify basic selection logging + selected := a.Model() logOutput := buf.String() assert.Contains(t, logOutput, "Model selected") assert.Contains(t, logOutput, "agent=scanner") + assert.Contains(t, logOutput, selected.ID()) assert.Contains(t, logOutput, "pool_size=2") + + // Verify override scenario logs correct pool_size + buf.Reset() + override := &mockProvider{id: "google/gemini-2.0-flash"} + a.SetModelOverride(override) + + selected = a.Model() + logOutput = buf.String() + + assert.Equal(t, "google/gemini-2.0-flash", selected.ID()) + assert.Contains(t, logOutput, "google/gemini-2.0-flash") + assert.Contains(t, logOutput, "pool_size=1") } func TestModelOverride_ConcurrentAccess(t *testing.T) {