Add OpenAI (GPT-5.6 Sol) as a second vision provider; gate server settings to a named admin
vision.py now dispatches per-model to _call_anthropic or _call_openai -- same prompt, same schema, same cardimage.py measurements either way, only the request/response shape differs. Confirmed the existing GRADING_SCHEMA already satisfies OpenAI's strict-mode requirement (every property listed in required, additionalProperties:false at every level) with no changes. Settings gained a second axis: which server key applies now depends on the selected model's provider, and friends' personal keys are stored per provider (with a one-time migration from the old single-key localStorage slot) since a Claude key and an OpenAI key aren't interchangeable. CARD_GRADER_ADMIN_USER names one username (read from the proxy's forwarded basic-auth header) who alone may write server settings; everyone else keeps the same read-only view CARD_GRADER_LOCK used to give everyone, while still being able to set their own personal key. Deployed here as ninja_hippo. CARD_GRADER_LOCK remains the fallback when no admin is named.
This commit is contained in:
parent
9d5c68b093
commit
034761e145
7 changed files with 351 additions and 122 deletions
94
README.md
94
README.md
|
|
@ -101,41 +101,51 @@ graded — so regrading a card keeps its photos for another full window.
|
|||
|
||||
### Optional packages
|
||||
|
||||
Two packages unlock real functionality. Each is checked for at runtime, so
|
||||
Three packages unlock real functionality. Each is checked for at runtime, so
|
||||
the app runs without them — you just lose that capability.
|
||||
|
||||
```bash
|
||||
python3 -m pip install --user anthropic # required — this is what does the grading
|
||||
python3 -m pip install --user anthropic # Claude models — Sonnet 5, Haiku 4.5, Opus 5
|
||||
python3 -m pip install --user openai # OpenAI models — GPT-5.6 Sol
|
||||
python3 -m pip install --user pillow # the corner/edge/surface measurement pipeline
|
||||
```
|
||||
|
||||
Without `anthropic`, grading simply doesn't work — add a key in Settings once
|
||||
it's installed. Without `pillow`, grading still runs, but on the full photo
|
||||
alone: no measured centering, no measured edge whitening, no magnified
|
||||
close-ups. Corners and edges will come back `cannot_assess` far more often,
|
||||
because a plain full-card photo genuinely doesn't show enough detail in those
|
||||
regions for a model to judge them from. Install `pillow` — it's the
|
||||
difference between an answer and a shrug.
|
||||
You only need whichever provider package matches the model you actually use
|
||||
— install both if you want the option to switch. Without `pillow`, grading
|
||||
still runs, but on the full photo alone: no measured centering, no measured
|
||||
edge whitening, no magnified close-ups. Corners and edges will come back
|
||||
`cannot_assess` far more often, because a plain full-card photo genuinely
|
||||
doesn't show enough detail in those regions for a model to judge them from.
|
||||
Install `pillow` — it's the difference between an answer and a shrug.
|
||||
|
||||
### API key
|
||||
### API keys
|
||||
|
||||
Get one at [console.anthropic.com](https://console.anthropic.com), then paste
|
||||
it into Settings in the app. Grading costs a few cents per card (Sonnet 5,
|
||||
~$0.04-0.06 depending on how many photos you send) — nothing is charged until
|
||||
you click "Estimate the grade."
|
||||
Get an Anthropic key at [console.anthropic.com](https://console.anthropic.com)
|
||||
and/or an OpenAI key at [platform.openai.com](https://platform.openai.com),
|
||||
then paste whichever you use into Settings in the app — each provider has its
|
||||
own field, since the keys aren't interchangeable and you may want to hold
|
||||
both. Grading costs a few cents per card either way (Sonnet 5 and GPT-5.6 Sol
|
||||
are both currently ~$0.04-0.06 depending on how many photos you send) —
|
||||
nothing is charged until you click "Estimate the grade."
|
||||
|
||||
## Why Sonnet 5, not a cheaper model
|
||||
|
||||
Settings lets you switch to Haiku 4.5 (about a third of the cost) or Opus 5
|
||||
(more expensive, marginally more careful). Sonnet is the default for a
|
||||
tested reason, not a hunch: run head-to-head against Haiku on the same
|
||||
photos, Haiku inverted a PSA centering-tolerance comparison — read 59/41 as
|
||||
*exceeding* the tolerance for a 9, when 59/41 is actually well inside it —
|
||||
and the error alone dragged its estimate three grade levels below Sonnet's.
|
||||
Haiku also lacks extended thinking entirely, which matters for exactly the
|
||||
judgment calls this task is full of: is this a scratch or a print texture,
|
||||
a reflection or real edge wear, a print line or a crease. If cost matters
|
||||
enough to switch, that's a real, deliberate tradeoff — not a free lunch.
|
||||
Settings lets you switch to Haiku 4.5 (about a third of the cost), Opus 5
|
||||
(more expensive, marginally more careful), or GPT-5.6 Sol (OpenAI, similar
|
||||
price to Sonnet). Sonnet is the default for a tested reason, not a hunch: run
|
||||
head-to-head against Haiku on the same photos, Haiku inverted a PSA
|
||||
centering-tolerance comparison — read 59/41 as *exceeding* the tolerance for
|
||||
a 9, when 59/41 is actually well inside it — and the error alone dragged its
|
||||
estimate three grade levels below Sonnet's. Haiku also lacks extended
|
||||
thinking entirely, which matters for exactly the judgment calls this task is
|
||||
full of: is this a scratch or a print texture, a reflection or real edge
|
||||
wear, a print line or a crease. If cost matters enough to switch, that's a
|
||||
real, deliberate tradeoff — not a free lunch.
|
||||
|
||||
GPT-5.6 Sol hasn't been run through the same head-to-head testing — it's
|
||||
wired up and usable, but nobody has checked it against real graded cards the
|
||||
way Haiku was checked here. Treat its results with the scrutiny that implies
|
||||
until someone does that comparison.
|
||||
|
||||
## The workflow
|
||||
|
||||
|
|
@ -224,12 +234,14 @@ includes a `Dockerfile`, `docker-compose.yml`, and an nginx config
|
|||
-d card-grader.yourdomain.com
|
||||
docker restart nginx
|
||||
```
|
||||
5. **Set your Anthropic API key** by opening `https://card-grader.yourdomain.com`
|
||||
yourself first, going to Settings, and pasting it in — this only works
|
||||
*before* `CARD_GRADER_LOCK=1` takes effect, so either set the key first
|
||||
and then bring the lock up, or temporarily comment out that line in
|
||||
`docker-compose.yml`, restart, set the key, uncomment it, and restart
|
||||
again.
|
||||
5. **Set your API key(s)** by opening `https://card-grader.yourdomain.com`
|
||||
yourself first, going to Settings, and pasting them in. If you're using
|
||||
`CARD_GRADER_ADMIN_USER`, this just works — settings stay writable for
|
||||
that one username indefinitely. If you're using the blanket
|
||||
`CARD_GRADER_LOCK=1` instead, that only works *before* the lock takes
|
||||
effect, so either set the key first and then bring the lock up, or
|
||||
temporarily comment out that line in `docker-compose.yml`, restart, set
|
||||
the key, uncomment it, and restart again.
|
||||
|
||||
If Unraid already has an nginx-based reverse proxy set up (Nginx Proxy
|
||||
Manager, SWAG, etc.), skip step 4 and just add a proxy host there pointing
|
||||
|
|
@ -268,11 +280,25 @@ key is being used**. Two things in the app deal with this:
|
|||
CARD_GRADER_LOCK=1 python3 app.py
|
||||
```
|
||||
|
||||
which refuses all settings writes. Without it, any visitor could overwrite
|
||||
your stored API key or switch the model to the most expensive one. Grading
|
||||
still works normally; only the settings write is blocked.
|
||||
which refuses all settings writes from everyone. Without it, any visitor
|
||||
could overwrite your stored API key or switch the model to the most
|
||||
expensive one. Grading still works normally; only the settings write is
|
||||
blocked.
|
||||
|
||||
Your stored API key is never sent to any browser regardless — the settings
|
||||
If your reverse proxy does per-user basic auth (as opposed to one shared
|
||||
password for everyone), you can name a single admin instead of locking
|
||||
settings out entirely:
|
||||
|
||||
```bash
|
||||
CARD_GRADER_ADMIN_USER=yourname python3 app.py
|
||||
```
|
||||
|
||||
Only that username may write settings; everyone else gets the same
|
||||
read-only view `CARD_GRADER_LOCK` gives everyone. Either way, everyone can
|
||||
still set their *own* personal key (point 1 above) — that never touches
|
||||
server settings at all.
|
||||
|
||||
Your stored API keys are never sent to any browser regardless — the settings
|
||||
endpoint returns only a boolean saying whether one is configured.
|
||||
|
||||
Everyone shares one `grades.db`, so the History list is communal. That's
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue