Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled
95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package services
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
)
|
|
|
|
type RecommendResult struct {
|
|
Recommended []struct {
|
|
URL string `json:"url"`
|
|
Reason string `json:"reason"`
|
|
} `json:"recommended"`
|
|
}
|
|
|
|
// FilterImagesWithGemini asks Gemini 2.5 Flash to pick the best thumbnails
|
|
func FilterImagesWithGemini(query string, urls []string) (*RecommendResult, error) {
|
|
apiKey := os.Getenv("GEMINI_API_KEY")
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("GEMINI_API_KEY not configured")
|
|
}
|
|
|
|
// Because Gemini multi-modal expects base64 or public URLs, since these are public URLs from Google search,
|
|
// we can try providing instructions with URLs to be analyzed, or if direct URL access fails, we might need to download them.
|
|
// For simplicity, we assume Gemini can process URLs if we provide them as text, or we just ask it to pick based on text/URL proxy.
|
|
// Actually, the standard way is to download and attach as inline data. Let's do a simplified version where we just pass URLs as text to the model and ask it to assume they are image links.
|
|
// Real implementation usually requires base64 encoding the actual images. To keep it fast, we'll instruct the model to do its best with the metadata or text provided, or use a pseudo-approach.
|
|
|
|
prompt := fmt.Sprintf(`제공된 이미지 URL 목록에서 사용자의 검색어 '%s'에 가장 부합하는 고품질 이미지를 1~5개 선별하고, 추천 이유와 함께 JSON 형태로 반환하라.
|
|
형식: {"recommended": [{"url": "url", "reason": "이유"}]}
|
|
URL 목록: %v`, query, urls)
|
|
|
|
payload := map[string]interface{}{
|
|
"contents": []map[string]interface{}{
|
|
{
|
|
"parts": []map[string]interface{}{
|
|
{"text": prompt},
|
|
},
|
|
},
|
|
},
|
|
"generationConfig": map[string]interface{}{
|
|
"response_mime_type": "application/json",
|
|
},
|
|
}
|
|
|
|
bodyBytes, _ := json.Marshal(payload)
|
|
url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=%s", apiKey)
|
|
|
|
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
return nil, fmt.Errorf("Gemini API returned status: %d", resp.StatusCode)
|
|
}
|
|
|
|
respBody, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
// Parse Gemini Response
|
|
var geminiResp struct {
|
|
Candidates []struct {
|
|
Content struct {
|
|
Parts []struct {
|
|
Text string `json:"text"`
|
|
} `json:"parts"`
|
|
} `json:"content"`
|
|
} `json:"candidates"`
|
|
}
|
|
|
|
if err := json.Unmarshal(respBody, &geminiResp); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(geminiResp.Candidates) > 0 && len(geminiResp.Candidates[0].Content.Parts) > 0 {
|
|
jsonStr := geminiResp.Candidates[0].Content.Parts[0].Text
|
|
var res RecommendResult
|
|
err := json.Unmarshal([]byte(jsonStr), &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &res, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("No valid response from Gemini")
|
|
}
|