# Del 9 – AI-integration (AAMOS + Gemini-lärar-tier)

> Uppdaterad 2026-08-08 efter scouting: AAMOS Core v1 är autentiseringsmässigt
> nåbar men saknar konfigurerad matmodell (`/v1/detect` faller tillbaka på YuNet
> ansiktsdetektering). Tills vidare används **Gemini 2.5 Flash** som
> lärar-tier för kylskåpsskanning, bakom samma `AamosClient`-gränssnitt.
> AAMOS/prod rörs inte.

Appen når AI **uteslutande via backend** (beslut 2026-08-02). Mobilappen har
aldrig direktkontakt och innehåller inga AI-hemligheter (spec §31, §61.13).

## Kontraktet (`packages/ai-contracts`)

Appen definierar ett versionerat, Zod-validerat kontrakt per uppgiftstyp. Input
valideras FÖRE nätverksanropet, output valideras EFTER – ett svar som bryter kontraktet
behandlas som fel och når aldrig användardata. Modeller, prompts och leverantörer kan
bytas fritt bakom kontraktet ("Food API ska få stabila kontrakt oavsett modell").

```
┌─────────┐   signed image URL   ┌──────────┐   AamosClient   ┌─────────────────┐
│   App   │ ───────────────────→ │ Food API │ ───────────────→│ Worker adapter  │
└─────────┘                      └──────────┘                 └─────────────────┘
                                                                    │
                                     ┌──────────────────┬──────────┴──────────┐
                                     │ AAMOS_MODE=http  │ AAMOS_MODE=gemini   │
                                     │ HttpAamosClient  │ GeminiAamosClient   │
                                     │ → AAMOS Core v1  │ → Gemini 2.5 Flash  │
                                     └──────────────────┴─────────────────────┘
```

`createAamosClient()` väljer adapter utifrån `AAMOS_MODE`. Inga anropare behöver
ändras. App→Food API→leverantörsgränsen är oförändrad.

## AAMOS Core v1-status

Reella endpoints (ingen `/v1/tasks` fiktion):

```text
POST /v1/detect      POST /v1/verify      POST /v1/compare
POST /v1/segment     POST /v1/classify    POST /v1/authenticate
POST /v1/score       POST /v1/explain     POST /v1/extract
POST /v1/track       GET  /v1/health
```

Scouting 2026-08-08 visade:

- `amos-core.service` på server-2 anropar `http://localhost:3207`.
- Värdport `3207` ägs av `aamos-identity-rust`, inte AI-inferens.
- AI-inferenscontainern `amos-ai-inference:latest` exponerar **värdport 3209**.
- Port mismatch gör att AAMOS Core aldrig når inferens.
- Även efter hypotetisk port-fix saknas `FOOD`/`GROCERY`/`VISION_MODEL`
  konfiguration i `.env.amos-core`; `/v1/detect` defaultar till `yunet`.

**Beslut:** rör inte AAMOS-prod. AAMOS kan återaktiveras i framtiden när en
matmodell finns konfigurerad, via `HttpAamosClient`.

## Gemini-lärar-tier (Skiva 1)

Aktiveras med `AAMOS_MODE=gemini` + `GEMINI_API_KEY`. Endast workern anropar
Gemini; API:et når aldrig nyckeln.

### Hanterade uppgiftstyper

- `ANALYZE_FRIDGE_IMAGE`
- `ANALYZE_PANTRY_IMAGE`

Övriga task types (`READ_RECEIPT`, `READ_NUTRITION_LABEL`, etc.) svarar
`failed` i Skiva 1 och faller tillbaka på appens befintliga flöden.

### Flöde

1. App laddar upp bild → Food API skapar `scan_job` (status `queued`).
2. Workern plockar jobbet, hämtar bilden från lagring (S3/mock).
3. `GeminiAamosClient` skickar bilden + svensk livsmedelsprompt till
   `generativelanguage.googleapis.com/v1beta/models/{GEMINI_MODEL}:generateContent`.
4. Gemini svarar med JSON: produkt, varumärke, kvantitet, enhet, kategori,
   bäst-före/sista-förbruk (om läsbart), konfidens, imageQualityIssues.
5. Workern mappar detekterade namn mot `canonical_ingredients` (mjuk matchning).
6. Resultatet sparas i `scan_jobs` som `awaiting_confirmation`.
7. Användaren granskar och bekräftar i appen. Aldrig auto-commit.
8. AI är aldrig facit för allergener/näring — mjölkprincipen orörd.

### Kostnadskontroll

- `scan_jobs.costUsd` lagrar verklig kostnad per anrop.
- `ai_usage_counters.ai_cost_usd_microcents` ackumulerar månadskostnad per
  användare (utan PII).
- Hushållskvoten `aiScans` respekteras (befintlig entitlements-logik).
- Global dagsbudget `GEMINI_DAILY_BUDGET_USD` via `BudgetStore`. Workern använder
  Redis-backed budget; tester använder in-memory budget. När budgeten är
  förbrukad nekar adaptern nya anrop.
- Loggning: status, latens, tokens, kostnad, taskType. Ingen bild, inget
  fritext, inga PII-fält.

### Säkerhet

- Nyckeln finns i `.env` på workerservrar (SSM i produktion) och checkas aldrig
  in.
- App/API validerar att `AAMOS_MODE=gemini` kräver `GEMINI_API_KEY` i produktion.
- `REQUIRE_REAL=1` i deploy-grinden gör att start failar om nyckel saknas.

## Routing & specialister (framtida)

Skiva 2 kommer lägga premium-eskaleringsstier (Gemini Pro / GPT-4o / Claude)
internt i adaptern vid låg konfidens, med eval/guldset som beslutar billig vs
dyr modell. Fram till dess: Gemini 2.5 Flash för allt i Skiva 1.

## Feedback → träning (Skiva 3)

Varje användarkorrigering i granska-flödet sparas som `ai_corrections`
(AI-utdata + korrigering + modell/promptversion + **samtyckessnapshot**).
Veckojobbet `BUILD_TRAINING_SAMPLE` exporterar endast rader där
`anonymized_improvement = granted` vid korrigeringstillfället; bilder kräver
separat `image_training`-samtycke. Personligt minne är aldrig träningsdata (§32).

## Evals (spec §34)

- `pnpm test`: hermetiska tester med inspelad Gemini-fixture, når aldrig nätet.
- `pnpm --filter @app/worker eval:scan`: live-eval mot litet guldset. Rapporterar
  precision, recall, kostnad, latens. Kräver `AAMOS_MODE=gemini` + nyckel.
- Regel: **ingen modell-/promptändring tas i produktion utan grön eval**.

## Lägen

| `AAMOS_MODE` | Användning                                                      |
| ------------ | --------------------------------------------------------------- |
| `mock`       | dev/test utan nätverk. Produkten vägrar starta i mock.          |
| `http`       | Riktig AAMOS Core när matmodell finns.                          |
| `gemini`     | Gemini 2.5 Flash som lärar-tier för kylskåpsskanning (Skiva 1). |
