fix: keep utility default agent and model paired (#1318)
This commit is contained in:
@@ -179,11 +179,13 @@ func (f fakeUtility) PreparePromptRequest(
|
||||
return nil, err
|
||||
}
|
||||
agentCLI, model := a.AgentID, a.Model
|
||||
if agentCLI == "" && defaults != nil {
|
||||
agentCLI = defaults.AgentID
|
||||
}
|
||||
if model == "" && defaults != nil {
|
||||
model = defaults.Model
|
||||
if defaults != nil {
|
||||
if model == "" {
|
||||
agentCLI = defaults.AgentID
|
||||
model = defaults.Model
|
||||
} else if agentCLI == "" {
|
||||
agentCLI = defaults.AgentID
|
||||
}
|
||||
}
|
||||
return &utilityservice.PromptRequest{
|
||||
UtilityID: utilityID,
|
||||
|
||||
@@ -92,7 +92,8 @@ func (c *Controller) GetTemplateVariables(ctx context.Context) *dto.TemplateVari
|
||||
}
|
||||
|
||||
// PreparePromptRequest prepares a prompt request by resolving the template.
|
||||
// If defaults is provided and the utility agent has empty agent_id/model, the defaults are used.
|
||||
// If defaults is provided and the utility agent has no model, the default
|
||||
// agent/model pair is used.
|
||||
func (c *Controller) PreparePromptRequest(ctx context.Context, req dto.ExecutePromptRequest, defaults *service.DefaultUtilitySettings, sessionless bool) (*service.PromptRequest, error) {
|
||||
tmplCtx := &template.Context{
|
||||
GitDiff: req.GitDiff,
|
||||
|
||||
@@ -172,7 +172,7 @@ type DefaultUtilitySettings struct {
|
||||
}
|
||||
|
||||
// PreparePromptRequest prepares a prompt request by resolving the template.
|
||||
// If the utility agent has empty AgentID/Model, the defaults are used.
|
||||
// If the utility agent has no Model, the default AgentID/Model pair is used.
|
||||
// When sessionless is true, missing template variables are substituted with
|
||||
// empty strings instead of being left as {{Var}}.
|
||||
func (s *Service) PreparePromptRequest(ctx context.Context, utilityID string, tmplCtx *template.Context, defaults *DefaultUtilitySettings, sessionless bool) (*PromptRequest, error) {
|
||||
@@ -189,14 +189,18 @@ func (s *Service) PreparePromptRequest(ctx context.Context, utilityID string, tm
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use agent's configured values, fall back to defaults if empty
|
||||
// Use agent-specific values when fully configured. If the model is empty,
|
||||
// treat the default agent/model as an inseparable pair so a default model
|
||||
// from one provider is never sent to another provider's ACP config.
|
||||
agentCLI := agent.AgentID
|
||||
model := agent.Model
|
||||
if agentCLI == "" && defaults != nil {
|
||||
agentCLI = defaults.AgentID
|
||||
}
|
||||
if model == "" && defaults != nil {
|
||||
model = defaults.Model
|
||||
if defaults != nil {
|
||||
if model == "" {
|
||||
agentCLI = defaults.AgentID
|
||||
model = defaults.Model
|
||||
} else if agentCLI == "" {
|
||||
agentCLI = defaults.AgentID
|
||||
}
|
||||
}
|
||||
|
||||
return &PromptRequest{
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/kandev/kandev/internal/utility/models"
|
||||
"github.com/kandev/kandev/internal/utility/template"
|
||||
)
|
||||
|
||||
type fakeRepository struct {
|
||||
agents map[string]*models.UtilityAgent
|
||||
}
|
||||
|
||||
func (r *fakeRepository) ListAgents(context.Context) ([]*models.UtilityAgent, error) {
|
||||
out := make([]*models.UtilityAgent, 0, len(r.agents))
|
||||
for _, agent := range r.agents {
|
||||
out = append(out, agent)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) GetAgentByID(_ context.Context, id string) (*models.UtilityAgent, error) {
|
||||
agent, ok := r.agents[id]
|
||||
if !ok {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
return agent, nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) GetAgentByName(_ context.Context, name string) (*models.UtilityAgent, error) {
|
||||
for _, agent := range r.agents {
|
||||
if agent.Name == name {
|
||||
return agent, nil
|
||||
}
|
||||
}
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (r *fakeRepository) CreateAgent(_ context.Context, agent *models.UtilityAgent) error {
|
||||
r.agents[agent.ID] = agent
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) UpdateAgent(_ context.Context, agent *models.UtilityAgent) error {
|
||||
r.agents[agent.ID] = agent
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) DeleteAgent(_ context.Context, id string) error {
|
||||
delete(r.agents, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) ListCalls(context.Context, string, int) ([]*models.UtilityAgentCall, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) GetCallByID(context.Context, string) (*models.UtilityAgentCall, error) {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (r *fakeRepository) CreateCall(context.Context, *models.UtilityAgentCall) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *fakeRepository) UpdateCall(context.Context, *models.UtilityAgentCall) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPreparePromptRequest_UsesDefaultAgentAndModelAsPairWhenModelIsUnset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := NewService(&fakeRepository{agents: map[string]*models.UtilityAgent{
|
||||
"builtin-enhance-prompt": {
|
||||
ID: "builtin-enhance-prompt",
|
||||
Prompt: "Improve {{UserPrompt}}",
|
||||
AgentID: "claude-acp",
|
||||
Model: "",
|
||||
Builtin: true,
|
||||
},
|
||||
}})
|
||||
|
||||
req, err := svc.PreparePromptRequest(
|
||||
context.Background(),
|
||||
"builtin-enhance-prompt",
|
||||
&template.Context{UserPrompt: "fix the bug"},
|
||||
&DefaultUtilitySettings{
|
||||
AgentID: "opencode-acp",
|
||||
Model: "opencode-go/deepseek-v4-flash",
|
||||
},
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("PreparePromptRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if req.AgentCLI != "opencode-acp" {
|
||||
t.Fatalf("AgentCLI = %q, want %q", req.AgentCLI, "opencode-acp")
|
||||
}
|
||||
if req.Model != "opencode-go/deepseek-v4-flash" {
|
||||
t.Fatalf("Model = %q, want %q", req.Model, "opencode-go/deepseek-v4-flash")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparePromptRequest_UsesDefaultAgentWhenAgentIDIsUnset(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := NewService(&fakeRepository{agents: map[string]*models.UtilityAgent{
|
||||
"custom": {
|
||||
ID: "custom",
|
||||
Prompt: "Do {{UserPrompt}}",
|
||||
AgentID: "",
|
||||
Model: "custom-model",
|
||||
},
|
||||
}})
|
||||
|
||||
req, err := svc.PreparePromptRequest(
|
||||
context.Background(),
|
||||
"custom",
|
||||
&template.Context{UserPrompt: "fix the bug"},
|
||||
&DefaultUtilitySettings{
|
||||
AgentID: "default-acp",
|
||||
Model: "default-model",
|
||||
},
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("PreparePromptRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if req.AgentCLI != "default-acp" {
|
||||
t.Fatalf("AgentCLI = %q, want %q", req.AgentCLI, "default-acp")
|
||||
}
|
||||
if req.Model != "custom-model" {
|
||||
t.Fatalf("Model = %q, want %q", req.Model, "custom-model")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparePromptRequest_PreservesConfiguredAgentAndModelWithoutDefaults(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := NewService(&fakeRepository{agents: map[string]*models.UtilityAgent{
|
||||
"custom": {
|
||||
ID: "custom",
|
||||
Prompt: "Do {{UserPrompt}}",
|
||||
AgentID: "custom-acp",
|
||||
Model: "custom-model",
|
||||
},
|
||||
}})
|
||||
|
||||
req, err := svc.PreparePromptRequest(
|
||||
context.Background(),
|
||||
"custom",
|
||||
&template.Context{UserPrompt: "fix the bug"},
|
||||
nil,
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("PreparePromptRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if req.AgentCLI != "custom-acp" {
|
||||
t.Fatalf("AgentCLI = %q, want %q", req.AgentCLI, "custom-acp")
|
||||
}
|
||||
if req.Model != "custom-model" {
|
||||
t.Fatalf("Model = %q, want %q", req.Model, "custom-model")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparePromptRequest_IgnoresDefaultsWhenAgentAndModelAreConfigured(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
svc := NewService(&fakeRepository{agents: map[string]*models.UtilityAgent{
|
||||
"custom": {
|
||||
ID: "custom",
|
||||
Prompt: "Do {{UserPrompt}}",
|
||||
AgentID: "custom-acp",
|
||||
Model: "custom-model",
|
||||
},
|
||||
}})
|
||||
|
||||
req, err := svc.PreparePromptRequest(
|
||||
context.Background(),
|
||||
"custom",
|
||||
&template.Context{UserPrompt: "fix the bug"},
|
||||
&DefaultUtilitySettings{
|
||||
AgentID: "default-acp",
|
||||
Model: "default-model",
|
||||
},
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("PreparePromptRequest() error = %v", err)
|
||||
}
|
||||
|
||||
if req.AgentCLI != "custom-acp" {
|
||||
t.Fatalf("AgentCLI = %q, want %q", req.AgentCLI, "custom-acp")
|
||||
}
|
||||
if req.Model != "custom-model" {
|
||||
t.Fatalf("Model = %q, want %q", req.Model, "custom-model")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user