summaryrefslogtreecommitdiff
path: root/internal/image/search.go
blob: 80d5e33faabd0b8e073f1e7ed29a619aabb8bb81 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package image

import (
	"context"
	"io"
)

// SearchResult represents a single image search result
type SearchResult struct {
	ID           string // Unique identifier
	URL          string // Direct URL to the image
	ThumbnailURL string // URL to thumbnail version
	Width        int    // Image width in pixels
	Height       int    // Image height in pixels
	Description  string // Image description or tags
	Attribution  string // Attribution text if required
	Source       string // Source provider (e.g., "pixabay", "unsplash")
}

// SearchOptions configures the image search
type SearchOptions struct {
	Query        string // Search query (Bulgarian word)
	Translation  string // English translation (if already available)
	Language     string // Language code (default: "bg")
	SafeSearch   bool   // Enable safe search filtering
	PerPage      int    // Number of results per page
	Page         int    // Page number (1-based)
	ImageType    string // Type: "photo", "illustration", "vector", "all"
	Orientation  string // Orientation: "horizontal", "vertical", "all"
	CustomPrompt    string  // Custom prompt for AI image generation
	AspectRatio     string  // Override aspect ratio (e.g. "9:16"); empty = provider default
	// ReferenceImages holds raw PNG bytes of previously generated images.
	// When non-empty, the NanoBanana client sends them as multimodal content
	// alongside the text prompt so the model can match character appearance
	// across pages (iterative chaining technique).
	ReferenceImages [][]byte
}

// DefaultSearchOptions returns sensible defaults for Bulgarian word searches
func DefaultSearchOptions(query string) *SearchOptions {
	return &SearchOptions{
		Query:       query,
		Language:    "bg",
		SafeSearch:  true,
		PerPage:     10,
		Page:        1,
		ImageType:   "photo",
		Orientation: "all",
	}
}

// AttributionProvider returns required attribution text for a search result.
// Kept separate from ImageSearcher so callers that only need attribution
// do not depend on Search/Download/Name.
type AttributionProvider interface {
	GetAttribution(result *SearchResult) string
}

// ImageSearcher defines the interface for image search providers.
// Providers that also carry attribution text implement AttributionProvider
// in addition to this interface.
type ImageSearcher interface {
	// Search performs an image search with the given options.
	Search(ctx context.Context, opts *SearchOptions) ([]SearchResult, error)

	// Download downloads an image from the given URL.
	Download(ctx context.Context, url string) (io.ReadCloser, error)

	// Name returns the name of the search provider.
	Name() string
}

// ImageClient combines ImageSearcher and AttributionProvider for callers
// that need full provider capabilities (search, download, attribution).
type ImageClient interface {
	ImageSearcher
	AttributionProvider
}

// SearchError represents an error from an image search provider
type SearchError struct {
	Provider string
	Code     string
	Message  string
}

func (e *SearchError) Error() string {
	return e.Provider + ": " + e.Message
}

// RateLimitError indicates that the API rate limit has been exceeded
type RateLimitError struct {
	Provider     string
	RetryAfter   int // Seconds to wait before retry
	LimitPerHour int
	LimitPerDay  int
}

func (e *RateLimitError) Error() string {
	return e.Provider + ": rate limit exceeded"
}