Files
ai-media-hub/backend/services/ai.go
AI Assistant d030e737cb
Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled
Initial commit for AI Media Hub
2026-03-12 14:13:05 +09:00

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