This commit is contained in:
@@ -22,6 +22,7 @@ type GeminiService struct {
|
||||
Client *http.Client
|
||||
GenerateEndpoint string
|
||||
TranslateEndpoint string
|
||||
Debug func(message string, data any)
|
||||
}
|
||||
|
||||
type AIRecommendation struct {
|
||||
@@ -50,7 +51,13 @@ func NewGeminiService(apiKey string) *GeminiService {
|
||||
|
||||
func (g *GeminiService) ExpandQuery(query string) ([]string, error) {
|
||||
englishBase := g.TranslateQuery(query)
|
||||
return buildSearchQueries(query, englishBase), nil
|
||||
expanded := buildSearchQueries(query, englishBase)
|
||||
g.debug("gemini:expand_query", map[string]any{
|
||||
"original": query,
|
||||
"english": englishBase,
|
||||
"expanded": expanded,
|
||||
})
|
||||
return expanded, nil
|
||||
}
|
||||
|
||||
func (g *GeminiService) TranslateQuery(query string) string {
|
||||
@@ -67,6 +74,7 @@ func (g *GeminiService) TranslateQuery(query string) string {
|
||||
}
|
||||
|
||||
if g.APIKey != "" {
|
||||
g.debug("gemini:translate_attempt", map[string]any{"mode": "gemini", "query": trimmed})
|
||||
body := map[string]any{
|
||||
"systemInstruction": map[string]any{
|
||||
"parts": []map[string]string{
|
||||
@@ -95,17 +103,25 @@ func (g *GeminiService) TranslateQuery(query string) string {
|
||||
if err == nil {
|
||||
translated := sanitizePlainEnglishLine(rawText)
|
||||
if translated != "" && !strings.EqualFold(translated, trimmed) && !isOvercompressedTranslation(trimmed, translated) {
|
||||
g.debug("gemini:translate_success", map[string]any{"mode": "gemini", "query": trimmed, "translated": translated})
|
||||
return translated
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
g.debug("gemini:translate_error", map[string]any{"mode": "gemini", "query": trimmed, "error": err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
g.debug("gemini:translate_attempt", map[string]any{"mode": "google", "query": trimmed})
|
||||
if translated, err := g.translateViaGoogle(trimmed); err == nil && translated != "" && isLikelyEnglishQuery(translated) && !isOvercompressedTranslation(trimmed, translated) {
|
||||
g.debug("gemini:translate_success", map[string]any{"mode": "google", "query": trimmed, "translated": translated})
|
||||
return translated
|
||||
}
|
||||
if translated := translateKoreanMediaTerms(normalizedIntent); translated != "" && !strings.EqualFold(translated, trimmed) {
|
||||
g.debug("gemini:translate_success", map[string]any{"mode": "dictionary", "query": trimmed, "translated": translated})
|
||||
return translated
|
||||
}
|
||||
g.debug("gemini:translate_fallback_original", map[string]any{"query": trimmed, "normalized": normalizedIntent})
|
||||
return strings.TrimSpace(normalizedIntent)
|
||||
}
|
||||
|
||||
@@ -148,6 +164,10 @@ func (g *GeminiService) Recommend(query string, candidates []SearchResult) ([]AI
|
||||
if len(candidates) == 0 {
|
||||
return []AIRecommendation{}, nil
|
||||
}
|
||||
g.debug("gemini:vision_start", map[string]any{
|
||||
"query": query,
|
||||
"candidateCount": len(candidates),
|
||||
})
|
||||
|
||||
type geminiPart map[string]any
|
||||
parts := []geminiPart{
|
||||
@@ -169,6 +189,12 @@ User query: ` + query,
|
||||
for idx := 0; idx < maxImages; idx++ {
|
||||
img, mimeType, err := fetchCandidateVisualInlineData(g.Client, candidates[idx])
|
||||
if err != nil {
|
||||
g.debug("gemini:vision_candidate_visual_error", map[string]any{
|
||||
"index": idx,
|
||||
"link": candidates[idx].Link,
|
||||
"source": candidates[idx].Source,
|
||||
"error": err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
visualCount++
|
||||
@@ -180,6 +206,11 @@ User query: ` + query,
|
||||
if visualCount == 0 {
|
||||
return nil, fmt.Errorf("no candidate thumbnails or preview frames could be fetched for gemini vision")
|
||||
}
|
||||
g.debug("gemini:vision_visuals_prepared", map[string]any{
|
||||
"query": query,
|
||||
"visualCount": visualCount,
|
||||
"maxImages": maxImages,
|
||||
})
|
||||
|
||||
body := map[string]any{
|
||||
"contents": []map[string]any{
|
||||
@@ -254,10 +285,20 @@ User query: ` + query,
|
||||
Recommended: recommended,
|
||||
})
|
||||
}
|
||||
g.debug("gemini:vision_complete", map[string]any{
|
||||
"query": query,
|
||||
"recommendationCount": len(recommendations),
|
||||
})
|
||||
|
||||
return recommendations, nil
|
||||
}
|
||||
|
||||
func (g *GeminiService) debug(message string, data any) {
|
||||
if g != nil && g.Debug != nil {
|
||||
g.Debug(message, data)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchImageAsInlineData(client *http.Client, imageURL, referer string) (string, string, error) {
|
||||
if strings.TrimSpace(imageURL) == "" {
|
||||
return "", "", fmt.Errorf("image url is empty")
|
||||
|
||||
Reference in New Issue
Block a user