This commit is contained in:
+1
-10
@@ -260,7 +260,7 @@ func (a *App) searchMedia(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "expanding query with Gemini", "progress": 10})
|
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "expanding query with Gemini", "progress": 10})
|
||||||
queryVariants, expandErr := a.GeminiService.ExpandQuery(req.Query)
|
queryVariants, _ := a.GeminiService.ExpandQuery(req.Query)
|
||||||
if len(queryVariants) == 0 {
|
if len(queryVariants) == 0 {
|
||||||
queryVariants = []string{req.Query}
|
queryVariants = []string{req.Query}
|
||||||
}
|
}
|
||||||
@@ -274,9 +274,6 @@ func (a *App) searchMedia(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
warning := "SearXNG returned no renderable results."
|
warning := "SearXNG returned no renderable results."
|
||||||
if expandErr != nil {
|
|
||||||
warning += " Query expansion failed: " + expandErr.Error()
|
|
||||||
}
|
|
||||||
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "no renderable search results", "progress": 100, "message": warning})
|
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "no renderable search results", "progress": 100, "message": warning})
|
||||||
c.JSON(http.StatusOK, gin.H{"results": []services.AIRecommendation{}, "warning": warning})
|
c.JSON(http.StatusOK, gin.H{"results": []services.AIRecommendation{}, "warning": warning})
|
||||||
return
|
return
|
||||||
@@ -300,18 +297,12 @@ func (a *App) searchMedia(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
warning := err.Error()
|
warning := err.Error()
|
||||||
if expandErr != nil {
|
|
||||||
warning = warning + " Query expansion failed: " + expandErr.Error()
|
|
||||||
}
|
|
||||||
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "Gemini Vision fallback to ranked results", "progress": 90, "message": warning})
|
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "Gemini Vision fallback to ranked results", "progress": 90, "message": warning})
|
||||||
c.JSON(http.StatusOK, gin.H{"results": fallback, "warning": warning, "queries": queryVariants})
|
c.JSON(http.StatusOK, gin.H{"results": fallback, "warning": warning, "queries": queryVariants})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response := gin.H{"results": mergeRecommendations(recommended, scored, 20), "queries": queryVariants}
|
response := gin.H{"results": mergeRecommendations(recommended, scored, 20), "queries": queryVariants}
|
||||||
if expandErr != nil {
|
|
||||||
response["warning"] = "Gemini query expansion failed: " + expandErr.Error() + ". Using the original query only."
|
|
||||||
}
|
|
||||||
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "search complete", "progress": 100})
|
a.Hub.Broadcast("progress", gin.H{"type": "search", "status": "search complete", "progress": 100})
|
||||||
c.JSON(http.StatusOK, response)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func NewGeminiService(apiKey string) *GeminiService {
|
|||||||
|
|
||||||
func (g *GeminiService) ExpandQuery(query string) ([]string, error) {
|
func (g *GeminiService) ExpandQuery(query string) ([]string, error) {
|
||||||
if g.APIKey == "" {
|
if g.APIKey == "" {
|
||||||
return []string{query}, nil
|
return fallbackQueryExpansion(query), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
body := map[string]any{
|
body := map[string]any{
|
||||||
@@ -79,7 +79,7 @@ User query: ` + query,
|
|||||||
|
|
||||||
rawText, err := g.generateText(body)
|
rawText, err := g.generateText(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{query}, err
|
return fallbackQueryExpansion(query), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonText, err := extractJSONObject(rawText)
|
jsonText, err := extractJSONObject(rawText)
|
||||||
@@ -120,21 +120,24 @@ User query: ` + query,
|
|||||||
}
|
}
|
||||||
rawText, retryErr := g.generateText(strictBody)
|
rawText, retryErr := g.generateText(strictBody)
|
||||||
if retryErr != nil {
|
if retryErr != nil {
|
||||||
return []string{query}, retryErr
|
return fallbackQueryExpansion(query), nil
|
||||||
}
|
}
|
||||||
jsonText, err = extractJSONObject(rawText)
|
jsonText, err = extractJSONObject(rawText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{query}, fmt.Errorf("gemini query expansion JSON extraction failed after strict retry: %w", err)
|
return fallbackQueryExpansion(query), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed QueryExpansion
|
var parsed QueryExpansion
|
||||||
if err := json.Unmarshal([]byte(jsonText), &parsed); err != nil {
|
if err := json.Unmarshal([]byte(jsonText), &parsed); err != nil {
|
||||||
return []string{query}, fmt.Errorf("gemini query expansion JSON parse failed: %w; raw=%q", err, truncateForError(rawText, 200))
|
return fallbackQueryExpansion(query), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
queries := []string{query}
|
queries := fallbackQueryExpansion(query)
|
||||||
seen := map[string]bool{strings.ToLower(strings.TrimSpace(query)): true}
|
seen := map[string]bool{}
|
||||||
|
for _, existing := range queries {
|
||||||
|
seen[strings.ToLower(strings.TrimSpace(existing))] = true
|
||||||
|
}
|
||||||
for _, item := range parsed.Querywords {
|
for _, item := range parsed.Querywords {
|
||||||
trimmed := strings.TrimSpace(item)
|
trimmed := strings.TrimSpace(item)
|
||||||
if trimmed == "" {
|
if trimmed == "" {
|
||||||
@@ -384,3 +387,35 @@ func truncateForError(text string, limit int) string {
|
|||||||
}
|
}
|
||||||
return trimmed[:limit] + "..."
|
return trimmed[:limit] + "..."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fallbackQueryExpansion(query string) []string {
|
||||||
|
base := strings.TrimSpace(query)
|
||||||
|
candidates := []string{
|
||||||
|
base,
|
||||||
|
base + " b-roll",
|
||||||
|
base + " stock footage",
|
||||||
|
base + " cinematic footage",
|
||||||
|
base + " establishing shot",
|
||||||
|
base + " editorial footage",
|
||||||
|
base + " urban scene",
|
||||||
|
base + " ambient footage",
|
||||||
|
base + " 4k footage",
|
||||||
|
base + " cinematic b-roll",
|
||||||
|
}
|
||||||
|
|
||||||
|
seen := map[string]bool{}
|
||||||
|
queries := make([]string, 0, len(candidates))
|
||||||
|
for _, item := range candidates {
|
||||||
|
trimmed := strings.TrimSpace(item)
|
||||||
|
if trimmed == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := strings.ToLower(trimmed)
|
||||||
|
if seen[key] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = true
|
||||||
|
queries = append(queries, trimmed)
|
||||||
|
}
|
||||||
|
return queries
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -136,6 +136,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="/app.js" defer></script>
|
<script src="/app.js?v=20260313b" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user