# Beer Matcher Service Overview

This document explains what the beer matcher feature must deliver so it can be cloned from the existing wine matcher with minimal guesswork. Use it as the source of truth before touching code.

## Goal
- Offer `/beer-matcher` with the same contract/behavior as `/wine-matcher`, but operating on beer lists.
- Share all infrastructure patterns (auth, metrics, retries) with the other services while keeping its configuration, models, and API keys isolated.
- Keep responses backward compatible with the wine matcher shape, only substituting field names that mention wine.

## API Contract

### Request (JSON)
```json
{
  "language": "en",
  "foods": [
    {"name": "Fish and Chips", "description": "Crispy cod, mushy peas"}
  ],
  "beers": [
    {"name": "Pacific Pale Ale", "description": "Citrus + pine", "price": "$8"},
    {"name": "Dry Stout", "price": "$9"}
  ],
  "userPrompt": "Highlight sessionable options",
  "model": "gpt-4o-mini"
}
```

| Field | Notes |
| --- | --- |
| `language` | Required, any locale string; output must honor it. |
| `foods` | Same structure as wine matcher: `name` + optional `description`. |
| `beers` | Mirrors `wines` payload but renamed. Preserve price strings exactly. |
| `userPrompt` | Optional free-form instructions; trim whitespace. |
| `model` | Optional override for the pairing + rating lookups, same as wine matcher. |

### Response (JSON)
```json
{
  "language": "en",
  "results": [
    {
      "food": "Fish and Chips",
      "matches": [
        {
          "beer": "Pacific Pale Ale",
          "reason": "Bright hops cut through the fried batter.",
          "beer_rating": "4.1",
          "price": "$8"
        }
      ],
      "notes": "Rotate in a stout on cooler days."
    }
  ],
  "version": "2.0.0"
}
```

- The schema mirrors `WinePairingResponse` but with `beer` and `beer_rating` keys.
- When `userPrompt` is present keep every match returned by the LLM (up to five). Otherwise randomly keep two matches, following the same sampling logic as wine pairing.
- Suggestions behave like the wine matcher: drop them when `matches` exists.

## Components

| Path | Purpose |
| --- | --- |
| `app/services/beer_pairing/config.py` | Pydantic config loader, resolves `OPENAI_API_KEY_BEER` + beer-specific models. |
| `app/services/beer_pairing/models/requests.py` | `BeerPairingRequest`, `BeerPairingFood`, and `BeerCardItem` definitions. |
| `app/services/beer_pairing/models/responses.py` | `BeerPairingResponse`, `BeerPairingResult`, `BeerPairingMatch`. |
| `app/services/beer_pairing/service.py` | Business logic cloned from `WinePairingService` with beer terminology and Untappd rating lookup helper. |
| `app/services/beer_pairing/routes.py` | FastAPI router exposing `/beer-matcher`, injecting auth + service instance. |
| `tests/services/test_beer_pairing_service.py` | Verifies prompt composition, sampling, rating enrichment, and prompt passthrough. |
| `tests/api/test_beer_matcher_route.py` | Router-level regression to ensure camelCase `userPrompt` works and auth dependency fires. |
| `config/services/beer_pairing.yaml` | Service-specific YAML (auth token env name, OpenAI models, toggles). |

## Configuration & Auth

- Environment variables
  - `BEER_SERVICE_KEY` (optional, falls back to `AUTHORIZATION_KEY`)
  - `OPENAI_API_KEY_BEER` for pairings
  - `OPENAI_API_KEY_BEER_RATINGS` or reuse the same key if not provided
- YAML stub:
  ```yaml
  # config/services/beer_pairing.yaml
  enabled: true
  auth:
    bearer_token_env: "BEER_SERVICE_KEY"
  openai:
    api_key_env: "OPENAI_API_KEY_BEER"
    models:
      beer_pairing: "gpt-4o-mini"
      untappd_lookup: "gpt-4o-mini"
    max_tokens: 2000
    temperature: 0
    timeout: 30.0
    max_retries: 3
    retry_delay: 1.0
  features:
    untappd_lookup: true
  ```
- Add `verify_beer_pairing_auth` in `app/shared/middleware/auth.py` so the router can protect requests with the service token.

## Observability

- Metrics: duplicate `OperationMetrics` usage with ids `beer_pairing_match` and `beer_pairing_untappd_lookup`.
- Logging: follow wine matcher log message names (`beer_pairing_started`, `beer_pairing_completed`, `beer_pairing_failed`) so dashboards can key off them.
- Errors: raise a new `BeerPairingException` from `app/shared/exceptions/base.py`.

## Deployment Hooks

- Register the service + router in `app/main.py` right after wine pairing.
- Update `.env.example` / deployment manifests with the new env variables.
- Document the endpoint in README + OpenAPI tags as needed (tag `beer_pairing`).
