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") }