The cloud branch of model-call.py was a stub (cloud_backend_not_implemented,
failed even with a key set); casan-step.mjs gated the judge on a hard-coded
Ollama ping. Wire up the real cloud path so a model can run without Ollama.
- model-call.py: add call_openai() and call_anthropic() (raw urllib, no new
dependency — matches the existing call_ollama). Endpoints hard-pinned to the
SSRF allowlist; keys read from env, never logged. Anthropic sends no
temperature/thinking (rejected as 400 on Opus 4.8/4.7; omitting thinking
keeps the terse one-word classify/judge answer). main() routes by
ollama:/openai:/anthropic: prefix; key-unset still fails closed honestly.
provider-usage.jsonl cost_source is per-backend, keeping ollama's exact
"ollama_local_real_tokens" tag that evidence/tests key on.
- casan-step.mjs: ollamaAvailable() -> modelAvailable() — when
CASAN_MODEL_PRIMARY is a cloud spec with its key set, the judge runs through
the cloud path; otherwise it pings local Ollama as before. Default
(unset CASAN_MODEL_PRIMARY) is unchanged.
- CASAN_MASTER_RUNBOOK.md: update sections 0/1/4/7/8 — cloud is now
implemented (not a stub); keep the honest "untested with a real key" +
CA-cert caveats.
Not verified against a live API key (none available); confirmed key-set makes
a real HTTPS call and key-unset fails closed. Gates unchanged:
security-gate PASS=11 FAIL=0, adversarial PASS=44 FAIL=0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>