README.md (3635B)
semif-api
HTTP API wrapper around TheoLeeCJ/SemIf,
consumed as a library (semif_phase1) — no scorer code is copied. See
../README.md for the quickstart and
../docs/llama-backend.md for the GGUF backend
(SEMIF_BACKEND=llama) and its NixOS options; the notes below cover the
torch path.
Run
nix develop <repo root>
semif-serve # 127.0.0.1:8321, or SEMIF_HOST/SEMIF_PORT
The torch model is loaded once at startup (env overrides: SEMIF_MODEL,
SEMIF_REVISION, SEMIF_MAX_TOKENS).
API
The web UI rides in the wheel as package data (src/semif_api/web/) and is
served same-origin at /ui/ — no CORS.
GET /healthz
Model metadata, backend, backend_status, and model_state (pending =
selected GGUF not yet confirmed by a score, loaded).
GET /models / POST /models — llama backend only
List the llama-server's model aliases, or switch with {"model": "<alias>"}.
The alias is validated before the current model is unloaded; unloading is
best-effort (failure returns a warning, and the old model may stay resident
until evicted). The new GGUF loads lazily on the first scored request; a
restart restores the configured default. Unknown alias → 422, server failure
→ 502. Torch returns switching: false.
POST /decide — one decision
{
"id": "support-1",
"state": "The deployment completed at 14:02 UTC...",
"question": "Is there evidence that the deployment succeeded?",
"options": [
{"id": "yes", "description": "The deployment succeeded."},
{"id": "no", "description": "The deployment did not succeed."},
{"id": "insufficient", "description": "The evidence is insufficient to decide."}
]
}
→ scorer result: option_ids, probabilities (conditional option scores —
uncalibrated as decision confidence), option_logits, prompt_sha256,
model/revision metadata.
POST /decide-batch — one shared state, many decisions
{
"state": "...shared evidence...",
"decisions": [{"id": "...", "question": "...", "options": [...]}, ...]
}
→ {"results": [...], "timing": {...}}, each result tagged shared_timing.
Every decision in a batch shares the exact same state. On llama, rows run
sequentially (timing.mode: llama-sequential) with server prefix caching.
POST /plan — llama backend only
Rule generation from an action transcript; torch refuses with 422 (it only
reads logits). Game-agnostic: the caller composes prompt from facts it owns
(goal, what triggered planning, rules currently in effect) plus transcript
turns (system|user|assistant). One chat completion under a universal system
prompt with thinking enabled; sampling is pinned to the model card's
thinking-mode settings.
{
"id": "self-plan-1",
"prompt": "Objective: ...\nTrigger: ...\nPrevious rules (the actor failed despite them — amend or replace):\n...",
"transcript": [{"role": "user", "content": "Observation: …\nChosen action: run\nOutcome: ..."}]
}
→ {"id", "rules", "reasoning", "truncated", "usage", "model", "total_seconds"}. rules is injected into later decision states; reasoning
is the thinking trace when the server surfaces one. See
../docs/games.md for the demos built on this.
Notes
- Input validation reuses upstream
validate_row; violations → 422. Remote backend failures → 502. - All GPU work serializes on a lock: batch shape changes bf16 results (documented upstream), so requests are never implicitly batched together.