API reference¶
Auto-generated from docstrings via
mkdocstrings. Source of truth remains
the code in src/laya_serve/.
App¶
laya_serve.app
¶
FastAPI application factory.
Endpoints (Jev-compatible):
POST /v1/systemone— evaluate state + questions, return typed answers.GET /v1/models— list servable model names/aliases.GET /healthz— liveness probe (not part of the Jev API).
Errors are always Jev-shaped ({"error": {"message", "field"}}):
401 bad key, 422 validation, 529 transient overload (with
Retry-After), 500 unexpected backend failure.
AuthError
¶
Bases: Exception
Raised when bearer auth fails; handled as a Jev-shaped 401.
Source code in src/laya_serve/app.py
34 35 36 37 | |
Schemas¶
laya_serve.schemas
¶
Jev-compatible Pydantic v2 schemas for the SystemOne API.
Mirrors POST /v1/systemone from the TypeSafe docs:
- Request:
{state, model, questions}where each question is one of choice / score / noul. - Response:
{model, answers, usage}with typed answers keyed by the caller-chosen question ids.
Wire format notes:
- Score
probabilities/legendkeys are strings on the wire ({"0": ...}); the Python SDK exposes them as ints. This server speaks the HTTP wire format. instructionsand criteria descriptions acceptstr | dict | list(structured prompts); dict/list values are rendered by the backend.
SystemOneRequest
¶
Bases: BaseModel
POST /v1/systemone request body.
Source code in src/laya_serve/schemas.py
83 84 85 86 87 88 89 90 | |
SystemOneResponse
¶
Bases: BaseModel
POST /v1/systemone response body.
Source code in src/laya_serve/schemas.py
132 133 134 135 136 137 138 139 | |
Settings¶
laya_serve.settings
¶
Runtime configuration, sourced from environment variables.
All variables use the LAYA_SERVE_ prefix, e.g. LAYA_SERVE_PRELOAD=true.
Settings
¶
Bases: BaseSettings
Source code in src/laya_serve/settings.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | |
extra_models_set
property
¶
Parsed :attr:extra_models as a set of names.
Service¶
laya_serve.service
¶
Application service: orchestrates validation, inference, and compat shaping.
enforce_budgets(state, questions, backend)
¶
Fail fast with 422 when the request exceeds the backend's token budgets.
Compares backend.count(state, questions) against the backend's
max_total_tokens / max_state_question_tokens (Jev: 64k total,
32k state+longest-question). Backends that predate the budget
interface skip enforcement. A broken counter fails open (warn + skip)
so a counting bug can never turn a small request into a 500.
Source code in src/laya_serve/service.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
evaluate(request, backend, settings)
¶
Run one SystemOne request and return a Jev-shaped response dict.
Source code in src/laya_serve/service.py
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | |
list_models(settings, backend)
¶
Return the models listing: the serving checkpoint plus Jev aliases.
Source code in src/laya_serve/service.py
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
Compat¶
laya_serve.compat
¶
Translate raw Laya backend payloads into the Jev wire format.
Known divergences between Laya's Agent.system_one output and Jev
(verified against laya/agent.py and docs.typesafe.ai/api.md):
- Laya attaches
action: {act_probability}to every answer; Jev has no such field. Stripped here. - Laya returns
confidenceonnoulanswers; Jevnoulanswers carry only{type, noul}. Stripped here. - Laya reports
model: "laya-rl-agent"; Jev echoes the resolved versioned model id. The caller supplies the id to report. - Laya always reports
output_tokens: 0; Jev reports a (free but nonzero) count. Preserved as-is and documented — this layer does not invent token counts.
All functions here are pure and backend-agnostic: they operate on plain dicts, so they are unit-testable without model weights.
CompatError
¶
Bases: ValueError
Raised when a backend payload cannot be shaped into a Jev answer.
Source code in src/laya_serve/compat.py
48 49 50 51 52 53 | |
resolve_model(requested, serving_model, extra_models=frozenset())
¶
Validate the requested model and return the id to report.
Known Jev names (plus operator-configured extras) resolve to
serving_model — the checkpoint actually answering, e.g.
"laya-english". Unknown names raise :class:CompatError.
Source code in src/laya_serve/compat.py
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
shape_choice(question_id, raw, options)
¶
Shape a raw choice answer; drops Laya-only action.
Source code in src/laya_serve/compat.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
shape_noul(question_id, raw)
¶
Shape a raw noul answer; drops Laya-only confidence/action.
Source code in src/laya_serve/compat.py
83 84 85 86 87 88 89 | |
shape_response(request_questions, raw_answers, usage, serving_model)
¶
Shape a full backend result into a Jev response dict (validated by caller).
request_questions are the validated question dicts (with type
and criteria); they define the expected answer shapes. raw_answers
is the backend's per-question output. Unknown/extra backend keys
(action, routing) are ignored.
Source code in src/laya_serve/compat.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
shape_score(question_id, raw, levels)
¶
Shape a raw score answer with stringified level keys; drops action.
Source code in src/laya_serve/compat.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
Inference¶
laya_serve.inference
¶
Inference backend abstraction.
The HTTP layer depends only on the :class:Backend protocol, never on
torch/laya directly, so the server (and its tests) import and run
without model weights installed:
- :class:
FakeBackend— deterministic, weight-free answers. Uniform distributions for choice/score,0.5for noul. Used for tests and local API development. - :class:
LayaBackend— wrapslaya.Router. Import oflaya(and thereforetorch) happens lazily inside the constructor, so merely importing this module stays cheap.
Context budgets (Jev: 64k total, 32k state+longest-question) are enforced
pre-inference from :meth:Backend.count, so oversize requests fail fast
with 422 instead of being silently truncated downstream.
Backend
¶
Bases: Protocol
Minimal interface the API layer needs from any inference backend.
Source code in src/laya_serve/inference.py
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
count(state, questions)
¶
Return (total, state_plus_longest) token estimates for budgets.
Source code in src/laya_serve/inference.py
112 113 114 | |
predict(state, questions)
¶
Return {"answers": {...}, "usage": {...}} with raw backend answers.
Source code in src/laya_serve/inference.py
108 109 110 | |
FakeBackend
¶
Deterministic weight-free backend for tests and local development.
Source code in src/laya_serve/inference.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
LayaBackend
¶
Production backend wrapping laya.Router (lazy import).
Source code in src/laya_serve/inference.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
count(state, questions)
¶
Estimate request tokens with a resident checkpoint tokenizer if any.
Falls back to the weight-free word count when no checkpoint is
loaded yet (cold start) or tokenization fails. Counts are raw
lengths without build_sequence truncation, so genuinely
oversize states still trip the budgets instead of saturating.
Source code in src/laya_serve/inference.py
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | |
OverloadedError
¶
Bases: Exception
Transient backend overload; handled as a Jev-shaped 529.
Backends raise this when inference could succeed on retry after a
short delay (GPU OOM under burst load, evicted checkpoint reload,
upstream timeout). Clients retry with backoff, honoring
Retry-After.
Source code in src/laya_serve/inference.py
57 58 59 60 61 62 63 64 65 66 | |
build_backend(settings)
¶
Instantiate the backend selected by settings.backend.
Source code in src/laya_serve/inference.py
236 237 238 239 240 241 242 | |
count_request(state, questions)
¶
Weight-free (total, state+longest-question) word count of a request.
Source code in src/laya_serve/inference.py
49 50 51 52 53 54 | |
is_overload(exc)
¶
Return whether exc looks like transient overload (retryable).
Source code in src/laya_serve/inference.py
92 93 94 95 | |
render_question_text(qdef)
¶
Rendered size basis of one question: instructions + criteria descriptions.
Source code in src/laya_serve/inference.py
38 39 40 41 42 43 44 45 46 | |
render_text(value)
¶
Render one value as text, mirroring laya.common.render_criterion.
Source code in src/laya_serve/inference.py
31 32 33 34 35 | |
CLI¶
laya_serve.cli
¶
CLI entry point: laya-serve.
main()
¶
Serve the app with uvicorn (host/port via UVIcorn env or defaults).
Source code in src/laya_serve/cli.py
11 12 13 14 15 | |