add model command to set default model
This commit is contained in:
parent
b767ca9c3c
commit
6345ea5636
5 changed files with 161 additions and 3 deletions
130
cmd/picoclaw/internal/model/command.go
Normal file
130
cmd/picoclaw/internal/model/command.go
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal"
|
||||||
|
"github.com/sipeed/picoclaw/pkg/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewModelCommand() *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "model [model_name]",
|
||||||
|
Short: "Show or change the default model",
|
||||||
|
Long: `Show or change the default model configuration.
|
||||||
|
|
||||||
|
If no argument is provided, shows the current default model.
|
||||||
|
If a model name is provided, sets it as the default model.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
picoclaw model # Show current default model
|
||||||
|
picoclaw model gpt-5.2 # Set gpt-5.2 as default
|
||||||
|
picoclaw model claude-sonnet-4.6 # Set claude-sonnet-4.6 as default`,
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
configPath := internal.GetConfigPath()
|
||||||
|
|
||||||
|
// Load current config
|
||||||
|
cfg, err := config.LoadConfig(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to load config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
// Show current default model
|
||||||
|
showCurrentModel(cfg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new default model
|
||||||
|
modelName := args[0]
|
||||||
|
return setDefaultModel(configPath, cfg, modelName)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func showCurrentModel(cfg *config.Config) {
|
||||||
|
defaultModel := cfg.Agents.Defaults.ModelName
|
||||||
|
if defaultModel == "" {
|
||||||
|
defaultModel = cfg.Agents.Defaults.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultModel == "" {
|
||||||
|
fmt.Println("No default model is currently set.")
|
||||||
|
fmt.Println("\nAvailable models in your config:")
|
||||||
|
listAvailableModels(cfg)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Current default model: %s\n", defaultModel)
|
||||||
|
fmt.Println("\nAvailable models in your config:")
|
||||||
|
listAvailableModels(cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listAvailableModels(cfg *config.Config) {
|
||||||
|
if len(cfg.ModelList) == 0 {
|
||||||
|
fmt.Println(" No models configured in model_list")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, model := range cfg.ModelList {
|
||||||
|
marker := " "
|
||||||
|
if i == 0 {
|
||||||
|
marker = "> "
|
||||||
|
}
|
||||||
|
if model.APIKey == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("%s- %s (%s)\n", marker, model.ModelName, model.Model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDefaultModel(configPath string, cfg *config.Config, modelName string) error {
|
||||||
|
// Validate that the model exists in model_list
|
||||||
|
modelFound := false
|
||||||
|
for _, model := range cfg.ModelList {
|
||||||
|
if model.APIKey != "" && model.ModelName == modelName {
|
||||||
|
modelFound = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !modelFound {
|
||||||
|
fmt.Printf("Error: Model '%s' not found in config.\n\n", modelName)
|
||||||
|
fmt.Println("Available models:")
|
||||||
|
listAvailableModels(cfg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the default model
|
||||||
|
// Clear old model field and set new model_name
|
||||||
|
oldModel := cfg.Agents.Defaults.ModelName
|
||||||
|
if oldModel == "" {
|
||||||
|
oldModel = cfg.Agents.Defaults.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.Agents.Defaults.ModelName = modelName
|
||||||
|
cfg.Agents.Defaults.Model = "" // Clear deprecated field
|
||||||
|
|
||||||
|
// Save config back to file
|
||||||
|
if err := config.SaveConfig(configPath, cfg); err != nil {
|
||||||
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("✓ Default model changed from '%s' to '%s'\n",
|
||||||
|
formatModelName(oldModel), modelName)
|
||||||
|
fmt.Println("\nThe new default model will be used for all agent interactions.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatModelName(name string) string {
|
||||||
|
if name == "" {
|
||||||
|
return "(none)"
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
28
cmd/picoclaw/internal/model/command_test.go
Normal file
28
cmd/picoclaw/internal/model/command_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewModelCommand(t *testing.T) {
|
||||||
|
cmd := NewModelCommand()
|
||||||
|
|
||||||
|
require.NotNil(t, cmd)
|
||||||
|
|
||||||
|
assert.Equal(t, "model [model_name]", cmd.Use)
|
||||||
|
assert.Equal(t, "Show or change the default model", cmd.Short)
|
||||||
|
|
||||||
|
assert.Len(t, cmd.Aliases, 0)
|
||||||
|
|
||||||
|
assert.False(t, cmd.HasFlags())
|
||||||
|
|
||||||
|
assert.Nil(t, cmd.Run)
|
||||||
|
assert.NotNil(t, cmd.RunE)
|
||||||
|
|
||||||
|
assert.Nil(t, cmd.PersistentPreRunE)
|
||||||
|
assert.Nil(t, cmd.PersistentPreRun)
|
||||||
|
assert.Nil(t, cmd.PersistentPostRun)
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/cron"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/cron"
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/gateway"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/gateway"
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/migrate"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/migrate"
|
||||||
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/model"
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/onboard"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/onboard"
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/skills"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/skills"
|
||||||
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/status"
|
"github.com/sipeed/picoclaw/cmd/picoclaw/internal/status"
|
||||||
|
|
@ -42,6 +43,7 @@ func NewPicoclawCommand() *cobra.Command {
|
||||||
cron.NewCronCommand(),
|
cron.NewCronCommand(),
|
||||||
migrate.NewMigrateCommand(),
|
migrate.NewMigrateCommand(),
|
||||||
skills.NewSkillsCommand(),
|
skills.NewSkillsCommand(),
|
||||||
|
model.NewModelCommand(),
|
||||||
version.NewVersionCommand(),
|
version.NewVersionCommand(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -269,8 +269,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
|
||||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
|
||||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ type AgentDefaults struct {
|
||||||
AllowReadOutsideWorkspace bool `json:"allow_read_outside_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_ALLOW_READ_OUTSIDE_WORKSPACE"`
|
AllowReadOutsideWorkspace bool `json:"allow_read_outside_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_ALLOW_READ_OUTSIDE_WORKSPACE"`
|
||||||
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
||||||
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL_NAME"`
|
ModelName string `json:"model_name,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL_NAME"`
|
||||||
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"` // Deprecated: use model_name instead
|
Model string `json:"model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"` // Deprecated: use model_name instead
|
||||||
ModelFallbacks []string `json:"model_fallbacks,omitempty"`
|
ModelFallbacks []string `json:"model_fallbacks,omitempty"`
|
||||||
ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"`
|
ImageModel string `json:"image_model,omitempty" env:"PICOCLAW_AGENTS_DEFAULTS_IMAGE_MODEL"`
|
||||||
ImageModelFallbacks []string `json:"image_model_fallbacks,omitempty"`
|
ImageModelFallbacks []string `json:"image_model_fallbacks,omitempty"`
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue