This reverts commit 6faff4d269.
This commit is contained in:
@@ -233,7 +233,6 @@
|
|||||||
- The result modal should now stay within viewport height, but this still needs real browser confirmation on multiple short-height displays because CSS-only constraints were the source of the latest user-visible regression.
|
- The result modal should now stay within viewport height, but this still needs real browser confirmation on multiple short-height displays because CSS-only constraints were the source of the latest user-visible regression.
|
||||||
- Artgrid preview playback now has a server-side ffmpeg transcode path for `.m3u8` style preview URLs, but this trades storage savings for runtime CPU cost.
|
- Artgrid preview playback now has a server-side ffmpeg transcode path for `.m3u8` style preview URLs, but this trades storage savings for runtime CPU cost.
|
||||||
- The reverted `5ca7aef` experiment showed that simply widening collector caps and Gemini candidate count can backfire when the added candidates are weak: final visible count fell sharply even though backend raw candidate count increased.
|
- The reverted `5ca7aef` experiment showed that simply widening collector caps and Gemini candidate count can backfire when the added candidates are weak: final visible count fell sharply even though backend raw candidate count increased.
|
||||||
- Gemini Vision batch execution can still see mixed failure modes: true model/output-format failures and ignorable low-value/no-visual candidate skips. These should not be conflated in user-facing warnings.
|
|
||||||
- The local self-test script is better than before, but it is still a smoke test, not full integration coverage.
|
- The local self-test script is better than before, but it is still a smoke test, not full integration coverage.
|
||||||
|
|
||||||
## Current Risks Around Search Quality
|
## Current Risks Around Search Quality
|
||||||
@@ -555,7 +554,6 @@
|
|||||||
## Highest-Value Next Steps
|
## Highest-Value Next Steps
|
||||||
- [ ] Reduce `/api/search` latency further without collapsing result count
|
- [ ] Reduce `/api/search` latency further without collapsing result count
|
||||||
- [ ] Rebuild the reverted search-expansion work from the previous stable baseline, but only after measuring where candidate quality collapses between ranked pool and final merge
|
- [ ] Rebuild the reverted search-expansion work from the previous stable baseline, but only after measuring where candidate quality collapses between ranked pool and final merge
|
||||||
- [ ] Continue hardening Gemini response parsing so truncated JSON or mixed-quality candidate batches degrade more gracefully without surfacing alarming warnings
|
|
||||||
- [ ] Build a repeatable repo-local bootstrap script or documented setup command set for non-root machines so fresh PC setup does not depend on shell history
|
- [ ] Build a repeatable repo-local bootstrap script or documented setup command set for non-root machines so fresh PC setup does not depend on shell history
|
||||||
- [ ] Improve Envato / Artgrid preview acquisition reliability so Gemini Vision sees real frames more often
|
- [ ] Improve Envato / Artgrid preview acquisition reliability so Gemini Vision sees real frames more often
|
||||||
- [ ] Browser-verify the new result modal at multiple viewport heights and confirm translated Source Summary readability on real long descriptions
|
- [ ] Browser-verify the new result modal at multiple viewport heights and confirm translated Source Summary readability on real long descriptions
|
||||||
@@ -626,25 +624,6 @@
|
|||||||
- If behavior in the browser does not match the latest backend/frontend code, the first assumption should be stale frontend assets until proven otherwise
|
- If behavior in the browser does not match the latest backend/frontend code, the first assumption should be stale frontend assets until proven otherwise
|
||||||
|
|
||||||
## Recent Change Log
|
## Recent Change Log
|
||||||
- Date: `2026-03-17`
|
|
||||||
- What changed:
|
|
||||||
- Reduced Gemini Vision batch size from `8` to `6` candidates to lower the chance of oversized/truncated JSON responses.
|
|
||||||
- Added an explicit Gemini output token cap for the vision JSON response path.
|
|
||||||
- Changed Gemini batch failure accounting so ignorable per-candidate skips such as low-value thumbnails or missing visuals no longer count as a user-facing partial batch failure when useful recommendations were still recovered.
|
|
||||||
- Added unit coverage for the hard-error filtering behavior.
|
|
||||||
- Why it changed:
|
|
||||||
- The user reported the warning `gemini vision partially failed on 2 of 2 batches`.
|
|
||||||
- The provided log `ai-media-hub-2026-03-17T04-35-33-028Z.log` showed two distinct failure classes during the second repeated search:
|
|
||||||
- a real batch-level JSON extraction failure caused by truncated Gemini output
|
|
||||||
- multiple ignorable sequential-retry skips caused by low-value thumbnails or missing visuals
|
|
||||||
- Those were being combined into an alarming partial-failure warning even though useful recommendations were still recovered.
|
|
||||||
- How it was verified:
|
|
||||||
- `go test ./...`
|
|
||||||
- `bash scripts/selftest.sh`
|
|
||||||
- What is still risky or incomplete:
|
|
||||||
- This reduces false-positive partial-failure warnings, but true Gemini output truncation can still happen occasionally if model behavior changes again.
|
|
||||||
- Further hardening may still be needed if Gemini starts returning different malformed JSON shapes.
|
|
||||||
|
|
||||||
- Date: `2026-03-17`
|
- Date: `2026-03-17`
|
||||||
- What changed:
|
- What changed:
|
||||||
- Reverted commit `5ca7aef` (`Strengthen search breadth and modal fitting`) to restore the previous stable search/modal baseline.
|
- Reverted commit `5ca7aef` (`Strengthen search breadth and modal fitting`) to restore the previous stable search/modal baseline.
|
||||||
|
|||||||
@@ -306,7 +306,6 @@ User query: ` + query,
|
|||||||
},
|
},
|
||||||
"generationConfig": map[string]any{
|
"generationConfig": map[string]any{
|
||||||
"responseMimeType": "application/json",
|
"responseMimeType": "application/json",
|
||||||
"maxOutputTokens": 1400,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,18 +213,3 @@ func TestMergeRecommendationsExcludesIrrelevantAndPendingFiller(t *testing.T) {
|
|||||||
t.Fatalf("unexpected merged result: %#v", merged)
|
t.Fatalf("unexpected merged result: %#v", merged)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterHardGeminiErrorsIgnoresLowValueVisualFailures(t *testing.T) {
|
|
||||||
errs := []string{
|
|
||||||
"candidate thumbnail is low value",
|
|
||||||
"no candidate thumbnails or preview frames could be fetched for gemini vision",
|
|
||||||
"gemini vision JSON extraction failed: no complete JSON object found",
|
|
||||||
}
|
|
||||||
filtered := filterHardGeminiErrors(errs)
|
|
||||||
if len(filtered) != 1 {
|
|
||||||
t.Fatalf("expected only hard errors to remain, got %#v", filtered)
|
|
||||||
}
|
|
||||||
if !strings.Contains(filtered[0], "JSON extraction failed") {
|
|
||||||
t.Fatalf("unexpected filtered errors: %#v", filtered)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func EvaluateAllCandidatesWithGemini(service *GeminiService, query string, ranke
|
|||||||
}
|
}
|
||||||
|
|
||||||
func EvaluateAllCandidatesWithGeminiWithDeadline(service *GeminiService, query string, ranked []SearchResult, deadline time.Time) ([]AIRecommendation, GeminiBatchStats, error) {
|
func EvaluateAllCandidatesWithGeminiWithDeadline(service *GeminiService, query string, ranked []SearchResult, deadline time.Time) ([]AIRecommendation, GeminiBatchStats, error) {
|
||||||
const chunkSize = 6
|
const chunkSize = 8
|
||||||
const maxConcurrentBatches = 2
|
const maxConcurrentBatches = 2
|
||||||
if service == nil {
|
if service == nil {
|
||||||
return nil, GeminiBatchStats{}, fmt.Errorf("gemini service is not configured")
|
return nil, GeminiBatchStats{}, fmt.Errorf("gemini service is not configured")
|
||||||
@@ -187,7 +187,6 @@ func EvaluateAllCandidatesWithGeminiWithDeadline(service *GeminiService, query s
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
recovered, recoveredErrs := recoverGeminiBatchSequentially(service, query, ranked, batch.index*chunkSize)
|
recovered, recoveredErrs := recoverGeminiBatchSequentially(service, query, ranked, batch.index*chunkSize)
|
||||||
hardErrs := filterHardGeminiErrors(recoveredErrs)
|
|
||||||
if len(recovered) > 0 {
|
if len(recovered) > 0 {
|
||||||
stats.SequentialRetried++
|
stats.SequentialRetried++
|
||||||
stats.Succeeded++
|
stats.Succeeded++
|
||||||
@@ -198,9 +197,9 @@ func EvaluateAllCandidatesWithGeminiWithDeadline(service *GeminiService, query s
|
|||||||
seen[item.Link] = true
|
seen[item.Link] = true
|
||||||
merged = append(merged, item)
|
merged = append(merged, item)
|
||||||
}
|
}
|
||||||
if len(hardErrs) > 0 {
|
if len(recoveredErrs) > 0 {
|
||||||
stats.Failed++
|
stats.Failed++
|
||||||
for _, recoveredErr := range hardErrs {
|
for _, recoveredErr := range recoveredErrs {
|
||||||
if len(stats.Errors) < 5 {
|
if len(stats.Errors) < 5 {
|
||||||
stats.Errors = append(stats.Errors, recoveredErr)
|
stats.Errors = append(stats.Errors, recoveredErr)
|
||||||
}
|
}
|
||||||
@@ -208,9 +207,6 @@ func EvaluateAllCandidatesWithGeminiWithDeadline(service *GeminiService, query s
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(hardErrs) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
stats.Failed++
|
stats.Failed++
|
||||||
if len(stats.Errors) < 5 {
|
if len(stats.Errors) < 5 {
|
||||||
stats.Errors = append(stats.Errors, batch.err.Error())
|
stats.Errors = append(stats.Errors, batch.err.Error())
|
||||||
@@ -333,35 +329,6 @@ func MergeUniqueRecommendations(base, extra []AIRecommendation) []AIRecommendati
|
|||||||
return merged
|
return merged
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterHardGeminiErrors(errs []string) []string {
|
|
||||||
filtered := make([]string, 0, len(errs))
|
|
||||||
for _, item := range errs {
|
|
||||||
if isIgnorableGeminiError(item) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filtered = append(filtered, item)
|
|
||||||
}
|
|
||||||
return filtered
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIgnorableGeminiError(message string) bool {
|
|
||||||
lower := strings.ToLower(strings.TrimSpace(message))
|
|
||||||
if lower == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, token := range []string{
|
|
||||||
"no candidate thumbnails or preview frames could be fetched for gemini vision",
|
|
||||||
"candidate thumbnail is low value",
|
|
||||||
"candidate has no thumbnail or preview video",
|
|
||||||
"image url is empty",
|
|
||||||
} {
|
|
||||||
if strings.Contains(lower, token) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeGeminiBatchStats(base, extra GeminiBatchStats) GeminiBatchStats {
|
func MergeGeminiBatchStats(base, extra GeminiBatchStats) GeminiBatchStats {
|
||||||
merged := base
|
merged := base
|
||||||
merged.CandidateCap += extra.CandidateCap
|
merged.CandidateCap += extra.CandidateCap
|
||||||
|
|||||||
Reference in New Issue
Block a user