This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const GeminiFallbackReason = "Gemini Vision 응답이 부족해 키워드 기준으로 보강된 결과입니다."
|
||||
|
||||
type GeminiBatchStats struct {
|
||||
CandidateCap int `json:"candidateCap"`
|
||||
Requested int `json:"requested"`
|
||||
@@ -84,9 +87,13 @@ func GeminiCandidateLimit(total int) int {
|
||||
return total
|
||||
}
|
||||
|
||||
func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranked []SearchResult) ([]AIRecommendation, GeminiBatchStats) {
|
||||
func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranked []SearchResult) ([]AIRecommendation, GeminiBatchStats, error) {
|
||||
const chunkSize = 8
|
||||
const maxConcurrentBatches = 2
|
||||
if service == nil {
|
||||
return nil, GeminiBatchStats{}, fmt.Errorf("gemini service is not configured")
|
||||
}
|
||||
|
||||
limit := GeminiCandidateLimit(len(ranked))
|
||||
stats := GeminiBatchStats{
|
||||
CandidateCap: limit,
|
||||
@@ -106,6 +113,9 @@ func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranke
|
||||
batches = append(batches, ranked[start:end])
|
||||
}
|
||||
stats.Batches = len(batches)
|
||||
if len(batches) == 0 {
|
||||
return []AIRecommendation{}, stats, nil
|
||||
}
|
||||
|
||||
results := make([]batchResult, len(batches))
|
||||
var wg sync.WaitGroup
|
||||
@@ -146,7 +156,41 @@ func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranke
|
||||
}
|
||||
}
|
||||
stats.RecommendedCount = len(merged)
|
||||
return merged, stats
|
||||
|
||||
switch {
|
||||
case len(merged) > 0 && stats.Failed == 0:
|
||||
return merged, stats, nil
|
||||
case len(merged) > 0 && stats.Failed > 0:
|
||||
return merged, stats, fmt.Errorf("gemini vision partially failed on %d of %d batches", stats.Failed, stats.Batches)
|
||||
case stats.Failed == stats.Batches:
|
||||
if len(stats.Errors) > 0 {
|
||||
return nil, stats, fmt.Errorf("gemini vision failed for all batches: %s", strings.Join(stats.Errors, "; "))
|
||||
}
|
||||
return nil, stats, fmt.Errorf("gemini vision failed for all batches")
|
||||
default:
|
||||
return nil, stats, fmt.Errorf("gemini vision returned no candidate evaluations")
|
||||
}
|
||||
}
|
||||
|
||||
func BuildFallbackRecommendations(ranked []SearchResult, limit int, reason string) []AIRecommendation {
|
||||
if strings.TrimSpace(reason) == "" {
|
||||
reason = GeminiFallbackReason
|
||||
}
|
||||
|
||||
fallback := make([]AIRecommendation, 0, min(limit, len(ranked)))
|
||||
for _, item := range ranked[:min(limit, len(ranked))] {
|
||||
fallback = append(fallback, AIRecommendation{
|
||||
Title: item.Title,
|
||||
Link: item.Link,
|
||||
Snippet: item.Snippet,
|
||||
ThumbnailURL: item.ThumbnailURL,
|
||||
PreviewVideoURL: item.PreviewVideoURL,
|
||||
Source: item.Source,
|
||||
Reason: reason,
|
||||
Recommended: false,
|
||||
})
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult, limit int) []AIRecommendation {
|
||||
@@ -184,7 +228,7 @@ func MergeRecommendations(recommended []AIRecommendation, ranked []SearchResult,
|
||||
ThumbnailURL: item.ThumbnailURL,
|
||||
PreviewVideoURL: item.PreviewVideoURL,
|
||||
Source: item.Source,
|
||||
Reason: "Gemini Vision 응답이 부족해 키워드 기준으로 보강된 결과입니다.",
|
||||
Reason: GeminiFallbackReason,
|
||||
Recommended: false,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user