configure image generation under tool settings

This commit is contained in:
Anton Bogdanovich 2026-05-05 14:11:00 -07:00
parent cd0b4f025e
commit 135f721dfc
6 changed files with 117 additions and 31 deletions

View file

@ -427,6 +427,10 @@
"i2c": { "i2c": {
"enabled": false "enabled": false
}, },
"image_generate": {
"enabled": false,
"model": "openai-codex/gpt-image-2"
},
"install_skill": { "install_skill": {
"enabled": true "enabled": true
}, },

View file

@ -156,6 +156,30 @@ If `range` is omitted, PicoClaw performs an unrestricted search.
} }
``` ```
## Image Generation Tool
The `image_generate` tool creates image files through a provider that supports
image generation.
| Config | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | bool | false | Enable the image generation tool |
| `model` | string | `gpt-image-2` | Image generation model. Values may include a provider prefix, for example `openai-codex/gpt-image-2` |
If `tools.image_generate.model` is not set, PicoClaw falls back to the legacy
`agents.defaults.image_model` setting, then to `gpt-image-2`.
```json
{
"tools": {
"image_generate": {
"enabled": true,
"model": "openai-codex/gpt-image-2"
}
}
}
```
## Exec Tool ## Exec Tool
The exec tool is used to execute shell commands. The exec tool is used to execute shell commands.

View file

@ -216,10 +216,7 @@ func registerSharedTools(
} }
if cfg.Tools.IsToolEnabled("image_generate") { if cfg.Tools.IsToolEnabled("image_generate") {
imageModel := cfg.Agents.Defaults.ImageModel imageModel := cfg.Tools.ImageGenerate.EffectiveModel(cfg.Agents.Defaults)
if imageModel == "" {
imageModel = "gpt-image-2"
}
agent.Tools.Register(tools.NewImageGenerateTool(agent.Workspace, imageModel, nil)) agent.Tools.Register(tools.NewImageGenerateTool(agent.Workspace, imageModel, nil))
} }

View file

@ -626,6 +626,21 @@ type ToolConfig struct {
Enabled bool `json:"enabled" yaml:"-" env:"ENABLED"` Enabled bool `json:"enabled" yaml:"-" env:"ENABLED"`
} }
type ImageGenerateToolsConfig struct {
ToolConfig `yaml:"-" envPrefix:"PICOCLAW_TOOLS_IMAGE_GENERATE_"`
Model string `json:"model,omitempty" yaml:"-" env:"PICOCLAW_TOOLS_IMAGE_GENERATE_MODEL"`
}
func (c ImageGenerateToolsConfig) EffectiveModel(defaults AgentDefaults) string {
if model := strings.TrimSpace(c.Model); model != "" {
return model
}
if model := strings.TrimSpace(defaults.ImageModel); model != "" {
return model
}
return "gpt-image-2"
}
type BraveConfig struct { type BraveConfig struct {
Enabled bool `json:"enabled" yaml:"-" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"` Enabled bool `json:"enabled" yaml:"-" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
APIKeys SecureStrings `json:"api_keys,omitzero" yaml:"api_keys,omitempty" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEYS"` APIKeys SecureStrings `json:"api_keys,omitzero" yaml:"api_keys,omitempty" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEYS"`
@ -827,7 +842,7 @@ type ToolsConfig struct {
EditFile ToolConfig `json:"edit_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_EDIT_FILE_"` EditFile ToolConfig `json:"edit_file" yaml:"-" envPrefix:"PICOCLAW_TOOLS_EDIT_FILE_"`
FindSkills ToolConfig `json:"find_skills" yaml:"-" envPrefix:"PICOCLAW_TOOLS_FIND_SKILLS_"` FindSkills ToolConfig `json:"find_skills" yaml:"-" envPrefix:"PICOCLAW_TOOLS_FIND_SKILLS_"`
I2C ToolConfig `json:"i2c" yaml:"-" envPrefix:"PICOCLAW_TOOLS_I2C_"` I2C ToolConfig `json:"i2c" yaml:"-" envPrefix:"PICOCLAW_TOOLS_I2C_"`
ImageGenerate ToolConfig `json:"image_generate" yaml:"-" envPrefix:"PICOCLAW_TOOLS_IMAGE_GENERATE_"` ImageGenerate ImageGenerateToolsConfig `json:"image_generate" yaml:"-"`
InstallSkill ToolConfig `json:"install_skill" yaml:"-" envPrefix:"PICOCLAW_TOOLS_INSTALL_SKILL_"` InstallSkill ToolConfig `json:"install_skill" yaml:"-" envPrefix:"PICOCLAW_TOOLS_INSTALL_SKILL_"`
ListDir ToolConfig `json:"list_dir" yaml:"-" envPrefix:"PICOCLAW_TOOLS_LIST_DIR_"` ListDir ToolConfig `json:"list_dir" yaml:"-" envPrefix:"PICOCLAW_TOOLS_LIST_DIR_"`
Message ToolConfig `json:"message" yaml:"-" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"` Message ToolConfig `json:"message" yaml:"-" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`

View file

@ -195,6 +195,50 @@ func TestLoadConfig_MCPMaxInlineTextChars(t *testing.T) {
} }
} }
func TestImageGenerateToolsConfig_EffectiveModel(t *testing.T) {
defaults := AgentDefaults{ImageModel: "legacy-image-model"}
if got := (ImageGenerateToolsConfig{}).EffectiveModel(defaults); got != "legacy-image-model" {
t.Fatalf("legacy fallback model = %q, want legacy-image-model", got)
}
cfg := ImageGenerateToolsConfig{Model: "openai-codex/gpt-image-2"}
if got := cfg.EffectiveModel(defaults); got != "openai-codex/gpt-image-2" {
t.Fatalf("tool model = %q, want openai-codex/gpt-image-2", got)
}
if got := (ImageGenerateToolsConfig{}).EffectiveModel(AgentDefaults{}); got != "gpt-image-2" {
t.Fatalf("default model = %q, want gpt-image-2", got)
}
}
func TestLoadConfig_ImageGenerateModel(t *testing.T) {
dir := t.TempDir()
configPath := filepath.Join(dir, "config.json")
raw := `{
"tools": {
"image_generate": {
"enabled": true,
"model": "openai-codex/gpt-image-2"
}
}
}`
if err := os.WriteFile(configPath, []byte(raw), 0o644); err != nil {
t.Fatalf("WriteFile(configPath): %v", err)
}
cfg, err := LoadConfig(configPath)
if err != nil {
t.Fatalf("LoadConfig() error: %v", err)
}
if !cfg.Tools.ImageGenerate.Enabled {
t.Fatal("cfg.Tools.ImageGenerate.Enabled should be true")
}
if got := cfg.Tools.ImageGenerate.Model; got != "openai-codex/gpt-image-2" {
t.Fatalf("cfg.Tools.ImageGenerate.Model = %q, want openai-codex/gpt-image-2", got)
}
}
func TestConfig_BackwardCompat_NoAgentsList(t *testing.T) { func TestConfig_BackwardCompat_NoAgentsList(t *testing.T) {
jsonData := `{ jsonData := `{
"agents": { "agents": {

View file

@ -397,9 +397,11 @@ func DefaultConfig() *Config {
SendFile: ToolConfig{ SendFile: ToolConfig{
Enabled: true, Enabled: true,
}, },
ImageGenerate: ToolConfig{ ImageGenerate: ImageGenerateToolsConfig{
ToolConfig: ToolConfig{
Enabled: false, Enabled: false,
}, },
},
SendTTS: ToolConfig{ SendTTS: ToolConfig{
Enabled: false, Enabled: false,
}, },