This commit is contained in:
+19
-5
@@ -196,13 +196,13 @@ func (a *App) previewDownload(c *gin.Context) {
|
|||||||
cmd := exec.Command("python3", a.WorkerScript, "--mode", "probe", "--url", req.URL)
|
cmd := exec.Command("python3", a.WorkerScript, "--mode", "probe", "--url", req.URL)
|
||||||
output, err := cmd.CombinedOutput()
|
output, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var preview PreviewResponse
|
var preview PreviewResponse
|
||||||
if err := json.Unmarshal(output, &preview); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, preview)
|
c.JSON(http.StatusOK, preview)
|
||||||
@@ -272,7 +272,7 @@ 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 {
|
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})
|
c.JSON(http.StatusOK, gin.H{"results": []services.AIRecommendation{}, "warning": warning})
|
||||||
return
|
return
|
||||||
@@ -295,7 +295,7 @@ func (a *App) searchMedia(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
warning := err.Error()
|
warning := err.Error()
|
||||||
if expandErr != nil {
|
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})
|
c.JSON(http.StatusOK, gin.H{"results": fallback, "warning": warning, "queries": queryVariants})
|
||||||
return
|
return
|
||||||
@@ -303,7 +303,7 @@ func (a *App) searchMedia(c *gin.Context) {
|
|||||||
|
|
||||||
response := gin.H{"results": mergeRecommendations(recommended, scored, 20), "queries": queryVariants}
|
response := gin.H{"results": mergeRecommendations(recommended, scored, 20), "queries": queryVariants}
|
||||||
if expandErr != nil {
|
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)
|
c.JSON(http.StatusOK, response)
|
||||||
}
|
}
|
||||||
@@ -423,3 +423,17 @@ func EnsurePaths(downloadsDir, workerScript string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ func (s *SearchService) search(query, categories, engine, source string) ([]Sear
|
|||||||
} `json:"results"`
|
} `json:"results"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
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))
|
results := make([]SearchResult, 0, len(payload.Results))
|
||||||
|
|||||||
@@ -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
|
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))
|
resp, err := g.Client.Post(endpoint, "application/json", bytes.NewReader(rawBody))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{query}, err
|
return []string{query}, fmt.Errorf("gemini query expansion request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
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 {
|
var payload struct {
|
||||||
@@ -83,15 +84,15 @@ Keep the original language when possible. User query: ` + query,
|
|||||||
} `json:"candidates"`
|
} `json:"candidates"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
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 {
|
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
|
var parsed QueryExpansion
|
||||||
if err := json.Unmarshal([]byte(payload.Candidates[0].Content.Parts[0].Text), &parsed); err != nil {
|
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}
|
queries := []string{query}
|
||||||
@@ -159,7 +160,7 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend
|
|||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
if resp.StatusCode >= 300 {
|
||||||
data, _ := io.ReadAll(io.LimitReader(resp.Body, 2048))
|
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 {
|
var payload struct {
|
||||||
@@ -172,10 +173,10 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend
|
|||||||
} `json:"candidates"`
|
} `json:"candidates"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
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 {
|
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 {
|
var parsed struct {
|
||||||
@@ -186,7 +187,7 @@ Mark only the best matches as recommended=true. Keep reasons concise. Recommend
|
|||||||
} `json:"recommendations"`
|
} `json:"recommendations"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal([]byte(payload.Candidates[0].Content.Parts[0].Text), &parsed); err != nil {
|
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))
|
recommendations := make([]AIRecommendation, 0, len(parsed.Recommendations))
|
||||||
|
|||||||
Reference in New Issue
Block a user