HTTP API
HTTP API
laya-serve は Laya を TypeSafe Jev の /v1/systemone ワイヤプロトコルで公開します。Jev 向けに書かれたクライアント —— hs-jev、typesafe-sdk、自作のもの —— は、ベース URL をこのサーバーに向けるだけでそのまま動きます。Laya の predict() の出力はすでにスキーマ互換で、サーバーが足すのは HTTP の表面だけです。意思決定のルートが 1 つ、ヘルスプローブ、任意の bearer チェック、リクエスト制限です。
pip install "laya[serve]"
laya-serve # http://0.0.0.0:8000
同じエントリポイントは任意の ASGI サーバーに埋め込んで動きます。laya.serve.create_app() が FastAPI アプリを構築し、任意で環境から作ったものではなく注入した Router を使えます(create_app(router))。
設定
すべて環境変数なので、1 つのイメージがノート PC の開発実行と systemd ユニットの両方を担えます。
| 環境変数 | 意味 | 既定 |
|---|---|---|
LAYA_HOST |
バインドアドレス | 0.0.0.0 |
LAYA_PORT |
バインドポート | 8000 |
LAYA_ROOT_PATH |
リバースプロキシの背後で提供するときの公開 URL プレフィックス | 空 |
LAYA_DEVICE |
すべてのチェックポイントの torch デバイス | auto |
LAYA_PRELOAD |
起動時にチェックポイントを構築する(遅延ではなく) | 1 |
LAYA_MODELS |
プリロードするカンマ区切りリスト(english,multilingual,typed-decisions)。空 = すべて |
すべて |
LAYA_THREADS |
CPU での torch の intra-op スレッド数を制限します。物理コア数以下に保ってください —— 論理コアへの過剰割り当ては大きな劣化です | torch の既定 |
LAYA_AUTO_TASK |
typed-decisions チェックポイントへ自動ルーティング | 0 |
LAYA_API_KEY |
設定すると Authorization: Bearer <key> を要求します |
なし |
LAYA_LOG_LEVEL |
uvicorn のログレベル | info |
LAYA_MAX_CONCURRENT |
認証を通過して同時に受け入れるリクエスト数。超過分は 503 |
16 |
/laya のようなプレフィックスの下で公開するデプロイでは、LAYA_ROOT_PATH=/laya を設定します。FastAPI は OpenAPI と Swagger UI の URL を生成するときにこれを使います。Laya へリクエストを転送する前に /laya を剥がすようリバースプロキシを設定してください。アプリのルートは内部的には /health と /v1/systemone のままです。
CUDA や ARM64 のイメージを含むコンテナについては Docker クイックスタートを参照してください。
エンドポイント
GET /health
常に開いていて(認証なし)、推論中も応答し続けます。CPU 依存のフォワードパスがイベントループではなく専用の worker で走るからです。
{"status": "ok", "loaded": ["english", "multilingual"], "revisions": {"english": "..."}, "device": "auto"}
loaded はメモリに常駐するチェックポイントを列挙し、revisions は各々が読み込まれた成果物リビジョンを列挙するので、デプロイは実際に何を提供しているかを確認できます。
POST /v1/systemone
1 つのリクエストが state と、それに対する任意個の質問を運びます。
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{
"state": "I was charged twice this month, I want my money back",
"questions": {
"queue": {"type": "choice", "instructions": "Which team?",
"criteria": {"billing": "billing and refunds", "tech": "login and app issues",
"other": "everything else"}},
"urgency": {"type": "score", "instructions": "How urgent?",
"criteria": ["calm", "firm", "angry", "furious"]}
}
}'
| フィールド | 必須 | 意味 |
|---|---|---|
state |
はい | 判断対象のテキスト、メール、チケット、JSON 文書。欠落または null の state は 400 |
questions |
はい | 質問 id をキーにしたオブジェクト。各質問は instructions と criteria を持つ choice / score / noul |
model |
いいえ | チェックポイントを名指しします。それ以外の値は無視されます(下記参照) |
task |
いいえ | ルーティングに任せずワークフロー名でチェックポイントを強制します。未知の名前はそれを名指しする 422 |
lang |
いいえ | 言語を名指しするときに検出をスキップする言語コード(de、en-US)。空または認識できないコードは検出に流れます |
lang_guess |
いいえ | クライアント自身の識別子による言語コード。lang の後、検出の前に参照されます。英語以外のコードは多言語チェックポイントへルーティングされます |
max_len |
いいえ | このリクエストのトークン窓の合計。LAYA_MAX_TOKEN_BUDGET で上限されます |
head_max_len |
いいえ | 選択肢プロンプトが共有するトークン窓。同じ上限。ある質問がそれを必要とするのはいつかは トークン予算の拡張を参照 |
min_confidence |
いいえ | [0.0, 1.0] の棄権しきい値。answer_confidence がそれを下回る答えは low_confidence の印を付けて返り、答え自体は保持されます |
model、task、lang、lang_guess、max_len、head_max_len、min_confidence は、JSON ボディが述べられる Router.predict の引数です。それぞれはリクエストが送ったときにだけ転送されるので、欠けていればそのデプロイ自身の Router(...) 設定が支配します。predict が取る 5 つのフック引数 —— hooks、on_predict_start、on_predict_end、hooks_raise、hooks_timeout —— は黙って捨てられるのではなく 422 で拒否されます。フックはサーバープロセス内で走る callable であり、最後の 2 つはデプロイがインストールしたフックがどう実行されるかを述べるもので、呼び出し元が送る値にここでの意味はありません。同じ 5 つは base_url を持つ LangChain ノード(laya.integrations.langchain)でもクライアント側で拒否されるので、チェーンと生の HTTP クライアントが同じ答えを得ます。
model を受け付けるのは、Jev クライアントがそれを送り続けられるようにするためです。公開 Hugging Face の id(convaiinnovations/laya-multilingual、convaiinnovations/laya-typed-decisions)、チェックポイント名(english、multilingual、typed-decisions)、およびそれらのエイリアスがチェックポイントを選びます。それ以外の値 —— jev-1 のような Jev の id を含む —— は「router に選ばせる」という意味で、レスポンスの routing ブロックが何がなぜ選ばれたかを記録します。
レスポンス
{
"model": "laya-rl-agent",
"answers": {
"queue": {"type": "choice", "choice": "billing",
"probabilities": {"billing": 0.9281, "tech": 0.0412, "other": 0.0307},
"confidence": 0.4534, "answer_confidence": 0.9281,
"action": {"act_probability": 1.0}},
"urgency": {"type": "score", "score": 2.6389,
"legend": {"0": "calm", "1": "firm", "2": "angry", "3": "furious"},
"probabilities": {"0": 0.0099, "1": 0.0713, "2": 0.536, "3": 0.3828},
"confidence": 0.3542, "answer_confidence": 0.536,
"action": {"act_probability": 1.0}}
},
"usage": {"input_tokens": 74, "output_tokens": 0},
"routing": {"model": "english", "repo": "convaiinnovations/laya", "reason": "English Latin text",
"detection": {"script": "latin", "language": "en", "is_english": true, "non_latin_fraction": 0.0}}
}
answers と usage は Jev クライアントがデコードするキーです。model は意思決定ヘッドの定数名で、答えたチェックポイントは routing(model、repo、reason、およびその背後の detection または lang_guess の証拠)にあります。
| 答えの型 | キー |
|---|---|
choice |
choice(argmax の選択肢)、選択肢ごとの probabilities |
score |
score(期待水準インデックス。水準の間に落ちることがあります)、"0".. "k-1" をキーにした probabilities、インデックスを水準テキストへ対応づける legend |
noul |
noul、yes の選択肢の確率 |
| すべて | confidence、answer_confidence、action.act_probability |
信頼度:2 つの数値は交換できません
answer_confidenceは報告された答えに載る確率の塊(max(p))です。temperature スケーリングが当てはめる量であり、このリポジトリの ECE の数値が計算される量でもあるので、ベンチマークと既知の制約ページが依拠するゲートの性質を担います —— ただし自分のトラフィックで temperature の当てはめが検証済みのチェックポイントに限ります。confidenceは型ごとに意味が異なります。choiceとscoreでは正規化エントロピー1 - H(p)/log(k)、noulではmax(p_yes, p_no)です(ここではanswer_confidenceと等しくなります)。
2 つを 1 つのしきい値で比較しないでください。Jev から移植するときの違いにも注意してください。TypeSafe は信頼度を (n*p_max - 1)/(n - 1) と定義するので、Jev のデプロイから持ち込んだしきい値は Laya のエントロピーの値に対しては別のゲートの仕方をします。
成功したレスポンスは Server-Timing: inference;dur=<ms> と X-Inference-Time-Ms も運びます。
制限
リクエストのガードレールはトークン化の前に検査されるので、大きすぎるリクエストはサーバーに読んだバイト以外のコストをかけません。それらはすべて 413 で、detail がどの制限に当たったかを述べます。
| 制限 | 値 |
|---|---|
| リクエストボディ | 2 MiB。ストリーミング中に強制 —— チャンク化や過少申告の Content-Length では回避できません |
state |
モデルに与えるテキストの 50,000 文字 —— 文字列の state ならその文字列そのもの、オブジェクトや配列なら json.dumps(state, ensure_ascii=False) |
| リクエストあたりの質問数 | 64 |
choice の質問あたりの選択肢数 |
100 |
score の質問あたりの水準数 |
32 |
| すべての質問を通じた選択肢数 | 512 |
| 同時に受け入れるリクエスト数 | LAYA_MAX_CONCURRENT(16) |
選択肢の上限は HTTP 専用の増幅ガードです。モデル自身は選択肢のトークンを head_max_len=192 の窓に収めるので、HTTP の上限内の質問でも、選択肢のテキストが合計でその予算を超えると 422 で拒否されえます。評価ハーネスは同じリクエストを HTTP 層なしでインプロセスで実行します。
エラー
| ステータス | いつ | ボディの detail |
|---|---|---|
400 |
ボディが有効な JSON でない、オブジェクトでない、questions がない、state が欠落または null、または questions がオブジェクトでない |
何がおかしいか |
401 |
LAYA_API_KEY が設定されていて bearer トークンが欠落または誤り |
invalid or missing bearer token |
413 |
上記のいずれかの制限 | どの制限がどれだけ超過したか |
422 |
質問が整形式の JSON だが Laya にとって不正(未知の型、head 予算を超える選択肢)、またはリクエスト制御(lang、min_confidence、フック引数)がこのエンドポイントの受け付ける形でない |
質問かフィールドを名指しし、何を直すか |
500 |
その他の理由で推論が失敗 | inference failed —— 常にこの文字列なので、パス・重み・メモリの状態が決して漏れません。原因はサーバーログにあります |
503 |
LAYA_MAX_CONCURRENT 個のリクエストがすでに処理中 |
server busy, try again later |
上限超過の負荷はキューされずに拒否されます。遅いボディをストリーミングしながら受け入れスロットを保持するクライアントが /health を飢えさせられず、拒否されたクライアントが残したスロットを再試行が取れます。
並行モデル
推論は CPU で数百ミリ秒から秒単位かかる同期の torch 呼び出しなので、イベントループでは決して走りません。リクエストは単一 worker の executor に渡され、一度に 1 つのフォワードパスになります —— 1 つのデバイス上の 1 つのチェックポイントが望む形です。受け入れ(LAYA_MAX_CONCURRENT セマフォ)はボディのどのバイトも読む前に検査され、推論を通じて保持されます。推論ゲートはボディが完了した後にだけ取得されるので、遅いクライアントは受け入れスロットは保持しても推論スロットは決して保持しません。
まだここにないもの
このサーバーは意図的に 1 つのプロトコルだけを話します。OpenAI 互換のエンドポイントもバッチエンドポイントもありません。代わりに 1 つのリクエストで複数の質問を実行してください。質問セットごとに 1 回のフォワードパスを共有するからです。laya CLI と MCP サーバーがローカルの用途を担います —— READMEを参照してください。