mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-09 14:28:01 -05:00
## CHANGES - Upgrade GitHub Actions to latest versions (v6, v21) - Add modernization check step in CI workflow - Replace strings manipulation with `strings.CutPrefix` and `strings.CutSuffix` - Replace manual loops with `slices.Contains` for validation - Use `strings.SplitSeq` for iterator-based string splitting - Replace `bytes.TrimPrefix` with `bytes.CutPrefix` for clarity - Use `strings.Builder` instead of string concatenation - Replace `fmt.Sprintf` with `fmt.Appendf` for efficiency - Simplify padding calculation with `max` builtin
143 lines
4.2 KiB
Go
143 lines
4.2 KiB
Go
package openai
|
|
|
|
// This file contains helper methods for image generation and processing
|
|
// using OpenAI's Responses API and Image API.
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/danielmiessler/fabric/internal/domain"
|
|
"github.com/openai/openai-go/packages/param"
|
|
"github.com/openai/openai-go/responses"
|
|
)
|
|
|
|
// ImageGenerationResponseType is the type used for image generation calls in responses
|
|
const ImageGenerationResponseType = "image_generation_call"
|
|
const ImageGenerationToolType = "image_generation"
|
|
|
|
// ImageGenerationSupportedModels lists all models that support image generation
|
|
var ImageGenerationSupportedModels = []string{
|
|
"gpt-4o",
|
|
"gpt-4o-mini",
|
|
"gpt-4.1",
|
|
"gpt-4.1-mini",
|
|
"gpt-4.1-nano",
|
|
"o3",
|
|
}
|
|
|
|
// supportsImageGeneration checks if the given model supports the image_generation tool
|
|
func supportsImageGeneration(model string) bool {
|
|
return slices.Contains(ImageGenerationSupportedModels, model)
|
|
}
|
|
|
|
// 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 *domain.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)
|
|
|
|
// Build the image generation tool with user parameters
|
|
imageGenTool := responses.ToolUnionParam{
|
|
OfImageGeneration: &responses.ToolImageGenerationParam{
|
|
Type: ImageGenerationToolType,
|
|
Model: "gpt-image-1",
|
|
OutputFormat: outputFormat,
|
|
},
|
|
}
|
|
|
|
// Set quality if specified by user (otherwise let OpenAI use default)
|
|
if opts.ImageQuality != "" {
|
|
imageGenTool.OfImageGeneration.Quality = opts.ImageQuality
|
|
}
|
|
|
|
// Set size if specified by user (otherwise let OpenAI use default)
|
|
if opts.ImageSize != "" {
|
|
imageGenTool.OfImageGeneration.Size = opts.ImageSize
|
|
}
|
|
|
|
// Set background if specified by user (otherwise let OpenAI use default)
|
|
if opts.ImageBackground != "" {
|
|
imageGenTool.OfImageGeneration.Background = opts.ImageBackground
|
|
}
|
|
|
|
// Set compression if specified by user (only for jpeg/webp)
|
|
if opts.ImageCompression != 0 {
|
|
imageGenTool.OfImageGeneration.OutputCompression = param.NewOpt(int64(opts.ImageCompression))
|
|
}
|
|
|
|
tools = append(tools, imageGenTool)
|
|
}
|
|
return tools
|
|
}
|
|
|
|
// shouldUseImageGeneration determines if image generation should be enabled
|
|
// This is a heuristic based on the presence of --image-file flag
|
|
func (o *Client) shouldUseImageGeneration(opts *domain.ChatOptions) bool {
|
|
return opts.ImageFile != ""
|
|
}
|
|
|
|
// extractAndSaveImages extracts generated images from the response and saves them
|
|
func (o *Client) extractAndSaveImages(resp *responses.Response, opts *domain.ChatOptions) error {
|
|
if opts.ImageFile == "" {
|
|
return nil // No image file specified, skip saving
|
|
}
|
|
|
|
// Extract image data from response
|
|
for _, item := range resp.Output {
|
|
if item.Type == ImageGenerationResponseType {
|
|
imageCall := item.AsImageGenerationCall()
|
|
if imageCall.Status == "completed" && imageCall.Result != "" {
|
|
// Decode base64 image data
|
|
imageData, err := base64.StdEncoding.DecodeString(imageCall.Result)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode image data: %w", err)
|
|
}
|
|
|
|
// Ensure directory exists
|
|
dir := filepath.Dir(opts.ImageFile)
|
|
if dir != "." {
|
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create directory %s: %w", dir, err)
|
|
}
|
|
}
|
|
|
|
// Save image to file
|
|
if err := os.WriteFile(opts.ImageFile, imageData, 0644); err != nil {
|
|
return fmt.Errorf("failed to save image to %s: %w", opts.ImageFile, err)
|
|
}
|
|
|
|
fmt.Printf("Image saved to: %s\n", opts.ImageFile)
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|