diff --git a/backend/handlers/api.go b/backend/handlers/api.go index b7e3dc2..644d2e3 100644 --- a/backend/handlers/api.go +++ b/backend/handlers/api.go @@ -196,13 +196,13 @@ func (a *App) previewDownload(c *gin.Context) { cmd := exec.Command("python3", a.WorkerScript, "--mode", "probe", "--url", req.URL) output, err := cmd.CombinedOutput() if err != nil { - c.JSON(http.StatusBadGateway, gin.H{"error": strings.TrimSpace(string(output))}) + c.JSON(http.StatusBadGateway, gin.H{"error": summarizeOutput("download preview probe failed", output, err)}) return } var preview PreviewResponse if err := json.Unmarshal(output, &preview); err != nil { - c.JSON(http.StatusBadGateway, gin.H{"error": "invalid probe response"}) + c.JSON(http.StatusBadGateway, gin.H{"error": summarizeOutput("download preview returned invalid JSON", output, err)}) return } c.JSON(http.StatusOK, preview) @@ -272,7 +272,7 @@ func (a *App) searchMedia(c *gin.Context) { if len(results) == 0 { warning := "SearXNG returned no renderable results." if expandErr != nil { - warning += " Query expansion fallback was used." + warning += " Query expansion failed: " + expandErr.Error() } c.JSON(http.StatusOK, gin.H{"results": []services.AIRecommendation{}, "warning": warning}) return @@ -295,7 +295,7 @@ func (a *App) searchMedia(c *gin.Context) { } warning := err.Error() if expandErr != nil { - warning = warning + " Query expansion fallback was used." + warning = warning + " Query expansion failed: " + expandErr.Error() } c.JSON(http.StatusOK, gin.H{"results": fallback, "warning": warning, "queries": queryVariants}) return @@ -303,7 +303,7 @@ func (a *App) searchMedia(c *gin.Context) { response := gin.H{"results": mergeRecommendations(recommended, scored, 20), "queries": queryVariants} if expandErr != nil { - response["warning"] = "Gemini query expansion failed, using the original query only." + response["warning"] = "Gemini query expansion failed: " + expandErr.Error() + ". Using the original query only." } c.JSON(http.StatusOK, response) } @@ -423,3 +423,17 @@ func EnsurePaths(downloadsDir, workerScript string) error { } return nil } + +func summarizeOutput(prefix string, output []byte, err error) string { + trimmed := strings.TrimSpace(string(output)) + if trimmed == "" && err != nil { + return prefix + ": " + err.Error() + } + if trimmed == "" { + return prefix + } + if len(trimmed) > 1200 { + trimmed = trimmed[:1200] + "..." + } + return prefix + ": " + trimmed +} diff --git a/backend/services/cse.go b/backend/services/cse.go index 59b9a47..fd05aa1 100644 --- a/backend/services/cse.go +++ b/backend/services/cse.go @@ -164,7 +164,7 @@ func (s *SearchService) search(query, categories, engine, source string) ([]Sear } `json:"results"` } if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { - return nil, err + return nil, fmt.Errorf("searxng JSON decode failed for query %q: %w", query, err) } results := make([]SearchResult, 0, len(payload.Results)) diff --git a/backend/services/gemini.go b/backend/services/gemini.go index 703451d..057f97e 100644 --- a/backend/services/gemini.go +++ b/backend/services/gemini.go @@ -65,12 +65,13 @@ Keep the original language when possible. User query: ` + query, endpoint := "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=" + g.APIKey resp, err := g.Client.Post(endpoint, "application/json", bytes.NewReader(rawBody)) if err != nil { - return []string{query}, err + return []string{query}, fmt.Errorf("gemini query expansion request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { - return []string{query}, fmt.Errorf("gemini returned status %d for query expansion", resp.StatusCode) + data, _ := io.ReadAll(io.LimitReader(resp.Body, 2048)) + return []string{query}, fmt.Errorf("gemini query expansion returned status %d: %s", resp.StatusCode, strings.TrimSpace(string(data))) } var payload struct { @@ -83,15 +84,15 @@ Keep the original language when possible. User query: ` + query, } `json:"candidates"` } if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { - return []string{query}, err + return []string{query}, fmt.Errorf("gemini query expansion response decode failed: %w", err) } if len(payload.Candidates) == 0 || len(payload.Candidates[0].Content.Parts) == 0 { - return []string{query}, nil + return []string{query}, fmt.Errorf("gemini query expansion returned no candidates") } var parsed QueryExpansion if err := json.Unmarshal([]byte(payload.Candidates[0].Content.Parts[0].Text), &parsed); err != nil { - return []string{query}, err + return []string{query}, fmt.Errorf("gemini query expansion JSON parse failed: %w", err) } queries := []string{query} @@ -159,7 +160,7 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend if resp.StatusCode >= 300 { data, _ := io.ReadAll(io.LimitReader(resp.Body, 2048)) - return nil, fmt.Errorf("gemini returned status %d: %s", resp.StatusCode, string(data)) + return nil, fmt.Errorf("gemini vision returned status %d: %s", resp.StatusCode, strings.TrimSpace(string(data))) } var payload struct { @@ -172,10 +173,10 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend } `json:"candidates"` } if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { - return nil, err + return nil, fmt.Errorf("gemini vision response decode failed: %w", err) } if len(payload.Candidates) == 0 || len(payload.Candidates[0].Content.Parts) == 0 { - return nil, fmt.Errorf("gemini returned no candidates") + return nil, fmt.Errorf("gemini vision returned no candidates") } var parsed struct { @@ -186,7 +187,7 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend } `json:"recommendations"` } if err := json.Unmarshal([]byte(payload.Candidates[0].Content.Parts[0].Text), &parsed); err != nil { - return nil, err + return nil, fmt.Errorf("gemini vision JSON parse failed: %w", err) } recommendations := make([]AIRecommendation, 0, len(parsed.Recommendations))