Stabilize Gemini visual fallback handling
build-push / docker (push) Successful in 4m4s

This commit is contained in:
AI Assistant
2026-03-16 17:22:12 +09:00
parent 001f4fd4bb
commit 4db2b1f963
5 changed files with 53 additions and 9 deletions
+10
View File
@@ -134,6 +134,16 @@ func TestLowValueThumbnailDetection(t *testing.T) {
}
}
func TestGoogleVideoCollectorPrefersYouTubeDerivedThumbnail(t *testing.T) {
result := googleVideoCollector{}.Enrich(nil, SearchResult{
Link: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
ThumbnailURL: "https://example.com/some-search-thumb.jpg",
})
if result.ThumbnailURL != "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg" {
t.Fatalf("expected derived youtube thumbnail, got %q", result.ThumbnailURL)
}
}
func TestGeminiCandidateLimitNeverExceedsCandidates(t *testing.T) {
if got := GeminiCandidateLimit(9); got != 9 {
t.Fatalf("expected Gemini limit to stay within candidate count, got %d", got)
+24 -7
View File
@@ -474,6 +474,7 @@ func newBrowserStyleImageRequest(imageURL, referer string) (*http.Request, error
}
func (g *GeminiService) fetchCandidateVisualInlineData(candidate SearchResult) (string, string, error) {
lastErr := fmt.Errorf("candidate has no thumbnail or preview video")
if candidate.PreviewVideoURL != "" && (candidate.Source == "Envato" || candidate.Source == "Artgrid") {
cacheKey := "frame\n" + candidate.PreviewVideoURL
if data, mimeType, ok := g.getCachedVisual(cacheKey); ok {
@@ -484,6 +485,7 @@ func (g *GeminiService) fetchCandidateVisualInlineData(candidate SearchResult) (
g.setCachedVisual(cacheKey, data, mimeType, 10*time.Minute)
return data, mimeType, nil
}
lastErr = err
}
if candidate.ThumbnailURL != "" {
if isLowValueThumbnail(candidate.ThumbnailURL) {
@@ -492,17 +494,31 @@ func (g *GeminiService) fetchCandidateVisualInlineData(candidate SearchResult) (
"source": candidate.Source,
"thumbnailUrl": candidate.ThumbnailURL,
})
return "", "", fmt.Errorf("candidate thumbnail is low value")
lastErr = fmt.Errorf("candidate thumbnail is low value")
} else {
cacheKey := "image\n" + candidate.ThumbnailURL
if data, mimeType, ok := g.getCachedVisual(cacheKey); ok {
return data, mimeType, nil
}
data, mimeType, err := fetchImageAsInlineData(g.Client, candidate.ThumbnailURL, candidate.Link)
if err == nil {
g.setCachedVisual(cacheKey, data, mimeType, 10*time.Minute)
return data, mimeType, nil
}
lastErr = err
}
cacheKey := "image\n" + candidate.ThumbnailURL
}
if fallbackThumbnail := deriveThumbnail(candidate.Link); fallbackThumbnail != "" && fallbackThumbnail != candidate.ThumbnailURL {
cacheKey := "image\n" + fallbackThumbnail
if data, mimeType, ok := g.getCachedVisual(cacheKey); ok {
return data, mimeType, nil
}
data, mimeType, err := fetchImageAsInlineData(g.Client, candidate.ThumbnailURL, candidate.Link)
data, mimeType, err := fetchImageAsInlineData(g.Client, fallbackThumbnail, candidate.Link)
if err == nil {
g.setCachedVisual(cacheKey, data, mimeType, 10*time.Minute)
return data, mimeType, nil
}
lastErr = err
}
if candidate.PreviewVideoURL != "" {
cacheKey := "frame\n" + candidate.PreviewVideoURL
@@ -511,12 +527,13 @@ func (g *GeminiService) fetchCandidateVisualInlineData(candidate SearchResult) (
}
data, mimeType, err := extractFrameFromVideo(candidate.PreviewVideoURL)
if err != nil {
return "", "", err
lastErr = err
} else {
g.setCachedVisual(cacheKey, data, mimeType, 10*time.Minute)
return data, mimeType, nil
}
g.setCachedVisual(cacheKey, data, mimeType, 10*time.Minute)
return data, mimeType, nil
}
return "", "", fmt.Errorf("candidate has no thumbnail or preview video")
return "", "", lastErr
}
func extractFrameFromVideo(videoURL string) (string, string, error) {
+2 -2
View File
@@ -59,8 +59,8 @@ func (googleVideoCollector) Accept(result SearchResult) bool {
return isUsefulGoogleVideoResult(result)
}
func (googleVideoCollector) Enrich(searcher *SearchService, result SearchResult) SearchResult {
if result.ThumbnailURL == "" {
result.ThumbnailURL = deriveThumbnail(result.Link)
if derived := deriveThumbnail(result.Link); derived != "" {
result.ThumbnailURL = derived
}
result.Source = strings.TrimSpace(result.Source)
if result.Source == "" {