whisper-money/tests/Feature/Ai
Jesús Mejías Leiva 350e0031f1
feat(ai): make the AI provider configurable (any laravel/ai provider, incl. local Ollama) (#718)
## Summary

Closes #716.

Both AI execution paths hard-coded `provider: Lab::Gemini`, so even
though `laravel/ai` already understands `OLLAMA_URL` and the model was
env-overridable, no other provider could ever be reached by the actual
UI features. This makes the **AI provider configurable**, keeping
**Gemini as the default** so existing deployments are unaffected.

Although issue #716 asked specifically for **Ollama**, the fix is
generic: because the provider is resolved through the
`Laravel\Ai\Enums\Lab` enum, this unlocks **any text provider
`laravel/ai` supports** — `gemini`, `openai`, `anthropic`, `azure`,
`groq`, `xai`, `deepseek`, `mistral`, and self-hosted `ollama`. Ollama
is the headline case (fully local, private processing), but nothing in
the code is Ollama-specific.

> Note: each provider still needs its own credentials configured for
`laravel/ai` (e.g. `GEMINI_API_KEY`, `OPENAI_API_KEY`, `OLLAMA_URL`),
and only text-capable providers apply — a non-text or unknown provider
fails fast.

## Changes

- **`config/ai_suggestions.php` / `config/ai_categorization.php`** — add
a `provider` key. Each reads its own `AI_SUGGESTIONS_PROVIDER` /
`AI_CATEGORIZATION_PROVIDER`, both falling back to a shared
`AI_PROVIDER` and finally `gemini`. So `AI_PROVIDER=<provider>` flips
every AI feature at once, and either feature can still be overridden
individually.
- **`app/Services/Ai/CategorizeTransactions.php` /
`app/Services/Ai/LaravelAiRuleSuggestionGenerator.php`** — resolve the
configured provider to the `Laravel\Ai\Enums\Lab` enum via
`Lab::from((string) config('...provider'))` and pass it to `prompt()`
(the SDK recommends referencing providers by the `Lab` enum rather than
a plain string). `Lab::from()` also **validates** the value: an unknown
provider fails fast with a clear `ValueError` instead of erroring deep
in the provider stack.
- **`.env.example`** — document the provider vars plus an Ollama block
(`OLLAMA_URL`, `OLLAMA_API_KEY`, `AI_CATEGORIZATION_MODEL`), kept
commented so defaults stay Gemini.
- **`README.md`** — new *AI Provider* section: the generic provider
switch, the list of supported text providers, and a local-Ollama
example.
- **Tests** — cover the `gemini` default and a non-Gemini (`ollama`)
override for both the categorization and rule-suggestion paths, plus the
fail-fast `ValueError` on an unknown provider.

## Usage

Any supported provider follows the same pattern — set `AI_PROVIDER`,
that provider's credentials, and the `*_MODEL` vars. Example, fully
local/private with Ollama:

```dotenv
AI_PROVIDER=ollama
OLLAMA_URL=http://ollama.example.local:11434
AI_SUGGESTIONS_MODEL=gemma3:12b
AI_CATEGORIZATION_MODEL=gemma3:12b
```

## Validation

- `./vendor/bin/pest tests/Feature/Ai` → **128 passed**.
- `vendor/bin/pint` and `vendor/bin/phpstan` (level 5) → clean.
- **End-to-end against a real Ollama server** (`gemma3:12b`), through
the actual application code (not faked):
  - Categorization: `MERCADONA COMPRA` → *Groceries*, confidence 0.95.
- Rule suggestion: `netflix` → *Subscriptions*, structured output
intact.

## Backward compatibility

Default provider is unchanged (`gemini`); no env changes are required
for existing installs.
2026-07-22 09:01:51 +02:00
..
AiCategorizationGateTest.php AI auto-categorization: open to pro + consent, nudge free users (#561) 2026-06-19 14:08:40 +00:00
AiRuleLearnerTest.php fix(ai): surface learned-rule toast in edit modal and guard weak description keys (#635) 2026-07-04 11:00:15 +00:00
ApplyRuleSuggestionsTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
CategorizationOverrideRouteTest.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
CategorizationSchemaTest.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
CategorizeBackfillCommandTest.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
CategorizeOnboardingTransactionsJobTest.php AI auto-categorization: open to pro + consent, nudge free users (#561) 2026-06-19 14:08:40 +00:00
CategorizeTransactionWithAiListenerTest.php fix: stop double-dispatching transaction listeners (N+1 insert into jobs) (#620) 2026-07-02 13:26:45 +00:00
CategorizeTransactionWithAiTest.php AI auto-categorization: open to pro + consent, nudge free users (#561) 2026-06-19 14:08:40 +00:00
CategorizeTransactionsTest.php feat(ai): make the AI provider configurable (any laravel/ai provider, incl. local Ollama) (#718) 2026-07-22 09:01:51 +02:00
CategorizeUncategorizedTransactionsTest.php fix(queue): raise retry_after above the longest job timeout (PHP-LARAVEL-2D) (#645) 2026-07-05 09:31:23 +00:00
CategoryOverrideHandlerTest.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
CategorySourceFilterTest.php feat(ai): auto-categorize transactions with AI (behind flag) (#535) 2026-06-15 16:35:20 +02:00
GenerateRuleSuggestionsJobTest.php fix(onboarding): stop the AI rule-suggestion step from hanging forever (#705) 2026-07-20 18:10:51 +00:00
RetryTransientAiCategorizationJobTest.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
RuleSuggestionAggregatorTest.php feat(ai): learn from category corrections so the AI stops repeating the same mistake (#608) 2026-06-29 19:12:15 +02:00
RuleSuggestionConsolidatorTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
RuleSuggestionControllerTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
RuleSuggestionGeneratorTest.php feat(ai): make the AI provider configurable (any laravel/ai provider, incl. local Ollama) (#718) 2026-07-22 09:01:51 +02:00
RuleSuggestionGuardTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00
SuggestRulesCommandTest.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00