feat: add image file validation and format detection for image generation

## CHANGES

• Add image file path validation with extension checking
• Implement dynamic output format detection from file extensions
• Update BuildChatOptions method to return error for validation
• Add comprehensive test coverage for image file validation
• Upgrade YAML library from v2 to v3
• Update shell completions to reflect supported image formats
• Add error handling for existing file conflicts
• Support PNG, JPEG, JPG, and WEBP image formats
This commit is contained in:
Kayvan Sylvan
2025-07-04 17:56:59 -07:00
parent 17d863fd57
commit e59156ac2b
10 changed files with 262 additions and 10 deletions

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/danielmiessler/fabric/common"
"github.com/openai/openai-go/responses"
@@ -17,15 +18,37 @@ import (
const ImageGenerationResponseType = "image_generation_call"
const ImageGenerationToolType = "image_generation"
// getOutputFormatFromExtension determines the API output format based on file extension
func getOutputFormatFromExtension(imagePath string) string {
if imagePath == "" {
return "png" // Default format
}
ext := strings.ToLower(filepath.Ext(imagePath))
switch ext {
case ".png":
return "png"
case ".webp":
return "webp"
case ".jpg":
return "jpeg"
case ".jpeg":
return "jpeg"
default:
return "png" // Default fallback
}
}
// addImageGenerationTool adds the image generation tool to the request if needed
func (o *Client) addImageGenerationTool(opts *common.ChatOptions, tools []responses.ToolUnionParam) []responses.ToolUnionParam {
// Check if the request seems to be asking for image generation
if o.shouldUseImageGeneration(opts) {
outputFormat := getOutputFormatFromExtension(opts.ImageFile)
imageGenTool := responses.ToolUnionParam{
OfImageGeneration: &responses.ToolImageGenerationParam{
Type: ImageGenerationToolType,
Model: "gpt-image-1",
OutputFormat: "png",
OutputFormat: outputFormat,
Quality: "auto",
Size: "auto",
},

View File

@@ -112,3 +112,109 @@ func TestBuildResponseParams_WithBothSearchAndImage(t *testing.T) {
assert.True(t, hasSearchTool, "Should have web search tool")
assert.True(t, hasImageTool, "Should have image generation tool")
}
func TestGetOutputFormatFromExtension(t *testing.T) {
tests := []struct {
name string
imagePath string
expectedFormat string
}{
{
name: "PNG extension",
imagePath: "/tmp/output.png",
expectedFormat: "png",
},
{
name: "WEBP extension",
imagePath: "/tmp/output.webp",
expectedFormat: "webp",
},
{
name: "JPG extension",
imagePath: "/tmp/output.jpg",
expectedFormat: "jpeg",
},
{
name: "JPEG extension",
imagePath: "/tmp/output.jpeg",
expectedFormat: "jpeg",
},
{
name: "Uppercase PNG extension",
imagePath: "/tmp/output.PNG",
expectedFormat: "png",
},
{
name: "Mixed case JPEG extension",
imagePath: "/tmp/output.JpEg",
expectedFormat: "jpeg",
},
{
name: "Empty path",
imagePath: "",
expectedFormat: "png",
},
{
name: "No extension",
imagePath: "/tmp/output",
expectedFormat: "png",
},
{
name: "Unsupported extension",
imagePath: "/tmp/output.gif",
expectedFormat: "png",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := getOutputFormatFromExtension(tt.imagePath)
assert.Equal(t, tt.expectedFormat, result)
})
}
}
func TestAddImageGenerationToolWithDynamicFormat(t *testing.T) {
client := NewClient()
tests := []struct {
name string
imageFile string
expectedFormat string
}{
{
name: "PNG file",
imageFile: "/tmp/output.png",
expectedFormat: "png",
},
{
name: "WEBP file",
imageFile: "/tmp/output.webp",
expectedFormat: "webp",
},
{
name: "JPG file",
imageFile: "/tmp/output.jpg",
expectedFormat: "jpeg",
},
{
name: "JPEG file",
imageFile: "/tmp/output.jpeg",
expectedFormat: "jpeg",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts := &common.ChatOptions{
ImageFile: tt.imageFile,
}
tools := client.addImageGenerationTool(opts, []responses.ToolUnionParam{})
assert.Len(t, tools, 1, "Should have one tool")
assert.NotNil(t, tools[0].OfImageGeneration, "Should be image generation tool")
assert.Equal(t, tt.expectedFormat, tools[0].OfImageGeneration.OutputFormat, "Output format should match file extension")
})
}
}