160 lines
4.2 KiB
Go
160 lines
4.2 KiB
Go
// Package adapters provides mediagen provider adapters for various AI services.
|
|
package adapters
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/gemini"
|
|
"git.threesix.ai/jordan/persona-community-2/pkg/mediagen"
|
|
)
|
|
|
|
// GeminiProvider adapts pkg/gemini to the mediagen interfaces.
|
|
type GeminiProvider struct {
|
|
client *gemini.Client
|
|
}
|
|
|
|
// NewGeminiProvider creates a new Gemini provider adapter.
|
|
func NewGeminiProvider(client *gemini.Client) *GeminiProvider {
|
|
return &GeminiProvider{client: client}
|
|
}
|
|
|
|
// Name implements mediagen.ImageGenerator and mediagen.VideoGenerator.
|
|
func (p *GeminiProvider) Name() string {
|
|
return "gemini"
|
|
}
|
|
|
|
// Health implements mediagen.ImageGenerator and mediagen.VideoGenerator.
|
|
func (p *GeminiProvider) Health(ctx context.Context) error {
|
|
if err := p.client.Health(ctx); err != nil {
|
|
return mediagen.NewProviderError("gemini", "Health", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// validImageMIMETypes lists MIME types supported for reference images
|
|
var geminiValidImageMIMETypes = map[string]bool{
|
|
"image/png": true,
|
|
"image/jpeg": true,
|
|
"image/webp": true,
|
|
}
|
|
|
|
// GenerateImage implements mediagen.ImageGenerator.
|
|
// Note: Gemini's native image generation currently supports only 1 image per request.
|
|
// The Count field is ignored.
|
|
func (p *GeminiProvider) GenerateImage(ctx context.Context, req mediagen.ImageRequest) (*mediagen.ImageResponse, error) {
|
|
// Validate reference image MIME type if provided
|
|
if len(req.ReferenceImage) > 0 && req.ReferenceMime != "" {
|
|
if !geminiValidImageMIMETypes[req.ReferenceMime] {
|
|
return nil, fmt.Errorf("unsupported reference image MIME type: %s (supported: image/png, image/jpeg, image/webp)", req.ReferenceMime)
|
|
}
|
|
}
|
|
|
|
// Map unified request to Gemini-specific request
|
|
gemReq := gemini.ImageRequest{
|
|
Prompt: req.Prompt,
|
|
Model: req.Model,
|
|
Size: req.Size,
|
|
AspectRatio: req.AspectRatio,
|
|
ReferenceImage: req.ReferenceImage,
|
|
ReferenceMime: req.ReferenceMime,
|
|
Seed: req.Seed,
|
|
}
|
|
|
|
// Call Gemini client
|
|
gemResp, err := p.client.GenerateImage(ctx, gemReq)
|
|
if err != nil {
|
|
return nil, mediagen.NewProviderError("gemini", "GenerateImage", err)
|
|
}
|
|
|
|
// Convert response to unified format
|
|
images := make([]mediagen.Image, 0, len(gemResp.Images))
|
|
for _, img := range gemResp.Images {
|
|
images = append(images, mediagen.Image{
|
|
Data: img.Data,
|
|
MimeType: img.MimeType,
|
|
})
|
|
}
|
|
|
|
return &mediagen.ImageResponse{
|
|
Images: images,
|
|
Seed: gemResp.Seed,
|
|
}, nil
|
|
}
|
|
|
|
// GenerateVideo implements mediagen.VideoGenerator.
|
|
func (p *GeminiProvider) GenerateVideo(ctx context.Context, req mediagen.VideoRequest) (*mediagen.VideoResponse, error) {
|
|
// Map unified request to Gemini-specific request
|
|
gemReq := gemini.VideoRequest{
|
|
Prompt: req.Prompt,
|
|
Model: req.Model,
|
|
AspectRatio: req.AspectRatio,
|
|
Duration: req.Duration,
|
|
}
|
|
|
|
// Convert first reference image if provided
|
|
if len(req.ReferenceImages) > 0 {
|
|
img := req.ReferenceImages[0]
|
|
gemReq.Image = img.Data
|
|
gemReq.ImageMimeType = img.MimeType
|
|
}
|
|
|
|
// Call Gemini client
|
|
gemResp, err := p.client.GenerateVideo(ctx, gemReq)
|
|
if err != nil {
|
|
return nil, mediagen.NewProviderError("gemini", "GenerateVideo", err)
|
|
}
|
|
|
|
// If video was returned as URI, download it
|
|
var videoData []byte
|
|
var mimeType string
|
|
var url string
|
|
|
|
if len(gemResp.Video.Data) > 0 {
|
|
videoData = gemResp.Video.Data
|
|
mimeType = gemResp.Video.MimeType
|
|
}
|
|
|
|
if gemResp.Video.URI != "" {
|
|
url = gemResp.Video.URI
|
|
|
|
// If we don't have data yet, download from URI
|
|
if len(videoData) == 0 {
|
|
downloaded, downloadErr := downloadVideo(ctx, gemResp.Video.URI)
|
|
if downloadErr != nil {
|
|
// Return URI-only response if download fails (caller can retry download)
|
|
return &mediagen.VideoResponse{
|
|
Videos: []mediagen.Video{
|
|
{
|
|
URL: url,
|
|
MimeType: "video/mp4",
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
videoData = downloaded
|
|
mimeType = "video/mp4"
|
|
}
|
|
}
|
|
|
|
if mimeType == "" {
|
|
mimeType = "video/mp4"
|
|
}
|
|
|
|
return &mediagen.VideoResponse{
|
|
Videos: []mediagen.Video{
|
|
{
|
|
Data: videoData,
|
|
MimeType: mimeType,
|
|
URL: url,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Compile-time interface check
|
|
var (
|
|
_ mediagen.ImageGenerator = (*GeminiProvider)(nil)
|
|
_ mediagen.VideoGenerator = (*GeminiProvider)(nil)
|
|
)
|