Expand description
improv-server: a small JSON HTTP API over an Improv model store.
The (blocking SQLite-backed) Mentat store is opened inside spawn_blocking
and never shared across tasks. The loaded model is cached in AppState
and replaced on every write this process makes (API or scheduler), so reads
cost one store load per write, not per request. The server assumes it owns
the store: a write from another process is not seen until restart.
ModulesΒ§
- scheduler π
- Hosted refresh scheduler: a background tokio task mirroring the CLIβs
standalone
improv serve-refreshdaemon (crates/cli/src/main.rs,cmd_serve_refresh), but running in-process alongside the API instead of as a separate process.
StructsΒ§
- AppState
- Cell π
- Eval
Edit π - One edit in an
/evalrequest:measureid,coordas[[cat_id, item_id], ...](numeric ids, matchingCoordKey), andvalue(nullclears the cell). - EvalReq π
- Measure
Summary π - NlDescribe
Req π - NlParse
Req π - Read
Only π - Request extension set by
require_tokenfor a read-only token. - SetCell
Req π
EnumsΒ§
ConstantsΒ§
- HEADER_
READ_ πTIMEOUT - Default deadline for a client to finish sending request headers (S10).
- MAX_
BODY_ πBYTES - Request body cap (S13). Over-limit bodies get 413.
- MAX_
EDITS π - Edits accepted per
/evalrequest (S13). More gets 400. - MAX_
IN_ πFLIGHT - Requests in flight across all routes (S10); the rest wait (then time out).
- REQUEST_
TIMEOUT π - Whole-request deadline, including time queued for a concurrency slot (S10).
StaticsΒ§
- NEXT_
ERROR_ πID - Id logged with each internal error and echoed to the client as
request_id.
FunctionsΒ§
- app
- Build the router. Exposed so tests can drive it via
oneshot. - category_
name π - ct_eq π
- Byte-wise xor accumulate; no early exit on the first differing byte.
- eval_
measure π - Incremental / what-if evaluation. Builds a short-lived live
Engineover all derived measures, applies the batch of edits as deltas, and returns the recomputed snapshot for measure:idas[{coord: [[cat_name,item_name]..], value}]. - get_
model π - health π
- list_
measures π - load_
model π - The current model: the cached copy, or (on a miss) one load from the store
on a blocking thread. The cache write lock is held across the load, so a
concurrent writerβs
save_modelreplaces it only after a stale load lands. - main π
- measure_
values π - nl_
describe π - nl_
parse π - readable_
cell π - Render a coordinate as
[[category_name, item_name], ...], sorted by category id for stability. - readable_
key π - require_
token π - Bearer-token gate. In open mode every request passes. Otherwise:
missing/blank
Authorization-> 401; present but not an accepted token -> 403; a read-only token passes with aReadOnlyextension the write paths refuse. The token value is never logged. - save_
model π - Save the model on a blocking thread and make it the cached copy. Call with
write_lockheld. - scheduler_
status_ πhandler - Scheduler observability: enabled/tick/last-refreshed. Protected (not a
liveness probe like
/health) since it can reveal which measures exist. - serve π
- Accept loop: hyper HTTP/1 with a header-read timeout (S10).
axum::serve(0.7) sets no header timer, so a client that never finishes its request line holds a socket forever (slow-loris). HTTP/1 only: thehyper-utilautobuilder would pull in h2, which the API does not need. - set_
cell π - startup_
check - Refuse open mode (no tokens) on a non-loopback address unless the
operator opts in with
IMPROV_ALLOW_OPEN=1(S15). - token_
matches π - Constant-time token check (S15): every accepted token is compared, and each comparison folds over all bytes, so timing reveals neither which token matched nor the length of a matching prefix.
- validate_
write π - S12: a write must target an existing input measure, at a coordinate naming
exactly the measureβs categories, each with an item of that category, and
carry a finite value (
Noneclears the cell).