whisper-money/app/Services/Ai
Víctor Falcón d504e70309
fix(ai): don't report expected transient provider overloads (PHP-LARAVEL-44) (#655)
## Issue (Sentry PHP-LARAVEL-44 — low volume: 0 users, 1 event)

`Laravel\Ai\Exceptions\ProviderOverloadedException: AI provider [gemini]
is overloaded` (underlying Gemini HTTP 503 "high demand"), thrown from
the AI rule-suggestion generator. This is an expected, transient,
self-healing provider overload — but
`LaravelAiRuleSuggestionGenerator::generate()` called `report()` on
every failed batch, so a transient hiccup surfaced in Sentry as an
error.

## Fix

Split the per-batch catch so a `FailoverableException` (overload /
rate-limit / insufficient-credits) is logged at `warning` **without**
reporting, while every other `Throwable` is still `report()`ed. This
mirrors the existing, test-enforced handling in the sibling
`CategorizeTransactions` service.

Unchanged (deliberately preserved):
- **Partial tolerance** — suggestions from batches that succeeded are
still kept.
- **Rethrow-when-all-fail** — a genuine total failure still rethrows, so
the run is marked `Failed` (the onboarding "Try again / Skip" UI) and is
reported **once** at the run level. Non-transient batch errors are still
reported immediately.

## What I deliberately did NOT do

Two review agents converged that adding a **retry backoff** (the other
candidate fix) is not worth it here and carries real risk:
- The run **already self-heals**: failed/empty runs don't count toward
the throttle, so the user's "Try again" button re-runs immediately at
zero cost.
- A synchronous per-batch `sleep` on the single-worker `database` queue,
across up to ~10 batches, could push the run past its **120s job timeout
(failing more often, not less)** and starve other queued jobs during a
provider brownout.

So this PR is scoped to the safe, high-signal change: stop reporting an
expected transient as an error. Genuine misconfigurations (bad
model/key/quota) throw non-`FailoverableException` types and remain
fully reported.

## Testing

- New: an expected transient overload returns the successful batches'
suggestions and is **not** reported
(`Exceptions::assertNothingReported()`); an unexpected batch failure
**is** reported (`assertReported(RuntimeException::class)`).
- Full `tests/Feature/Ai` suite green (121 tests); Pint and Larastan
clean.

Fixes PHP-LARAVEL-44

---
🤖 Opened by the autonomous Sentry-triage loop. Auto-merge enabled:
low-risk, mirrors an existing tested pattern, keeps genuine failures
visible.
2026-07-07 10:55:02 +00:00
..
Contracts feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
AiCategorizationGate.php AI auto-categorization: open to pro + consent, nudge free users (#561) 2026-06-19 14:08:40 +00:00
AiCategorizer.php feat(ai): manage AI consent outside onboarding with live backfill (#591) 2026-06-25 10:50:35 +02:00
AiCohortReportCollector.php feat(ai): add weekly AI-suggestions cohort report (#530) 2026-06-13 23:23:34 +02:00
AiRuleLearner.php fix(ai): surface learned-rule toast in edit modal and guard weak description keys (#635) 2026-07-04 11:00:15 +00:00
ApplyRuleSuggestions.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
CategorizationOutcome.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
CategorizeTransactions.php fix(ai): handle transient AI provider overloads — stop the Sentry noise and retry the dropped work (#595) 2026-06-26 20:07:06 +02:00
CategoryCatalog.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
CategoryOverrideHandler.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
DescriptionTokenizer.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
GenerateRuleSuggestions.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
LaravelAiRuleSuggestionGenerator.php fix(ai): don't report expected transient provider overloads (PHP-LARAVEL-44) (#655) 2026-07-07 10:55:02 +00:00
RuleSuggestionAggregator.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
RuleSuggestionAvailability.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
RuleSuggestionConsolidator.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
RuleSuggestionGuard.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
UncategorizedTransactionMatcher.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00