Add GIPHY image search feature
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
|
||||
type GeminiService struct {
|
||||
APIKey string
|
||||
Model string
|
||||
Client *http.Client
|
||||
GenerateEndpoint string
|
||||
TranslateEndpoint string
|
||||
@@ -69,11 +70,15 @@ type QueryExpansion struct {
|
||||
Querywords []string `json:"querywords"`
|
||||
}
|
||||
|
||||
func NewGeminiService(apiKey string) *GeminiService {
|
||||
func NewGeminiService(apiKey, model string) *GeminiService {
|
||||
if strings.TrimSpace(model) == "" {
|
||||
model = "gemini-2.5-flash"
|
||||
}
|
||||
return &GeminiService{
|
||||
APIKey: apiKey,
|
||||
Model: model,
|
||||
Client: &http.Client{Timeout: 40 * time.Second},
|
||||
GenerateEndpoint: "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent",
|
||||
GenerateEndpoint: "https://generativelanguage.googleapis.com/v1beta/models/" + model + ":generateContent",
|
||||
TranslateEndpoint: "https://translate.googleapis.com/translate_a/single",
|
||||
visualCache: map[string]cachedVisualData{},
|
||||
translationCache: map[string]cachedStringValue{},
|
||||
@@ -99,6 +104,75 @@ func (g *GeminiService) ExpandQuery(query string) ([]string, error) {
|
||||
return expanded, nil
|
||||
}
|
||||
|
||||
func (g *GeminiService) ExpandImageQueries(query string) ([]string, error) {
|
||||
trimmed := strings.TrimSpace(query)
|
||||
if trimmed == "" {
|
||||
return nil, fmt.Errorf("query is empty")
|
||||
}
|
||||
cacheKey := "image-expansion\n" + trimmed
|
||||
if cached, ok := g.getCachedExpansion(cacheKey); ok {
|
||||
g.debug("gemini:image_expand_cache_hit", map[string]any{"query": trimmed, "expanded": cached})
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
fallback := buildFallbackImageQueries(trimmed, g.TranslateQuery(trimmed))
|
||||
if g.APIKey == "" {
|
||||
g.setCachedExpansion(cacheKey, fallback, 15*time.Minute)
|
||||
return fallback, fmt.Errorf("gemini api key is not configured")
|
||||
}
|
||||
|
||||
body := map[string]any{
|
||||
"systemInstruction": map[string]any{
|
||||
"parts": []map[string]string{{
|
||||
"text": "Return exactly 5 concise English search queries for GIPHY image or GIF search. Respond with JSON only in this shape: {\"queries\":[\"...\",\"...\",\"...\",\"...\",\"...\"]}. Keep the queries meaning-preserving, practical, deduplicated, and concise.",
|
||||
}},
|
||||
},
|
||||
"contents": []map[string]any{{
|
||||
"parts": []map[string]string{{
|
||||
"text": "User query: " + trimmed + "\nGenerate exactly 5 English search queries for GIPHY image or GIF search. Include a direct translation, a common phrasing, and only relevant related variants.",
|
||||
}},
|
||||
}},
|
||||
"generationConfig": map[string]any{
|
||||
"responseMimeType": "application/json",
|
||||
"temperature": 0.2,
|
||||
"maxOutputTokens": 160,
|
||||
},
|
||||
}
|
||||
|
||||
rawText, err := g.generateText(body)
|
||||
if err != nil {
|
||||
g.debug("gemini:image_expand_error", map[string]any{"query": trimmed, "error": err.Error()})
|
||||
g.setCachedExpansion(cacheKey, fallback, 15*time.Minute)
|
||||
return fallback, err
|
||||
}
|
||||
jsonText, err := extractJSONObject(rawText)
|
||||
if err != nil {
|
||||
g.debug("gemini:image_expand_parse_error", map[string]any{"query": trimmed, "error": err.Error()})
|
||||
g.setCachedExpansion(cacheKey, fallback, 15*time.Minute)
|
||||
return fallback, err
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Queries []string `json:"queries"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(jsonText), &payload); err != nil {
|
||||
g.debug("gemini:image_expand_json_error", map[string]any{"query": trimmed, "error": err.Error(), "raw": truncateForError(rawText, 200)})
|
||||
g.setCachedExpansion(cacheKey, fallback, 15*time.Minute)
|
||||
return fallback, err
|
||||
}
|
||||
queries := normalizeImageExpansionQueries(payload.Queries)
|
||||
if len(queries) != 5 {
|
||||
err := fmt.Errorf("gemini image expansion returned %d queries", len(queries))
|
||||
g.debug("gemini:image_expand_invalid_count", map[string]any{"query": trimmed, "queries": queries, "error": err.Error()})
|
||||
g.setCachedExpansion(cacheKey, fallback, 15*time.Minute)
|
||||
return fallback, err
|
||||
}
|
||||
|
||||
g.setCachedExpansion(cacheKey, queries, 15*time.Minute)
|
||||
g.debug("gemini:image_expand_success", map[string]any{"query": trimmed, "queries": queries})
|
||||
return queries, nil
|
||||
}
|
||||
|
||||
func (g *GeminiService) TranslateSummaryToKorean(text string) (string, error) {
|
||||
trimmed := strings.TrimSpace(text)
|
||||
if trimmed == "" {
|
||||
|
||||
Reference in New Issue
Block a user